Path: blob/main/contrib/llvm-project/llvm/lib/MC/MCParser/COFFMasmParser.cpp
35294 views
//===- COFFMasmParser.cpp - COFF MASM Assembly Parser ---------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "llvm/ADT/StringRef.h"9#include "llvm/ADT/Twine.h"10#include "llvm/BinaryFormat/COFF.h"11#include "llvm/MC/MCAsmMacro.h"12#include "llvm/MC/MCContext.h"13#include "llvm/MC/MCParser/MCAsmLexer.h"14#include "llvm/MC/MCParser/MCAsmParserExtension.h"15#include "llvm/MC/MCParser/MCTargetAsmParser.h"16#include "llvm/MC/MCSectionCOFF.h"17#include "llvm/MC/MCStreamer.h"18#include "llvm/MC/MCSymbolCOFF.h"19#include "llvm/MC/SectionKind.h"20#include "llvm/Support/Casting.h"21#include "llvm/Support/SMLoc.h"22#include <cstdint>23#include <utility>2425using namespace llvm;2627namespace {2829class COFFMasmParser : public MCAsmParserExtension {30template <bool (COFFMasmParser::*HandlerMethod)(StringRef, SMLoc)>31void addDirectiveHandler(StringRef Directive) {32MCAsmParser::ExtensionDirectiveHandler Handler =33std::make_pair(this, HandleDirective<COFFMasmParser, HandlerMethod>);34getParser().addDirectiveHandler(Directive, Handler);35}3637bool ParseSectionSwitch(StringRef SectionName, unsigned Characteristics);3839bool ParseSectionSwitch(StringRef SectionName, unsigned Characteristics,40StringRef COMDATSymName, COFF::COMDATType Type,41Align Alignment);4243bool ParseDirectiveProc(StringRef, SMLoc);44bool ParseDirectiveEndProc(StringRef, SMLoc);45bool ParseDirectiveSegment(StringRef, SMLoc);46bool ParseDirectiveSegmentEnd(StringRef, SMLoc);47bool ParseDirectiveIncludelib(StringRef, SMLoc);48bool ParseDirectiveOption(StringRef, SMLoc);4950bool ParseDirectiveAlias(StringRef, SMLoc);5152bool ParseSEHDirectiveAllocStack(StringRef, SMLoc);53bool ParseSEHDirectiveEndProlog(StringRef, SMLoc);5455bool IgnoreDirective(StringRef, SMLoc) {56while (!getLexer().is(AsmToken::EndOfStatement)) {57Lex();58}59return false;60}6162void Initialize(MCAsmParser &Parser) override {63// Call the base implementation.64MCAsmParserExtension::Initialize(Parser);6566// x64 directives67addDirectiveHandler<&COFFMasmParser::ParseSEHDirectiveAllocStack>(68".allocstack");69addDirectiveHandler<&COFFMasmParser::ParseSEHDirectiveEndProlog>(70".endprolog");7172// Code label directives73// label74// org7576// Conditional control flow directives77// .break78// .continue79// .else80// .elseif81// .endif82// .endw83// .if84// .repeat85// .until86// .untilcxz87// .while8889// Data allocation directives90// align91// even92// mmword93// tbyte94// xmmword95// ymmword9697// Listing control directives98addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".cref");99addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".list");100addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listall");101addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listif");102addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacro");103addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".listmacroall");104addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nocref");105addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolist");106addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistif");107addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".nolistmacro");108addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("page");109addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("subtitle");110addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".tfcond");111addDirectiveHandler<&COFFMasmParser::IgnoreDirective>("title");112113// Macro directives114// goto115116// Miscellaneous directives117addDirectiveHandler<&COFFMasmParser::ParseDirectiveAlias>("alias");118// assume119// .fpo120addDirectiveHandler<&COFFMasmParser::ParseDirectiveIncludelib>(121"includelib");122addDirectiveHandler<&COFFMasmParser::ParseDirectiveOption>("option");123// popcontext124// pushcontext125// .safeseh126127// Procedure directives128addDirectiveHandler<&COFFMasmParser::ParseDirectiveEndProc>("endp");129// invoke (32-bit only)130addDirectiveHandler<&COFFMasmParser::ParseDirectiveProc>("proc");131// proto132133// Processor directives; all ignored134addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386");135addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".386p");136addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".387");137addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486");138addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".486p");139addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586");140addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".586p");141addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686");142addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".686p");143addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".k3d");144addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".mmx");145addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".xmm");146147// Scope directives148// comm149// externdef150151// Segment directives152// .alpha (32-bit only, order segments alphabetically)153// .dosseg (32-bit only, order segments in DOS convention)154// .seq (32-bit only, order segments sequentially)155addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegmentEnd>("ends");156// group (32-bit only)157addDirectiveHandler<&COFFMasmParser::ParseDirectiveSegment>("segment");158159// Simplified segment directives160addDirectiveHandler<&COFFMasmParser::ParseSectionDirectiveCode>(".code");161// .const162addDirectiveHandler<163&COFFMasmParser::ParseSectionDirectiveInitializedData>(".data");164addDirectiveHandler<165&COFFMasmParser::ParseSectionDirectiveUninitializedData>(".data?");166// .exit167// .fardata168// .fardata?169addDirectiveHandler<&COFFMasmParser::IgnoreDirective>(".model");170// .stack171// .startup172173// String directives, written <name> <directive> <params>174// catstr (equivalent to <name> TEXTEQU <params>)175// instr (equivalent to <name> = @InStr(<params>))176// sizestr (equivalent to <name> = @SizeStr(<params>))177// substr (equivalent to <name> TEXTEQU @SubStr(<params>))178179// Structure and record directives180// record181// typedef182}183184bool ParseSectionDirectiveCode(StringRef, SMLoc) {185return ParseSectionSwitch(".text", COFF::IMAGE_SCN_CNT_CODE |186COFF::IMAGE_SCN_MEM_EXECUTE |187COFF::IMAGE_SCN_MEM_READ);188}189190bool ParseSectionDirectiveInitializedData(StringRef, SMLoc) {191return ParseSectionSwitch(".data", COFF::IMAGE_SCN_CNT_INITIALIZED_DATA |192COFF::IMAGE_SCN_MEM_READ |193COFF::IMAGE_SCN_MEM_WRITE);194}195196bool ParseSectionDirectiveUninitializedData(StringRef, SMLoc) {197return ParseSectionSwitch(".bss", COFF::IMAGE_SCN_CNT_UNINITIALIZED_DATA |198COFF::IMAGE_SCN_MEM_READ |199COFF::IMAGE_SCN_MEM_WRITE);200}201202/// Stack of active procedure definitions.203SmallVector<StringRef, 1> CurrentProcedures;204SmallVector<bool, 1> CurrentProceduresFramed;205206public:207COFFMasmParser() = default;208};209210} // end anonymous namespace.211212bool COFFMasmParser::ParseSectionSwitch(StringRef SectionName,213unsigned Characteristics) {214return ParseSectionSwitch(SectionName, Characteristics, "",215(COFF::COMDATType)0, Align(16));216}217218bool COFFMasmParser::ParseSectionSwitch(StringRef SectionName,219unsigned Characteristics,220StringRef COMDATSymName,221COFF::COMDATType Type,222Align Alignment) {223if (getLexer().isNot(AsmToken::EndOfStatement))224return TokError("unexpected token in section switching directive");225Lex();226227MCSection *Section = getContext().getCOFFSection(SectionName, Characteristics,228COMDATSymName, Type);229Section->setAlignment(Alignment);230getStreamer().switchSection(Section);231232return false;233}234235bool COFFMasmParser::ParseDirectiveSegment(StringRef Directive, SMLoc Loc) {236StringRef SegmentName;237if (!getLexer().is(AsmToken::Identifier))238return TokError("expected identifier in directive");239SegmentName = getTok().getIdentifier();240Lex();241242StringRef SectionName = SegmentName;243SmallVector<char, 247> SectionNameVector;244245StringRef Class;246if (SegmentName == "_TEXT" || SegmentName.starts_with("_TEXT$")) {247if (SegmentName.size() == 5) {248SectionName = ".text";249} else {250SectionName =251(".text$" + SegmentName.substr(6)).toStringRef(SectionNameVector);252}253Class = "CODE";254}255256// Parse all options to end of statement.257// Alignment defaults to PARA if unspecified.258int64_t Alignment = 16;259// Default flags are used only if no characteristics are set.260bool DefaultCharacteristics = true;261unsigned Flags = 0;262// "obsolete" according to the documentation, but still supported.263bool Readonly = false;264while (getLexer().isNot(AsmToken::EndOfStatement)) {265switch (getTok().getKind()) {266default:267break;268case AsmToken::String: {269// Class identifier; overrides Kind.270Class = getTok().getStringContents();271Lex();272break;273}274case AsmToken::Identifier: {275SMLoc KeywordLoc = getTok().getLoc();276StringRef Keyword;277if (getParser().parseIdentifier(Keyword)) {278llvm_unreachable("failed to parse identifier at an identifier token");279}280if (Keyword.equals_insensitive("byte")) {281Alignment = 1;282} else if (Keyword.equals_insensitive("word")) {283Alignment = 2;284} else if (Keyword.equals_insensitive("dword")) {285Alignment = 4;286} else if (Keyword.equals_insensitive("para")) {287Alignment = 16;288} else if (Keyword.equals_insensitive("page")) {289Alignment = 256;290} else if (Keyword.equals_insensitive("align")) {291if (getParser().parseToken(AsmToken::LParen) ||292getParser().parseIntToken(Alignment,293"Expected integer alignment") ||294getParser().parseToken(AsmToken::RParen)) {295return Error(getTok().getLoc(),296"Expected (n) following ALIGN in SEGMENT directive");297}298if (!isPowerOf2_64(Alignment) || Alignment > 8192) {299return Error(KeywordLoc,300"ALIGN argument must be a power of 2 from 1 to 8192");301}302} else if (Keyword.equals_insensitive("alias")) {303if (getParser().parseToken(AsmToken::LParen) ||304!getTok().is(AsmToken::String))305return Error(306getTok().getLoc(),307"Expected (string) following ALIAS in SEGMENT directive");308SectionName = getTok().getStringContents();309Lex();310if (getParser().parseToken(AsmToken::RParen))311return Error(312getTok().getLoc(),313"Expected (string) following ALIAS in SEGMENT directive");314} else if (Keyword.equals_insensitive("readonly")) {315Readonly = true;316} else {317unsigned Characteristic =318StringSwitch<unsigned>(Keyword)319.CaseLower("info", COFF::IMAGE_SCN_LNK_INFO)320.CaseLower("read", COFF::IMAGE_SCN_MEM_READ)321.CaseLower("write", COFF::IMAGE_SCN_MEM_WRITE)322.CaseLower("execute", COFF::IMAGE_SCN_MEM_EXECUTE)323.CaseLower("shared", COFF::IMAGE_SCN_MEM_SHARED)324.CaseLower("nopage", COFF::IMAGE_SCN_MEM_NOT_PAGED)325.CaseLower("nocache", COFF::IMAGE_SCN_MEM_NOT_CACHED)326.CaseLower("discard", COFF::IMAGE_SCN_MEM_DISCARDABLE)327.Default(-1);328if (Characteristic == static_cast<unsigned>(-1)) {329return Error(KeywordLoc,330"Expected characteristic in SEGMENT directive; found '" +331Keyword + "'");332}333Flags |= Characteristic;334DefaultCharacteristics = false;335}336}337}338}339340SectionKind Kind = StringSwitch<SectionKind>(Class)341.CaseLower("data", SectionKind::getData())342.CaseLower("code", SectionKind::getText())343.CaseLower("const", SectionKind::getReadOnly())344.Default(SectionKind::getData());345if (Kind.isText()) {346if (DefaultCharacteristics) {347Flags |= COFF::IMAGE_SCN_MEM_EXECUTE | COFF::IMAGE_SCN_MEM_READ;348}349Flags |= COFF::IMAGE_SCN_CNT_CODE;350} else {351if (DefaultCharacteristics) {352Flags |= COFF::IMAGE_SCN_MEM_READ | COFF::IMAGE_SCN_MEM_WRITE;353}354Flags |= COFF::IMAGE_SCN_CNT_INITIALIZED_DATA;355}356if (Readonly) {357Flags &= ~COFF::IMAGE_SCN_MEM_WRITE;358}359360MCSection *Section = getContext().getCOFFSection(SectionName, Flags, "",361(COFF::COMDATType)(0));362if (Alignment != 0) {363Section->setAlignment(Align(Alignment));364}365getStreamer().switchSection(Section);366return false;367}368369/// ParseDirectiveSegmentEnd370/// ::= identifier "ends"371bool COFFMasmParser::ParseDirectiveSegmentEnd(StringRef Directive, SMLoc Loc) {372StringRef SegmentName;373if (!getLexer().is(AsmToken::Identifier))374return TokError("expected identifier in directive");375SegmentName = getTok().getIdentifier();376377// Ignore; no action necessary.378Lex();379return false;380}381382/// ParseDirectiveIncludelib383/// ::= "includelib" identifier384bool COFFMasmParser::ParseDirectiveIncludelib(StringRef Directive, SMLoc Loc) {385StringRef Lib;386if (getParser().parseIdentifier(Lib))387return TokError("expected identifier in includelib directive");388389unsigned Flags = COFF::IMAGE_SCN_MEM_PRELOAD | COFF::IMAGE_SCN_MEM_16BIT;390getStreamer().pushSection();391getStreamer().switchSection(getContext().getCOFFSection(392".drectve", Flags, "", (COFF::COMDATType)(0)));393getStreamer().emitBytes("/DEFAULTLIB:");394getStreamer().emitBytes(Lib);395getStreamer().emitBytes(" ");396getStreamer().popSection();397return false;398}399400/// ParseDirectiveOption401/// ::= "option" option-list402bool COFFMasmParser::ParseDirectiveOption(StringRef Directive, SMLoc Loc) {403auto parseOption = [&]() -> bool {404StringRef Option;405if (getParser().parseIdentifier(Option))406return TokError("expected identifier for option name");407if (Option.equals_insensitive("prologue")) {408StringRef MacroId;409if (parseToken(AsmToken::Colon) || getParser().parseIdentifier(MacroId))410return TokError("expected :macroId after OPTION PROLOGUE");411if (MacroId.equals_insensitive("none")) {412// Since we currently don't implement prologues/epilogues, NONE is our413// default.414return false;415}416return TokError("OPTION PROLOGUE is currently unsupported");417}418if (Option.equals_insensitive("epilogue")) {419StringRef MacroId;420if (parseToken(AsmToken::Colon) || getParser().parseIdentifier(MacroId))421return TokError("expected :macroId after OPTION EPILOGUE");422if (MacroId.equals_insensitive("none")) {423// Since we currently don't implement prologues/epilogues, NONE is our424// default.425return false;426}427return TokError("OPTION EPILOGUE is currently unsupported");428}429return TokError("OPTION '" + Option + "' is currently unsupported");430};431432if (parseMany(parseOption))433return addErrorSuffix(" in OPTION directive");434return false;435}436437/// ParseDirectiveProc438/// TODO(epastor): Implement parameters and other attributes.439/// ::= label "proc" [[distance]]440/// statements441/// label "endproc"442bool COFFMasmParser::ParseDirectiveProc(StringRef Directive, SMLoc Loc) {443StringRef Label;444if (getParser().parseIdentifier(Label))445return Error(Loc, "expected identifier for procedure");446if (getLexer().is(AsmToken::Identifier)) {447StringRef nextVal = getTok().getString();448SMLoc nextLoc = getTok().getLoc();449if (nextVal.equals_insensitive("far")) {450// TODO(epastor): Handle far procedure definitions.451Lex();452return Error(nextLoc, "far procedure definitions not yet supported");453} else if (nextVal.equals_insensitive("near")) {454Lex();455nextVal = getTok().getString();456nextLoc = getTok().getLoc();457}458}459MCSymbolCOFF *Sym = cast<MCSymbolCOFF>(getContext().getOrCreateSymbol(Label));460461// Define symbol as simple external function462Sym->setExternal(true);463Sym->setType(COFF::IMAGE_SYM_DTYPE_FUNCTION << COFF::SCT_COMPLEX_TYPE_SHIFT);464465bool Framed = false;466if (getLexer().is(AsmToken::Identifier) &&467getTok().getString().equals_insensitive("frame")) {468Lex();469Framed = true;470getStreamer().emitWinCFIStartProc(Sym, Loc);471}472getStreamer().emitLabel(Sym, Loc);473474CurrentProcedures.push_back(Label);475CurrentProceduresFramed.push_back(Framed);476return false;477}478bool COFFMasmParser::ParseDirectiveEndProc(StringRef Directive, SMLoc Loc) {479StringRef Label;480SMLoc LabelLoc = getTok().getLoc();481if (getParser().parseIdentifier(Label))482return Error(LabelLoc, "expected identifier for procedure end");483484if (CurrentProcedures.empty())485return Error(Loc, "endp outside of procedure block");486else if (!CurrentProcedures.back().equals_insensitive(Label))487return Error(LabelLoc, "endp does not match current procedure '" +488CurrentProcedures.back() + "'");489490if (CurrentProceduresFramed.back()) {491getStreamer().emitWinCFIEndProc(Loc);492}493CurrentProcedures.pop_back();494CurrentProceduresFramed.pop_back();495return false;496}497498bool COFFMasmParser::ParseDirectiveAlias(StringRef Directive, SMLoc Loc) {499std::string AliasName, ActualName;500if (getTok().isNot(AsmToken::Less) ||501getParser().parseAngleBracketString(AliasName))502return Error(getTok().getLoc(), "expected <aliasName>");503if (getParser().parseToken(AsmToken::Equal))504return addErrorSuffix(" in " + Directive + " directive");505if (getTok().isNot(AsmToken::Less) ||506getParser().parseAngleBracketString(ActualName))507return Error(getTok().getLoc(), "expected <actualName>");508509MCSymbol *Alias = getContext().getOrCreateSymbol(AliasName);510MCSymbol *Actual = getContext().getOrCreateSymbol(ActualName);511512getStreamer().emitWeakReference(Alias, Actual);513514return false;515}516517bool COFFMasmParser::ParseSEHDirectiveAllocStack(StringRef Directive,518SMLoc Loc) {519int64_t Size;520SMLoc SizeLoc = getTok().getLoc();521if (getParser().parseAbsoluteExpression(Size))522return Error(SizeLoc, "expected integer size");523if (Size % 8 != 0)524return Error(SizeLoc, "stack size must be a multiple of 8");525getStreamer().emitWinCFIAllocStack(static_cast<unsigned>(Size), Loc);526return false;527}528529bool COFFMasmParser::ParseSEHDirectiveEndProlog(StringRef Directive,530SMLoc Loc) {531getStreamer().emitWinCFIEndProlog(Loc);532return false;533}534535namespace llvm {536537MCAsmParserExtension *createCOFFMasmParser() { return new COFFMasmParser; }538539} // end namespace llvm540541542