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/MCAsmMacro.h" 13 #include "llvm/MC/MCContext.h" 14 #include "llvm/MC/MCParser/MCAsmLexer.h" 15 #include "llvm/MC/MCParser/MCAsmParserExtension.h" 16 #include "llvm/MC/MCParser/MCTargetAsmParser.h" 17 #include "llvm/MC/MCSectionCOFF.h" 18 #include "llvm/MC/MCStreamer.h" 19 #include "llvm/MC/MCSymbolCOFF.h" 20 #include "llvm/MC/SectionKind.h" 21 #include "llvm/Support/Casting.h" 22 #include "llvm/Support/SMLoc.h" 23 #include <cstdint> 24 #include <utility> 25 26 using namespace llvm; 27 28 namespace { 29 30 class COFFMasmParser : public MCAsmParserExtension { 31 template <bool (COFFMasmParser::*HandlerMethod)(StringRef, SMLoc)> 32 void addDirectiveHandler(StringRef Directive) { 33 MCAsmParser::ExtensionDirectiveHandler Handler = 34 std::make_pair(this, HandleDirective<COFFMasmParser, HandlerMethod>); 35 getParser().addDirectiveHandler(Directive, Handler); 36 } 37 38 bool ParseSectionSwitch(StringRef SectionName, unsigned Characteristics); 39 40 bool ParseSectionSwitch(StringRef SectionName, unsigned Characteristics, 41 StringRef COMDATSymName, COFF::COMDATType Type, 42 Align Alignment); 43 44 bool ParseDirectiveProc(StringRef, SMLoc); 45 bool ParseDirectiveEndProc(StringRef, SMLoc); 46 bool ParseDirectiveSegment(StringRef, SMLoc); 47 bool ParseDirectiveSegmentEnd(StringRef, SMLoc); 48 bool ParseDirectiveIncludelib(StringRef, SMLoc); 49 bool ParseDirectiveOption(StringRef, SMLoc); 50 51 bool ParseDirectiveAlias(StringRef, SMLoc); 52 53 bool ParseSEHDirectiveAllocStack(StringRef, SMLoc); 54 bool ParseSEHDirectiveEndProlog(StringRef, SMLoc); 55 56 bool IgnoreDirective(StringRef, SMLoc) { 57 while (!getLexer().is(AsmToken::EndOfStatement)) { 58 Lex(); 59 } 60 return false; 61 } 62 63 void Initialize(MCAsmParser &Parser) override { 64 // Call the base implementation. 65 MCAsmParserExtension::Initialize(Parser); 66 67 // x64 directives 68 addDirectiveHandler<&COFFMasmParser::ParseSEHDirectiveAllocStack>( 69 ".allocstack"); 70 addDirectiveHandler<&COFFMasmParser::ParseSEHDirectiveEndProlog>( 71 ".endprolog"); 72 73 // Code label directives 74 // label 75 // org 76 77 // Conditional control flow directives 78 // .break 79 // .continue 80 // .else 81 // .elseif 82 // .endif 83 // .endw 84 // .if 85 // .repeat 86 // .until 87 // .untilcxz 88 // .while 89 90 // Data allocation directives 91 // align 92 // even 93 // mmword 94 // tbyte 95 // xmmword 96 // ymmword 97 98 // Listing control directives 99 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".cref"); 100 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".list"); 101 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listall"); 102 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listif"); 103 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacro"); 104 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacroall"); 105 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nocref"); 106 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolist"); 107 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistif"); 108 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistmacro"); 109 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("page"); 110 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("subtitle"); 111 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".tfcond"); 112 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("title"); 113 114 // Macro directives 115 // goto 116 117 // Miscellaneous directives 118 addDirectiveHandler<&COFFMasmParser::ParseDirectiveAlias>("alias"); 119 // assume 120 // .fpo 121 addDirectiveHandler<&COFFMasmParser::ParseDirectiveIncludelib>( 122 "includelib"); 123 addDirectiveHandler<&COFFMasmParser::ParseDirectiveOption>("option"); 124 // popcontext 125 // pushcontext 126 // .safeseh 127 128 // Procedure directives 129 addDirectiveHandler<&COFFMasmParser::ParseDirectiveEndProc>("endp"); 130 // invoke (32-bit only) 131 addDirectiveHandler<&COFFMasmParser::ParseDirectiveProc>("proc"); 132 // proto 133 134 // Processor directives; all ignored 135 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386"); 136 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386p"); 137 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".387"); 138 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486"); 139 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486p"); 140 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586"); 141 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586p"); 142 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686"); 143 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686p"); 144 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".k3d"); 145 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".mmx"); 146 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".xmm"); 147 148 // Scope directives 149 // comm 150 // externdef 151 152 // Segment directives 153 // .alpha (32-bit only, order segments alphabetically) 154 // .dosseg (32-bit only, order segments in DOS convention) 155 // .seq (32-bit only, order segments sequentially) 156 addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegmentEnd>("ends"); 157 // group (32-bit only) 158 addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegment>("segment"); 159 160 // Simplified segment directives 161 addDirectiveHandler<&COFFMasmParser::ParseSectionDirectiveCode>(".code"); 162 // .const 163 addDirectiveHandler< 164 &COFFMasmParser::ParseSectionDirectiveInitializedData>(".data"); 165 addDirectiveHandler< 166 &COFFMasmParser::ParseSectionDirectiveUninitializedData>(".data?"); 167 // .exit 168 // .fardata 169 // .fardata? 170 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".model"); 171 // .stack 172 // .startup 173 174 // String directives, written <name> <directive> <params> 175 // catstr (equivalent to <name> TEXTEQU <params>) 176 // instr (equivalent to <name> = @InStr(<params>)) 177 // sizestr (equivalent to <name> = @SizeStr(<params>)) 178 // substr (equivalent to <name> TEXTEQU @SubStr(<params>)) 179 180 // Structure and record directives 181 // record 182 // typedef 183 } 184 185 bool ParseSectionDirectiveCode(StringRef, SMLoc) { 186 return ParseSectionSwitch(".text", COFF::IMAGE_SCN_CNT_CODE | 187 COFF::IMAGE_SCN_MEM_EXECUTE | 188 COFF::IMAGE_SCN_MEM_READ); 189 } 190 191 bool ParseSectionDirectiveInitializedData(StringRef, SMLoc) { 192 return ParseSectionSwitch(".data", COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | 193 COFF::IMAGE_SCN_MEM_READ | 194 COFF::IMAGE_SCN_MEM_WRITE); 195 } 196 197 bool ParseSectionDirectiveUninitializedData(StringRef, SMLoc) { 198 return ParseSectionSwitch(".bss", COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA | 199 COFF::IMAGE_SCN_MEM_READ | 200 COFF::IMAGE_SCN_MEM_WRITE); 201 } 202 203 /// Stack of active procedure definitions. 204 SmallVector<StringRef, 1> CurrentProcedures; 205 SmallVector<bool, 1> CurrentProceduresFramed; 206 207 public: 208 COFFMasmParser() = default; 209 }; 210 211 } // end anonymous namespace. 212 213 bool COFFMasmParser::ParseSectionSwitch(StringRef SectionName, 214 unsigned Characteristics) { 215 return ParseSectionSwitch(SectionName, Characteristics, "", 216 (COFF::COMDATType)0, Align(16)); 217 } 218 219 bool COFFMasmParser::ParseSectionSwitch(StringRef SectionName, 220 unsigned Characteristics, 221 StringRef COMDATSymName, 222 COFF::COMDATType Type, 223 Align Alignment) { 224 if (getLexer().isNot(AsmToken::EndOfStatement)) 225 return TokError("unexpected token in section switching directive"); 226 Lex(); 227 228 MCSection *Section = getContext().getCOFFSection(SectionName, Characteristics, 229 COMDATSymName, Type); 230 Section->setAlignment(Alignment); 231 getStreamer().switchSection(Section); 232 233 return false; 234 } 235 236 bool COFFMasmParser::ParseDirectiveSegment(StringRef Directive, SMLoc Loc) { 237 StringRef SegmentName; 238 if (!getLexer().is(AsmToken::Identifier)) 239 return TokError("expected identifier in directive"); 240 SegmentName = getTok().getIdentifier(); 241 Lex(); 242 243 StringRef SectionName = SegmentName; 244 SmallVector<char, 247> SectionNameVector; 245 246 StringRef Class; 247 if (SegmentName == "_TEXT" || SegmentName.starts_with("_TEXT$")) { 248 if (SegmentName.size() == 5) { 249 SectionName = ".text"; 250 } else { 251 SectionName = 252 (".text$" + SegmentName.substr(6)).toStringRef(SectionNameVector); 253 } 254 Class = "CODE"; 255 } 256 257 // Parse all options to end of statement. 258 // Alignment defaults to PARA if unspecified. 259 int64_t Alignment = 16; 260 // Default flags are used only if no characteristics are set. 261 bool DefaultCharacteristics = true; 262 unsigned Flags = 0; 263 // "obsolete" according to the documentation, but still supported. 264 bool Readonly = false; 265 while (getLexer().isNot(AsmToken::EndOfStatement)) { 266 switch (getTok().getKind()) { 267 default: 268 break; 269 case AsmToken::String: { 270 // Class identifier; overrides Kind. 271 Class = getTok().getStringContents(); 272 Lex(); 273 break; 274 } 275 case AsmToken::Identifier: { 276 SMLoc KeywordLoc = getTok().getLoc(); 277 StringRef Keyword; 278 if (getParser().parseIdentifier(Keyword)) { 279 llvm_unreachable("failed to parse identifier at an identifier token"); 280 } 281 if (Keyword.equals_insensitive("byte")) { 282 Alignment = 1; 283 } else if (Keyword.equals_insensitive("word")) { 284 Alignment = 2; 285 } else if (Keyword.equals_insensitive("dword")) { 286 Alignment = 4; 287 } else if (Keyword.equals_insensitive("para")) { 288 Alignment = 16; 289 } else if (Keyword.equals_insensitive("page")) { 290 Alignment = 256; 291 } else if (Keyword.equals_insensitive("align")) { 292 if (getParser().parseToken(AsmToken::LParen) || 293 getParser().parseIntToken(Alignment, 294 "Expected integer alignment") || 295 getParser().parseToken(AsmToken::RParen)) { 296 return Error(getTok().getLoc(), 297 "Expected (n) following ALIGN in SEGMENT directive"); 298 } 299 if (!isPowerOf2_64(Alignment) || Alignment > 8192) { 300 return Error(KeywordLoc, 301 "ALIGN argument must be a power of 2 from 1 to 8192"); 302 } 303 } else if (Keyword.equals_insensitive("alias")) { 304 if (getParser().parseToken(AsmToken::LParen) || 305 !getTok().is(AsmToken::String)) 306 return Error( 307 getTok().getLoc(), 308 "Expected (string) following ALIAS in SEGMENT directive"); 309 SectionName = getTok().getStringContents(); 310 Lex(); 311 if (getParser().parseToken(AsmToken::RParen)) 312 return Error( 313 getTok().getLoc(), 314 "Expected (string) following ALIAS in SEGMENT directive"); 315 } else if (Keyword.equals_insensitive("readonly")) { 316 Readonly = true; 317 } else { 318 unsigned Characteristic = 319 StringSwitch<unsigned>(Keyword) 320 .CaseLower("info", COFF::IMAGE_SCN_LNK_INFO) 321 .CaseLower("read", COFF::IMAGE_SCN_MEM_READ) 322 .CaseLower("write", COFF::IMAGE_SCN_MEM_WRITE) 323 .CaseLower("execute", COFF::IMAGE_SCN_MEM_EXECUTE) 324 .CaseLower("shared", COFF::IMAGE_SCN_MEM_SHARED) 325 .CaseLower("nopage", COFF::IMAGE_SCN_MEM_NOT_PAGED) 326 .CaseLower("nocache", COFF::IMAGE_SCN_MEM_NOT_CACHED) 327 .CaseLower("discard", COFF::IMAGE_SCN_MEM_DISCARDABLE) 328 .Default(-1); 329 if (Characteristic == static_cast<unsigned>(-1)) { 330 return Error(KeywordLoc, 331 "Expected characteristic in SEGMENT directive; found '" + 332 Keyword + "'"); 333 } 334 Flags |= Characteristic; 335 DefaultCharacteristics = false; 336 } 337 } 338 } 339 } 340 341 SectionKind Kind = StringSwitch<SectionKind>(Class) 342 .CaseLower("data", SectionKind::getData()) 343 .CaseLower("code", SectionKind::getText()) 344 .CaseLower("const", SectionKind::getReadOnly()) 345 .Default(SectionKind::getData()); 346 if (Kind.isText()) { 347 if (DefaultCharacteristics) { 348 Flags |= COFF::IMAGE_SCN_MEM_EXECUTE | COFF::IMAGE_SCN_MEM_READ; 349 } 350 Flags |= COFF::IMAGE_SCN_CNT_CODE; 351 } else { 352 if (DefaultCharacteristics) { 353 Flags |= COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE; 354 } 355 Flags |= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA; 356 } 357 if (Readonly) { 358 Flags &= ~COFF::IMAGE_SCN_MEM_WRITE; 359 } 360 361 MCSection *Section = getContext().getCOFFSection(SectionName, Flags, "", 362 (COFF::COMDATType)(0)); 363 if (Alignment != 0) { 364 Section->setAlignment(Align(Alignment)); 365 } 366 getStreamer().switchSection(Section); 367 return false; 368 } 369 370 /// ParseDirectiveSegmentEnd 371 /// ::= identifier "ends" 372 bool COFFMasmParser::ParseDirectiveSegmentEnd(StringRef Directive, SMLoc Loc) { 373 StringRef SegmentName; 374 if (!getLexer().is(AsmToken::Identifier)) 375 return TokError("expected identifier in directive"); 376 SegmentName = getTok().getIdentifier(); 377 378 // Ignore; no action necessary. 379 Lex(); 380 return false; 381 } 382 383 /// ParseDirectiveIncludelib 384 /// ::= "includelib" identifier 385 bool COFFMasmParser::ParseDirectiveIncludelib(StringRef Directive, SMLoc Loc) { 386 StringRef Lib; 387 if (getParser().parseIdentifier(Lib)) 388 return TokError("expected identifier in includelib directive"); 389 390 unsigned Flags = COFF::IMAGE_SCN_MEM_PRELOAD | COFF::IMAGE_SCN_MEM_16BIT; 391 getStreamer().pushSection(); 392 getStreamer().switchSection(getContext().getCOFFSection( 393 ".drectve", Flags, "", (COFF::COMDATType)(0))); 394 getStreamer().emitBytes("/DEFAULTLIB:"); 395 getStreamer().emitBytes(Lib); 396 getStreamer().emitBytes(" "); 397 getStreamer().popSection(); 398 return false; 399 } 400 401 /// ParseDirectiveOption 402 /// ::= "option" option-list 403 bool COFFMasmParser::ParseDirectiveOption(StringRef Directive, SMLoc Loc) { 404 auto parseOption = [&]() -> bool { 405 StringRef Option; 406 if (getParser().parseIdentifier(Option)) 407 return TokError("expected identifier for option name"); 408 if (Option.equals_insensitive("prologue")) { 409 StringRef MacroId; 410 if (parseToken(AsmToken::Colon) || getParser().parseIdentifier(MacroId)) 411 return TokError("expected :macroId after OPTION PROLOGUE"); 412 if (MacroId.equals_insensitive("none")) { 413 // Since we currently don't implement prologues/epilogues, NONE is our 414 // default. 415 return false; 416 } 417 return TokError("OPTION PROLOGUE is currently unsupported"); 418 } 419 if (Option.equals_insensitive("epilogue")) { 420 StringRef MacroId; 421 if (parseToken(AsmToken::Colon) || getParser().parseIdentifier(MacroId)) 422 return TokError("expected :macroId after OPTION EPILOGUE"); 423 if (MacroId.equals_insensitive("none")) { 424 // Since we currently don't implement prologues/epilogues, NONE is our 425 // default. 426 return false; 427 } 428 return TokError("OPTION EPILOGUE is currently unsupported"); 429 } 430 return TokError("OPTION '" + Option + "' is currently unsupported"); 431 }; 432 433 if (parseMany(parseOption)) 434 return addErrorSuffix(" in OPTION directive"); 435 return false; 436 } 437 438 /// ParseDirectiveProc 439 /// TODO(epastor): Implement parameters and other attributes. 440 /// ::= label "proc" [[distance]] 441 /// statements 442 /// label "endproc" 443 bool COFFMasmParser::ParseDirectiveProc(StringRef Directive, SMLoc Loc) { 444 StringRef Label; 445 if (getParser().parseIdentifier(Label)) 446 return Error(Loc, "expected identifier for procedure"); 447 if (getLexer().is(AsmToken::Identifier)) { 448 StringRef nextVal = getTok().getString(); 449 SMLoc nextLoc = getTok().getLoc(); 450 if (nextVal.equals_insensitive("far")) { 451 // TODO(epastor): Handle far procedure definitions. 452 Lex(); 453 return Error(nextLoc, "far procedure definitions not yet supported"); 454 } else if (nextVal.equals_insensitive("near")) { 455 Lex(); 456 nextVal = getTok().getString(); 457 nextLoc = getTok().getLoc(); 458 } 459 } 460 MCSymbolCOFF *Sym = cast<MCSymbolCOFF>(getContext().getOrCreateSymbol(Label)); 461 462 // Define symbol as simple external function 463 Sym->setExternal(true); 464 Sym->setType(COFF::IMAGE_SYM_DTYPE_FUNCTION << COFF::SCT_COMPLEX_TYPE_SHIFT); 465 466 bool Framed = false; 467 if (getLexer().is(AsmToken::Identifier) && 468 getTok().getString().equals_insensitive("frame")) { 469 Lex(); 470 Framed = true; 471 getStreamer().emitWinCFIStartProc(Sym, Loc); 472 } 473 getStreamer().emitLabel(Sym, Loc); 474 475 CurrentProcedures.push_back(Label); 476 CurrentProceduresFramed.push_back(Framed); 477 return false; 478 } 479 bool COFFMasmParser::ParseDirectiveEndProc(StringRef Directive, SMLoc Loc) { 480 StringRef Label; 481 SMLoc LabelLoc = getTok().getLoc(); 482 if (getParser().parseIdentifier(Label)) 483 return Error(LabelLoc, "expected identifier for procedure end"); 484 485 if (CurrentProcedures.empty()) 486 return Error(Loc, "endp outside of procedure block"); 487 else if (!CurrentProcedures.back().equals_insensitive(Label)) 488 return Error(LabelLoc, "endp does not match current procedure '" + 489 CurrentProcedures.back() + "'"); 490 491 if (CurrentProceduresFramed.back()) { 492 getStreamer().emitWinCFIEndProc(Loc); 493 } 494 CurrentProcedures.pop_back(); 495 CurrentProceduresFramed.pop_back(); 496 return false; 497 } 498 499 bool COFFMasmParser::ParseDirectiveAlias(StringRef Directive, SMLoc Loc) { 500 std::string AliasName, ActualName; 501 if (getTok().isNot(AsmToken::Less) || 502 getParser().parseAngleBracketString(AliasName)) 503 return Error(getTok().getLoc(), "expected <aliasName>"); 504 if (getParser().parseToken(AsmToken::Equal)) 505 return addErrorSuffix(" in " + Directive + " directive"); 506 if (getTok().isNot(AsmToken::Less) || 507 getParser().parseAngleBracketString(ActualName)) 508 return Error(getTok().getLoc(), "expected <actualName>"); 509 510 MCSymbol *Alias = getContext().getOrCreateSymbol(AliasName); 511 MCSymbol *Actual = getContext().getOrCreateSymbol(ActualName); 512 513 getStreamer().emitWeakReference(Alias, Actual); 514 515 return false; 516 } 517 518 bool COFFMasmParser::ParseSEHDirectiveAllocStack(StringRef Directive, 519 SMLoc Loc) { 520 int64_t Size; 521 SMLoc SizeLoc = getTok().getLoc(); 522 if (getParser().parseAbsoluteExpression(Size)) 523 return Error(SizeLoc, "expected integer size"); 524 if (Size % 8 != 0) 525 return Error(SizeLoc, "stack size must be a multiple of 8"); 526 getStreamer().emitWinCFIAllocStack(static_cast<unsigned>(Size), Loc); 527 return false; 528 } 529 530 bool COFFMasmParser::ParseSEHDirectiveEndProlog(StringRef Directive, 531 SMLoc Loc) { 532 getStreamer().emitWinCFIEndProlog(Loc); 533 return false; 534 } 535 536 namespace llvm { 537 538 MCAsmParserExtension *createCOFFMasmParser() { return new COFFMasmParser; } 539 540 } // end namespace llvm 541