diff options
author | orivej <[email protected]> | 2022-02-10 16:44:49 +0300 |
---|---|---|
committer | Daniil Cherednik <[email protected]> | 2022-02-10 16:44:49 +0300 |
commit | 718c552901d703c502ccbefdfc3c9028d608b947 (patch) | |
tree | 46534a98bbefcd7b1f3faa5b52c138ab27db75b7 /contrib/libs/llvm12/lib/Object/COFFImportFile.cpp | |
parent | e9656aae26e0358d5378e5b63dcac5c8dbe0e4d0 (diff) |
Restoring authorship annotation for <[email protected]>. Commit 1 of 2.
Diffstat (limited to 'contrib/libs/llvm12/lib/Object/COFFImportFile.cpp')
-rw-r--r-- | contrib/libs/llvm12/lib/Object/COFFImportFile.cpp | 1250 |
1 files changed, 625 insertions, 625 deletions
diff --git a/contrib/libs/llvm12/lib/Object/COFFImportFile.cpp b/contrib/libs/llvm12/lib/Object/COFFImportFile.cpp index 69bbf70b43a..1244cabe75a 100644 --- a/contrib/libs/llvm12/lib/Object/COFFImportFile.cpp +++ b/contrib/libs/llvm12/lib/Object/COFFImportFile.cpp @@ -1,625 +1,625 @@ -//===- 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/Object/Archive.h" -#include "llvm/Object/ArchiveWriter.h" -#include "llvm/Object/COFF.h" -#include "llvm/Support/Error.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 { - -static bool is32bit(MachineTypes Machine) { - switch (Machine) { - default: - llvm_unreachable("unsupported machine"); - case IMAGE_FILE_MACHINE_ARM64: - case IMAGE_FILE_MACHINE_AMD64: - return false; - case IMAGE_FILE_MACHINE_ARMNT: - case IMAGE_FILE_MACHINE_I386: - return true; - } -} - -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: - return IMAGE_REL_ARM64_ADDR32NB; - case IMAGE_FILE_MACHINE_I386: - return IMAGE_REL_I386_DIR32NB; - } -} - -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> 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); - strcpy(reinterpret_cast<char *>(&B[Pos]), S.c_str()); - 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.startswith("_") && ExtName.contains('@') && !MinGW) - return IMPORT_NAME; - if (Sym != ExtName) - return IMPORT_NAME_UNDECORATE; - if (Machine == IMAGE_FILE_MACHINE_I386 && Sym.startswith("_")) - 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.startswith("_") && To.startswith("_")) { - 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(); -} - -static const std::string NullImportDescriptorSymbolName = - "__NULL_IMPORT_DESCRIPTOR"; - -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 Machine; - BumpPtrAllocator Alloc; - StringRef ImportName; - StringRef Library; - std::string ImportDescriptorSymbolName; - std::string NullThunkSymbolName; - -public: - ObjectFactory(StringRef S, MachineTypes M) - : Machine(M), ImportName(S), Library(S.drop_back(4)), - ImportDescriptorSymbolName(("__IMPORT_DESCRIPTOR_" + Library).str()), - NullThunkSymbolName(("\x7f" + Library + "_NULL_THUNK_DATA").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); - - // Create a weak external file which is described in PE/COFF Aux Format 3. - NewArchiveMember createWeakExternal(StringRef Sym, StringRef Weak, bool Imp); -}; -} // 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(Machine), - 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(is32bit(Machine) ? IMAGE_FILE_32BIT_MACHINE : C_Invalid), - }; - 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(Machine))}, - {u32(offsetof(coff_import_directory_table_entry, ImportLookupTableRVA)), - u32(3), u16(getImgRelRelocation(Machine))}, - {u32(offsetof(coff_import_directory_table_entry, ImportAddressTableRVA)), - u32(4), u16(getImgRelRelocation(Machine))}, - }; - 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(Machine), - u16(NumberOfSections), - u32(0), - u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + - // .idata$3 - sizeof(coff_import_directory_table_entry)), - u32(NumberOfSymbols), - u16(0), - u16(is32bit(Machine) ? IMAGE_FILE_32BIT_MACHINE : C_Invalid), - }; - 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 = is32bit(Machine) ? 4 : 8; - - // COFF Header - coff_file_header Header{ - u16(Machine), - u16(NumberOfSections), - u32(0), - u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + - // .idata$5 - VASize + - // .idata$4 - VASize), - u32(NumberOfSymbols), - u16(0), - u16(is32bit(Machine) ? IMAGE_FILE_32BIT_MACHINE : C_Invalid), - }; - 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((is32bit(Machine) ? IMAGE_SCN_ALIGN_4BYTES - : IMAGE_SCN_ALIGN_8BYTES) | - 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((is32bit(Machine) ? IMAGE_SCN_ALIGN_4BYTES - : IMAGE_SCN_ALIGN_8BYTES) | - IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | - IMAGE_SCN_MEM_WRITE)}, - }; - append(Buffer, SectionTable); - - // .idata$5, ILT - append(Buffer, u32(0)); - if (!is32bit(Machine)) - append(Buffer, u32(0)); - - // .idata$4, IAT - append(Buffer, u32(0)); - if (!is32bit(Machine)) - 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) { - size_t ImpSize = ImportName.size() + Sym.size() + 2; // +2 for NULs - 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()); - - return {MemoryBufferRef(StringRef(Buf, Size), ImportName)}; -} - -NewArchiveMember ObjectFactory::createWeakExternal(StringRef Sym, - StringRef Weak, bool Imp) { - 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) { - - std::vector<NewArchiveMember> Members; - ObjectFactory OF(llvm::sys::path::filename(ImportName), Machine); - - 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)); - - for (COFFShortExport E : Exports) { - 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; - ImportNameType NameType = E.Noname - ? IMPORT_ORDINAL - : getNameType(SymbolName, E.Name, - Machine, MinGW); - Expected<std::string> Name = E.ExtName.empty() - ? std::string(SymbolName) - : replace(SymbolName, E.Name, E.ExtName); - - if (!Name) - return Name.takeError(); - - if (!E.AliasTarget.empty() && *Name != E.AliasTarget) { - Members.push_back(OF.createWeakExternal(E.AliasTarget, *Name, false)); - Members.push_back(OF.createWeakExternal(E.AliasTarget, *Name, true)); - continue; - } - - Members.push_back( - OF.createShortImport(*Name, E.Ordinal, ImportType, NameType)); - } - - return writeArchive(Path, Members, /*WriteSymtab*/ true, - object::Archive::K_GNU, - /*Deterministic*/ true, /*Thin*/ false); -} - -} // namespace object -} // namespace llvm +//===- 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/Object/Archive.h" +#include "llvm/Object/ArchiveWriter.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Error.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 { + +static bool is32bit(MachineTypes Machine) { + switch (Machine) { + default: + llvm_unreachable("unsupported machine"); + case IMAGE_FILE_MACHINE_ARM64: + case IMAGE_FILE_MACHINE_AMD64: + return false; + case IMAGE_FILE_MACHINE_ARMNT: + case IMAGE_FILE_MACHINE_I386: + return true; + } +} + +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: + return IMAGE_REL_ARM64_ADDR32NB; + case IMAGE_FILE_MACHINE_I386: + return IMAGE_REL_I386_DIR32NB; + } +} + +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> 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); + strcpy(reinterpret_cast<char *>(&B[Pos]), S.c_str()); + 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.startswith("_") && ExtName.contains('@') && !MinGW) + return IMPORT_NAME; + if (Sym != ExtName) + return IMPORT_NAME_UNDECORATE; + if (Machine == IMAGE_FILE_MACHINE_I386 && Sym.startswith("_")) + 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.startswith("_") && To.startswith("_")) { + 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(); +} + +static const std::string NullImportDescriptorSymbolName = + "__NULL_IMPORT_DESCRIPTOR"; + +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 Machine; + BumpPtrAllocator Alloc; + StringRef ImportName; + StringRef Library; + std::string ImportDescriptorSymbolName; + std::string NullThunkSymbolName; + +public: + ObjectFactory(StringRef S, MachineTypes M) + : Machine(M), ImportName(S), Library(S.drop_back(4)), + ImportDescriptorSymbolName(("__IMPORT_DESCRIPTOR_" + Library).str()), + NullThunkSymbolName(("\x7f" + Library + "_NULL_THUNK_DATA").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); + + // Create a weak external file which is described in PE/COFF Aux Format 3. + NewArchiveMember createWeakExternal(StringRef Sym, StringRef Weak, bool Imp); +}; +} // 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(Machine), + 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(is32bit(Machine) ? IMAGE_FILE_32BIT_MACHINE : C_Invalid), + }; + 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(Machine))}, + {u32(offsetof(coff_import_directory_table_entry, ImportLookupTableRVA)), + u32(3), u16(getImgRelRelocation(Machine))}, + {u32(offsetof(coff_import_directory_table_entry, ImportAddressTableRVA)), + u32(4), u16(getImgRelRelocation(Machine))}, + }; + 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(Machine), + u16(NumberOfSections), + u32(0), + u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + + // .idata$3 + sizeof(coff_import_directory_table_entry)), + u32(NumberOfSymbols), + u16(0), + u16(is32bit(Machine) ? IMAGE_FILE_32BIT_MACHINE : C_Invalid), + }; + 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 = is32bit(Machine) ? 4 : 8; + + // COFF Header + coff_file_header Header{ + u16(Machine), + u16(NumberOfSections), + u32(0), + u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section)) + + // .idata$5 + VASize + + // .idata$4 + VASize), + u32(NumberOfSymbols), + u16(0), + u16(is32bit(Machine) ? IMAGE_FILE_32BIT_MACHINE : C_Invalid), + }; + 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((is32bit(Machine) ? IMAGE_SCN_ALIGN_4BYTES + : IMAGE_SCN_ALIGN_8BYTES) | + 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((is32bit(Machine) ? IMAGE_SCN_ALIGN_4BYTES + : IMAGE_SCN_ALIGN_8BYTES) | + IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | + IMAGE_SCN_MEM_WRITE)}, + }; + append(Buffer, SectionTable); + + // .idata$5, ILT + append(Buffer, u32(0)); + if (!is32bit(Machine)) + append(Buffer, u32(0)); + + // .idata$4, IAT + append(Buffer, u32(0)); + if (!is32bit(Machine)) + 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) { + size_t ImpSize = ImportName.size() + Sym.size() + 2; // +2 for NULs + 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()); + + return {MemoryBufferRef(StringRef(Buf, Size), ImportName)}; +} + +NewArchiveMember ObjectFactory::createWeakExternal(StringRef Sym, + StringRef Weak, bool Imp) { + 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) { + + std::vector<NewArchiveMember> Members; + ObjectFactory OF(llvm::sys::path::filename(ImportName), Machine); + + 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)); + + for (COFFShortExport E : Exports) { + 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; + ImportNameType NameType = E.Noname + ? IMPORT_ORDINAL + : getNameType(SymbolName, E.Name, + Machine, MinGW); + Expected<std::string> Name = E.ExtName.empty() + ? std::string(SymbolName) + : replace(SymbolName, E.Name, E.ExtName); + + if (!Name) + return Name.takeError(); + + if (!E.AliasTarget.empty() && *Name != E.AliasTarget) { + Members.push_back(OF.createWeakExternal(E.AliasTarget, *Name, false)); + Members.push_back(OF.createWeakExternal(E.AliasTarget, *Name, true)); + continue; + } + + Members.push_back( + OF.createShortImport(*Name, E.Ordinal, ImportType, NameType)); + } + + return writeArchive(Path, Members, /*WriteSymtab*/ true, + object::Archive::K_GNU, + /*Deterministic*/ true, /*Thin*/ false); +} + +} // namespace object +} // namespace llvm |