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