xref: /llvm-project/llvm/lib/Object/COFFModuleDefinition.cpp (revision afe8549269fc9957daea2dea697fbe41ba5bbbc4)
1 //===--- COFFModuleDefinition.cpp - Simple DEF parser ---------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // Windows-specific.
11 // A parser for the module-definition file (.def file).
12 //
13 // The format of module-definition files are described in this document:
14 // https://msdn.microsoft.com/en-us/library/28d6s79h.aspx
15 //
16 //===----------------------------------------------------------------------===//
17 
18 #include "llvm/Object/COFFModuleDefinition.h"
19 #include "llvm/ADT/StringRef.h"
20 #include "llvm/ADT/StringSwitch.h"
21 #include "llvm/Object/COFF.h"
22 #include "llvm/Object/COFFImportFile.h"
23 #include "llvm/Object/Error.h"
24 #include "llvm/Support/Error.h"
25 #include "llvm/Support/raw_ostream.h"
26 
27 using namespace llvm::COFF;
28 using namespace llvm;
29 
30 namespace llvm {
31 namespace object {
32 
33 enum Kind {
34   Unknown,
35   Eof,
36   Identifier,
37   Comma,
38   Equal,
39   KwBase,
40   KwConstant,
41   KwData,
42   KwExports,
43   KwHeapsize,
44   KwLibrary,
45   KwName,
46   KwNoname,
47   KwPrivate,
48   KwStacksize,
49   KwVersion,
50 };
51 
52 struct Token {
53   explicit Token(Kind T = Unknown, StringRef S = "") : K(T), Value(S) {}
54   Kind K;
55   StringRef Value;
56 };
57 
58 static bool isDecorated(StringRef Sym, bool MingwDef) {
59   // mingw does not prepend "_".
60   return (!MingwDef && Sym.startswith("_")) || Sym.startswith("@") ||
61          Sym.startswith("?");
62 }
63 
64 static Error createError(const Twine &Err) {
65   return make_error<StringError>(StringRef(Err.str()),
66                                  object_error::parse_failed);
67 }
68 
69 class Lexer {
70 public:
71   Lexer(StringRef S) : Buf(S) {}
72 
73   Token lex() {
74     Buf = Buf.trim();
75     if (Buf.empty())
76       return Token(Eof);
77 
78     switch (Buf[0]) {
79     case '\0':
80       return Token(Eof);
81     case ';': {
82       size_t End = Buf.find('\n');
83       Buf = (End == Buf.npos) ? "" : Buf.drop_front(End);
84       return lex();
85     }
86     case '=':
87       Buf = Buf.drop_front();
88       // GNU dlltool accepts both = and ==.
89       if (Buf.startswith("="))
90         Buf = Buf.drop_front();
91       return Token(Equal, "=");
92     case ',':
93       Buf = Buf.drop_front();
94       return Token(Comma, ",");
95     case '"': {
96       StringRef S;
97       std::tie(S, Buf) = Buf.substr(1).split('"');
98       return Token(Identifier, S);
99     }
100     default: {
101       size_t End = Buf.find_first_of("=,\r\n \t\v");
102       StringRef Word = Buf.substr(0, End);
103       Kind K = llvm::StringSwitch<Kind>(Word)
104                    .Case("BASE", KwBase)
105                    .Case("CONSTANT", KwConstant)
106                    .Case("DATA", KwData)
107                    .Case("EXPORTS", KwExports)
108                    .Case("HEAPSIZE", KwHeapsize)
109                    .Case("LIBRARY", KwLibrary)
110                    .Case("NAME", KwName)
111                    .Case("NONAME", KwNoname)
112                    .Case("PRIVATE", KwPrivate)
113                    .Case("STACKSIZE", KwStacksize)
114                    .Case("VERSION", KwVersion)
115                    .Default(Identifier);
116       Buf = (End == Buf.npos) ? "" : Buf.drop_front(End);
117       return Token(K, Word);
118     }
119     }
120   }
121 
122 private:
123   StringRef Buf;
124 };
125 
126 class Parser {
127 public:
128   explicit Parser(StringRef S, MachineTypes M, bool B)
129       : Lex(S), Machine(M), MingwDef(B) {}
130 
131   Expected<COFFModuleDefinition> parse() {
132     do {
133       if (Error Err = parseOne())
134         return std::move(Err);
135     } while (Tok.K != Eof);
136     return Info;
137   }
138 
139 private:
140   void read() {
141     if (Stack.empty()) {
142       Tok = Lex.lex();
143       return;
144     }
145     Tok = Stack.back();
146     Stack.pop_back();
147   }
148 
149   Error readAsInt(uint64_t *I) {
150     read();
151     if (Tok.K != Identifier || Tok.Value.getAsInteger(10, *I))
152       return createError("integer expected");
153     return Error::success();
154   }
155 
156   Error expect(Kind Expected, StringRef Msg) {
157     read();
158     if (Tok.K != Expected)
159       return createError(Msg);
160     return Error::success();
161   }
162 
163   void unget() { Stack.push_back(Tok); }
164 
165   Error parseOne() {
166     read();
167     switch (Tok.K) {
168     case Eof:
169       return Error::success();
170     case KwExports:
171       for (;;) {
172         read();
173         if (Tok.K != Identifier) {
174           unget();
175           return Error::success();
176         }
177         if (Error Err = parseExport())
178           return Err;
179       }
180     case KwHeapsize:
181       return parseNumbers(&Info.HeapReserve, &Info.HeapCommit);
182     case KwStacksize:
183       return parseNumbers(&Info.StackReserve, &Info.StackCommit);
184     case KwLibrary:
185     case KwName: {
186       bool IsDll = Tok.K == KwLibrary; // Check before parseName.
187       std::string Name;
188       if (Error Err = parseName(&Name, &Info.ImageBase))
189         return Err;
190       // Append the appropriate file extension if not already present.
191       StringRef Ext = IsDll ? ".dll" : ".exe";
192       if (!StringRef(Name).endswith_lower(Ext))
193         Name += Ext;
194 
195       // Set the output file, but don't override /out if it was already passed.
196       if (Info.OutputFile.empty())
197         Info.OutputFile = Name;
198       return Error::success();
199     }
200     case KwVersion:
201       return parseVersion(&Info.MajorImageVersion, &Info.MinorImageVersion);
202     default:
203       return createError("unknown directive: " + Tok.Value);
204     }
205   }
206 
207   Error parseExport() {
208     COFFShortExport E;
209     E.Name = Tok.Value;
210     read();
211     if (Tok.K == Equal) {
212       read();
213       if (Tok.K != Identifier)
214         return createError("identifier expected, but got " + Tok.Value);
215       E.ExtName = E.Name;
216       E.Name = Tok.Value;
217     } else {
218       unget();
219     }
220 
221     if (Machine == IMAGE_FILE_MACHINE_I386) {
222       if (!isDecorated(E.Name, MingwDef))
223         E.Name = (std::string("_").append(E.Name));
224       if (!E.ExtName.empty() && !isDecorated(E.ExtName, MingwDef))
225         E.ExtName = (std::string("_").append(E.ExtName));
226     }
227 
228     for (;;) {
229       read();
230       if (Tok.K == Identifier && Tok.Value[0] == '@') {
231         Tok.Value.drop_front().getAsInteger(10, E.Ordinal);
232         read();
233         if (Tok.K == KwNoname) {
234           E.Noname = true;
235         } else {
236           unget();
237         }
238         continue;
239       }
240       if (Tok.K == KwData) {
241         E.Data = true;
242         continue;
243       }
244       if (Tok.K == KwConstant) {
245         E.Constant = true;
246         continue;
247       }
248       if (Tok.K == KwPrivate) {
249         E.Private = true;
250         continue;
251       }
252       unget();
253       Info.Exports.push_back(E);
254       return Error::success();
255     }
256   }
257 
258   // HEAPSIZE/STACKSIZE reserve[,commit]
259   Error parseNumbers(uint64_t *Reserve, uint64_t *Commit) {
260     if (Error Err = readAsInt(Reserve))
261       return Err;
262     read();
263     if (Tok.K != Comma) {
264       unget();
265       Commit = nullptr;
266       return Error::success();
267     }
268     if (Error Err = readAsInt(Commit))
269       return Err;
270     return Error::success();
271   }
272 
273   // NAME outputPath [BASE=address]
274   Error parseName(std::string *Out, uint64_t *Baseaddr) {
275     read();
276     if (Tok.K == Identifier) {
277       *Out = Tok.Value;
278     } else {
279       *Out = "";
280       unget();
281       return Error::success();
282     }
283     read();
284     if (Tok.K == KwBase) {
285       if (Error Err = expect(Equal, "'=' expected"))
286         return Err;
287       if (Error Err = readAsInt(Baseaddr))
288         return Err;
289     } else {
290       unget();
291       *Baseaddr = 0;
292     }
293     return Error::success();
294   }
295 
296   // VERSION major[.minor]
297   Error parseVersion(uint32_t *Major, uint32_t *Minor) {
298     read();
299     if (Tok.K != Identifier)
300       return createError("identifier expected, but got " + Tok.Value);
301     StringRef V1, V2;
302     std::tie(V1, V2) = Tok.Value.split('.');
303     if (V1.getAsInteger(10, *Major))
304       return createError("integer expected, but got " + Tok.Value);
305     if (V2.empty())
306       *Minor = 0;
307     else if (V2.getAsInteger(10, *Minor))
308       return createError("integer expected, but got " + Tok.Value);
309     return Error::success();
310   }
311 
312   Lexer Lex;
313   Token Tok;
314   std::vector<Token> Stack;
315   MachineTypes Machine;
316   COFFModuleDefinition Info;
317   bool MingwDef;
318 };
319 
320 Expected<COFFModuleDefinition> parseCOFFModuleDefinition(MemoryBufferRef MB,
321                                                          MachineTypes Machine,
322                                                          bool MingwDef) {
323   return Parser(MB.getBuffer(), Machine, MingwDef).parse();
324 }
325 
326 } // namespace object
327 } // namespace llvm
328