Path: blob/main/contrib/llvm-project/llvm/lib/Object/COFFModuleDefinition.cpp
35232 views
//===--- COFFModuleDefinition.cpp - Simple DEF 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//===----------------------------------------------------------------------===//7//8// Windows-specific.9// A parser for the module-definition file (.def file).10//11// The format of module-definition files are described in this document:12// https://msdn.microsoft.com/en-us/library/28d6s79h.aspx13//14//===----------------------------------------------------------------------===//1516#include "llvm/Object/COFFModuleDefinition.h"17#include "llvm/ADT/StringRef.h"18#include "llvm/ADT/StringSwitch.h"19#include "llvm/Object/COFFImportFile.h"20#include "llvm/Object/Error.h"21#include "llvm/Support/Error.h"22#include "llvm/Support/Path.h"2324using namespace llvm::COFF;25using namespace llvm;2627namespace llvm {28namespace object {2930enum Kind {31Unknown,32Eof,33Identifier,34Comma,35Equal,36EqualEqual,37KwBase,38KwConstant,39KwData,40KwExports,41KwExportAs,42KwHeapsize,43KwLibrary,44KwName,45KwNoname,46KwPrivate,47KwStacksize,48KwVersion,49};5051struct Token {52explicit Token(Kind T = Unknown, StringRef S = "") : K(T), Value(S) {}53Kind K;54StringRef Value;55};5657static bool isDecorated(StringRef Sym, bool MingwDef) {58// In def files, the symbols can either be listed decorated or undecorated.59//60// - For cdecl symbols, only the undecorated form is allowed.61// - For fastcall and vectorcall symbols, both fully decorated or62// undecorated forms can be present.63// - For stdcall symbols in non-MinGW environments, the decorated form is64// fully decorated with leading underscore and trailing stack argument65// size - like "_Func@0".66// - In MinGW def files, a decorated stdcall symbol does not include the67// leading underscore though, like "Func@0".6869// This function controls whether a leading underscore should be added to70// the given symbol name or not. For MinGW, treat a stdcall symbol name such71// as "Func@0" as undecorated, i.e. a leading underscore must be added.72// For non-MinGW, look for '@' in the whole string and consider "_Func@0"73// as decorated, i.e. don't add any more leading underscores.74// We can't check for a leading underscore here, since function names75// themselves can start with an underscore, while a second one still needs76// to be added.77return Sym.starts_with("@") || Sym.contains("@@") || Sym.starts_with("?") ||78(!MingwDef && Sym.contains('@'));79}8081class Lexer {82public:83Lexer(StringRef S) : Buf(S) {}8485Token lex() {86Buf = Buf.trim();87if (Buf.empty())88return Token(Eof);8990switch (Buf[0]) {91case '\0':92return Token(Eof);93case ';': {94size_t End = Buf.find('\n');95Buf = (End == Buf.npos) ? "" : Buf.drop_front(End);96return lex();97}98case '=':99Buf = Buf.drop_front();100if (Buf.consume_front("="))101return Token(EqualEqual, "==");102return Token(Equal, "=");103case ',':104Buf = Buf.drop_front();105return Token(Comma, ",");106case '"': {107StringRef S;108std::tie(S, Buf) = Buf.substr(1).split('"');109return Token(Identifier, S);110}111default: {112size_t End = Buf.find_first_of("=,;\r\n \t\v");113StringRef Word = Buf.substr(0, End);114Kind K = llvm::StringSwitch<Kind>(Word)115.Case("BASE", KwBase)116.Case("CONSTANT", KwConstant)117.Case("DATA", KwData)118.Case("EXPORTS", KwExports)119.Case("EXPORTAS", KwExportAs)120.Case("HEAPSIZE", KwHeapsize)121.Case("LIBRARY", KwLibrary)122.Case("NAME", KwName)123.Case("NONAME", KwNoname)124.Case("PRIVATE", KwPrivate)125.Case("STACKSIZE", KwStacksize)126.Case("VERSION", KwVersion)127.Default(Identifier);128Buf = (End == Buf.npos) ? "" : Buf.drop_front(End);129return Token(K, Word);130}131}132}133134private:135StringRef Buf;136};137138class Parser {139public:140explicit Parser(StringRef S, MachineTypes M, bool B, bool AU)141: Lex(S), Machine(M), MingwDef(B), AddUnderscores(AU) {142if (Machine != IMAGE_FILE_MACHINE_I386)143AddUnderscores = false;144}145146Expected<COFFModuleDefinition> parse() {147do {148if (Error Err = parseOne())149return std::move(Err);150} while (Tok.K != Eof);151return Info;152}153154private:155void read() {156if (Stack.empty()) {157Tok = Lex.lex();158return;159}160Tok = Stack.back();161Stack.pop_back();162}163164Error readAsInt(uint64_t *I) {165read();166if (Tok.K != Identifier || Tok.Value.getAsInteger(10, *I))167return createError("integer expected");168return Error::success();169}170171Error expect(Kind Expected, StringRef Msg) {172read();173if (Tok.K != Expected)174return createError(Msg);175return Error::success();176}177178void unget() { Stack.push_back(Tok); }179180Error parseOne() {181read();182switch (Tok.K) {183case Eof:184return Error::success();185case KwExports:186for (;;) {187read();188if (Tok.K != Identifier) {189unget();190return Error::success();191}192if (Error Err = parseExport())193return Err;194}195case KwHeapsize:196return parseNumbers(&Info.HeapReserve, &Info.HeapCommit);197case KwStacksize:198return parseNumbers(&Info.StackReserve, &Info.StackCommit);199case KwLibrary:200case KwName: {201bool IsDll = Tok.K == KwLibrary; // Check before parseName.202std::string Name;203if (Error Err = parseName(&Name, &Info.ImageBase))204return Err;205206Info.ImportName = Name;207208// Set the output file, but don't override /out if it was already passed.209if (Info.OutputFile.empty()) {210Info.OutputFile = Name;211// Append the appropriate file extension if not already present.212if (!sys::path::has_extension(Name))213Info.OutputFile += IsDll ? ".dll" : ".exe";214}215216return Error::success();217}218case KwVersion:219return parseVersion(&Info.MajorImageVersion, &Info.MinorImageVersion);220default:221return createError("unknown directive: " + Tok.Value);222}223}224225Error parseExport() {226COFFShortExport E;227E.Name = std::string(Tok.Value);228read();229if (Tok.K == Equal) {230read();231if (Tok.K != Identifier)232return createError("identifier expected, but got " + Tok.Value);233E.ExtName = E.Name;234E.Name = std::string(Tok.Value);235} else {236unget();237}238239if (AddUnderscores) {240if (!isDecorated(E.Name, MingwDef))241E.Name = (std::string("_").append(E.Name));242if (!E.ExtName.empty() && !isDecorated(E.ExtName, MingwDef))243E.ExtName = (std::string("_").append(E.ExtName));244}245246for (;;) {247read();248if (Tok.K == Identifier && Tok.Value[0] == '@') {249if (Tok.Value == "@") {250// "foo @ 10"251read();252Tok.Value.getAsInteger(10, E.Ordinal);253} else if (Tok.Value.drop_front().getAsInteger(10, E.Ordinal)) {254// "foo \n @bar" - Not an ordinal modifier at all, but the next255// export (fastcall decorated) - complete the current one.256unget();257Info.Exports.push_back(E);258return Error::success();259}260// "foo @10"261read();262if (Tok.K == KwNoname) {263E.Noname = true;264} else {265unget();266}267continue;268}269if (Tok.K == KwData) {270E.Data = true;271continue;272}273if (Tok.K == KwConstant) {274E.Constant = true;275continue;276}277if (Tok.K == KwPrivate) {278E.Private = true;279continue;280}281if (Tok.K == EqualEqual) {282read();283E.ImportName = std::string(Tok.Value);284continue;285}286// EXPORTAS must be at the end of export definition287if (Tok.K == KwExportAs) {288read();289if (Tok.K == Eof)290return createError(291"unexpected end of file, EXPORTAS identifier expected");292E.ExportAs = std::string(Tok.Value);293} else {294unget();295}296Info.Exports.push_back(E);297return Error::success();298}299}300301// HEAPSIZE/STACKSIZE reserve[,commit]302Error parseNumbers(uint64_t *Reserve, uint64_t *Commit) {303if (Error Err = readAsInt(Reserve))304return Err;305read();306if (Tok.K != Comma) {307unget();308Commit = nullptr;309return Error::success();310}311if (Error Err = readAsInt(Commit))312return Err;313return Error::success();314}315316// NAME outputPath [BASE=address]317Error parseName(std::string *Out, uint64_t *Baseaddr) {318read();319if (Tok.K == Identifier) {320*Out = std::string(Tok.Value);321} else {322*Out = "";323unget();324return Error::success();325}326read();327if (Tok.K == KwBase) {328if (Error Err = expect(Equal, "'=' expected"))329return Err;330if (Error Err = readAsInt(Baseaddr))331return Err;332} else {333unget();334*Baseaddr = 0;335}336return Error::success();337}338339// VERSION major[.minor]340Error parseVersion(uint32_t *Major, uint32_t *Minor) {341read();342if (Tok.K != Identifier)343return createError("identifier expected, but got " + Tok.Value);344StringRef V1, V2;345std::tie(V1, V2) = Tok.Value.split('.');346if (V1.getAsInteger(10, *Major))347return createError("integer expected, but got " + Tok.Value);348if (V2.empty())349*Minor = 0;350else if (V2.getAsInteger(10, *Minor))351return createError("integer expected, but got " + Tok.Value);352return Error::success();353}354355Lexer Lex;356Token Tok;357std::vector<Token> Stack;358MachineTypes Machine;359COFFModuleDefinition Info;360bool MingwDef;361bool AddUnderscores;362};363364Expected<COFFModuleDefinition> parseCOFFModuleDefinition(MemoryBufferRef MB,365MachineTypes Machine,366bool MingwDef,367bool AddUnderscores) {368return Parser(MB.getBuffer(), Machine, MingwDef, AddUnderscores).parse();369}370371} // namespace object372} // namespace llvm373374375