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