1 //===- COFFMasmParser.cpp - COFF MASM Assembly Parser ---------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "llvm/ADT/StringRef.h" 10 #include "llvm/ADT/Twine.h" 11 #include "llvm/BinaryFormat/COFF.h" 12 #include "llvm/MC/MCContext.h" 13 #include "llvm/MC/MCParser/MCAsmLexer.h" 14 #include "llvm/MC/MCParser/MCAsmParserExtension.h" 15 #include "llvm/MC/MCSectionCOFF.h" 16 #include "llvm/MC/MCStreamer.h" 17 #include "llvm/MC/MCSymbolCOFF.h" 18 #include "llvm/MC/SectionKind.h" 19 #include "llvm/Support/Casting.h" 20 #include "llvm/Support/SMLoc.h" 21 #include <cstdint> 22 #include <utility> 23 24 using namespace llvm; 25 26 namespace { 27 28 class COFFMasmParser : public MCAsmParserExtension { 29 template <bool (COFFMasmParser::*HandlerMethod)(StringRef, SMLoc)> 30 void addDirectiveHandler(StringRef Directive) { 31 MCAsmParser::ExtensionDirectiveHandler Handler = 32 std::make_pair(this, HandleDirective<COFFMasmParser, HandlerMethod>); 33 getParser().addDirectiveHandler(Directive, Handler); 34 } 35 36 bool ParseSectionSwitch(StringRef Section, unsigned Characteristics, 37 SectionKind Kind); 38 39 bool ParseSectionSwitch(StringRef Section, unsigned Characteristics, 40 SectionKind Kind, StringRef COMDATSymName, 41 COFF::COMDATType Type); 42 43 bool ParseDirectiveProc(StringRef, SMLoc); 44 bool ParseDirectiveEndProc(StringRef, SMLoc); 45 bool ParseDirectiveSegment(StringRef, SMLoc); 46 bool ParseDirectiveSegmentEnd(StringRef, SMLoc); 47 bool ParseDirectiveIncludelib(StringRef, SMLoc); 48 49 bool ParseDirectiveAlias(StringRef, SMLoc); 50 51 bool ParseSEHDirectiveAllocStack(StringRef, SMLoc); 52 bool ParseSEHDirectiveEndProlog(StringRef, SMLoc); 53 54 bool IgnoreDirective(StringRef, SMLoc) { 55 while (!getLexer().is(AsmToken::EndOfStatement)) { 56 Lex(); 57 } 58 return false; 59 } 60 61 void Initialize(MCAsmParser &Parser) override { 62 // Call the base implementation. 63 MCAsmParserExtension::Initialize(Parser); 64 65 // x64 directives 66 addDirectiveHandler<&COFFMasmParser::ParseSEHDirectiveAllocStack>( 67 ".allocstack"); 68 addDirectiveHandler<&COFFMasmParser::ParseSEHDirectiveEndProlog>( 69 ".endprolog"); 70 71 // Code label directives 72 // label 73 // org 74 75 // Conditional control flow directives 76 // .break 77 // .continue 78 // .else 79 // .elseif 80 // .endif 81 // .endw 82 // .if 83 // .repeat 84 // .until 85 // .untilcxz 86 // .while 87 88 // Data allocation directives 89 // align 90 // even 91 // mmword 92 // tbyte 93 // xmmword 94 // ymmword 95 96 // Listing control directives 97 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".cref"); 98 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".list"); 99 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listall"); 100 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listif"); 101 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacro"); 102 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacroall"); 103 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nocref"); 104 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolist"); 105 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistif"); 106 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistmacro"); 107 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("page"); 108 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("subtitle"); 109 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".tfcond"); 110 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("title"); 111 112 // Macro directives 113 // goto 114 115 // Miscellaneous directives 116 addDirectiveHandler<&COFFMasmParser::ParseDirectiveAlias>("alias"); 117 // assume 118 // .fpo 119 addDirectiveHandler<&COFFMasmParser::ParseDirectiveIncludelib>( 120 "includelib"); 121 // option 122 // popcontext 123 // pushcontext 124 // .safeseh 125 126 // Procedure directives 127 addDirectiveHandler<&COFFMasmParser::ParseDirectiveEndProc>("endp"); 128 // invoke (32-bit only) 129 addDirectiveHandler<&COFFMasmParser::ParseDirectiveProc>("proc"); 130 // proto 131 132 // Processor directives; all ignored 133 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386"); 134 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386p"); 135 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".387"); 136 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486"); 137 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486p"); 138 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586"); 139 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586p"); 140 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686"); 141 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686p"); 142 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".k3d"); 143 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".mmx"); 144 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".xmm"); 145 146 // Scope directives 147 // comm 148 // externdef 149 150 // Segment directives 151 // .alpha (32-bit only, order segments alphabetically) 152 // .dosseg (32-bit only, order segments in DOS convention) 153 // .seq (32-bit only, order segments sequentially) 154 addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegmentEnd>("ends"); 155 // group (32-bit only) 156 addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegment>("segment"); 157 158 // Simplified segment directives 159 addDirectiveHandler<&COFFMasmParser::ParseSectionDirectiveCode>(".code"); 160 // .const 161 addDirectiveHandler< 162 &COFFMasmParser::ParseSectionDirectiveInitializedData>(".data"); 163 addDirectiveHandler< 164 &COFFMasmParser::ParseSectionDirectiveUninitializedData>(".data?"); 165 // .exit 166 // .fardata 167 // .fardata? 168 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".model"); 169 // .stack 170 // .startup 171 172 // String directives, written <name> <directive> <params> 173 // catstr (equivalent to <name> TEXTEQU <params>) 174 // instr (equivalent to <name> = @InStr(<params>)) 175 // sizestr (equivalent to <name> = @SizeStr(<params>)) 176 // substr (equivalent to <name> TEXTEQU @SubStr(<params>)) 177 178 // Structure and record directives 179 // record 180 // typedef 181 } 182 183 bool ParseSectionDirectiveCode(StringRef, SMLoc) { 184 return ParseSectionSwitch(".text", 185 COFF::IMAGE_SCN_CNT_CODE 186 | COFF::IMAGE_SCN_MEM_EXECUTE 187 | COFF::IMAGE_SCN_MEM_READ, 188 SectionKind::getText()); 189 } 190 191 bool ParseSectionDirectiveInitializedData(StringRef, SMLoc) { 192 return ParseSectionSwitch(".data", 193 COFF::IMAGE_SCN_CNT_INITIALIZED_DATA 194 | COFF::IMAGE_SCN_MEM_READ 195 | COFF::IMAGE_SCN_MEM_WRITE, 196 SectionKind::getData()); 197 } 198 199 bool ParseSectionDirectiveUninitializedData(StringRef, SMLoc) { 200 return ParseSectionSwitch(".bss", 201 COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA 202 | COFF::IMAGE_SCN_MEM_READ 203 | COFF::IMAGE_SCN_MEM_WRITE, 204 SectionKind::getBSS()); 205 } 206 207 StringRef CurrentProcedure; 208 bool CurrentProcedureFramed; 209 210 public: 211 COFFMasmParser() = default; 212 }; 213 214 } // end anonymous namespace. 215 216 static SectionKind computeSectionKind(unsigned Flags) { 217 if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE) 218 return SectionKind::getText(); 219 if (Flags & COFF::IMAGE_SCN_MEM_READ && 220 (Flags & COFF::IMAGE_SCN_MEM_WRITE) == 0) 221 return SectionKind::getReadOnly(); 222 return SectionKind::getData(); 223 } 224 225 bool COFFMasmParser::ParseSectionSwitch(StringRef Section, 226 unsigned Characteristics, 227 SectionKind Kind) { 228 return ParseSectionSwitch(Section, Characteristics, Kind, "", 229 (COFF::COMDATType)0); 230 } 231 232 bool COFFMasmParser::ParseSectionSwitch(StringRef Section, 233 unsigned Characteristics, 234 SectionKind Kind, 235 StringRef COMDATSymName, 236 COFF::COMDATType Type) { 237 if (getLexer().isNot(AsmToken::EndOfStatement)) 238 return TokError("unexpected token in section switching directive"); 239 Lex(); 240 241 getStreamer().switchSection(getContext().getCOFFSection( 242 Section, Characteristics, Kind, COMDATSymName, Type)); 243 244 return false; 245 } 246 247 bool COFFMasmParser::ParseDirectiveSegment(StringRef Directive, SMLoc Loc) { 248 StringRef SegmentName; 249 if (!getLexer().is(AsmToken::Identifier)) 250 return TokError("expected identifier in directive"); 251 SegmentName = getTok().getIdentifier(); 252 Lex(); 253 254 StringRef SectionName = SegmentName; 255 SmallVector<char, 247> SectionNameVector; 256 unsigned Flags = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | 257 COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE; 258 if (SegmentName == "_TEXT" || SegmentName.startswith("_TEXT$")) { 259 if (SegmentName.size() == 5) { 260 SectionName = ".text"; 261 } else { 262 SectionName = 263 (".text$" + SegmentName.substr(6)).toStringRef(SectionNameVector); 264 } 265 Flags = COFF::IMAGE_SCN_CNT_CODE | COFF::IMAGE_SCN_MEM_EXECUTE | 266 COFF::IMAGE_SCN_MEM_READ; 267 } 268 SectionKind Kind = computeSectionKind(Flags); 269 getStreamer().switchSection(getContext().getCOFFSection( 270 SectionName, Flags, Kind, "", (COFF::COMDATType)(0))); 271 return false; 272 } 273 274 /// ParseDirectiveSegmentEnd 275 /// ::= identifier "ends" 276 bool COFFMasmParser::ParseDirectiveSegmentEnd(StringRef Directive, SMLoc Loc) { 277 StringRef SegmentName; 278 if (!getLexer().is(AsmToken::Identifier)) 279 return TokError("expected identifier in directive"); 280 SegmentName = getTok().getIdentifier(); 281 282 // Ignore; no action necessary. 283 Lex(); 284 return false; 285 } 286 287 /// ParseDirectiveIncludelib 288 /// ::= "includelib" identifier 289 bool COFFMasmParser::ParseDirectiveIncludelib(StringRef Directive, SMLoc Loc) { 290 StringRef Lib; 291 if (getParser().parseIdentifier(Lib)) 292 return TokError("expected identifier in includelib directive"); 293 294 unsigned Flags = COFF::IMAGE_SCN_MEM_PRELOAD | COFF::IMAGE_SCN_MEM_16BIT; 295 SectionKind Kind = computeSectionKind(Flags); 296 getStreamer().pushSection(); 297 getStreamer().switchSection(getContext().getCOFFSection( 298 ".drectve", Flags, Kind, "", (COFF::COMDATType)(0))); 299 getStreamer().emitBytes("/DEFAULTLIB:"); 300 getStreamer().emitBytes(Lib); 301 getStreamer().emitBytes(" "); 302 getStreamer().popSection(); 303 return false; 304 } 305 306 /// ParseDirectiveProc 307 /// TODO(epastor): Implement parameters and other attributes. 308 /// ::= label "proc" [[distance]] 309 /// statements 310 /// label "endproc" 311 bool COFFMasmParser::ParseDirectiveProc(StringRef Directive, SMLoc Loc) { 312 StringRef Label; 313 if (getParser().parseIdentifier(Label)) 314 return Error(Loc, "expected identifier for procedure"); 315 if (getLexer().is(AsmToken::Identifier)) { 316 StringRef nextVal = getTok().getString(); 317 SMLoc nextLoc = getTok().getLoc(); 318 if (nextVal.equals_insensitive("far")) { 319 // TODO(epastor): Handle far procedure definitions. 320 Lex(); 321 return Error(nextLoc, "far procedure definitions not yet supported"); 322 } else if (nextVal.equals_insensitive("near")) { 323 Lex(); 324 nextVal = getTok().getString(); 325 nextLoc = getTok().getLoc(); 326 } 327 } 328 MCSymbolCOFF *Sym = cast<MCSymbolCOFF>(getContext().getOrCreateSymbol(Label)); 329 330 // Define symbol as simple external function 331 Sym->setExternal(true); 332 Sym->setType(COFF::IMAGE_SYM_DTYPE_FUNCTION << COFF::SCT_COMPLEX_TYPE_SHIFT); 333 334 bool Framed = false; 335 if (getLexer().is(AsmToken::Identifier) && 336 getTok().getString().equals_insensitive("frame")) { 337 Lex(); 338 Framed = true; 339 getStreamer().emitWinCFIStartProc(Sym, Loc); 340 } 341 getStreamer().emitLabel(Sym, Loc); 342 343 CurrentProcedure = Label; 344 CurrentProcedureFramed = Framed; 345 return false; 346 } 347 bool COFFMasmParser::ParseDirectiveEndProc(StringRef Directive, SMLoc Loc) { 348 StringRef Label; 349 SMLoc LabelLoc = getTok().getLoc(); 350 if (getParser().parseIdentifier(Label)) 351 return Error(LabelLoc, "expected identifier for procedure end"); 352 353 if (CurrentProcedure.empty()) 354 return Error(Loc, "endp outside of procedure block"); 355 else if (CurrentProcedure != Label) 356 return Error(LabelLoc, "endp does not match current procedure '" + 357 CurrentProcedure + "'"); 358 359 if (CurrentProcedureFramed) { 360 getStreamer().emitWinCFIEndProc(Loc); 361 } 362 CurrentProcedure = ""; 363 CurrentProcedureFramed = false; 364 return false; 365 } 366 367 bool COFFMasmParser::ParseDirectiveAlias(StringRef Directive, SMLoc Loc) { 368 std::string AliasName, ActualName; 369 if (getTok().isNot(AsmToken::Less) || 370 getParser().parseAngleBracketString(AliasName)) 371 return Error(getTok().getLoc(), "expected <aliasName>"); 372 if (getParser().parseToken(AsmToken::Equal)) 373 return addErrorSuffix(" in " + Directive + " directive"); 374 if (getTok().isNot(AsmToken::Less) || 375 getParser().parseAngleBracketString(ActualName)) 376 return Error(getTok().getLoc(), "expected <actualName>"); 377 378 MCSymbol *Alias = getContext().getOrCreateSymbol(AliasName); 379 MCSymbol *Actual = getContext().getOrCreateSymbol(ActualName); 380 381 getStreamer().emitWeakReference(Alias, Actual); 382 383 return false; 384 } 385 386 bool COFFMasmParser::ParseSEHDirectiveAllocStack(StringRef Directive, 387 SMLoc Loc) { 388 int64_t Size; 389 SMLoc SizeLoc = getTok().getLoc(); 390 if (getParser().parseAbsoluteExpression(Size)) 391 return Error(SizeLoc, "expected integer size"); 392 if (Size % 8 != 0) 393 return Error(SizeLoc, "stack size must be a multiple of 8"); 394 getStreamer().emitWinCFIAllocStack(static_cast<unsigned>(Size), Loc); 395 return false; 396 } 397 398 bool COFFMasmParser::ParseSEHDirectiveEndProlog(StringRef Directive, 399 SMLoc Loc) { 400 getStreamer().emitWinCFIEndProlog(Loc); 401 return false; 402 } 403 404 namespace llvm { 405 406 MCAsmParserExtension *createCOFFMasmParser() { return new COFFMasmParser; } 407 408 } // end namespace llvm 409