1 //===- COFFImportFile.cpp - COFF short import file implementation ---------===// 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 // This file defines the writeImportLibrary function. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "llvm/Object/COFFImportFile.h" 15 #include "llvm/ADT/ArrayRef.h" 16 #include "llvm/Object/Archive.h" 17 #include "llvm/Object/ArchiveWriter.h" 18 #include "llvm/Object/COFF.h" 19 #include "llvm/Support/Error.h" 20 #include "llvm/Support/Path.h" 21 22 #include <cstdint> 23 #include <map> 24 #include <set> 25 #include <string> 26 #include <vector> 27 28 using namespace llvm::COFF; 29 using namespace llvm::object; 30 using namespace llvm; 31 32 namespace llvm { 33 namespace object { 34 35 static bool is32bit(MachineTypes Machine) { 36 switch (Machine) { 37 default: 38 llvm_unreachable("unsupported machine"); 39 case IMAGE_FILE_MACHINE_AMD64: 40 return false; 41 case IMAGE_FILE_MACHINE_ARMNT: 42 case IMAGE_FILE_MACHINE_I386: 43 return true; 44 } 45 } 46 47 static uint16_t getImgRelRelocation(MachineTypes Machine) { 48 switch (Machine) { 49 default: 50 llvm_unreachable("unsupported machine"); 51 case IMAGE_FILE_MACHINE_AMD64: 52 return IMAGE_REL_AMD64_ADDR32NB; 53 case IMAGE_FILE_MACHINE_ARMNT: 54 return IMAGE_REL_ARM_ADDR32NB; 55 case IMAGE_FILE_MACHINE_I386: 56 return IMAGE_REL_I386_DIR32NB; 57 } 58 } 59 60 template <class T> static void append(std::vector<uint8_t> &B, const T &Data) { 61 size_t S = B.size(); 62 B.resize(S + sizeof(T)); 63 memcpy(&B[S], &Data, sizeof(T)); 64 } 65 66 static void writeStringTable(std::vector<uint8_t> &B, 67 ArrayRef<const std::string> Strings) { 68 // The COFF string table consists of a 4-byte value which is the size of the 69 // table, including the length field itself. This value is followed by the 70 // string content itself, which is an array of null-terminated C-style 71 // strings. The termination is important as they are referenced to by offset 72 // by the symbol entity in the file format. 73 74 size_t Pos = B.size(); 75 size_t Offset = B.size(); 76 77 // Skip over the length field, we will fill it in later as we will have 78 // computed the length while emitting the string content itself. 79 Pos += sizeof(uint32_t); 80 81 for (const auto &S : Strings) { 82 B.resize(Pos + S.length() + 1); 83 strcpy(reinterpret_cast<char *>(&B[Pos]), S.c_str()); 84 Pos += S.length() + 1; 85 } 86 87 // Backfill the length of the table now that it has been computed. 88 support::ulittle32_t Length(B.size() - Offset); 89 support::endian::write32le(&B[Offset], Length); 90 } 91 92 static ImportNameType getNameType(StringRef Sym, StringRef ExtName, 93 MachineTypes Machine) { 94 if (Sym != ExtName) 95 return IMPORT_NAME_UNDECORATE; 96 if (Machine == IMAGE_FILE_MACHINE_I386 && Sym.startswith("_")) 97 return IMPORT_NAME_NOPREFIX; 98 return IMPORT_NAME; 99 } 100 101 static Expected<std::string> replace(StringRef S, StringRef From, 102 StringRef To) { 103 size_t Pos = S.find(From); 104 105 // From and To may be mangled, but substrings in S may not. 106 if (Pos == StringRef::npos && From.startswith("_") && To.startswith("_")) { 107 From = From.substr(1); 108 To = To.substr(1); 109 Pos = S.find(From); 110 } 111 112 if (Pos == StringRef::npos) { 113 return make_error<StringError>( 114 StringRef(Twine(S + ": replacing '" + From + 115 "' with '" + To + "' failed").str()), object_error::parse_failed); 116 } 117 118 return (Twine(S.substr(0, Pos)) + To + S.substr(Pos + From.size())).str(); 119 } 120 121 static const std::string NullImportDescriptorSymbolName = 122 "__NULL_IMPORT_DESCRIPTOR"; 123 124 namespace { 125 // This class constructs various small object files necessary to support linking 126 // symbols imported from a DLL. The contents are pretty strictly defined and 127 // nearly entirely static. The details of the structures files are defined in 128 // WINNT.h and the PE/COFF specification. 129 class ObjectFactory { 130 using u16 = support::ulittle16_t; 131 using u32 = support::ulittle32_t; 132 MachineTypes Machine; 133 BumpPtrAllocator Alloc; 134 StringRef DLLName; 135 StringRef Library; 136 std::string ImportDescriptorSymbolName; 137 std::string NullThunkSymbolName; 138 139 public: 140 ObjectFactory(StringRef S, MachineTypes M) 141 : Machine(M), DLLName(S), Library(S.drop_back(4)), 142 ImportDescriptorSymbolName(("__IMPORT_DESCRIPTOR_" + Library).str()), 143 NullThunkSymbolName(("\x7f" + Library + "_NULL_THUNK_DATA").str()) {} 144 145 // Creates an Import Descriptor. This is a small object file which contains a 146 // reference to the terminators and contains the library name (entry) for the 147 // import name table. It will force the linker to construct the necessary 148 // structure to import symbols from the DLL. 149 NewArchiveMember createImportDescriptor(std::vector<uint8_t> &Buffer); 150 151 // Creates a NULL import descriptor. This is a small object file whcih 152 // contains a NULL import descriptor. It is used to terminate the imports 153 // from a specific DLL. 154 NewArchiveMember createNullImportDescriptor(std::vector<uint8_t> &Buffer); 155 156 // Create a NULL Thunk Entry. This is a small object file which contains a 157 // NULL Import Address Table entry and a NULL Import Lookup Table Entry. It 158 // is used to terminate the IAT and ILT. 159 NewArchiveMember createNullThunk(std::vector<uint8_t> &Buffer); 160 161 // Create a short import file which is described in PE/COFF spec 7. Import 162 // Library Format. 163 NewArchiveMember createShortImport(StringRef Sym, uint16_t Ordinal, 164 ImportType Type, ImportNameType NameType); 165 166 // Create a weak external file which is described in PE/COFF Aux Format 3. 167 NewArchiveMember createWeakExternal(StringRef Sym, StringRef Weak, bool Imp); 168 }; 169 } // namespace 170 171 NewArchiveMember 172 ObjectFactory::createImportDescriptor(std::vector<uint8_t> &Buffer) { 173 const uint32_t NumberOfSections = 2; 174 const uint32_t NumberOfSymbols = 7; 175 const uint32_t NumberOfRelocations = 3; 176 177 // COFF Header 178 coff_file_header Header{ 179 u16(Machine), 180 u16(NumberOfSections), 181 u32(0), 182 u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + 183 // .idata$2 184 sizeof(coff_import_directory_table_entry) + 185 NumberOfRelocations * sizeof(coff_relocation) + 186 // .idata$4 187 (DLLName.size() + 1)), 188 u32(NumberOfSymbols), 189 u16(0), 190 u16(is32bit(Machine) ? IMAGE_FILE_32BIT_MACHINE : 0), 191 }; 192 append(Buffer, Header); 193 194 // Section Header Table 195 const coff_section SectionTable[NumberOfSections] = { 196 {{'.', 'i', 'd', 'a', 't', 'a', '$', '2'}, 197 u32(0), 198 u32(0), 199 u32(sizeof(coff_import_directory_table_entry)), 200 u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)), 201 u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) + 202 sizeof(coff_import_directory_table_entry)), 203 u32(0), 204 u16(NumberOfRelocations), 205 u16(0), 206 u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | 207 IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, 208 {{'.', 'i', 'd', 'a', 't', 'a', '$', '6'}, 209 u32(0), 210 u32(0), 211 u32(DLLName.size() + 1), 212 u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) + 213 sizeof(coff_import_directory_table_entry) + 214 NumberOfRelocations * sizeof(coff_relocation)), 215 u32(0), 216 u32(0), 217 u16(0), 218 u16(0), 219 u32(IMAGE_SCN_ALIGN_2BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | 220 IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, 221 }; 222 append(Buffer, SectionTable); 223 224 // .idata$2 225 const coff_import_directory_table_entry ImportDescriptor{ 226 u32(0), u32(0), u32(0), u32(0), u32(0), 227 }; 228 append(Buffer, ImportDescriptor); 229 230 const coff_relocation RelocationTable[NumberOfRelocations] = { 231 {u32(offsetof(coff_import_directory_table_entry, NameRVA)), u32(2), 232 u16(getImgRelRelocation(Machine))}, 233 {u32(offsetof(coff_import_directory_table_entry, ImportLookupTableRVA)), 234 u32(3), u16(getImgRelRelocation(Machine))}, 235 {u32(offsetof(coff_import_directory_table_entry, ImportAddressTableRVA)), 236 u32(4), u16(getImgRelRelocation(Machine))}, 237 }; 238 append(Buffer, RelocationTable); 239 240 // .idata$6 241 auto S = Buffer.size(); 242 Buffer.resize(S + DLLName.size() + 1); 243 memcpy(&Buffer[S], DLLName.data(), DLLName.size()); 244 Buffer[S + DLLName.size()] = '\0'; 245 246 // Symbol Table 247 coff_symbol16 SymbolTable[NumberOfSymbols] = { 248 {{{0, 0, 0, 0, 0, 0, 0, 0}}, 249 u32(0), 250 u16(1), 251 u16(0), 252 IMAGE_SYM_CLASS_EXTERNAL, 253 0}, 254 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '2'}}, 255 u32(0), 256 u16(1), 257 u16(0), 258 IMAGE_SYM_CLASS_SECTION, 259 0}, 260 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '6'}}, 261 u32(0), 262 u16(2), 263 u16(0), 264 IMAGE_SYM_CLASS_STATIC, 265 0}, 266 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '4'}}, 267 u32(0), 268 u16(0), 269 u16(0), 270 IMAGE_SYM_CLASS_SECTION, 271 0}, 272 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '5'}}, 273 u32(0), 274 u16(0), 275 u16(0), 276 IMAGE_SYM_CLASS_SECTION, 277 0}, 278 {{{0, 0, 0, 0, 0, 0, 0, 0}}, 279 u32(0), 280 u16(0), 281 u16(0), 282 IMAGE_SYM_CLASS_EXTERNAL, 283 0}, 284 {{{0, 0, 0, 0, 0, 0, 0, 0}}, 285 u32(0), 286 u16(0), 287 u16(0), 288 IMAGE_SYM_CLASS_EXTERNAL, 289 0}, 290 }; 291 // TODO: Name.Offset.Offset here and in the all similar places below 292 // suggests a names refactoring. Maybe StringTableOffset.Value? 293 SymbolTable[0].Name.Offset.Offset = 294 sizeof(uint32_t); 295 SymbolTable[5].Name.Offset.Offset = 296 sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1; 297 SymbolTable[6].Name.Offset.Offset = 298 sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1 + 299 NullImportDescriptorSymbolName.length() + 1; 300 append(Buffer, SymbolTable); 301 302 // String Table 303 writeStringTable(Buffer, 304 {ImportDescriptorSymbolName, NullImportDescriptorSymbolName, 305 NullThunkSymbolName}); 306 307 StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()}; 308 return {MemoryBufferRef(F, DLLName)}; 309 } 310 311 NewArchiveMember 312 ObjectFactory::createNullImportDescriptor(std::vector<uint8_t> &Buffer) { 313 const uint32_t NumberOfSections = 1; 314 const uint32_t NumberOfSymbols = 1; 315 316 // COFF Header 317 coff_file_header Header{ 318 u16(Machine), 319 u16(NumberOfSections), 320 u32(0), 321 u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + 322 // .idata$3 323 sizeof(coff_import_directory_table_entry)), 324 u32(NumberOfSymbols), 325 u16(0), 326 u16(is32bit(Machine) ? IMAGE_FILE_32BIT_MACHINE : 0), 327 }; 328 append(Buffer, Header); 329 330 // Section Header Table 331 const coff_section SectionTable[NumberOfSections] = { 332 {{'.', 'i', 'd', 'a', 't', 'a', '$', '3'}, 333 u32(0), 334 u32(0), 335 u32(sizeof(coff_import_directory_table_entry)), 336 u32(sizeof(coff_file_header) + 337 (NumberOfSections * sizeof(coff_section))), 338 u32(0), 339 u32(0), 340 u16(0), 341 u16(0), 342 u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | 343 IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, 344 }; 345 append(Buffer, SectionTable); 346 347 // .idata$3 348 const coff_import_directory_table_entry ImportDescriptor{ 349 u32(0), u32(0), u32(0), u32(0), u32(0), 350 }; 351 append(Buffer, ImportDescriptor); 352 353 // Symbol Table 354 coff_symbol16 SymbolTable[NumberOfSymbols] = { 355 {{{0, 0, 0, 0, 0, 0, 0, 0}}, 356 u32(0), 357 u16(1), 358 u16(0), 359 IMAGE_SYM_CLASS_EXTERNAL, 360 0}, 361 }; 362 SymbolTable[0].Name.Offset.Offset = sizeof(uint32_t); 363 append(Buffer, SymbolTable); 364 365 // String Table 366 writeStringTable(Buffer, {NullImportDescriptorSymbolName}); 367 368 StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()}; 369 return {MemoryBufferRef(F, DLLName)}; 370 } 371 372 NewArchiveMember ObjectFactory::createNullThunk(std::vector<uint8_t> &Buffer) { 373 const uint32_t NumberOfSections = 2; 374 const uint32_t NumberOfSymbols = 1; 375 uint32_t VASize = is32bit(Machine) ? 4 : 8; 376 377 // COFF Header 378 coff_file_header Header{ 379 u16(Machine), 380 u16(NumberOfSections), 381 u32(0), 382 u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + 383 // .idata$5 384 VASize + 385 // .idata$4 386 VASize), 387 u32(NumberOfSymbols), 388 u16(0), 389 u16(is32bit(Machine) ? IMAGE_FILE_32BIT_MACHINE : 0), 390 }; 391 append(Buffer, Header); 392 393 // Section Header Table 394 const coff_section SectionTable[NumberOfSections] = { 395 {{'.', 'i', 'd', 'a', 't', 'a', '$', '5'}, 396 u32(0), 397 u32(0), 398 u32(VASize), 399 u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)), 400 u32(0), 401 u32(0), 402 u16(0), 403 u16(0), 404 u32((is32bit(Machine) ? IMAGE_SCN_ALIGN_4BYTES 405 : IMAGE_SCN_ALIGN_8BYTES) | 406 IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | 407 IMAGE_SCN_MEM_WRITE)}, 408 {{'.', 'i', 'd', 'a', 't', 'a', '$', '4'}, 409 u32(0), 410 u32(0), 411 u32(VASize), 412 u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) + 413 VASize), 414 u32(0), 415 u32(0), 416 u16(0), 417 u16(0), 418 u32((is32bit(Machine) ? IMAGE_SCN_ALIGN_4BYTES 419 : IMAGE_SCN_ALIGN_8BYTES) | 420 IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | 421 IMAGE_SCN_MEM_WRITE)}, 422 }; 423 append(Buffer, SectionTable); 424 425 // .idata$5, ILT 426 append(Buffer, u32(0)); 427 if (!is32bit(Machine)) 428 append(Buffer, u32(0)); 429 430 // .idata$4, IAT 431 append(Buffer, u32(0)); 432 if (!is32bit(Machine)) 433 append(Buffer, u32(0)); 434 435 // Symbol Table 436 coff_symbol16 SymbolTable[NumberOfSymbols] = { 437 {{{0, 0, 0, 0, 0, 0, 0, 0}}, 438 u32(0), 439 u16(1), 440 u16(0), 441 IMAGE_SYM_CLASS_EXTERNAL, 442 0}, 443 }; 444 SymbolTable[0].Name.Offset.Offset = sizeof(uint32_t); 445 append(Buffer, SymbolTable); 446 447 // String Table 448 writeStringTable(Buffer, {NullThunkSymbolName}); 449 450 StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()}; 451 return {MemoryBufferRef{F, DLLName}}; 452 } 453 454 NewArchiveMember ObjectFactory::createShortImport(StringRef Sym, 455 uint16_t Ordinal, 456 ImportType ImportType, 457 ImportNameType NameType) { 458 size_t ImpSize = DLLName.size() + Sym.size() + 2; // +2 for NULs 459 size_t Size = sizeof(coff_import_header) + ImpSize; 460 char *Buf = Alloc.Allocate<char>(Size); 461 memset(Buf, 0, Size); 462 char *P = Buf; 463 464 // Write short import library. 465 auto *Imp = reinterpret_cast<coff_import_header *>(P); 466 P += sizeof(*Imp); 467 Imp->Sig2 = 0xFFFF; 468 Imp->Machine = Machine; 469 Imp->SizeOfData = ImpSize; 470 if (Ordinal > 0) 471 Imp->OrdinalHint = Ordinal; 472 Imp->TypeInfo = (NameType << 2) | ImportType; 473 474 // Write symbol name and DLL name. 475 memcpy(P, Sym.data(), Sym.size()); 476 P += Sym.size() + 1; 477 memcpy(P, DLLName.data(), DLLName.size()); 478 479 return {MemoryBufferRef(StringRef(Buf, Size), DLLName)}; 480 } 481 482 NewArchiveMember ObjectFactory::createWeakExternal(StringRef Sym, 483 StringRef Weak, bool Imp) { 484 std::vector<uint8_t> Buffer; 485 const uint32_t NumberOfSections = 1; 486 const uint32_t NumberOfSymbols = 5; 487 488 // COFF Header 489 coff_file_header Header{ 490 u16(0), 491 u16(NumberOfSections), 492 u32(0), 493 u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section))), 494 u32(NumberOfSymbols), 495 u16(0), 496 u16(0), 497 }; 498 append(Buffer, Header); 499 500 // Section Header Table 501 const coff_section SectionTable[NumberOfSections] = { 502 {{'.', 'd', 'r', 'e', 'c', 't', 'v', 'e'}, 503 u32(0), 504 u32(0), 505 u32(0), 506 u32(0), 507 u32(0), 508 u32(0), 509 u16(0), 510 u16(0), 511 u32(IMAGE_SCN_LNK_INFO | IMAGE_SCN_LNK_REMOVE)}}; 512 append(Buffer, SectionTable); 513 514 // Symbol Table 515 coff_symbol16 SymbolTable[NumberOfSymbols] = { 516 {{{'@', 'c', 'o', 'm', 'p', '.', 'i', 'd'}}, 517 u32(0), 518 u16(0xFFFF), 519 u16(0), 520 IMAGE_SYM_CLASS_STATIC, 521 0}, 522 {{{'@', 'f', 'e', 'a', 't', '.', '0', '0'}}, 523 u32(0), 524 u16(0xFFFF), 525 u16(0), 526 IMAGE_SYM_CLASS_STATIC, 527 0}, 528 {{{0, 0, 0, 0, 0, 0, 0, 0}}, 529 u32(0), 530 u16(0), 531 u16(0), 532 IMAGE_SYM_CLASS_EXTERNAL, 533 0}, 534 {{{0, 0, 0, 0, 0, 0, 0, 0}}, 535 u32(0), 536 u16(0), 537 u16(0), 538 IMAGE_SYM_CLASS_WEAK_EXTERNAL, 539 1}, 540 {{{2, 0, 0, 0, 3, 0, 0, 0}}, u32(0), u16(0), u16(0), uint8_t(0), 0}, 541 }; 542 SymbolTable[2].Name.Offset.Offset = sizeof(uint32_t); 543 544 //__imp_ String Table 545 if (Imp) { 546 SymbolTable[3].Name.Offset.Offset = sizeof(uint32_t) + Sym.size() + 7; 547 writeStringTable(Buffer, {std::string("__imp_").append(Sym), 548 std::string("__imp_").append(Weak)}); 549 } else { 550 SymbolTable[3].Name.Offset.Offset = sizeof(uint32_t) + Sym.size() + 1; 551 writeStringTable(Buffer, {Sym, Weak}); 552 } 553 append(Buffer, SymbolTable); 554 555 // Copied here so we can still use writeStringTable 556 char *Buf = Alloc.Allocate<char>(Buffer.size()); 557 memcpy(Buf, Buffer.data(), Buffer.size()); 558 return {MemoryBufferRef(StringRef(Buf, Buffer.size()), DLLName)}; 559 } 560 561 std::error_code writeImportLibrary(StringRef DLLName, StringRef Path, 562 ArrayRef<COFFShortExport> Exports, 563 MachineTypes Machine) { 564 565 std::vector<NewArchiveMember> Members; 566 ObjectFactory OF(llvm::sys::path::filename(DLLName), Machine); 567 568 std::vector<uint8_t> ImportDescriptor; 569 Members.push_back(OF.createImportDescriptor(ImportDescriptor)); 570 571 std::vector<uint8_t> NullImportDescriptor; 572 Members.push_back(OF.createNullImportDescriptor(NullImportDescriptor)); 573 574 std::vector<uint8_t> NullThunk; 575 Members.push_back(OF.createNullThunk(NullThunk)); 576 577 for (COFFShortExport E : Exports) { 578 if (E.Private) 579 continue; 580 581 if (E.isWeak()) { 582 Members.push_back(OF.createWeakExternal(E.Name, E.ExtName, false)); 583 Members.push_back(OF.createWeakExternal(E.Name, E.ExtName, true)); 584 continue; 585 } 586 587 ImportType ImportType = IMPORT_CODE; 588 if (E.Data) 589 ImportType = IMPORT_DATA; 590 if (E.Constant) 591 ImportType = IMPORT_CONST; 592 593 StringRef SymbolName = E.isWeak() ? E.ExtName : E.Name; 594 ImportNameType NameType = getNameType(SymbolName, E.Name, Machine); 595 Expected<std::string> Name = E.ExtName.empty() 596 ? SymbolName 597 : replace(SymbolName, E.Name, E.ExtName); 598 599 if (!Name) { 600 return errorToErrorCode(Name.takeError()); 601 } 602 603 Members.push_back( 604 OF.createShortImport(*Name, E.Ordinal, ImportType, NameType)); 605 } 606 607 std::pair<StringRef, std::error_code> Result = 608 writeArchive(Path, Members, /*WriteSymtab*/ true, object::Archive::K_GNU, 609 /*Deterministic*/ true, /*Thin*/ false); 610 611 return Result.second; 612 } 613 614 } // namespace object 615 } // namespace llvm 616