//===- 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/Triple.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/COFF.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDirectives.h" #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCParser/MCAsmLexer.h" #include "llvm/MC/MCParser/MCAsmParserExtension.h" #include "llvm/MC/MCParser/MCAsmParserUtils.h" #include "llvm/MC/MCParser/MCTargetAsmParser.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSectionCOFF.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSymbolCOFF.h" #include "llvm/MC/SectionKind.h" #include "llvm/Support/SMLoc.h" #include #include #include #include using namespace llvm; namespace { class COFFMasmParser : public MCAsmParserExtension { template void addDirectiveHandler(StringRef Directive) { MCAsmParser::ExtensionDirectiveHandler Handler = std::make_pair(this, HandleDirective); getParser().addDirectiveHandler(Directive, Handler); } bool ParseSectionSwitch(StringRef Section, unsigned Characteristics, SectionKind Kind); bool ParseSectionSwitch(StringRef Section, unsigned Characteristics, SectionKind Kind, StringRef COMDATSymName, COFF::COMDATType Type); bool ParseDirectiveProc(StringRef, SMLoc); bool ParseDirectiveEndProc(StringRef, SMLoc); bool ParseDirectiveSegment(StringRef, SMLoc); bool ParseDirectiveSegmentEnd(StringRef, SMLoc); bool ParseDirectiveIncludelib(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"); // 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 // catstr (equivalent to TEXTEQU ) // instr (equivalent to = @InStr()) // sizestr (equivalent to = @SizeStr()) // substr (equivalent to TEXTEQU @SubStr()) // 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()); } StringRef CurrentProcedure; bool CurrentProcedureFramed; public: COFFMasmParser() = default; }; } // end anonymous namespace. static SectionKind computeSectionKind(unsigned Flags) { if (Flags & COFF::IMAGE_SCN_MEM_EXECUTE) return SectionKind::getText(); if (Flags & COFF::IMAGE_SCN_MEM_READ && (Flags & COFF::IMAGE_SCN_MEM_WRITE) == 0) return SectionKind::getReadOnly(); return SectionKind::getData(); } bool COFFMasmParser::ParseSectionSwitch(StringRef Section, unsigned Characteristics, SectionKind Kind) { return ParseSectionSwitch(Section, Characteristics, Kind, "", (COFF::COMDATType)0); } bool COFFMasmParser::ParseSectionSwitch(StringRef Section, unsigned Characteristics, SectionKind Kind, StringRef COMDATSymName, COFF::COMDATType Type) { if (getLexer().isNot(AsmToken::EndOfStatement)) return TokError("unexpected token in section switching directive"); Lex(); getStreamer().SwitchSection(getContext().getCOFFSection( Section, Characteristics, Kind, COMDATSymName, Type)); 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 SectionNameVector; unsigned Flags = COFF::IMAGE_SCN_CNT_INITIALIZED_DATA | COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE; if (SegmentName == "_TEXT" || SegmentName.startswith("_TEXT$")) { if (SegmentName.size() == 5) { SectionName = ".text"; } else { SectionName = (".text$" + SegmentName.substr(6)).toStringRef(SectionNameVector); } Flags = COFF::IMAGE_SCN_CNT_CODE | COFF::IMAGE_SCN_MEM_EXECUTE | COFF::IMAGE_SCN_MEM_READ; } SectionKind Kind = computeSectionKind(Flags); getStreamer().SwitchSection(getContext().getCOFFSection( SectionName, Flags, Kind, "", (COFF::COMDATType)(0))); 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 = computeSectionKind(Flags); getStreamer().PushSection(); getStreamer().SwitchSection(getContext().getCOFFSection( ".drectve", Flags, Kind, "", (COFF::COMDATType)(0))); getStreamer().emitBytes("/DEFAULTLIB:"); getStreamer().emitBytes(Lib); getStreamer().emitBytes(" "); getStreamer().PopSection(); 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_lower("far")) { // TODO(epastor): Handle far procedure definitions. Lex(); return Error(nextLoc, "far procedure definitions not yet supported"); } else if (nextVal.equals_lower("near")) { Lex(); nextVal = getTok().getString(); nextLoc = getTok().getLoc(); } } MCSymbolCOFF *Sym = cast(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_lower("frame")) { Lex(); Framed = true; getStreamer().EmitWinCFIStartProc(Sym, Loc); } getStreamer().emitLabel(Sym, Loc); CurrentProcedure = Label; CurrentProcedureFramed = 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 (CurrentProcedure.empty()) return Error(Loc, "endp outside of procedure block"); else if (CurrentProcedure != Label) return Error(LabelLoc, "endp does not match current procedure '" + CurrentProcedure + "'"); if (CurrentProcedureFramed) { getStreamer().EmitWinCFIEndProc(Loc); } CurrentProcedure = ""; CurrentProcedureFramed = false; 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 "); if (getParser().parseToken(AsmToken::Equal)) return addErrorSuffix(" in " + Directive + " directive"); if (getTok().isNot(AsmToken::Less) || getParser().parseAngleBracketString(ActualName)) return Error(getTok().getLoc(), "expected "); 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(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