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