123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547 |
- //===- COFFMasmParser.cpp - COFF MASM Assembly Parser ---------------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #include "llvm/ADT/StringRef.h"
- #include "llvm/ADT/Twine.h"
- #include "llvm/BinaryFormat/COFF.h"
- #include "llvm/MC/MCAsmMacro.h"
- #include "llvm/MC/MCContext.h"
- #include "llvm/MC/MCParser/MCAsmLexer.h"
- #include "llvm/MC/MCParser/MCAsmParserExtension.h"
- #include "llvm/MC/MCParser/MCTargetAsmParser.h"
- #include "llvm/MC/MCSectionCOFF.h"
- #include "llvm/MC/MCStreamer.h"
- #include "llvm/MC/MCSymbolCOFF.h"
- #include "llvm/MC/SectionKind.h"
- #include "llvm/Support/Casting.h"
- #include "llvm/Support/SMLoc.h"
- #include <cstdint>
- #include <utility>
- using namespace llvm;
- namespace {
- class COFFMasmParser : public MCAsmParserExtension {
- template <bool (COFFMasmParser::*HandlerMethod)(StringRef, SMLoc)>
- void addDirectiveHandler(StringRef Directive) {
- MCAsmParser::ExtensionDirectiveHandler Handler =
- std::make_pair(this, HandleDirective<COFFMasmParser, HandlerMethod>);
- getParser().addDirectiveHandler(Directive, Handler);
- }
- bool ParseSectionSwitch(StringRef SectionName, unsigned Characteristics,
- SectionKind Kind);
- bool ParseSectionSwitch(StringRef SectionName, unsigned Characteristics,
- SectionKind Kind, StringRef COMDATSymName,
- COFF::COMDATType Type, Align Alignment);
- bool ParseDirectiveProc(StringRef, SMLoc);
- bool ParseDirectiveEndProc(StringRef, SMLoc);
- bool ParseDirectiveSegment(StringRef, SMLoc);
- bool ParseDirectiveSegmentEnd(StringRef, SMLoc);
- bool ParseDirectiveIncludelib(StringRef, SMLoc);
- bool ParseDirectiveOption(StringRef, SMLoc);
- bool ParseDirectiveAlias(StringRef, SMLoc);
- bool ParseSEHDirectiveAllocStack(StringRef, SMLoc);
- bool ParseSEHDirectiveEndProlog(StringRef, SMLoc);
- bool IgnoreDirective(StringRef, SMLoc) {
- while (!getLexer().is(AsmToken::EndOfStatement)) {
- Lex();
- }
- return false;
- }
- void Initialize(MCAsmParser &Parser) override {
- // Call the base implementation.
- MCAsmParserExtension::Initialize(Parser);
- // x64 directives
- addDirectiveHandler<&COFFMasmParser::ParseSEHDirectiveAllocStack>(
- ".allocstack");
- addDirectiveHandler<&COFFMasmParser::ParseSEHDirectiveEndProlog>(
- ".endprolog");
- // Code label directives
- // label
- // org
- // Conditional control flow directives
- // .break
- // .continue
- // .else
- // .elseif
- // .endif
- // .endw
- // .if
- // .repeat
- // .until
- // .untilcxz
- // .while
- // Data allocation directives
- // align
- // even
- // mmword
- // tbyte
- // xmmword
- // ymmword
- // Listing control directives
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".cref");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".list");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listall");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listif");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacro");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacroall");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nocref");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolist");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistif");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistmacro");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("page");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("subtitle");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".tfcond");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("title");
- // Macro directives
- // goto
- // Miscellaneous directives
- addDirectiveHandler<&COFFMasmParser::ParseDirectiveAlias>("alias");
- // assume
- // .fpo
- addDirectiveHandler<&COFFMasmParser::ParseDirectiveIncludelib>(
- "includelib");
- addDirectiveHandler<&COFFMasmParser::ParseDirectiveOption>("option");
- // popcontext
- // pushcontext
- // .safeseh
- // Procedure directives
- addDirectiveHandler<&COFFMasmParser::ParseDirectiveEndProc>("endp");
- // invoke (32-bit only)
- addDirectiveHandler<&COFFMasmParser::ParseDirectiveProc>("proc");
- // proto
- // Processor directives; all ignored
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386p");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".387");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486p");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586p");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686p");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".k3d");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".mmx");
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".xmm");
- // Scope directives
- // comm
- // externdef
- // Segment directives
- // .alpha (32-bit only, order segments alphabetically)
- // .dosseg (32-bit only, order segments in DOS convention)
- // .seq (32-bit only, order segments sequentially)
- addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegmentEnd>("ends");
- // group (32-bit only)
- addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegment>("segment");
- // Simplified segment directives
- addDirectiveHandler<&COFFMasmParser::ParseSectionDirectiveCode>(".code");
- // .const
- addDirectiveHandler<
- &COFFMasmParser::ParseSectionDirectiveInitializedData>(".data");
- addDirectiveHandler<
- &COFFMasmParser::ParseSectionDirectiveUninitializedData>(".data?");
- // .exit
- // .fardata
- // .fardata?
- addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".model");
- // .stack
- // .startup
- // String directives, written <name> <directive> <params>
- // catstr (equivalent to <name> TEXTEQU <params>)
- // instr (equivalent to <name> = @InStr(<params>))
- // sizestr (equivalent to <name> = @SizeStr(<params>))
- // substr (equivalent to <name> TEXTEQU @SubStr(<params>))
- // Structure and record directives
- // record
- // typedef
- }
- bool ParseSectionDirectiveCode(StringRef, SMLoc) {
- return ParseSectionSwitch(".text",
- COFF::IMAGE_SCN_CNT_CODE
- | COFF::IMAGE_SCN_MEM_EXECUTE
- | COFF::IMAGE_SCN_MEM_READ,
- SectionKind::getText());
- }
- bool ParseSectionDirectiveInitializedData(StringRef, SMLoc) {
- return ParseSectionSwitch(".data",
- COFF::IMAGE_SCN_CNT_INITIALIZED_DATA
- | COFF::IMAGE_SCN_MEM_READ
- | COFF::IMAGE_SCN_MEM_WRITE,
- SectionKind::getData());
- }
- bool ParseSectionDirectiveUninitializedData(StringRef, SMLoc) {
- return ParseSectionSwitch(".bss",
- COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA
- | COFF::IMAGE_SCN_MEM_READ
- | COFF::IMAGE_SCN_MEM_WRITE,
- SectionKind::getBSS());
- }
- /// Stack of active procedure definitions.
- SmallVector<StringRef, 1> CurrentProcedures;
- SmallVector<bool, 1> CurrentProceduresFramed;
- public:
- COFFMasmParser() = default;
- };
- } // end anonymous namespace.
- bool COFFMasmParser::ParseSectionSwitch(StringRef SectionName,
- unsigned Characteristics,
- SectionKind Kind) {
- return ParseSectionSwitch(SectionName, Characteristics, Kind, "",
- (COFF::COMDATType)0, Align(16));
- }
- bool COFFMasmParser::ParseSectionSwitch(
- StringRef SectionName, unsigned Characteristics, SectionKind Kind,
- StringRef COMDATSymName, COFF::COMDATType Type, Align Alignment) {
- if (getLexer().isNot(AsmToken::EndOfStatement))
- return TokError("unexpected token in section switching directive");
- Lex();
- MCSection *Section = getContext().getCOFFSection(SectionName, Characteristics,
- Kind, COMDATSymName, Type);
- Section->setAlignment(Alignment);
- getStreamer().switchSection(Section);
- return false;
- }
- bool COFFMasmParser::ParseDirectiveSegment(StringRef Directive, SMLoc Loc) {
- StringRef SegmentName;
- if (!getLexer().is(AsmToken::Identifier))
- return TokError("expected identifier in directive");
- SegmentName = getTok().getIdentifier();
- Lex();
- StringRef SectionName = SegmentName;
- SmallVector<char, 247> SectionNameVector;
- StringRef Class;
- if (SegmentName == "_TEXT" || SegmentName.startswith("_TEXT$")) {
- if (SegmentName.size() == 5) {
- SectionName = ".text";
- } else {
- SectionName =
- (".text$" + SegmentName.substr(6)).toStringRef(SectionNameVector);
- }
- Class = "CODE";
- }
- // Parse all options to end of statement.
- // Alignment defaults to PARA if unspecified.
- int64_t Alignment = 16;
- // Default flags are used only if no characteristics are set.
- bool DefaultCharacteristics = true;
- unsigned Flags = 0;
- // "obsolete" according to the documentation, but still supported.
- bool Readonly = false;
- while (getLexer().isNot(AsmToken::EndOfStatement)) {
- switch (getTok().getKind()) {
- default:
- break;
- case AsmToken::String: {
- // Class identifier; overrides Kind.
- Class = getTok().getStringContents();
- Lex();
- break;
- }
- case AsmToken::Identifier: {
- SMLoc KeywordLoc = getTok().getLoc();
- StringRef Keyword;
- if (getParser().parseIdentifier(Keyword)) {
- llvm_unreachable("failed to parse identifier at an identifier token");
- }
- if (Keyword.equals_insensitive("byte")) {
- Alignment = 1;
- } else if (Keyword.equals_insensitive("word")) {
- Alignment = 2;
- } else if (Keyword.equals_insensitive("dword")) {
- Alignment = 4;
- } else if (Keyword.equals_insensitive("para")) {
- Alignment = 16;
- } else if (Keyword.equals_insensitive("page")) {
- Alignment = 256;
- } else if (Keyword.equals_insensitive("align")) {
- if (getParser().parseToken(AsmToken::LParen) ||
- getParser().parseIntToken(Alignment,
- "Expected integer alignment") ||
- getParser().parseToken(AsmToken::RParen)) {
- return Error(getTok().getLoc(),
- "Expected (n) following ALIGN in SEGMENT directive");
- }
- if (!isPowerOf2_64(Alignment) || Alignment > 8192) {
- return Error(KeywordLoc,
- "ALIGN argument must be a power of 2 from 1 to 8192");
- }
- } else if (Keyword.equals_insensitive("alias")) {
- if (getParser().parseToken(AsmToken::LParen) ||
- !getTok().is(AsmToken::String))
- return Error(
- getTok().getLoc(),
- "Expected (string) following ALIAS in SEGMENT directive");
- SectionName = getTok().getStringContents();
- Lex();
- if (getParser().parseToken(AsmToken::RParen))
- return Error(
- getTok().getLoc(),
- "Expected (string) following ALIAS in SEGMENT directive");
- } else if (Keyword.equals_insensitive("readonly")) {
- Readonly = true;
- } else {
- unsigned Characteristic =
- StringSwitch<unsigned>(Keyword)
- .CaseLower("info", COFF::IMAGE_SCN_LNK_INFO)
- .CaseLower("read", COFF::IMAGE_SCN_MEM_READ)
- .CaseLower("write", COFF::IMAGE_SCN_MEM_WRITE)
- .CaseLower("execute", COFF::IMAGE_SCN_MEM_EXECUTE)
- .CaseLower("shared", COFF::IMAGE_SCN_MEM_SHARED)
- .CaseLower("nopage", COFF::IMAGE_SCN_MEM_NOT_PAGED)
- .CaseLower("nocache", COFF::IMAGE_SCN_MEM_NOT_CACHED)
- .CaseLower("discard", COFF::IMAGE_SCN_MEM_DISCARDABLE)
- .Default(-1);
- if (Characteristic == static_cast<unsigned>(-1)) {
- return Error(KeywordLoc,
- "Expected characteristic in SEGMENT directive; found '" +
- Keyword + "'");
- }
- Flags |= Characteristic;
- DefaultCharacteristics = false;
- }
- }
- }
- }
- SectionKind Kind = StringSwitch<SectionKind>(Class)
- .CaseLower("data", SectionKind::getData())
- .CaseLower("code", SectionKind::getText())
- .CaseLower("const", SectionKind::getReadOnly())
- .Default(SectionKind::getData());
- if (Kind.isText()) {
- if (DefaultCharacteristics) {
- Flags |= COFF::IMAGE_SCN_MEM_EXECUTE | COFF::IMAGE_SCN_MEM_READ;
- }
- Flags |= COFF::IMAGE_SCN_CNT_CODE;
- } else {
- if (DefaultCharacteristics) {
- Flags |= COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE;
- }
- Flags |= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;
- }
- if (Readonly) {
- Flags &= ~COFF::IMAGE_SCN_MEM_WRITE;
- }
- MCSection *Section = getContext().getCOFFSection(SectionName, Flags, Kind, "",
- (COFF::COMDATType)(0));
- if (Alignment != 0) {
- Section->setAlignment(Align(Alignment));
- }
- getStreamer().switchSection(Section);
- return false;
- }
- /// ParseDirectiveSegmentEnd
- /// ::= identifier "ends"
- bool COFFMasmParser::ParseDirectiveSegmentEnd(StringRef Directive, SMLoc Loc) {
- StringRef SegmentName;
- if (!getLexer().is(AsmToken::Identifier))
- return TokError("expected identifier in directive");
- SegmentName = getTok().getIdentifier();
- // Ignore; no action necessary.
- Lex();
- return false;
- }
- /// ParseDirectiveIncludelib
- /// ::= "includelib" identifier
- bool COFFMasmParser::ParseDirectiveIncludelib(StringRef Directive, SMLoc Loc) {
- StringRef Lib;
- if (getParser().parseIdentifier(Lib))
- return TokError("expected identifier in includelib directive");
- unsigned Flags = COFF::IMAGE_SCN_MEM_PRELOAD | COFF::IMAGE_SCN_MEM_16BIT;
- SectionKind Kind = SectionKind::getData();
- getStreamer().pushSection();
- getStreamer().switchSection(getContext().getCOFFSection(
- ".drectve", Flags, Kind, "", (COFF::COMDATType)(0)));
- getStreamer().emitBytes("/DEFAULTLIB:");
- getStreamer().emitBytes(Lib);
- getStreamer().emitBytes(" ");
- getStreamer().popSection();
- return false;
- }
- /// ParseDirectiveOption
- /// ::= "option" option-list
- bool COFFMasmParser::ParseDirectiveOption(StringRef Directive, SMLoc Loc) {
- auto parseOption = [&]() -> bool {
- StringRef Option;
- if (getParser().parseIdentifier(Option))
- return TokError("expected identifier for option name");
- if (Option.equals_insensitive("prologue")) {
- StringRef MacroId;
- if (parseToken(AsmToken::Colon) || getParser().parseIdentifier(MacroId))
- return TokError("expected :macroId after OPTION PROLOGUE");
- if (MacroId.equals_insensitive("none")) {
- // Since we currently don't implement prologues/epilogues, NONE is our
- // default.
- return false;
- }
- return TokError("OPTION PROLOGUE is currently unsupported");
- }
- if (Option.equals_insensitive("epilogue")) {
- StringRef MacroId;
- if (parseToken(AsmToken::Colon) || getParser().parseIdentifier(MacroId))
- return TokError("expected :macroId after OPTION EPILOGUE");
- if (MacroId.equals_insensitive("none")) {
- // Since we currently don't implement prologues/epilogues, NONE is our
- // default.
- return false;
- }
- return TokError("OPTION EPILOGUE is currently unsupported");
- }
- return TokError("OPTION '" + Option + "' is currently unsupported");
- };
- if (parseMany(parseOption))
- return addErrorSuffix(" in OPTION directive");
- return false;
- }
- /// ParseDirectiveProc
- /// TODO(epastor): Implement parameters and other attributes.
- /// ::= label "proc" [[distance]]
- /// statements
- /// label "endproc"
- bool COFFMasmParser::ParseDirectiveProc(StringRef Directive, SMLoc Loc) {
- StringRef Label;
- if (getParser().parseIdentifier(Label))
- return Error(Loc, "expected identifier for procedure");
- if (getLexer().is(AsmToken::Identifier)) {
- StringRef nextVal = getTok().getString();
- SMLoc nextLoc = getTok().getLoc();
- if (nextVal.equals_insensitive("far")) {
- // TODO(epastor): Handle far procedure definitions.
- Lex();
- return Error(nextLoc, "far procedure definitions not yet supported");
- } else if (nextVal.equals_insensitive("near")) {
- Lex();
- nextVal = getTok().getString();
- nextLoc = getTok().getLoc();
- }
- }
- MCSymbolCOFF *Sym = cast<MCSymbolCOFF>(getContext().getOrCreateSymbol(Label));
- // Define symbol as simple external function
- Sym->setExternal(true);
- Sym->setType(COFF::IMAGE_SYM_DTYPE_FUNCTION << COFF::SCT_COMPLEX_TYPE_SHIFT);
- bool Framed = false;
- if (getLexer().is(AsmToken::Identifier) &&
- getTok().getString().equals_insensitive("frame")) {
- Lex();
- Framed = true;
- getStreamer().emitWinCFIStartProc(Sym, Loc);
- }
- getStreamer().emitLabel(Sym, Loc);
- CurrentProcedures.push_back(Label);
- CurrentProceduresFramed.push_back(Framed);
- return false;
- }
- bool COFFMasmParser::ParseDirectiveEndProc(StringRef Directive, SMLoc Loc) {
- StringRef Label;
- SMLoc LabelLoc = getTok().getLoc();
- if (getParser().parseIdentifier(Label))
- return Error(LabelLoc, "expected identifier for procedure end");
- if (CurrentProcedures.empty())
- return Error(Loc, "endp outside of procedure block");
- else if (!CurrentProcedures.back().equals_insensitive(Label))
- return Error(LabelLoc, "endp does not match current procedure '" +
- CurrentProcedures.back() + "'");
- if (CurrentProceduresFramed.back()) {
- getStreamer().emitWinCFIEndProc(Loc);
- }
- CurrentProcedures.pop_back();
- CurrentProceduresFramed.pop_back();
- return false;
- }
- bool COFFMasmParser::ParseDirectiveAlias(StringRef Directive, SMLoc Loc) {
- std::string AliasName, ActualName;
- if (getTok().isNot(AsmToken::Less) ||
- getParser().parseAngleBracketString(AliasName))
- return Error(getTok().getLoc(), "expected <aliasName>");
- if (getParser().parseToken(AsmToken::Equal))
- return addErrorSuffix(" in " + Directive + " directive");
- if (getTok().isNot(AsmToken::Less) ||
- getParser().parseAngleBracketString(ActualName))
- return Error(getTok().getLoc(), "expected <actualName>");
- MCSymbol *Alias = getContext().getOrCreateSymbol(AliasName);
- MCSymbol *Actual = getContext().getOrCreateSymbol(ActualName);
- getStreamer().emitWeakReference(Alias, Actual);
- return false;
- }
- bool COFFMasmParser::ParseSEHDirectiveAllocStack(StringRef Directive,
- SMLoc Loc) {
- int64_t Size;
- SMLoc SizeLoc = getTok().getLoc();
- if (getParser().parseAbsoluteExpression(Size))
- return Error(SizeLoc, "expected integer size");
- if (Size % 8 != 0)
- return Error(SizeLoc, "stack size must be a multiple of 8");
- getStreamer().emitWinCFIAllocStack(static_cast<unsigned>(Size), Loc);
- return false;
- }
- bool COFFMasmParser::ParseSEHDirectiveEndProlog(StringRef Directive,
- SMLoc Loc) {
- getStreamer().emitWinCFIEndProlog(Loc);
- return false;
- }
- namespace llvm {
- MCAsmParserExtension *createCOFFMasmParser() { return new COFFMasmParser; }
- } // end namespace llvm
|