1 //===--- COFFModuleDefinition.cpp - Simple DEF parser ---------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
10 // A parser for the module-definition file (.def file).
12 // The format of module-definition files are described in this document:
13 // https://msdn.microsoft.com/en-us/library/28d6s79h.aspx
15 //===----------------------------------------------------------------------===//
17 #include "llvm/Object/COFFModuleDefinition.h"
18 #include "llvm/ADT/StringRef.h"
19 #include "llvm/ADT/StringSwitch.h"
20 #include "llvm/Object/COFF.h"
21 #include "llvm/Object/COFFImportFile.h"
22 #include "llvm/Object/Error.h"
23 #include "llvm/Support/Error.h"
24 #include "llvm/Support/Path.h"
25 #include "llvm/Support/raw_ostream.h"
27 using namespace llvm::COFF;
54 explicit Token(Kind T = Unknown, StringRef S = "") : K(T), Value(S) {}
59 static bool isDecorated(StringRef Sym, bool MingwDef) {
60 // In def files, the symbols can either be listed decorated or undecorated.
62 // - For cdecl symbols, only the undecorated form is allowed.
63 // - For fastcall and vectorcall symbols, both fully decorated or
64 // undecorated forms can be present.
65 // - For stdcall symbols in non-MinGW environments, the decorated form is
66 // fully decorated with leading underscore and trailing stack argument
67 // size - like "_Func@0".
68 // - In MinGW def files, a decorated stdcall symbol does not include the
69 // leading underscore though, like "Func@0".
71 // This function controls whether a leading underscore should be added to
72 // the given symbol name or not. For MinGW, treat a stdcall symbol name such
73 // as "Func@0" as undecorated, i.e. a leading underscore must be added.
74 // For non-MinGW, look for '@' in the whole string and consider "_Func@0"
75 // as decorated, i.e. don't add any more leading underscores.
76 // We can't check for a leading underscore here, since function names
77 // themselves can start with an underscore, while a second one still needs
79 return Sym.startswith("@") || Sym.contains("@@") || Sym.startswith("?") ||
80 (!MingwDef && Sym.contains('@'));
83 static Error createError(const Twine &Err) {
84 return make_error<StringError>(StringRef(Err.str()),
85 object_error::parse_failed);
90 Lexer(StringRef S) : Buf(S) {}
101 size_t End = Buf.find('\n');
102 Buf = (End == Buf.npos) ? "" : Buf.drop_front(End);
106 Buf = Buf.drop_front();
107 if (Buf.startswith("=")) {
108 Buf = Buf.drop_front();
109 return Token(EqualEqual, "==");
111 return Token(Equal, "=");
113 Buf = Buf.drop_front();
114 return Token(Comma, ",");
117 std::tie(S, Buf) = Buf.substr(1).split('"');
118 return Token(Identifier, S);
121 size_t End = Buf.find_first_of("=,;\r\n \t\v");
122 StringRef Word = Buf.substr(0, End);
123 Kind K = llvm::StringSwitch<Kind>(Word)
124 .Case("BASE", KwBase)
125 .Case("CONSTANT", KwConstant)
126 .Case("DATA", KwData)
127 .Case("EXPORTS", KwExports)
128 .Case("HEAPSIZE", KwHeapsize)
129 .Case("LIBRARY", KwLibrary)
130 .Case("NAME", KwName)
131 .Case("NONAME", KwNoname)
132 .Case("PRIVATE", KwPrivate)
133 .Case("STACKSIZE", KwStacksize)
134 .Case("VERSION", KwVersion)
135 .Default(Identifier);
136 Buf = (End == Buf.npos) ? "" : Buf.drop_front(End);
137 return Token(K, Word);
148 explicit Parser(StringRef S, MachineTypes M, bool B)
149 : Lex(S), Machine(M), MingwDef(B) {}
151 Expected<COFFModuleDefinition> parse() {
153 if (Error Err = parseOne())
154 return std::move(Err);
155 } while (Tok.K != Eof);
169 Error readAsInt(uint64_t *I) {
171 if (Tok.K != Identifier || Tok.Value.getAsInteger(10, *I))
172 return createError("integer expected");
173 return Error::success();
176 Error expect(Kind Expected, StringRef Msg) {
178 if (Tok.K != Expected)
179 return createError(Msg);
180 return Error::success();
183 void unget() { Stack.push_back(Tok); }
189 return Error::success();
193 if (Tok.K != Identifier) {
195 return Error::success();
197 if (Error Err = parseExport())
201 return parseNumbers(&Info.HeapReserve, &Info.HeapCommit);
203 return parseNumbers(&Info.StackReserve, &Info.StackCommit);
206 bool IsDll = Tok.K == KwLibrary; // Check before parseName.
208 if (Error Err = parseName(&Name, &Info.ImageBase))
211 Info.ImportName = Name;
213 // Set the output file, but don't override /out if it was already passed.
214 if (Info.OutputFile.empty()) {
215 Info.OutputFile = Name;
216 // Append the appropriate file extension if not already present.
217 if (!sys::path::has_extension(Name))
218 Info.OutputFile += IsDll ? ".dll" : ".exe";
221 return Error::success();
224 return parseVersion(&Info.MajorImageVersion, &Info.MinorImageVersion);
226 return createError("unknown directive: " + Tok.Value);
230 Error parseExport() {
234 if (Tok.K == Equal) {
236 if (Tok.K != Identifier)
237 return createError("identifier expected, but got " + Tok.Value);
244 if (Machine == IMAGE_FILE_MACHINE_I386) {
245 if (!isDecorated(E.Name, MingwDef))
246 E.Name = (std::string("_").append(E.Name));
247 if (!E.ExtName.empty() && !isDecorated(E.ExtName, MingwDef))
248 E.ExtName = (std::string("_").append(E.ExtName));
253 if (Tok.K == Identifier && Tok.Value[0] == '@') {
254 if (Tok.Value == "@") {
257 Tok.Value.getAsInteger(10, E.Ordinal);
258 } else if (Tok.Value.drop_front().getAsInteger(10, E.Ordinal)) {
259 // "foo \n @bar" - Not an ordinal modifier at all, but the next
260 // export (fastcall decorated) - complete the current one.
262 Info.Exports.push_back(E);
263 return Error::success();
267 if (Tok.K == KwNoname) {
274 if (Tok.K == KwData) {
278 if (Tok.K == KwConstant) {
282 if (Tok.K == KwPrivate) {
286 if (Tok.K == EqualEqual) {
288 E.AliasTarget = Tok.Value;
289 if (Machine == IMAGE_FILE_MACHINE_I386 && !isDecorated(E.AliasTarget, MingwDef))
290 E.AliasTarget = std::string("_").append(E.AliasTarget);
294 Info.Exports.push_back(E);
295 return Error::success();
299 // HEAPSIZE/STACKSIZE reserve[,commit]
300 Error parseNumbers(uint64_t *Reserve, uint64_t *Commit) {
301 if (Error Err = readAsInt(Reserve))
304 if (Tok.K != Comma) {
307 return Error::success();
309 if (Error Err = readAsInt(Commit))
311 return Error::success();
314 // NAME outputPath [BASE=address]
315 Error parseName(std::string *Out, uint64_t *Baseaddr) {
317 if (Tok.K == Identifier) {
322 return Error::success();
325 if (Tok.K == KwBase) {
326 if (Error Err = expect(Equal, "'=' expected"))
328 if (Error Err = readAsInt(Baseaddr))
334 return Error::success();
337 // VERSION major[.minor]
338 Error parseVersion(uint32_t *Major, uint32_t *Minor) {
340 if (Tok.K != Identifier)
341 return createError("identifier expected, but got " + Tok.Value);
343 std::tie(V1, V2) = Tok.Value.split('.');
344 if (V1.getAsInteger(10, *Major))
345 return createError("integer expected, but got " + Tok.Value);
348 else if (V2.getAsInteger(10, *Minor))
349 return createError("integer expected, but got " + Tok.Value);
350 return Error::success();
355 std::vector<Token> Stack;
356 MachineTypes Machine;
357 COFFModuleDefinition Info;
361 Expected<COFFModuleDefinition> parseCOFFModuleDefinition(MemoryBufferRef MB,
362 MachineTypes Machine,
364 return Parser(MB.getBuffer(), Machine, MingwDef).parse();
367 } // namespace object