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