416 lines
14 KiB
C++
416 lines
14 KiB
C++
//===- 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 <cassert>
|
|
#include <cstdint>
|
|
#include <limits>
|
|
#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 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 <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());
|
|
}
|
|
|
|
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<char, 247> 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<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_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 <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
|