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/COFFImportFile.h"
21 #include "llvm/Object/Error.h"
22 #include "llvm/Support/Error.h"
23 #include "llvm/Support/Path.h"
25 using namespace llvm::COFF
;
53 explicit Token(Kind T
= Unknown
, StringRef S
= "") : K(T
), Value(S
) {}
58 static bool isDecorated(StringRef Sym
, bool MingwDef
) {
59 // In def files, the symbols can either be listed decorated or undecorated.
61 // - For cdecl symbols, only the undecorated form is allowed.
62 // - For fastcall and vectorcall symbols, both fully decorated or
63 // undecorated forms can be present.
64 // - For stdcall symbols in non-MinGW environments, the decorated form is
65 // fully decorated with leading underscore and trailing stack argument
66 // size - like "_Func@0".
67 // - In MinGW def files, a decorated stdcall symbol does not include the
68 // leading underscore though, like "Func@0".
70 // This function controls whether a leading underscore should be added to
71 // the given symbol name or not. For MinGW, treat a stdcall symbol name such
72 // as "Func@0" as undecorated, i.e. a leading underscore must be added.
73 // For non-MinGW, look for '@' in the whole string and consider "_Func@0"
74 // as decorated, i.e. don't add any more leading underscores.
75 // We can't check for a leading underscore here, since function names
76 // themselves can start with an underscore, while a second one still needs
78 return Sym
.starts_with("@") || Sym
.contains("@@") || Sym
.starts_with("?") ||
79 (!MingwDef
&& Sym
.contains('@'));
84 Lexer(StringRef S
) : Buf(S
) {}
95 size_t End
= Buf
.find('\n');
96 Buf
= (End
== Buf
.npos
) ? "" : Buf
.drop_front(End
);
100 Buf
= Buf
.drop_front();
101 if (Buf
.consume_front("="))
102 return Token(EqualEqual
, "==");
103 return Token(Equal
, "=");
105 Buf
= Buf
.drop_front();
106 return Token(Comma
, ",");
109 std::tie(S
, Buf
) = Buf
.substr(1).split('"');
110 return Token(Identifier
, S
);
113 size_t End
= Buf
.find_first_of("=,;\r\n \t\v");
114 StringRef Word
= Buf
.substr(0, End
);
115 Kind K
= llvm::StringSwitch
<Kind
>(Word
)
116 .Case("BASE", KwBase
)
117 .Case("CONSTANT", KwConstant
)
118 .Case("DATA", KwData
)
119 .Case("EXPORTS", KwExports
)
120 .Case("EXPORTAS", KwExportAs
)
121 .Case("HEAPSIZE", KwHeapsize
)
122 .Case("LIBRARY", KwLibrary
)
123 .Case("NAME", KwName
)
124 .Case("NONAME", KwNoname
)
125 .Case("PRIVATE", KwPrivate
)
126 .Case("STACKSIZE", KwStacksize
)
127 .Case("VERSION", KwVersion
)
128 .Default(Identifier
);
129 Buf
= (End
== Buf
.npos
) ? "" : Buf
.drop_front(End
);
130 return Token(K
, Word
);
141 explicit Parser(StringRef S
, MachineTypes M
, bool B
, bool AU
)
142 : Lex(S
), Machine(M
), MingwDef(B
), AddUnderscores(AU
) {
143 if (Machine
!= IMAGE_FILE_MACHINE_I386
)
144 AddUnderscores
= false;
147 Expected
<COFFModuleDefinition
> parse() {
149 if (Error Err
= parseOne())
150 return std::move(Err
);
151 } while (Tok
.K
!= Eof
);
165 Error
readAsInt(uint64_t *I
) {
167 if (Tok
.K
!= Identifier
|| Tok
.Value
.getAsInteger(10, *I
))
168 return createError("integer expected");
169 return Error::success();
172 Error
expect(Kind Expected
, StringRef Msg
) {
174 if (Tok
.K
!= Expected
)
175 return createError(Msg
);
176 return Error::success();
179 void unget() { Stack
.push_back(Tok
); }
185 return Error::success();
189 if (Tok
.K
!= Identifier
) {
191 return Error::success();
193 if (Error Err
= parseExport())
197 return parseNumbers(&Info
.HeapReserve
, &Info
.HeapCommit
);
199 return parseNumbers(&Info
.StackReserve
, &Info
.StackCommit
);
202 bool IsDll
= Tok
.K
== KwLibrary
; // Check before parseName.
204 if (Error Err
= parseName(&Name
, &Info
.ImageBase
))
207 Info
.ImportName
= Name
;
209 // Set the output file, but don't override /out if it was already passed.
210 if (Info
.OutputFile
.empty()) {
211 Info
.OutputFile
= Name
;
212 // Append the appropriate file extension if not already present.
213 if (!sys::path::has_extension(Name
))
214 Info
.OutputFile
+= IsDll
? ".dll" : ".exe";
217 return Error::success();
220 return parseVersion(&Info
.MajorImageVersion
, &Info
.MinorImageVersion
);
222 return createError("unknown directive: " + Tok
.Value
);
226 Error
parseExport() {
228 E
.Name
= std::string(Tok
.Value
);
230 if (Tok
.K
== Equal
) {
232 if (Tok
.K
!= Identifier
)
233 return createError("identifier expected, but got " + Tok
.Value
);
235 E
.Name
= std::string(Tok
.Value
);
240 if (AddUnderscores
) {
241 if (!isDecorated(E
.Name
, MingwDef
))
242 E
.Name
= (std::string("_").append(E
.Name
));
243 if (!E
.ExtName
.empty() && !isDecorated(E
.ExtName
, MingwDef
))
244 E
.ExtName
= (std::string("_").append(E
.ExtName
));
249 if (Tok
.K
== Identifier
&& Tok
.Value
[0] == '@') {
250 if (Tok
.Value
== "@") {
253 Tok
.Value
.getAsInteger(10, E
.Ordinal
);
254 } else if (Tok
.Value
.drop_front().getAsInteger(10, E
.Ordinal
)) {
255 // "foo \n @bar" - Not an ordinal modifier at all, but the next
256 // export (fastcall decorated) - complete the current one.
258 Info
.Exports
.push_back(E
);
259 return Error::success();
263 if (Tok
.K
== KwNoname
) {
270 if (Tok
.K
== KwData
) {
274 if (Tok
.K
== KwConstant
) {
278 if (Tok
.K
== KwPrivate
) {
282 if (Tok
.K
== EqualEqual
) {
284 E
.ImportName
= std::string(Tok
.Value
);
287 // EXPORTAS must be at the end of export definition
288 if (Tok
.K
== KwExportAs
) {
292 "unexpected end of file, EXPORTAS identifier expected");
293 E
.ExportAs
= std::string(Tok
.Value
);
297 Info
.Exports
.push_back(E
);
298 return Error::success();
302 // HEAPSIZE/STACKSIZE reserve[,commit]
303 Error
parseNumbers(uint64_t *Reserve
, uint64_t *Commit
) {
304 if (Error Err
= readAsInt(Reserve
))
307 if (Tok
.K
!= Comma
) {
310 return Error::success();
312 if (Error Err
= readAsInt(Commit
))
314 return Error::success();
317 // NAME outputPath [BASE=address]
318 Error
parseName(std::string
*Out
, uint64_t *Baseaddr
) {
320 if (Tok
.K
== Identifier
) {
321 *Out
= std::string(Tok
.Value
);
325 return Error::success();
328 if (Tok
.K
== KwBase
) {
329 if (Error Err
= expect(Equal
, "'=' expected"))
331 if (Error Err
= readAsInt(Baseaddr
))
337 return Error::success();
340 // VERSION major[.minor]
341 Error
parseVersion(uint32_t *Major
, uint32_t *Minor
) {
343 if (Tok
.K
!= Identifier
)
344 return createError("identifier expected, but got " + Tok
.Value
);
346 std::tie(V1
, V2
) = Tok
.Value
.split('.');
347 if (V1
.getAsInteger(10, *Major
))
348 return createError("integer expected, but got " + Tok
.Value
);
351 else if (V2
.getAsInteger(10, *Minor
))
352 return createError("integer expected, but got " + Tok
.Value
);
353 return Error::success();
358 std::vector
<Token
> Stack
;
359 MachineTypes Machine
;
360 COFFModuleDefinition Info
;
365 Expected
<COFFModuleDefinition
> parseCOFFModuleDefinition(MemoryBufferRef MB
,
366 MachineTypes Machine
,
368 bool AddUnderscores
) {
369 return Parser(MB
.getBuffer(), Machine
, MingwDef
, AddUnderscores
).parse();
372 } // namespace object