1 //===- COFFImportFile.cpp - COFF short import file implementation ---------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines the writeImportLibrary function. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "llvm/Object/COFFImportFile.h" 14 #include "llvm/ADT/ArrayRef.h" 15 #include "llvm/ADT/SmallVector.h" 16 #include "llvm/ADT/StringMap.h" 17 #include "llvm/ADT/Twine.h" 18 #include "llvm/Object/Archive.h" 19 #include "llvm/Object/ArchiveWriter.h" 20 #include "llvm/Object/COFF.h" 21 #include "llvm/Support/Allocator.h" 22 #include "llvm/Support/Endian.h" 23 #include "llvm/Support/Error.h" 24 #include "llvm/Support/ErrorHandling.h" 25 #include "llvm/Support/Path.h" 26 27 #include <cstdint> 28 #include <string> 29 #include <vector> 30 31 using namespace llvm::COFF; 32 using namespace llvm::object; 33 using namespace llvm; 34 35 namespace llvm { 36 namespace object { 37 38 StringRef COFFImportFile::getFileFormatName() const { 39 switch (getMachine()) { 40 case COFF::IMAGE_FILE_MACHINE_I386: 41 return "COFF-import-file-i386"; 42 case COFF::IMAGE_FILE_MACHINE_AMD64: 43 return "COFF-import-file-x86-64"; 44 case COFF::IMAGE_FILE_MACHINE_ARMNT: 45 return "COFF-import-file-ARM"; 46 case COFF::IMAGE_FILE_MACHINE_ARM64: 47 return "COFF-import-file-ARM64"; 48 case COFF::IMAGE_FILE_MACHINE_ARM64EC: 49 return "COFF-import-file-ARM64EC"; 50 case COFF::IMAGE_FILE_MACHINE_ARM64X: 51 return "COFF-import-file-ARM64X"; 52 default: 53 return "COFF-import-file-<unknown arch>"; 54 } 55 } 56 57 static StringRef applyNameType(ImportNameType Type, StringRef name) { 58 auto ltrim1 = [](StringRef s, StringRef chars) { 59 return !s.empty() && chars.contains(s[0]) ? s.substr(1) : s; 60 }; 61 62 switch (Type) { 63 case IMPORT_NAME_NOPREFIX: 64 name = ltrim1(name, "?@_"); 65 break; 66 case IMPORT_NAME_UNDECORATE: 67 name = ltrim1(name, "?@_"); 68 name = name.substr(0, name.find('@')); 69 break; 70 default: 71 break; 72 } 73 return name; 74 } 75 76 StringRef COFFImportFile::getExportName() const { 77 const coff_import_header *hdr = getCOFFImportHeader(); 78 StringRef name = Data.getBuffer().substr(sizeof(*hdr)).split('\0').first; 79 80 switch (hdr->getNameType()) { 81 case IMPORT_ORDINAL: 82 name = ""; 83 break; 84 case IMPORT_NAME_NOPREFIX: 85 case IMPORT_NAME_UNDECORATE: 86 name = applyNameType(static_cast<ImportNameType>(hdr->getNameType()), name); 87 break; 88 case IMPORT_NAME_EXPORTAS: { 89 // Skip DLL name 90 name = Data.getBuffer().substr(sizeof(*hdr) + name.size() + 1); 91 name = name.split('\0').second.split('\0').first; 92 break; 93 } 94 default: 95 break; 96 } 97 98 return name; 99 } 100 101 Error COFFImportFile::printSymbolName(raw_ostream &OS, DataRefImpl Symb) const { 102 switch (Symb.p) { 103 case ImpSymbol: 104 OS << "__imp_"; 105 break; 106 case ECAuxSymbol: 107 OS << "__imp_aux_"; 108 break; 109 } 110 const char *Name = Data.getBufferStart() + sizeof(coff_import_header); 111 if (Symb.p != ECThunkSymbol && COFF::isArm64EC(getMachine())) { 112 if (std::optional<std::string> DemangledName = 113 getArm64ECDemangledFunctionName(Name)) { 114 OS << StringRef(*DemangledName); 115 return Error::success(); 116 } 117 } 118 OS << StringRef(Name); 119 return Error::success(); 120 } 121 122 static uint16_t getImgRelRelocation(MachineTypes Machine) { 123 switch (Machine) { 124 default: 125 llvm_unreachable("unsupported machine"); 126 case IMAGE_FILE_MACHINE_AMD64: 127 return IMAGE_REL_AMD64_ADDR32NB; 128 case IMAGE_FILE_MACHINE_ARMNT: 129 return IMAGE_REL_ARM_ADDR32NB; 130 case IMAGE_FILE_MACHINE_ARM64: 131 case IMAGE_FILE_MACHINE_ARM64EC: 132 case IMAGE_FILE_MACHINE_ARM64X: 133 return IMAGE_REL_ARM64_ADDR32NB; 134 case IMAGE_FILE_MACHINE_I386: 135 return IMAGE_REL_I386_DIR32NB; 136 case IMAGE_FILE_MACHINE_R4000: 137 return IMAGE_REL_MIPS_REFWORDNB; 138 } 139 } 140 141 template <class T> static void append(std::vector<uint8_t> &B, const T &Data) { 142 size_t S = B.size(); 143 B.resize(S + sizeof(T)); 144 memcpy(&B[S], &Data, sizeof(T)); 145 } 146 147 static void writeStringTable(std::vector<uint8_t> &B, 148 ArrayRef<const std::string_view> Strings) { 149 // The COFF string table consists of a 4-byte value which is the size of the 150 // table, including the length field itself. This value is followed by the 151 // string content itself, which is an array of null-terminated C-style 152 // strings. The termination is important as they are referenced to by offset 153 // by the symbol entity in the file format. 154 155 size_t Pos = B.size(); 156 size_t Offset = B.size(); 157 158 // Skip over the length field, we will fill it in later as we will have 159 // computed the length while emitting the string content itself. 160 Pos += sizeof(uint32_t); 161 162 for (const auto &S : Strings) { 163 B.resize(Pos + S.length() + 1); 164 std::copy(S.begin(), S.end(), std::next(B.begin(), Pos)); 165 B[Pos + S.length()] = 0; 166 Pos += S.length() + 1; 167 } 168 169 // Backfill the length of the table now that it has been computed. 170 support::ulittle32_t Length(B.size() - Offset); 171 support::endian::write32le(&B[Offset], Length); 172 } 173 174 static ImportNameType getNameType(StringRef Sym, StringRef ExtName, 175 MachineTypes Machine, bool MinGW) { 176 // A decorated stdcall function in MSVC is exported with the 177 // type IMPORT_NAME, and the exported function name includes the 178 // the leading underscore. In MinGW on the other hand, a decorated 179 // stdcall function still omits the underscore (IMPORT_NAME_NOPREFIX). 180 // See the comment in isDecorated in COFFModuleDefinition.cpp for more 181 // details. 182 if (ExtName.starts_with("_") && ExtName.contains('@') && !MinGW) 183 return IMPORT_NAME; 184 if (Sym != ExtName) 185 return IMPORT_NAME_UNDECORATE; 186 if (Machine == IMAGE_FILE_MACHINE_I386 && Sym.starts_with("_")) 187 return IMPORT_NAME_NOPREFIX; 188 return IMPORT_NAME; 189 } 190 191 static Expected<std::string> replace(StringRef S, StringRef From, 192 StringRef To) { 193 size_t Pos = S.find(From); 194 195 // From and To may be mangled, but substrings in S may not. 196 if (Pos == StringRef::npos && From.starts_with("_") && To.starts_with("_")) { 197 From = From.substr(1); 198 To = To.substr(1); 199 Pos = S.find(From); 200 } 201 202 if (Pos == StringRef::npos) { 203 return make_error<StringError>( 204 StringRef(Twine(S + ": replacing '" + From + 205 "' with '" + To + "' failed").str()), object_error::parse_failed); 206 } 207 208 return (Twine(S.substr(0, Pos)) + To + S.substr(Pos + From.size())).str(); 209 } 210 211 namespace { 212 // This class constructs various small object files necessary to support linking 213 // symbols imported from a DLL. The contents are pretty strictly defined and 214 // nearly entirely static. The details of the structures files are defined in 215 // WINNT.h and the PE/COFF specification. 216 class ObjectFactory { 217 using u16 = support::ulittle16_t; 218 using u32 = support::ulittle32_t; 219 MachineTypes NativeMachine; 220 BumpPtrAllocator Alloc; 221 StringRef ImportName; 222 StringRef Library; 223 std::string ImportDescriptorSymbolName; 224 std::string NullThunkSymbolName; 225 226 public: 227 ObjectFactory(StringRef S, MachineTypes M) 228 : NativeMachine(M), ImportName(S), Library(llvm::sys::path::stem(S)), 229 ImportDescriptorSymbolName((ImportDescriptorPrefix + Library).str()), 230 NullThunkSymbolName( 231 (NullThunkDataPrefix + Library + NullThunkDataSuffix).str()) {} 232 233 // Creates an Import Descriptor. This is a small object file which contains a 234 // reference to the terminators and contains the library name (entry) for the 235 // import name table. It will force the linker to construct the necessary 236 // structure to import symbols from the DLL. 237 NewArchiveMember createImportDescriptor(std::vector<uint8_t> &Buffer); 238 239 // Creates a NULL import descriptor. This is a small object file whcih 240 // contains a NULL import descriptor. It is used to terminate the imports 241 // from a specific DLL. 242 NewArchiveMember createNullImportDescriptor(std::vector<uint8_t> &Buffer); 243 244 // Create a NULL Thunk Entry. This is a small object file which contains a 245 // NULL Import Address Table entry and a NULL Import Lookup Table Entry. It 246 // is used to terminate the IAT and ILT. 247 NewArchiveMember createNullThunk(std::vector<uint8_t> &Buffer); 248 249 // Create a short import file which is described in PE/COFF spec 7. Import 250 // Library Format. 251 NewArchiveMember createShortImport(StringRef Sym, uint16_t Ordinal, 252 ImportType Type, ImportNameType NameType, 253 StringRef ExportName, 254 MachineTypes Machine); 255 256 // Create a weak external file which is described in PE/COFF Aux Format 3. 257 NewArchiveMember createWeakExternal(StringRef Sym, StringRef Weak, bool Imp, 258 MachineTypes Machine); 259 260 bool is64Bit() const { return COFF::is64Bit(NativeMachine); } 261 }; 262 } // namespace 263 264 NewArchiveMember 265 ObjectFactory::createImportDescriptor(std::vector<uint8_t> &Buffer) { 266 const uint32_t NumberOfSections = 2; 267 const uint32_t NumberOfSymbols = 7; 268 const uint32_t NumberOfRelocations = 3; 269 270 // COFF Header 271 coff_file_header Header{ 272 u16(NativeMachine), 273 u16(NumberOfSections), 274 u32(0), 275 u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + 276 // .idata$2 277 sizeof(coff_import_directory_table_entry) + 278 NumberOfRelocations * sizeof(coff_relocation) + 279 // .idata$4 280 (ImportName.size() + 1)), 281 u32(NumberOfSymbols), 282 u16(0), 283 u16(is64Bit() ? C_Invalid : IMAGE_FILE_32BIT_MACHINE), 284 }; 285 append(Buffer, Header); 286 287 // Section Header Table 288 const coff_section SectionTable[NumberOfSections] = { 289 {{'.', 'i', 'd', 'a', 't', 'a', '$', '2'}, 290 u32(0), 291 u32(0), 292 u32(sizeof(coff_import_directory_table_entry)), 293 u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)), 294 u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) + 295 sizeof(coff_import_directory_table_entry)), 296 u32(0), 297 u16(NumberOfRelocations), 298 u16(0), 299 u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | 300 IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, 301 {{'.', 'i', 'd', 'a', 't', 'a', '$', '6'}, 302 u32(0), 303 u32(0), 304 u32(ImportName.size() + 1), 305 u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) + 306 sizeof(coff_import_directory_table_entry) + 307 NumberOfRelocations * sizeof(coff_relocation)), 308 u32(0), 309 u32(0), 310 u16(0), 311 u16(0), 312 u32(IMAGE_SCN_ALIGN_2BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | 313 IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, 314 }; 315 append(Buffer, SectionTable); 316 317 // .idata$2 318 const coff_import_directory_table_entry ImportDescriptor{ 319 u32(0), u32(0), u32(0), u32(0), u32(0), 320 }; 321 append(Buffer, ImportDescriptor); 322 323 const coff_relocation RelocationTable[NumberOfRelocations] = { 324 {u32(offsetof(coff_import_directory_table_entry, NameRVA)), u32(2), 325 u16(getImgRelRelocation(NativeMachine))}, 326 {u32(offsetof(coff_import_directory_table_entry, ImportLookupTableRVA)), 327 u32(3), u16(getImgRelRelocation(NativeMachine))}, 328 {u32(offsetof(coff_import_directory_table_entry, ImportAddressTableRVA)), 329 u32(4), u16(getImgRelRelocation(NativeMachine))}, 330 }; 331 append(Buffer, RelocationTable); 332 333 // .idata$6 334 auto S = Buffer.size(); 335 Buffer.resize(S + ImportName.size() + 1); 336 memcpy(&Buffer[S], ImportName.data(), ImportName.size()); 337 Buffer[S + ImportName.size()] = '\0'; 338 339 // Symbol Table 340 coff_symbol16 SymbolTable[NumberOfSymbols] = { 341 {{{0, 0, 0, 0, 0, 0, 0, 0}}, 342 u32(0), 343 u16(1), 344 u16(0), 345 IMAGE_SYM_CLASS_EXTERNAL, 346 0}, 347 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '2'}}, 348 u32(0), 349 u16(1), 350 u16(0), 351 IMAGE_SYM_CLASS_SECTION, 352 0}, 353 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '6'}}, 354 u32(0), 355 u16(2), 356 u16(0), 357 IMAGE_SYM_CLASS_STATIC, 358 0}, 359 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '4'}}, 360 u32(0), 361 u16(0), 362 u16(0), 363 IMAGE_SYM_CLASS_SECTION, 364 0}, 365 {{{'.', 'i', 'd', 'a', 't', 'a', '$', '5'}}, 366 u32(0), 367 u16(0), 368 u16(0), 369 IMAGE_SYM_CLASS_SECTION, 370 0}, 371 {{{0, 0, 0, 0, 0, 0, 0, 0}}, 372 u32(0), 373 u16(0), 374 u16(0), 375 IMAGE_SYM_CLASS_EXTERNAL, 376 0}, 377 {{{0, 0, 0, 0, 0, 0, 0, 0}}, 378 u32(0), 379 u16(0), 380 u16(0), 381 IMAGE_SYM_CLASS_EXTERNAL, 382 0}, 383 }; 384 // TODO: Name.Offset.Offset here and in the all similar places below 385 // suggests a names refactoring. Maybe StringTableOffset.Value? 386 SymbolTable[0].Name.Offset.Offset = 387 sizeof(uint32_t); 388 SymbolTable[5].Name.Offset.Offset = 389 sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1; 390 SymbolTable[6].Name.Offset.Offset = 391 sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1 + 392 NullImportDescriptorSymbolName.length() + 1; 393 append(Buffer, SymbolTable); 394 395 // String Table 396 writeStringTable(Buffer, 397 {ImportDescriptorSymbolName, NullImportDescriptorSymbolName, 398 NullThunkSymbolName}); 399 400 StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()}; 401 return {MemoryBufferRef(F, ImportName)}; 402 } 403 404 NewArchiveMember 405 ObjectFactory::createNullImportDescriptor(std::vector<uint8_t> &Buffer) { 406 const uint32_t NumberOfSections = 1; 407 const uint32_t NumberOfSymbols = 1; 408 409 // COFF Header 410 coff_file_header Header{ 411 u16(NativeMachine), 412 u16(NumberOfSections), 413 u32(0), 414 u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + 415 // .idata$3 416 sizeof(coff_import_directory_table_entry)), 417 u32(NumberOfSymbols), 418 u16(0), 419 u16(is64Bit() ? C_Invalid : IMAGE_FILE_32BIT_MACHINE), 420 }; 421 append(Buffer, Header); 422 423 // Section Header Table 424 const coff_section SectionTable[NumberOfSections] = { 425 {{'.', 'i', 'd', 'a', 't', 'a', '$', '3'}, 426 u32(0), 427 u32(0), 428 u32(sizeof(coff_import_directory_table_entry)), 429 u32(sizeof(coff_file_header) + 430 (NumberOfSections * sizeof(coff_section))), 431 u32(0), 432 u32(0), 433 u16(0), 434 u16(0), 435 u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | 436 IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, 437 }; 438 append(Buffer, SectionTable); 439 440 // .idata$3 441 const coff_import_directory_table_entry ImportDescriptor{ 442 u32(0), u32(0), u32(0), u32(0), u32(0), 443 }; 444 append(Buffer, ImportDescriptor); 445 446 // Symbol Table 447 coff_symbol16 SymbolTable[NumberOfSymbols] = { 448 {{{0, 0, 0, 0, 0, 0, 0, 0}}, 449 u32(0), 450 u16(1), 451 u16(0), 452 IMAGE_SYM_CLASS_EXTERNAL, 453 0}, 454 }; 455 SymbolTable[0].Name.Offset.Offset = sizeof(uint32_t); 456 append(Buffer, SymbolTable); 457 458 // String Table 459 writeStringTable(Buffer, {NullImportDescriptorSymbolName}); 460 461 StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()}; 462 return {MemoryBufferRef(F, ImportName)}; 463 } 464 465 NewArchiveMember ObjectFactory::createNullThunk(std::vector<uint8_t> &Buffer) { 466 const uint32_t NumberOfSections = 2; 467 const uint32_t NumberOfSymbols = 1; 468 uint32_t VASize = is64Bit() ? 8 : 4; 469 470 // COFF Header 471 coff_file_header Header{ 472 u16(NativeMachine), 473 u16(NumberOfSections), 474 u32(0), 475 u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + 476 // .idata$5 477 VASize + 478 // .idata$4 479 VASize), 480 u32(NumberOfSymbols), 481 u16(0), 482 u16(is64Bit() ? C_Invalid : IMAGE_FILE_32BIT_MACHINE), 483 }; 484 append(Buffer, Header); 485 486 // Section Header Table 487 const coff_section SectionTable[NumberOfSections] = { 488 {{'.', 'i', 'd', 'a', 't', 'a', '$', '5'}, 489 u32(0), 490 u32(0), 491 u32(VASize), 492 u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)), 493 u32(0), 494 u32(0), 495 u16(0), 496 u16(0), 497 u32((is64Bit() ? IMAGE_SCN_ALIGN_8BYTES : IMAGE_SCN_ALIGN_4BYTES) | 498 IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | 499 IMAGE_SCN_MEM_WRITE)}, 500 {{'.', 'i', 'd', 'a', 't', 'a', '$', '4'}, 501 u32(0), 502 u32(0), 503 u32(VASize), 504 u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) + 505 VASize), 506 u32(0), 507 u32(0), 508 u16(0), 509 u16(0), 510 u32((is64Bit() ? IMAGE_SCN_ALIGN_8BYTES : IMAGE_SCN_ALIGN_4BYTES) | 511 IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | 512 IMAGE_SCN_MEM_WRITE)}, 513 }; 514 append(Buffer, SectionTable); 515 516 // .idata$5, ILT 517 append(Buffer, u32(0)); 518 if (is64Bit()) 519 append(Buffer, u32(0)); 520 521 // .idata$4, IAT 522 append(Buffer, u32(0)); 523 if (is64Bit()) 524 append(Buffer, u32(0)); 525 526 // Symbol Table 527 coff_symbol16 SymbolTable[NumberOfSymbols] = { 528 {{{0, 0, 0, 0, 0, 0, 0, 0}}, 529 u32(0), 530 u16(1), 531 u16(0), 532 IMAGE_SYM_CLASS_EXTERNAL, 533 0}, 534 }; 535 SymbolTable[0].Name.Offset.Offset = sizeof(uint32_t); 536 append(Buffer, SymbolTable); 537 538 // String Table 539 writeStringTable(Buffer, {NullThunkSymbolName}); 540 541 StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()}; 542 return {MemoryBufferRef{F, ImportName}}; 543 } 544 545 NewArchiveMember 546 ObjectFactory::createShortImport(StringRef Sym, uint16_t Ordinal, 547 ImportType ImportType, ImportNameType NameType, 548 StringRef ExportName, MachineTypes Machine) { 549 size_t ImpSize = ImportName.size() + Sym.size() + 2; // +2 for NULs 550 if (!ExportName.empty()) 551 ImpSize += ExportName.size() + 1; 552 size_t Size = sizeof(coff_import_header) + ImpSize; 553 char *Buf = Alloc.Allocate<char>(Size); 554 memset(Buf, 0, Size); 555 char *P = Buf; 556 557 // Write short import library. 558 auto *Imp = reinterpret_cast<coff_import_header *>(P); 559 P += sizeof(*Imp); 560 Imp->Sig2 = 0xFFFF; 561 Imp->Machine = Machine; 562 Imp->SizeOfData = ImpSize; 563 if (Ordinal > 0) 564 Imp->OrdinalHint = Ordinal; 565 Imp->TypeInfo = (NameType << 2) | ImportType; 566 567 // Write symbol name and DLL name. 568 memcpy(P, Sym.data(), Sym.size()); 569 P += Sym.size() + 1; 570 memcpy(P, ImportName.data(), ImportName.size()); 571 if (!ExportName.empty()) { 572 P += ImportName.size() + 1; 573 memcpy(P, ExportName.data(), ExportName.size()); 574 } 575 576 return {MemoryBufferRef(StringRef(Buf, Size), ImportName)}; 577 } 578 579 NewArchiveMember ObjectFactory::createWeakExternal(StringRef Sym, 580 StringRef Weak, bool Imp, 581 MachineTypes Machine) { 582 std::vector<uint8_t> Buffer; 583 const uint32_t NumberOfSections = 1; 584 const uint32_t NumberOfSymbols = 5; 585 586 // COFF Header 587 coff_file_header Header{ 588 u16(Machine), 589 u16(NumberOfSections), 590 u32(0), 591 u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section))), 592 u32(NumberOfSymbols), 593 u16(0), 594 u16(0), 595 }; 596 append(Buffer, Header); 597 598 // Section Header Table 599 const coff_section SectionTable[NumberOfSections] = { 600 {{'.', 'd', 'r', 'e', 'c', 't', 'v', 'e'}, 601 u32(0), 602 u32(0), 603 u32(0), 604 u32(0), 605 u32(0), 606 u32(0), 607 u16(0), 608 u16(0), 609 u32(IMAGE_SCN_LNK_INFO | IMAGE_SCN_LNK_REMOVE)}}; 610 append(Buffer, SectionTable); 611 612 // Symbol Table 613 coff_symbol16 SymbolTable[NumberOfSymbols] = { 614 {{{'@', 'c', 'o', 'm', 'p', '.', 'i', 'd'}}, 615 u32(0), 616 u16(0xFFFF), 617 u16(0), 618 IMAGE_SYM_CLASS_STATIC, 619 0}, 620 {{{'@', 'f', 'e', 'a', 't', '.', '0', '0'}}, 621 u32(0), 622 u16(0xFFFF), 623 u16(0), 624 IMAGE_SYM_CLASS_STATIC, 625 0}, 626 {{{0, 0, 0, 0, 0, 0, 0, 0}}, 627 u32(0), 628 u16(0), 629 u16(0), 630 IMAGE_SYM_CLASS_EXTERNAL, 631 0}, 632 {{{0, 0, 0, 0, 0, 0, 0, 0}}, 633 u32(0), 634 u16(0), 635 u16(0), 636 IMAGE_SYM_CLASS_WEAK_EXTERNAL, 637 1}, 638 {{{2, 0, 0, 0, IMAGE_WEAK_EXTERN_SEARCH_ALIAS, 0, 0, 0}}, 639 u32(0), 640 u16(0), 641 u16(0), 642 IMAGE_SYM_CLASS_NULL, 643 0}, 644 }; 645 SymbolTable[2].Name.Offset.Offset = sizeof(uint32_t); 646 647 //__imp_ String Table 648 StringRef Prefix = Imp ? "__imp_" : ""; 649 SymbolTable[3].Name.Offset.Offset = 650 sizeof(uint32_t) + Sym.size() + Prefix.size() + 1; 651 append(Buffer, SymbolTable); 652 writeStringTable(Buffer, {(Prefix + Sym).str(), 653 (Prefix + Weak).str()}); 654 655 // Copied here so we can still use writeStringTable 656 char *Buf = Alloc.Allocate<char>(Buffer.size()); 657 memcpy(Buf, Buffer.data(), Buffer.size()); 658 return {MemoryBufferRef(StringRef(Buf, Buffer.size()), ImportName)}; 659 } 660 661 Error writeImportLibrary(StringRef ImportName, StringRef Path, 662 ArrayRef<COFFShortExport> Exports, 663 MachineTypes Machine, bool MinGW, 664 ArrayRef<COFFShortExport> NativeExports) { 665 666 MachineTypes NativeMachine = Machine; 667 if (isArm64EC(Machine)) { 668 NativeMachine = IMAGE_FILE_MACHINE_ARM64; 669 Machine = IMAGE_FILE_MACHINE_ARM64EC; 670 } 671 672 std::vector<NewArchiveMember> Members; 673 ObjectFactory OF(llvm::sys::path::filename(ImportName), NativeMachine); 674 675 std::vector<uint8_t> ImportDescriptor; 676 Members.push_back(OF.createImportDescriptor(ImportDescriptor)); 677 678 std::vector<uint8_t> NullImportDescriptor; 679 Members.push_back(OF.createNullImportDescriptor(NullImportDescriptor)); 680 681 std::vector<uint8_t> NullThunk; 682 Members.push_back(OF.createNullThunk(NullThunk)); 683 684 auto addExports = [&](ArrayRef<COFFShortExport> Exp, 685 MachineTypes M) -> Error { 686 StringMap<std::string> RegularImports; 687 struct Deferred { 688 std::string Name; 689 ImportType ImpType; 690 const COFFShortExport *Export; 691 }; 692 SmallVector<Deferred, 0> Renames; 693 for (const COFFShortExport &E : Exp) { 694 if (E.Private) 695 continue; 696 697 ImportType ImportType = IMPORT_CODE; 698 if (E.Data) 699 ImportType = IMPORT_DATA; 700 if (E.Constant) 701 ImportType = IMPORT_CONST; 702 703 StringRef SymbolName = E.SymbolName.empty() ? E.Name : E.SymbolName; 704 std::string Name; 705 706 if (E.ExtName.empty()) { 707 Name = std::string(SymbolName); 708 } else { 709 Expected<std::string> ReplacedName = 710 object::replace(SymbolName, E.Name, E.ExtName); 711 if (!ReplacedName) 712 return ReplacedName.takeError(); 713 Name.swap(*ReplacedName); 714 } 715 716 ImportNameType NameType; 717 std::string ExportName; 718 if (E.Noname) { 719 NameType = IMPORT_ORDINAL; 720 } else if (!E.ExportAs.empty()) { 721 NameType = IMPORT_NAME_EXPORTAS; 722 ExportName = E.ExportAs; 723 } else if (!E.ImportName.empty()) { 724 // If we need to import from a specific ImportName, we may need to use 725 // a weak alias (which needs another import to point at). But if we can 726 // express ImportName based on the symbol name and a specific NameType, 727 // prefer that over an alias. 728 if (Machine == IMAGE_FILE_MACHINE_I386 && 729 applyNameType(IMPORT_NAME_UNDECORATE, Name) == E.ImportName) 730 NameType = IMPORT_NAME_UNDECORATE; 731 else if (Machine == IMAGE_FILE_MACHINE_I386 && 732 applyNameType(IMPORT_NAME_NOPREFIX, Name) == E.ImportName) 733 NameType = IMPORT_NAME_NOPREFIX; 734 else if (isArm64EC(M)) { 735 NameType = IMPORT_NAME_EXPORTAS; 736 ExportName = E.ImportName; 737 } else if (Name == E.ImportName) 738 NameType = IMPORT_NAME; 739 else { 740 Deferred D; 741 D.Name = Name; 742 D.ImpType = ImportType; 743 D.Export = &E; 744 Renames.push_back(D); 745 continue; 746 } 747 } else { 748 NameType = getNameType(SymbolName, E.Name, M, MinGW); 749 } 750 751 // On ARM64EC, use EXPORTAS to import demangled name for mangled symbols. 752 if (ImportType == IMPORT_CODE && isArm64EC(M)) { 753 if (std::optional<std::string> MangledName = 754 getArm64ECMangledFunctionName(Name)) { 755 if (!E.Noname && ExportName.empty()) { 756 NameType = IMPORT_NAME_EXPORTAS; 757 ExportName.swap(Name); 758 } 759 Name = std::move(*MangledName); 760 } else if (!E.Noname && ExportName.empty()) { 761 std::optional<std::string> DemangledName = 762 getArm64ECDemangledFunctionName(Name); 763 if (!DemangledName) 764 return make_error<StringError>( 765 StringRef(Twine("Invalid ARM64EC function name '" + Name + "'") 766 .str()), 767 object_error::parse_failed); 768 NameType = IMPORT_NAME_EXPORTAS; 769 ExportName = std::move(*DemangledName); 770 } 771 } 772 773 RegularImports[applyNameType(NameType, Name)] = Name; 774 Members.push_back(OF.createShortImport(Name, E.Ordinal, ImportType, 775 NameType, ExportName, M)); 776 } 777 for (const auto &D : Renames) { 778 auto It = RegularImports.find(D.Export->ImportName); 779 if (It != RegularImports.end()) { 780 // We have a regular import entry for a symbol with the name we 781 // want to reference; produce an alias pointing at that. 782 StringRef Symbol = It->second; 783 if (D.ImpType == IMPORT_CODE) 784 Members.push_back(OF.createWeakExternal(Symbol, D.Name, false, M)); 785 Members.push_back(OF.createWeakExternal(Symbol, D.Name, true, M)); 786 } else { 787 Members.push_back(OF.createShortImport(D.Name, D.Export->Ordinal, 788 D.ImpType, IMPORT_NAME_EXPORTAS, 789 D.Export->ImportName, M)); 790 } 791 } 792 return Error::success(); 793 }; 794 795 if (Error e = addExports(Exports, Machine)) 796 return e; 797 if (Error e = addExports(NativeExports, NativeMachine)) 798 return e; 799 800 return writeArchive(Path, Members, SymtabWritingMode::NormalSymtab, 801 object::Archive::K_COFF, 802 /*Deterministic*/ true, /*Thin*/ false, 803 /*OldArchiveBuf*/ nullptr, isArm64EC(Machine)); 804 } 805 806 } // namespace object 807 } // namespace llvm 808