| //===- COFFImportFile.cpp - COFF short import file implementation ---------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file defines the writeImportLibrary function. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/Object/COFFImportFile.h" | |
| #include "llvm/ADT/ArrayRef.h" | |
| #include "llvm/ADT/SmallVector.h" | |
| #include "llvm/ADT/StringMap.h" | |
| #include "llvm/ADT/Twine.h" | |
| #include "llvm/Object/Archive.h" | |
| #include "llvm/Object/ArchiveWriter.h" | |
| #include "llvm/Object/COFF.h" | |
| #include "llvm/Support/Allocator.h" | |
| #include "llvm/Support/Endian.h" | |
| #include "llvm/Support/Error.h" | |
| #include "llvm/Support/ErrorHandling.h" | |
| #include "llvm/Support/Path.h" | |
| #include <cstdint> | |
| #include <string> | |
| #include <vector> | |
| using namespace llvm::COFF; | |
| using namespace llvm::object; | |
| using namespace llvm; | |
| namespace llvm { | |
| namespace object { | |
| StringRef COFFImportFile::getFileFormatName() const { | |
| switch (getMachine()) { | |
| case COFF::IMAGE_FILE_MACHINE_I386: | |
| return "COFF-import-file-i386"; | |
| case COFF::IMAGE_FILE_MACHINE_AMD64: | |
| return "COFF-import-file-x86-64"; | |
| case COFF::IMAGE_FILE_MACHINE_ARMNT: | |
| return "COFF-import-file-ARM"; | |
| case COFF::IMAGE_FILE_MACHINE_ARM64: | |
| return "COFF-import-file-ARM64"; | |
| case COFF::IMAGE_FILE_MACHINE_ARM64EC: | |
| return "COFF-import-file-ARM64EC"; | |
| case COFF::IMAGE_FILE_MACHINE_ARM64X: | |
| return "COFF-import-file-ARM64X"; | |
| default: | |
| return "COFF-import-file-<unknown arch>"; | |
| } | |
| } | |
| static StringRef applyNameType(ImportNameType Type, StringRef name) { | |
| auto ltrim1 = [](StringRef s, StringRef chars) { | |
| return !s.empty() && chars.contains(s[0]) ? s.substr(1) : s; | |
| }; | |
| switch (Type) { | |
| case IMPORT_NAME_NOPREFIX: | |
| name = ltrim1(name, "?@_"); | |
| break; | |
| case IMPORT_NAME_UNDECORATE: | |
| name = ltrim1(name, "?@_"); | |
| name = name.substr(0, name.find('@')); | |
| break; | |
| default: | |
| break; | |
| } | |
| return name; | |
| } | |
| StringRef COFFImportFile::getExportName() const { | |
| const coff_import_header *hdr = getCOFFImportHeader(); | |
| StringRef name = Data.getBuffer().substr(sizeof(*hdr)).split('\0').first; | |
| switch (hdr->getNameType()) { | |
| case IMPORT_ORDINAL: | |
| name = ""; | |
| break; | |
| case IMPORT_NAME_NOPREFIX: | |
| case IMPORT_NAME_UNDECORATE: | |
| name = applyNameType(static_cast<ImportNameType>(hdr->getNameType()), name); | |
| break; | |
| case IMPORT_NAME_EXPORTAS: { | |
| // Skip DLL name | |
| name = Data.getBuffer().substr(sizeof(*hdr) + name.size() + 1); | |
| name = name.split('\0').second.split('\0').first; | |
| break; | |
| } | |
| default: | |
| break; | |
| } | |
| return name; | |
| } | |
| Error COFFImportFile::printSymbolName(raw_ostream &OS, DataRefImpl Symb) const { | |
| switch (Symb.p) { | |
| case ImpSymbol: | |
| OS << "__imp_"; | |
| break; | |
| case ECAuxSymbol: | |
| OS << "__imp_aux_"; | |
| break; | |
| } | |
| const char *Name = Data.getBufferStart() + sizeof(coff_import_header); | |
| if (Symb.p != ECThunkSymbol && COFF::isArm64EC(getMachine())) { | |
| if (std::optional<std::string> DemangledName = | |
| getArm64ECDemangledFunctionName(Name)) { | |
| OS << StringRef(*DemangledName); | |
| return Error::success(); | |
| } | |
| } | |
| OS << StringRef(Name); | |
| return Error::success(); | |
| } | |
| static uint16_t getImgRelRelocation(MachineTypes Machine) { | |
| switch (Machine) { | |
| default: | |
| llvm_unreachable("unsupported machine"); | |
| case IMAGE_FILE_MACHINE_AMD64: | |
| return IMAGE_REL_AMD64_ADDR32NB; | |
| case IMAGE_FILE_MACHINE_ARMNT: | |
| return IMAGE_REL_ARM_ADDR32NB; | |
| case IMAGE_FILE_MACHINE_ARM64: | |
| case IMAGE_FILE_MACHINE_ARM64EC: | |
| case IMAGE_FILE_MACHINE_ARM64X: | |
| return IMAGE_REL_ARM64_ADDR32NB; | |
| case IMAGE_FILE_MACHINE_I386: | |
| return IMAGE_REL_I386_DIR32NB; | |
| case IMAGE_FILE_MACHINE_R4000: | |
| return IMAGE_REL_MIPS_REFWORDNB; | |
| } | |
| } | |
| template <class T> static void append(std::vector<uint8_t> &B, const T &Data) { | |
| size_t S = B.size(); | |
| B.resize(S + sizeof(T)); | |
| memcpy(&B[S], &Data, sizeof(T)); | |
| } | |
| static void writeStringTable(std::vector<uint8_t> &B, | |
| ArrayRef<const std::string_view> Strings) { | |
| // The COFF string table consists of a 4-byte value which is the size of the | |
| // table, including the length field itself. This value is followed by the | |
| // string content itself, which is an array of null-terminated C-style | |
| // strings. The termination is important as they are referenced to by offset | |
| // by the symbol entity in the file format. | |
| size_t Pos = B.size(); | |
| size_t Offset = B.size(); | |
| // Skip over the length field, we will fill it in later as we will have | |
| // computed the length while emitting the string content itself. | |
| Pos += sizeof(uint32_t); | |
| for (const auto &S : Strings) { | |
| B.resize(Pos + S.length() + 1); | |
| llvm::copy(S, std::next(B.begin(), Pos)); | |
| B[Pos + S.length()] = 0; | |
| Pos += S.length() + 1; | |
| } | |
| // Backfill the length of the table now that it has been computed. | |
| support::ulittle32_t Length(B.size() - Offset); | |
| support::endian::write32le(&B[Offset], Length); | |
| } | |
| static ImportNameType getNameType(StringRef Sym, StringRef ExtName, | |
| MachineTypes Machine, bool MinGW) { | |
| // A decorated stdcall function in MSVC is exported with the | |
| // type IMPORT_NAME, and the exported function name includes the | |
| // the leading underscore. In MinGW on the other hand, a decorated | |
| // stdcall function still omits the underscore (IMPORT_NAME_NOPREFIX). | |
| // See the comment in isDecorated in COFFModuleDefinition.cpp for more | |
| // details. | |
| if (ExtName.starts_with("_") && ExtName.contains('@') && !MinGW) | |
| return IMPORT_NAME; | |
| if (Sym != ExtName) | |
| return IMPORT_NAME_UNDECORATE; | |
| if (Machine == IMAGE_FILE_MACHINE_I386 && Sym.starts_with("_")) | |
| return IMPORT_NAME_NOPREFIX; | |
| return IMPORT_NAME; | |
| } | |
| static Expected<std::string> replace(StringRef S, StringRef From, | |
| StringRef To) { | |
| size_t Pos = S.find(From); | |
| // From and To may be mangled, but substrings in S may not. | |
| if (Pos == StringRef::npos && From.starts_with("_") && To.starts_with("_")) { | |
| From = From.substr(1); | |
| To = To.substr(1); | |
| Pos = S.find(From); | |
| } | |
| if (Pos == StringRef::npos) { | |
| return make_error<StringError>( | |
| StringRef(Twine(S + ": replacing '" + From + | |
| "' with '" + To + "' failed").str()), object_error::parse_failed); | |
| } | |
| return (Twine(S.substr(0, Pos)) + To + S.substr(Pos + From.size())).str(); | |
| } | |
| namespace { | |
| // This class constructs various small object files necessary to support linking | |
| // symbols imported from a DLL. The contents are pretty strictly defined and | |
| // nearly entirely static. The details of the structures files are defined in | |
| // WINNT.h and the PE/COFF specification. | |
| class ObjectFactory { | |
| using u16 = support::ulittle16_t; | |
| using u32 = support::ulittle32_t; | |
| MachineTypes NativeMachine; | |
| BumpPtrAllocator Alloc; | |
| StringRef ImportName; | |
| StringRef Library; | |
| std::string ImportDescriptorSymbolName; | |
| std::string NullThunkSymbolName; | |
| public: | |
| ObjectFactory(StringRef S, MachineTypes M) | |
| : NativeMachine(M), ImportName(S), Library(llvm::sys::path::stem(S)), | |
| ImportDescriptorSymbolName((ImportDescriptorPrefix + Library).str()), | |
| NullThunkSymbolName( | |
| (NullThunkDataPrefix + Library + NullThunkDataSuffix).str()) {} | |
| // Creates an Import Descriptor. This is a small object file which contains a | |
| // reference to the terminators and contains the library name (entry) for the | |
| // import name table. It will force the linker to construct the necessary | |
| // structure to import symbols from the DLL. | |
| NewArchiveMember createImportDescriptor(std::vector<uint8_t> &Buffer); | |
| // Creates a NULL import descriptor. This is a small object file whcih | |
| // contains a NULL import descriptor. It is used to terminate the imports | |
| // from a specific DLL. | |
| NewArchiveMember createNullImportDescriptor(std::vector<uint8_t> &Buffer); | |
| // Create a NULL Thunk Entry. This is a small object file which contains a | |
| // NULL Import Address Table entry and a NULL Import Lookup Table Entry. It | |
| // is used to terminate the IAT and ILT. | |
| NewArchiveMember createNullThunk(std::vector<uint8_t> &Buffer); | |
| // Create a short import file which is described in PE/COFF spec 7. Import | |
| // Library Format. | |
| NewArchiveMember createShortImport(StringRef Sym, uint16_t Ordinal, | |
| ImportType Type, ImportNameType NameType, | |
| StringRef ExportName, | |
| MachineTypes Machine); | |
| // Create a weak external file which is described in PE/COFF Aux Format 3. | |
| NewArchiveMember createWeakExternal(StringRef Sym, StringRef Weak, bool Imp, | |
| MachineTypes Machine); | |
| bool is64Bit() const { return COFF::is64Bit(NativeMachine); } | |
| }; | |
| } // namespace | |
| NewArchiveMember | |
| ObjectFactory::createImportDescriptor(std::vector<uint8_t> &Buffer) { | |
| const uint32_t NumberOfSections = 2; | |
| const uint32_t NumberOfSymbols = 7; | |
| const uint32_t NumberOfRelocations = 3; | |
| // COFF Header | |
| coff_file_header Header{ | |
| u16(NativeMachine), | |
| u16(NumberOfSections), | |
| u32(0), | |
| u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + | |
| // .idata$2 | |
| sizeof(coff_import_directory_table_entry) + | |
| NumberOfRelocations * sizeof(coff_relocation) + | |
| // .idata$4 | |
| (ImportName.size() + 1)), | |
| u32(NumberOfSymbols), | |
| u16(0), | |
| u16(is64Bit() ? C_Invalid : IMAGE_FILE_32BIT_MACHINE), | |
| }; | |
| append(Buffer, Header); | |
| // Section Header Table | |
| const coff_section SectionTable[NumberOfSections] = { | |
| {{'.', 'i', 'd', 'a', 't', 'a', '$', '2'}, | |
| u32(0), | |
| u32(0), | |
| u32(sizeof(coff_import_directory_table_entry)), | |
| u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)), | |
| u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) + | |
| sizeof(coff_import_directory_table_entry)), | |
| u32(0), | |
| u16(NumberOfRelocations), | |
| u16(0), | |
| u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | | |
| IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, | |
| {{'.', 'i', 'd', 'a', 't', 'a', '$', '6'}, | |
| u32(0), | |
| u32(0), | |
| u32(ImportName.size() + 1), | |
| u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) + | |
| sizeof(coff_import_directory_table_entry) + | |
| NumberOfRelocations * sizeof(coff_relocation)), | |
| u32(0), | |
| u32(0), | |
| u16(0), | |
| u16(0), | |
| u32(IMAGE_SCN_ALIGN_2BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | | |
| IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, | |
| }; | |
| append(Buffer, SectionTable); | |
| // .idata$2 | |
| const coff_import_directory_table_entry ImportDescriptor{ | |
| u32(0), u32(0), u32(0), u32(0), u32(0), | |
| }; | |
| append(Buffer, ImportDescriptor); | |
| const coff_relocation RelocationTable[NumberOfRelocations] = { | |
| {u32(offsetof(coff_import_directory_table_entry, NameRVA)), u32(2), | |
| u16(getImgRelRelocation(NativeMachine))}, | |
| {u32(offsetof(coff_import_directory_table_entry, ImportLookupTableRVA)), | |
| u32(3), u16(getImgRelRelocation(NativeMachine))}, | |
| {u32(offsetof(coff_import_directory_table_entry, ImportAddressTableRVA)), | |
| u32(4), u16(getImgRelRelocation(NativeMachine))}, | |
| }; | |
| append(Buffer, RelocationTable); | |
| // .idata$6 | |
| auto S = Buffer.size(); | |
| Buffer.resize(S + ImportName.size() + 1); | |
| memcpy(&Buffer[S], ImportName.data(), ImportName.size()); | |
| Buffer[S + ImportName.size()] = '\0'; | |
| // Symbol Table | |
| coff_symbol16 SymbolTable[NumberOfSymbols] = { | |
| {{{0, 0, 0, 0, 0, 0, 0, 0}}, | |
| u32(0), | |
| u16(1), | |
| u16(0), | |
| IMAGE_SYM_CLASS_EXTERNAL, | |
| 0}, | |
| {{{'.', 'i', 'd', 'a', 't', 'a', '$', '2'}}, | |
| u32(0), | |
| u16(1), | |
| u16(0), | |
| IMAGE_SYM_CLASS_SECTION, | |
| 0}, | |
| {{{'.', 'i', 'd', 'a', 't', 'a', '$', '6'}}, | |
| u32(0), | |
| u16(2), | |
| u16(0), | |
| IMAGE_SYM_CLASS_STATIC, | |
| 0}, | |
| {{{'.', 'i', 'd', 'a', 't', 'a', '$', '4'}}, | |
| u32(0), | |
| u16(0), | |
| u16(0), | |
| IMAGE_SYM_CLASS_SECTION, | |
| 0}, | |
| {{{'.', 'i', 'd', 'a', 't', 'a', '$', '5'}}, | |
| u32(0), | |
| u16(0), | |
| u16(0), | |
| IMAGE_SYM_CLASS_SECTION, | |
| 0}, | |
| {{{0, 0, 0, 0, 0, 0, 0, 0}}, | |
| u32(0), | |
| u16(0), | |
| u16(0), | |
| IMAGE_SYM_CLASS_EXTERNAL, | |
| 0}, | |
| {{{0, 0, 0, 0, 0, 0, 0, 0}}, | |
| u32(0), | |
| u16(0), | |
| u16(0), | |
| IMAGE_SYM_CLASS_EXTERNAL, | |
| 0}, | |
| }; | |
| // TODO: Name.Offset.Offset here and in the all similar places below | |
| // suggests a names refactoring. Maybe StringTableOffset.Value? | |
| SymbolTable[0].Name.Offset.Offset = | |
| sizeof(uint32_t); | |
| SymbolTable[5].Name.Offset.Offset = | |
| sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1; | |
| SymbolTable[6].Name.Offset.Offset = | |
| sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1 + | |
| NullImportDescriptorSymbolName.length() + 1; | |
| append(Buffer, SymbolTable); | |
| // String Table | |
| writeStringTable(Buffer, | |
| {ImportDescriptorSymbolName, NullImportDescriptorSymbolName, | |
| NullThunkSymbolName}); | |
| StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()}; | |
| return {MemoryBufferRef(F, ImportName)}; | |
| } | |
| NewArchiveMember | |
| ObjectFactory::createNullImportDescriptor(std::vector<uint8_t> &Buffer) { | |
| const uint32_t NumberOfSections = 1; | |
| const uint32_t NumberOfSymbols = 1; | |
| // COFF Header | |
| coff_file_header Header{ | |
| u16(NativeMachine), | |
| u16(NumberOfSections), | |
| u32(0), | |
| u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + | |
| // .idata$3 | |
| sizeof(coff_import_directory_table_entry)), | |
| u32(NumberOfSymbols), | |
| u16(0), | |
| u16(is64Bit() ? C_Invalid : IMAGE_FILE_32BIT_MACHINE), | |
| }; | |
| append(Buffer, Header); | |
| // Section Header Table | |
| const coff_section SectionTable[NumberOfSections] = { | |
| {{'.', 'i', 'd', 'a', 't', 'a', '$', '3'}, | |
| u32(0), | |
| u32(0), | |
| u32(sizeof(coff_import_directory_table_entry)), | |
| u32(sizeof(coff_file_header) + | |
| (NumberOfSections * sizeof(coff_section))), | |
| u32(0), | |
| u32(0), | |
| u16(0), | |
| u16(0), | |
| u32(IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | | |
| IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)}, | |
| }; | |
| append(Buffer, SectionTable); | |
| // .idata$3 | |
| const coff_import_directory_table_entry ImportDescriptor{ | |
| u32(0), u32(0), u32(0), u32(0), u32(0), | |
| }; | |
| append(Buffer, ImportDescriptor); | |
| // Symbol Table | |
| coff_symbol16 SymbolTable[NumberOfSymbols] = { | |
| {{{0, 0, 0, 0, 0, 0, 0, 0}}, | |
| u32(0), | |
| u16(1), | |
| u16(0), | |
| IMAGE_SYM_CLASS_EXTERNAL, | |
| 0}, | |
| }; | |
| SymbolTable[0].Name.Offset.Offset = sizeof(uint32_t); | |
| append(Buffer, SymbolTable); | |
| // String Table | |
| writeStringTable(Buffer, {NullImportDescriptorSymbolName}); | |
| StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()}; | |
| return {MemoryBufferRef(F, ImportName)}; | |
| } | |
| NewArchiveMember ObjectFactory::createNullThunk(std::vector<uint8_t> &Buffer) { | |
| const uint32_t NumberOfSections = 2; | |
| const uint32_t NumberOfSymbols = 1; | |
| uint32_t VASize = is64Bit() ? 8 : 4; | |
| // COFF Header | |
| coff_file_header Header{ | |
| u16(NativeMachine), | |
| u16(NumberOfSections), | |
| u32(0), | |
| u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + | |
| // .idata$5 | |
| VASize + | |
| // .idata$4 | |
| VASize), | |
| u32(NumberOfSymbols), | |
| u16(0), | |
| u16(is64Bit() ? C_Invalid : IMAGE_FILE_32BIT_MACHINE), | |
| }; | |
| append(Buffer, Header); | |
| // Section Header Table | |
| const coff_section SectionTable[NumberOfSections] = { | |
| {{'.', 'i', 'd', 'a', 't', 'a', '$', '5'}, | |
| u32(0), | |
| u32(0), | |
| u32(VASize), | |
| u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section)), | |
| u32(0), | |
| u32(0), | |
| u16(0), | |
| u16(0), | |
| u32((is64Bit() ? IMAGE_SCN_ALIGN_8BYTES : IMAGE_SCN_ALIGN_4BYTES) | | |
| IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | | |
| IMAGE_SCN_MEM_WRITE)}, | |
| {{'.', 'i', 'd', 'a', 't', 'a', '$', '4'}, | |
| u32(0), | |
| u32(0), | |
| u32(VASize), | |
| u32(sizeof(coff_file_header) + NumberOfSections * sizeof(coff_section) + | |
| VASize), | |
| u32(0), | |
| u32(0), | |
| u16(0), | |
| u16(0), | |
| u32((is64Bit() ? IMAGE_SCN_ALIGN_8BYTES : IMAGE_SCN_ALIGN_4BYTES) | | |
| IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | | |
| IMAGE_SCN_MEM_WRITE)}, | |
| }; | |
| append(Buffer, SectionTable); | |
| // .idata$5, ILT | |
| append(Buffer, u32(0)); | |
| if (is64Bit()) | |
| append(Buffer, u32(0)); | |
| // .idata$4, IAT | |
| append(Buffer, u32(0)); | |
| if (is64Bit()) | |
| append(Buffer, u32(0)); | |
| // Symbol Table | |
| coff_symbol16 SymbolTable[NumberOfSymbols] = { | |
| {{{0, 0, 0, 0, 0, 0, 0, 0}}, | |
| u32(0), | |
| u16(1), | |
| u16(0), | |
| IMAGE_SYM_CLASS_EXTERNAL, | |
| 0}, | |
| }; | |
| SymbolTable[0].Name.Offset.Offset = sizeof(uint32_t); | |
| append(Buffer, SymbolTable); | |
| // String Table | |
| writeStringTable(Buffer, {NullThunkSymbolName}); | |
| StringRef F{reinterpret_cast<const char *>(Buffer.data()), Buffer.size()}; | |
| return {MemoryBufferRef{F, ImportName}}; | |
| } | |
| NewArchiveMember | |
| ObjectFactory::createShortImport(StringRef Sym, uint16_t Ordinal, | |
| ImportType ImportType, ImportNameType NameType, | |
| StringRef ExportName, MachineTypes Machine) { | |
| size_t ImpSize = ImportName.size() + Sym.size() + 2; // +2 for NULs | |
| if (!ExportName.empty()) | |
| ImpSize += ExportName.size() + 1; | |
| size_t Size = sizeof(coff_import_header) + ImpSize; | |
| char *Buf = Alloc.Allocate<char>(Size); | |
| memset(Buf, 0, Size); | |
| char *P = Buf; | |
| // Write short import library. | |
| auto *Imp = reinterpret_cast<coff_import_header *>(P); | |
| P += sizeof(*Imp); | |
| Imp->Sig2 = 0xFFFF; | |
| Imp->Machine = Machine; | |
| Imp->SizeOfData = ImpSize; | |
| if (Ordinal > 0) | |
| Imp->OrdinalHint = Ordinal; | |
| Imp->TypeInfo = (NameType << 2) | ImportType; | |
| // Write symbol name and DLL name. | |
| memcpy(P, Sym.data(), Sym.size()); | |
| P += Sym.size() + 1; | |
| memcpy(P, ImportName.data(), ImportName.size()); | |
| if (!ExportName.empty()) { | |
| P += ImportName.size() + 1; | |
| memcpy(P, ExportName.data(), ExportName.size()); | |
| } | |
| return {MemoryBufferRef(StringRef(Buf, Size), ImportName)}; | |
| } | |
| NewArchiveMember ObjectFactory::createWeakExternal(StringRef Sym, | |
| StringRef Weak, bool Imp, | |
| MachineTypes Machine) { | |
| std::vector<uint8_t> Buffer; | |
| const uint32_t NumberOfSections = 1; | |
| const uint32_t NumberOfSymbols = 5; | |
| // COFF Header | |
| coff_file_header Header{ | |
| u16(Machine), | |
| u16(NumberOfSections), | |
| u32(0), | |
| u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section))), | |
| u32(NumberOfSymbols), | |
| u16(0), | |
| u16(0), | |
| }; | |
| append(Buffer, Header); | |
| // Section Header Table | |
| const coff_section SectionTable[NumberOfSections] = { | |
| {{'.', 'd', 'r', 'e', 'c', 't', 'v', 'e'}, | |
| u32(0), | |
| u32(0), | |
| u32(0), | |
| u32(0), | |
| u32(0), | |
| u32(0), | |
| u16(0), | |
| u16(0), | |
| u32(IMAGE_SCN_LNK_INFO | IMAGE_SCN_LNK_REMOVE)}}; | |
| append(Buffer, SectionTable); | |
| // Symbol Table | |
| coff_symbol16 SymbolTable[NumberOfSymbols] = { | |
| {{{'@', 'c', 'o', 'm', 'p', '.', 'i', 'd'}}, | |
| u32(0), | |
| u16(0xFFFF), | |
| u16(0), | |
| IMAGE_SYM_CLASS_STATIC, | |
| 0}, | |
| {{{'@', 'f', 'e', 'a', 't', '.', '0', '0'}}, | |
| u32(0), | |
| u16(0xFFFF), | |
| u16(0), | |
| IMAGE_SYM_CLASS_STATIC, | |
| 0}, | |
| {{{0, 0, 0, 0, 0, 0, 0, 0}}, | |
| u32(0), | |
| u16(0), | |
| u16(0), | |
| IMAGE_SYM_CLASS_EXTERNAL, | |
| 0}, | |
| {{{0, 0, 0, 0, 0, 0, 0, 0}}, | |
| u32(0), | |
| u16(0), | |
| u16(0), | |
| IMAGE_SYM_CLASS_WEAK_EXTERNAL, | |
| 1}, | |
| {{{2, 0, 0, 0, IMAGE_WEAK_EXTERN_SEARCH_ALIAS, 0, 0, 0}}, | |
| u32(0), | |
| u16(0), | |
| u16(0), | |
| IMAGE_SYM_CLASS_NULL, | |
| 0}, | |
| }; | |
| SymbolTable[2].Name.Offset.Offset = sizeof(uint32_t); | |
| //__imp_ String Table | |
| StringRef Prefix = Imp ? "__imp_" : ""; | |
| SymbolTable[3].Name.Offset.Offset = | |
| sizeof(uint32_t) + Sym.size() + Prefix.size() + 1; | |
| append(Buffer, SymbolTable); | |
| writeStringTable(Buffer, {(Prefix + Sym).str(), | |
| (Prefix + Weak).str()}); | |
| // Copied here so we can still use writeStringTable | |
| char *Buf = Alloc.Allocate<char>(Buffer.size()); | |
| memcpy(Buf, Buffer.data(), Buffer.size()); | |
| return {MemoryBufferRef(StringRef(Buf, Buffer.size()), ImportName)}; | |
| } | |
| Error writeImportLibrary(StringRef ImportName, StringRef Path, | |
| ArrayRef<COFFShortExport> Exports, | |
| MachineTypes Machine, bool MinGW, | |
| ArrayRef<COFFShortExport> NativeExports) { | |
| MachineTypes NativeMachine = Machine; | |
| if (isArm64EC(Machine)) { | |
| NativeMachine = IMAGE_FILE_MACHINE_ARM64; | |
| Machine = IMAGE_FILE_MACHINE_ARM64EC; | |
| } | |
| std::vector<NewArchiveMember> Members; | |
| ObjectFactory OF(llvm::sys::path::filename(ImportName), NativeMachine); | |
| std::vector<uint8_t> ImportDescriptor; | |
| Members.push_back(OF.createImportDescriptor(ImportDescriptor)); | |
| std::vector<uint8_t> NullImportDescriptor; | |
| Members.push_back(OF.createNullImportDescriptor(NullImportDescriptor)); | |
| std::vector<uint8_t> NullThunk; | |
| Members.push_back(OF.createNullThunk(NullThunk)); | |
| auto addExports = [&](ArrayRef<COFFShortExport> Exp, | |
| MachineTypes M) -> Error { | |
| StringMap<std::string> RegularImports; | |
| struct Deferred { | |
| std::string Name; | |
| ImportType ImpType; | |
| const COFFShortExport *Export; | |
| }; | |
| SmallVector<Deferred, 0> Renames; | |
| for (const COFFShortExport &E : Exp) { | |
| if (E.Private) | |
| continue; | |
| ImportType ImportType = IMPORT_CODE; | |
| if (E.Data) | |
| ImportType = IMPORT_DATA; | |
| if (E.Constant) | |
| ImportType = IMPORT_CONST; | |
| StringRef SymbolName = E.SymbolName.empty() ? E.Name : E.SymbolName; | |
| std::string Name; | |
| if (E.ExtName.empty()) { | |
| Name = std::string(SymbolName); | |
| } else { | |
| Expected<std::string> ReplacedName = | |
| object::replace(SymbolName, E.Name, E.ExtName); | |
| if (!ReplacedName) | |
| return ReplacedName.takeError(); | |
| Name.swap(*ReplacedName); | |
| } | |
| ImportNameType NameType; | |
| std::string ExportName; | |
| if (E.Noname) { | |
| NameType = IMPORT_ORDINAL; | |
| } else if (!E.ExportAs.empty()) { | |
| NameType = IMPORT_NAME_EXPORTAS; | |
| ExportName = E.ExportAs; | |
| } else if (!E.ImportName.empty()) { | |
| // If we need to import from a specific ImportName, we may need to use | |
| // a weak alias (which needs another import to point at). But if we can | |
| // express ImportName based on the symbol name and a specific NameType, | |
| // prefer that over an alias. | |
| if (Machine == IMAGE_FILE_MACHINE_I386 && | |
| applyNameType(IMPORT_NAME_UNDECORATE, Name) == E.ImportName) | |
| NameType = IMPORT_NAME_UNDECORATE; | |
| else if (Machine == IMAGE_FILE_MACHINE_I386 && | |
| applyNameType(IMPORT_NAME_NOPREFIX, Name) == E.ImportName) | |
| NameType = IMPORT_NAME_NOPREFIX; | |
| else if (isArm64EC(M)) { | |
| NameType = IMPORT_NAME_EXPORTAS; | |
| ExportName = E.ImportName; | |
| } else if (Name == E.ImportName) | |
| NameType = IMPORT_NAME; | |
| else { | |
| Deferred D; | |
| D.Name = Name; | |
| D.ImpType = ImportType; | |
| D.Export = &E; | |
| Renames.push_back(D); | |
| continue; | |
| } | |
| } else { | |
| NameType = getNameType(SymbolName, E.Name, M, MinGW); | |
| } | |
| // On ARM64EC, use EXPORTAS to import demangled name for mangled symbols. | |
| if (ImportType == IMPORT_CODE && isArm64EC(M)) { | |
| if (std::optional<std::string> MangledName = | |
| getArm64ECMangledFunctionName(Name)) { | |
| if (!E.Noname && ExportName.empty()) { | |
| NameType = IMPORT_NAME_EXPORTAS; | |
| ExportName.swap(Name); | |
| } | |
| Name = std::move(*MangledName); | |
| } else if (!E.Noname && ExportName.empty()) { | |
| std::optional<std::string> DemangledName = | |
| getArm64ECDemangledFunctionName(Name); | |
| if (!DemangledName) | |
| return make_error<StringError>( | |
| StringRef(Twine("Invalid ARM64EC function name '" + Name + "'") | |
| .str()), | |
| object_error::parse_failed); | |
| NameType = IMPORT_NAME_EXPORTAS; | |
| ExportName = std::move(*DemangledName); | |
| } | |
| } | |
| RegularImports[applyNameType(NameType, Name)] = Name; | |
| Members.push_back(OF.createShortImport(Name, E.Ordinal, ImportType, | |
| NameType, ExportName, M)); | |
| } | |
| for (const auto &D : Renames) { | |
| auto It = RegularImports.find(D.Export->ImportName); | |
| if (It != RegularImports.end()) { | |
| // We have a regular import entry for a symbol with the name we | |
| // want to reference; produce an alias pointing at that. | |
| StringRef Symbol = It->second; | |
| if (D.ImpType == IMPORT_CODE) | |
| Members.push_back(OF.createWeakExternal(Symbol, D.Name, false, M)); | |
| Members.push_back(OF.createWeakExternal(Symbol, D.Name, true, M)); | |
| } else { | |
| Members.push_back(OF.createShortImport(D.Name, D.Export->Ordinal, | |
| D.ImpType, IMPORT_NAME_EXPORTAS, | |
| D.Export->ImportName, M)); | |
| } | |
| } | |
| return Error::success(); | |
| }; | |
| if (Error e = addExports(Exports, Machine)) | |
| return e; | |
| if (Error e = addExports(NativeExports, NativeMachine)) | |
| return e; | |
| return writeArchive(Path, Members, SymtabWritingMode::NormalSymtab, | |
| object::Archive::K_COFF, | |
| /*Deterministic*/ true, /*Thin*/ false, | |
| /*OldArchiveBuf*/ nullptr, isArm64EC(Machine)); | |
| } | |
| } // namespace object | |
| } // namespace llvm |