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