xref: /llvm-project/llvm/lib/Object/COFFModuleDefinition.cpp (revision e234901a8469fd62d7b9e490832fb8a36d5590df)
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       // Append the appropriate file extension if not already present.
192       if (!sys::path::has_extension(Name))
193         Name += IsDll ? ".dll" : ".exe";
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