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