541 lines · cpp
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-exception6//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/AsmLexer.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/SMLoc.h"22#include <cstdint>23 24using namespace llvm;25 26namespace {27 28class 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 SectionName, unsigned Characteristics);37 38 bool parseSectionSwitch(StringRef SectionName, unsigned Characteristics,39 StringRef COMDATSymName, COFF::COMDATType Type,40 Align Alignment);41 42 bool parseDirectiveProc(StringRef, SMLoc);43 bool parseDirectiveEndProc(StringRef, SMLoc);44 bool parseDirectiveSegment(StringRef, SMLoc);45 bool parseDirectiveSegmentEnd(StringRef, SMLoc);46 bool parseDirectiveIncludelib(StringRef, SMLoc);47 bool parseDirectiveOption(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 directives66 addDirectiveHandler<&COFFMasmParser::parseSEHDirectiveAllocStack>(67 ".allocstack");68 addDirectiveHandler<&COFFMasmParser::parseSEHDirectiveEndProlog>(69 ".endprolog");70 71 // Code label directives72 // label73 // org74 75 // Conditional control flow directives76 // .break77 // .continue78 // .else79 // .elseif80 // .endif81 // .endw82 // .if83 // .repeat84 // .until85 // .untilcxz86 // .while87 88 // Data allocation directives89 // align90 // even91 // mmword92 // tbyte93 // xmmword94 // ymmword95 96 // Listing control directives97 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 directives113 // goto114 115 // Miscellaneous directives116 addDirectiveHandler<&COFFMasmParser::parseDirectiveAlias>("alias");117 // assume118 // .fpo119 addDirectiveHandler<&COFFMasmParser::parseDirectiveIncludelib>(120 "includelib");121 addDirectiveHandler<&COFFMasmParser::parseDirectiveOption>("option");122 // popcontext123 // pushcontext124 // .safeseh125 126 // Procedure directives127 addDirectiveHandler<&COFFMasmParser::parseDirectiveEndProc>("endp");128 // invoke (32-bit only)129 addDirectiveHandler<&COFFMasmParser::parseDirectiveProc>("proc");130 // proto131 132 // Processor directives; all ignored133 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 directives147 // comm148 // externdef149 150 // Segment directives151 // .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 directives159 addDirectiveHandler<&COFFMasmParser::parseSectionDirectiveCode>(".code");160 // .const161 addDirectiveHandler<&COFFMasmParser::parseSectionDirectiveInitializedData>(162 ".data");163 addDirectiveHandler<164 &COFFMasmParser::parseSectionDirectiveUninitializedData>(".data?");165 // .exit166 // .fardata167 // .fardata?168 addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".model");169 // .stack170 // .startup171 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 directives179 // record180 // typedef181 }182 183 bool parseSectionDirectiveCode(StringRef, SMLoc) {184 return parseSectionSwitch(".text", COFF::IMAGE_SCN_CNT_CODE |185 COFF::IMAGE_SCN_MEM_EXECUTE |186 COFF::IMAGE_SCN_MEM_READ);187 }188 189 bool parseSectionDirectiveInitializedData(StringRef, SMLoc) {190 return parseSectionSwitch(".data", COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |191 COFF::IMAGE_SCN_MEM_READ |192 COFF::IMAGE_SCN_MEM_WRITE);193 }194 195 bool parseSectionDirectiveUninitializedData(StringRef, SMLoc) {196 return parseSectionSwitch(".bss", COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA |197 COFF::IMAGE_SCN_MEM_READ |198 COFF::IMAGE_SCN_MEM_WRITE);199 }200 201 /// Stack of active procedure definitions.202 SmallVector<StringRef, 1> CurrentProcedures;203 SmallVector<bool, 1> CurrentProceduresFramed;204 205public:206 COFFMasmParser() = default;207};208 209} // end anonymous namespace.210 211bool COFFMasmParser::parseSectionSwitch(StringRef SectionName,212 unsigned Characteristics) {213 return parseSectionSwitch(SectionName, Characteristics, "",214 (COFF::COMDATType)0, Align(16));215}216 217bool COFFMasmParser::parseSectionSwitch(StringRef SectionName,218 unsigned Characteristics,219 StringRef COMDATSymName,220 COFF::COMDATType Type,221 Align Alignment) {222 if (getLexer().isNot(AsmToken::EndOfStatement))223 return TokError("unexpected token in section switching directive");224 Lex();225 226 MCSection *Section = getContext().getCOFFSection(SectionName, Characteristics,227 COMDATSymName, Type);228 Section->setAlignment(Alignment);229 getStreamer().switchSection(Section);230 231 return false;232}233 234bool COFFMasmParser::parseDirectiveSegment(StringRef Directive, SMLoc Loc) {235 StringRef SegmentName;236 if (!getLexer().is(AsmToken::Identifier))237 return TokError("expected identifier in directive");238 SegmentName = getTok().getIdentifier();239 Lex();240 241 StringRef SectionName = SegmentName;242 SmallVector<char, 247> SectionNameVector;243 244 StringRef Class;245 if (SegmentName == "_TEXT" || SegmentName.starts_with("_TEXT$")) {246 if (SegmentName.size() == 5) {247 SectionName = ".text";248 } else {249 SectionName =250 (".text$" + SegmentName.substr(6)).toStringRef(SectionNameVector);251 }252 Class = "CODE";253 }254 255 // Parse all options to end of statement.256 // Alignment defaults to PARA if unspecified.257 int64_t Alignment = 16;258 // Default flags are used only if no characteristics are set.259 bool DefaultCharacteristics = true;260 unsigned Flags = 0;261 // "obsolete" according to the documentation, but still supported.262 bool Readonly = false;263 while (getLexer().isNot(AsmToken::EndOfStatement)) {264 switch (getTok().getKind()) {265 default:266 break;267 case AsmToken::String: {268 // Class identifier; overrides Kind.269 Class = getTok().getStringContents();270 Lex();271 break;272 }273 case AsmToken::Identifier: {274 SMLoc KeywordLoc = getTok().getLoc();275 StringRef Keyword;276 if (getParser().parseIdentifier(Keyword)) {277 llvm_unreachable("failed to parse identifier at an identifier token");278 }279 if (Keyword.equals_insensitive("byte")) {280 Alignment = 1;281 } else if (Keyword.equals_insensitive("word")) {282 Alignment = 2;283 } else if (Keyword.equals_insensitive("dword")) {284 Alignment = 4;285 } else if (Keyword.equals_insensitive("para")) {286 Alignment = 16;287 } else if (Keyword.equals_insensitive("page")) {288 Alignment = 256;289 } else if (Keyword.equals_insensitive("align")) {290 if (getParser().parseToken(AsmToken::LParen) ||291 getParser().parseIntToken(Alignment,292 "Expected integer alignment") ||293 getParser().parseToken(AsmToken::RParen)) {294 return Error(getTok().getLoc(),295 "Expected (n) following ALIGN in SEGMENT directive");296 }297 if (!isPowerOf2_64(Alignment) || Alignment > 8192) {298 return Error(KeywordLoc,299 "ALIGN argument must be a power of 2 from 1 to 8192");300 }301 } else if (Keyword.equals_insensitive("alias")) {302 if (getParser().parseToken(AsmToken::LParen) ||303 !getTok().is(AsmToken::String))304 return Error(305 getTok().getLoc(),306 "Expected (string) following ALIAS in SEGMENT directive");307 SectionName = getTok().getStringContents();308 Lex();309 if (getParser().parseToken(AsmToken::RParen))310 return Error(311 getTok().getLoc(),312 "Expected (string) following ALIAS in SEGMENT directive");313 } else if (Keyword.equals_insensitive("readonly")) {314 Readonly = true;315 } else {316 unsigned Characteristic =317 StringSwitch<unsigned>(Keyword)318 .CaseLower("info", COFF::IMAGE_SCN_LNK_INFO)319 .CaseLower("read", COFF::IMAGE_SCN_MEM_READ)320 .CaseLower("write", COFF::IMAGE_SCN_MEM_WRITE)321 .CaseLower("execute", COFF::IMAGE_SCN_MEM_EXECUTE)322 .CaseLower("shared", COFF::IMAGE_SCN_MEM_SHARED)323 .CaseLower("nopage", COFF::IMAGE_SCN_MEM_NOT_PAGED)324 .CaseLower("nocache", COFF::IMAGE_SCN_MEM_NOT_CACHED)325 .CaseLower("discard", COFF::IMAGE_SCN_MEM_DISCARDABLE)326 .Default(-1);327 if (Characteristic == static_cast<unsigned>(-1)) {328 return Error(KeywordLoc,329 "Expected characteristic in SEGMENT directive; found '" +330 Keyword + "'");331 }332 Flags |= Characteristic;333 DefaultCharacteristics = false;334 }335 }336 }337 }338 339 SectionKind Kind = StringSwitch<SectionKind>(Class)340 .CaseLower("data", SectionKind::getData())341 .CaseLower("code", SectionKind::getText())342 .CaseLower("const", SectionKind::getReadOnly())343 .Default(SectionKind::getData());344 if (Kind.isText()) {345 if (DefaultCharacteristics) {346 Flags |= COFF::IMAGE_SCN_MEM_EXECUTE | COFF::IMAGE_SCN_MEM_READ;347 }348 Flags |= COFF::IMAGE_SCN_CNT_CODE;349 } else {350 if (DefaultCharacteristics) {351 Flags |= COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE;352 }353 Flags |= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;354 }355 if (Readonly) {356 Flags &= ~COFF::IMAGE_SCN_MEM_WRITE;357 }358 359 MCSection *Section = getContext().getCOFFSection(SectionName, Flags, "",360 (COFF::COMDATType)(0));361 if (Alignment != 0) {362 Section->setAlignment(Align(Alignment));363 }364 getStreamer().switchSection(Section);365 return false;366}367 368/// parseDirectiveSegmentEnd369/// ::= identifier "ends"370bool COFFMasmParser::parseDirectiveSegmentEnd(StringRef Directive, SMLoc Loc) {371 StringRef SegmentName;372 if (!getLexer().is(AsmToken::Identifier))373 return TokError("expected identifier in directive");374 SegmentName = getTok().getIdentifier();375 376 // Ignore; no action necessary.377 Lex();378 return false;379}380 381/// parseDirectiveIncludelib382/// ::= "includelib" identifier383bool COFFMasmParser::parseDirectiveIncludelib(StringRef Directive, SMLoc Loc) {384 StringRef Lib;385 if (getParser().parseIdentifier(Lib))386 return TokError("expected identifier in includelib directive");387 388 unsigned Flags = COFF::IMAGE_SCN_MEM_PRELOAD | COFF::IMAGE_SCN_MEM_16BIT;389 getStreamer().pushSection();390 getStreamer().switchSection(getContext().getCOFFSection(391 ".drectve", Flags, "", (COFF::COMDATType)(0)));392 getStreamer().emitBytes("/DEFAULTLIB:");393 getStreamer().emitBytes(Lib);394 getStreamer().emitBytes(" ");395 getStreamer().popSection();396 return false;397}398 399/// parseDirectiveOption400/// ::= "option" option-list401bool COFFMasmParser::parseDirectiveOption(StringRef Directive, SMLoc Loc) {402 auto parseOption = [&]() -> bool {403 StringRef Option;404 if (getParser().parseIdentifier(Option))405 return TokError("expected identifier for option name");406 if (Option.equals_insensitive("prologue")) {407 StringRef MacroId;408 if (parseToken(AsmToken::Colon) || getParser().parseIdentifier(MacroId))409 return TokError("expected :macroId after OPTION PROLOGUE");410 if (MacroId.equals_insensitive("none")) {411 // Since we currently don't implement prologues/epilogues, NONE is our412 // default.413 return false;414 }415 return TokError("OPTION PROLOGUE is currently unsupported");416 }417 if (Option.equals_insensitive("epilogue")) {418 StringRef MacroId;419 if (parseToken(AsmToken::Colon) || getParser().parseIdentifier(MacroId))420 return TokError("expected :macroId after OPTION EPILOGUE");421 if (MacroId.equals_insensitive("none")) {422 // Since we currently don't implement prologues/epilogues, NONE is our423 // default.424 return false;425 }426 return TokError("OPTION EPILOGUE is currently unsupported");427 }428 return TokError("OPTION '" + Option + "' is currently unsupported");429 };430 431 if (parseMany(parseOption))432 return addErrorSuffix(" in OPTION directive");433 return false;434}435 436/// parseDirectiveProc437/// TODO(epastor): Implement parameters and other attributes.438/// ::= label "proc" [[distance]]439/// statements440/// label "endproc"441bool COFFMasmParser::parseDirectiveProc(StringRef Directive, SMLoc Loc) {442 if (!getStreamer().getCurrentFragment())443 return Error(getTok().getLoc(), "expected section directive");444 445 MCSymbol *Sym;446 if (getParser().parseSymbol(Sym))447 return Error(Loc, "expected identifier for procedure");448 if (getLexer().is(AsmToken::Identifier)) {449 StringRef nextVal = getTok().getString();450 SMLoc nextLoc = getTok().getLoc();451 if (nextVal.equals_insensitive("far")) {452 // TODO(epastor): Handle far procedure definitions.453 Lex();454 return Error(nextLoc, "far procedure definitions not yet supported");455 } else if (nextVal.equals_insensitive("near")) {456 Lex();457 nextVal = getTok().getString();458 nextLoc = getTok().getLoc();459 }460 }461 462 // Define symbol as simple external function463 auto *COFFSym = static_cast<MCSymbolCOFF *>(Sym);464 COFFSym->setExternal(true);465 COFFSym->setType(COFF::IMAGE_SYM_DTYPE_FUNCTION466 << COFF::SCT_COMPLEX_TYPE_SHIFT);467 468 bool Framed = false;469 if (getLexer().is(AsmToken::Identifier) &&470 getTok().getString().equals_insensitive("frame")) {471 Lex();472 Framed = true;473 getStreamer().emitWinCFIStartProc(Sym, Loc);474 }475 getStreamer().emitLabel(Sym, Loc);476 477 CurrentProcedures.push_back(Sym->getName());478 CurrentProceduresFramed.push_back(Framed);479 return false;480}481bool COFFMasmParser::parseDirectiveEndProc(StringRef Directive, SMLoc Loc) {482 StringRef Label;483 SMLoc LabelLoc = getTok().getLoc();484 if (getParser().parseIdentifier(Label))485 return Error(LabelLoc, "expected identifier for procedure end");486 487 if (CurrentProcedures.empty())488 return Error(Loc, "endp outside of procedure block");489 else if (!CurrentProcedures.back().equals_insensitive(Label))490 return Error(LabelLoc, "endp does not match current procedure '" +491 CurrentProcedures.back() + "'");492 493 if (CurrentProceduresFramed.back()) {494 getStreamer().emitWinCFIEndProc(Loc);495 }496 CurrentProcedures.pop_back();497 CurrentProceduresFramed.pop_back();498 return false;499}500 501bool COFFMasmParser::parseDirectiveAlias(StringRef Directive, SMLoc Loc) {502 std::string AliasName, ActualName;503 if (getTok().isNot(AsmToken::Less) ||504 getParser().parseAngleBracketString(AliasName))505 return Error(getTok().getLoc(), "expected <aliasName>");506 if (getParser().parseToken(AsmToken::Equal))507 return addErrorSuffix(" in " + Directive + " directive");508 if (getTok().isNot(AsmToken::Less) ||509 getParser().parseAngleBracketString(ActualName))510 return Error(getTok().getLoc(), "expected <actualName>");511 512 MCSymbol *Alias = getContext().parseSymbol(AliasName);513 MCSymbol *Actual = getContext().parseSymbol(ActualName);514 515 getStreamer().emitWeakReference(Alias, Actual);516 517 return false;518}519 520bool COFFMasmParser::parseSEHDirectiveAllocStack(StringRef Directive,521 SMLoc Loc) {522 int64_t Size;523 SMLoc SizeLoc = getTok().getLoc();524 if (getParser().parseAbsoluteExpression(Size))525 return Error(SizeLoc, "expected integer size");526 if (Size % 8 != 0)527 return Error(SizeLoc, "stack size must be a multiple of 8");528 getStreamer().emitWinCFIAllocStack(static_cast<unsigned>(Size), Loc);529 return false;530}531 532bool COFFMasmParser::parseSEHDirectiveEndProlog(StringRef Directive,533 SMLoc Loc) {534 getStreamer().emitWinCFIEndProlog(Loc);535 return false;536}537 538MCAsmParserExtension *llvm::createCOFFMasmParser() {539 return new COFFMasmParser;540}541