Index: COFF/CMakeLists.txt =================================================================== --- COFF/CMakeLists.txt +++ COFF/CMakeLists.txt @@ -10,6 +10,7 @@ Error.cpp ICF.cpp InputFiles.cpp + Librarian.cpp MarkLive.cpp ModuleDef.cpp PDB.cpp Index: COFF/DriverUtils.cpp =================================================================== --- COFF/DriverUtils.cpp +++ COFF/DriverUtils.cpp @@ -19,15 +19,12 @@ #include "Symbols.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSwitch.h" -#include "llvm/Object/Archive.h" -#include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/COFF.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileUtilities.h" -#include "llvm/Support/Path.h" #include "llvm/Support/Process.h" #include "llvm/Support/Program.h" #include "llvm/Support/raw_ostream.h" @@ -572,146 +569,6 @@ return std::move(*Ret); } -static std::string writeToTempFile(StringRef Contents) { - SmallString<128> Path; - int FD; - if (llvm::sys::fs::createTemporaryFile("tmp", "def", FD, Path)) { - llvm::errs() << "failed to create a temporary file\n"; - return ""; - } - llvm::raw_fd_ostream OS(FD, /*shouldClose*/ true); - OS << Contents; - return Path.str(); -} - -static std::string getImplibPath() { - if (!Config->Implib.empty()) - return Config->Implib; - SmallString<128> Out = StringRef(Config->OutputFile); - sys::path::replace_extension(Out, ".lib"); - return Out.str(); -} - -static std::unique_ptr createEmptyImportLibrary() { - std::string S = (Twine("LIBRARY \"") + - llvm::sys::path::filename(Config->OutputFile) + "\"\n") - .str(); - std::string Path1 = writeToTempFile(S); - std::string Path2 = getImplibPath(); - llvm::FileRemover Remover1(Path1); - llvm::FileRemover Remover2(Path2); - - Executor E("lib.exe"); - E.add("/nologo"); - E.add("/machine:" + machineToStr(Config->Machine)); - E.add(Twine("/def:") + Path1); - E.add(Twine("/out:") + Path2); - E.run(); - - ErrorOr> BufOrErr = - MemoryBuffer::getFile(Path2, -1, false); - error(BufOrErr, Twine("Failed to open ") + Path2); - return MemoryBuffer::getMemBufferCopy((*BufOrErr)->getBuffer()); -} - -static std::vector -readMembers(const object::Archive &Archive) { - std::vector V; - for (const auto &ChildOrErr : Archive.children()) { - error(ChildOrErr, "Archive::Child::getName failed"); - const object::Archive::Child C(*ChildOrErr); - NewArchiveMember M = - check(NewArchiveMember::getOldMember(C, /*Deterministic=*/true), - "NewArchiveMember::getOldMember failed"); - V.emplace_back(std::move(M)); - } - return V; -} - -// This class creates short import files which is described in -// PE/COFF spec 7. Import Library Format. -class ShortImportCreator { -public: - ShortImportCreator(StringRef S) : DLLName(S) {} - - NewArchiveMember create(StringRef Sym, uint16_t Ordinal, - ImportNameType NameType, bool isData) { - size_t ImpSize = DLLName.size() + Sym.size() + 2; // +2 for NULs - size_t Size = sizeof(coff_import_header) + ImpSize; - char *Buf = Alloc.Allocate(Size); - memset(Buf, 0, Size); - char *P = Buf; - - // Write short import library. - auto *Imp = reinterpret_cast(P); - P += sizeof(*Imp); - Imp->Sig2 = 0xFFFF; - Imp->Machine = Config->Machine; - Imp->SizeOfData = ImpSize; - if (Ordinal > 0) - Imp->OrdinalHint = Ordinal; - Imp->TypeInfo = (isData ? IMPORT_DATA : IMPORT_CODE); - Imp->TypeInfo |= NameType << 2; - - // Write symbol name and DLL name. - memcpy(P, Sym.data(), Sym.size()); - P += Sym.size() + 1; - memcpy(P, DLLName.data(), DLLName.size()); - - return NewArchiveMember(MemoryBufferRef(StringRef(Buf, Size), DLLName)); - } - -private: - BumpPtrAllocator Alloc; - StringRef DLLName; -}; - -static ImportNameType getNameType(StringRef Sym, StringRef ExtName) { - if (Sym != ExtName) - return IMPORT_NAME_UNDECORATE; - if (Config->Machine == I386 && Sym.startswith("_")) - return IMPORT_NAME_NOPREFIX; - return IMPORT_NAME; -} - -static std::string replace(StringRef S, StringRef From, StringRef To) { - size_t Pos = S.find(From); - assert(Pos != StringRef::npos); - return (Twine(S.substr(0, Pos)) + To + S.substr(Pos + From.size())).str(); -} - -// Creates an import library for a DLL. In this function, we first -// create an empty import library using lib.exe and then adds short -// import files to that file. -void writeImportLibrary() { - std::unique_ptr Buf = createEmptyImportLibrary(); - llvm::Error Err; - object::Archive Archive(Buf->getMemBufferRef(), Err); - error(std::move(Err), "Error reading an empty import file"); - std::vector Members = readMembers(Archive); - - std::string DLLName = llvm::sys::path::filename(Config->OutputFile); - ShortImportCreator ShortImport(DLLName); - for (Export &E : Config->Exports) { - if (E.Private) - continue; - if (E.ExtName.empty()) { - Members.push_back(ShortImport.create( - E.SymbolName, E.Ordinal, getNameType(E.SymbolName, E.Name), E.Data)); - } else { - Members.push_back(ShortImport.create( - replace(E.SymbolName, E.Name, E.ExtName), E.Ordinal, - getNameType(E.SymbolName, E.Name), E.Data)); - } - } - - std::string Path = getImplibPath(); - std::pair Result = - writeArchive(Path, Members, /*WriteSymtab*/ true, object::Archive::K_GNU, - /*Deterministic*/ true, /*Thin*/ false); - error(Result.second, Twine("Failed to write ") + Path); -} - // Create OptTable // Create prefix string literals used in Options.td Index: COFF/Librarian.cpp =================================================================== --- /dev/null +++ COFF/Librarian.cpp @@ -0,0 +1,484 @@ +//===- Librarian.cpp ------------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains functions for the Librarian. The librarian creates and +// manages libraries of the Common Object File Format (COFF) object files. It +// primarily is used for creating static libraries and import libraries. +// +//===----------------------------------------------------------------------===// + +#include "Config.h" +#include "Driver.h" +#include "Error.h" +#include "Symbols.h" +#include "llvm/Object/Archive.h" +#include "llvm/Object/ArchiveWriter.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Path.h" + +using namespace llvm::COFF; +using namespace llvm::object; +using namespace llvm; + +static bool is32bit() { + switch (lld::coff::Config->Machine) { + default: + llvm_unreachable("unsupported machine"); + case IMAGE_FILE_MACHINE_AMD64: + return false; + case IMAGE_FILE_MACHINE_ARMNT: + case IMAGE_FILE_MACHINE_I386: + return true; + } +} + +static uint16_t getImgRelRelocation() { + switch (lld::coff::Config->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_I386: + return IMAGE_REL_I386_DIR32NB; + } +} + +static char *writeFileHeader(char *P, uint32_t Sections, uint32_t RawDataSize, + uint32_t Symbols) { + auto *Header = reinterpret_cast(P); + Header->Machine = lld::coff::Config->Machine; + Header->NumberOfSections = Sections; + Header->PointerToSymbolTable = sizeof(coff_file_header) + + (Sections * sizeof(coff_section)) + + RawDataSize; + Header->NumberOfSymbols = Symbols; + Header->Characteristics = is32bit() ? IMAGE_FILE_32BIT_MACHINE : 0; + return P + sizeof(*Header); +} + +static char *writeSectionTable(char *P, ArrayRef Sections) { + for (const auto &S : Sections) { + auto *Section = reinterpret_cast(P); + strncpy(Section->Name, S.Name, sizeof(Section->Name)); + Section->SizeOfRawData = S.SizeOfRawData; + Section->PointerToRawData = S.PointerToRawData; + Section->PointerToRelocations = S.PointerToRelocations; + Section->NumberOfRelocations = S.NumberOfRelocations; + Section->Characteristics = S.Characteristics; + P += sizeof(*Section); + } + return P; +} + +static char *writeRelocationTable(char *P, + ArrayRef Relocations) { + for (const auto &R : Relocations) { + auto *Relocation = reinterpret_cast(P); + Relocation->VirtualAddress = R.VirtualAddress; + Relocation->SymbolTableIndex = R.SymbolTableIndex; + Relocation->Type = R.Type; + P += sizeof(*Relocation); + } + return P; +} + +static char *writeSymbolTable(char *P, ArrayRef Symbols) { + for (const auto &S : Symbols) { + auto *Symbol = reinterpret_cast(P); + memcpy(&Symbol->Name, S.Name, sizeof(Symbol->Name)); + Symbol->SectionNumber = S.SectionNumber; + Symbol->Type = S.Type; + Symbol->StorageClass = S.StorageClass; + P += sizeof(*Symbol); + } + return P; +} + +static char *writeStringTable(char *P, ArrayRef Strings) { + char *Size = P; + size_t Length = sizeof(uint32_t); + P += sizeof(uint32_t); + + for (const auto &S : Strings) { + strcpy(P, S.c_str()); + P += S.length() + 1; + Length += S.length() + 1; + } + + *reinterpret_cast(Size) = Length; + return P; +} + +static std::string getImplibPath() { + if (!lld::coff::Config->Implib.empty()) + return lld::coff::Config->Implib; + SmallString<128> Out = StringRef(lld::coff::Config->OutputFile); + sys::path::replace_extension(Out, ".lib"); + return Out.str(); +} + +static ImportNameType getNameType(StringRef Sym, StringRef ExtName) { + if (Sym != ExtName) + return IMPORT_NAME_UNDECORATE; + if (lld::coff::Config->Machine == lld::coff::I386 && Sym.startswith("_")) + return IMPORT_NAME_NOPREFIX; + return IMPORT_NAME; +} + +static std::string replace(StringRef S, StringRef From, StringRef To) { + size_t Pos = S.find(From); + assert(Pos != StringRef::npos); + return (Twine(S.substr(0, Pos)) + To + S.substr(Pos + From.size())).str(); +} + +static const std::string NullImportDescriptorSymbolName = + "__NULL_IMPORT_DESCRIPTOR"; + +namespace { +class ObjectFactory { + BumpPtrAllocator Alloc; + StringRef DLLName; + StringRef Library; + std::string ImportDescriptorSymbolName; + std::string NullThunkSymbolName; + +public: + ObjectFactory(StringRef S) + : DLLName(S), Library(S.drop_back(4)), + ImportDescriptorSymbolName(("__IMPORT_DESCRIPTOR_" + Library).str()), + NullThunkSymbolName(("\x7f" + Library + "_NULL_THUNK_DATA").str()) {} + + NewArchiveMember createImportDescriptor() { + static const uint32_t Sections = 2; + static const uint32_t Symbols = 7; + static const uint32_t Relocations = 3; + + uint32_t Size = + /* COFF Header */ + sizeof(coff_file_header) + + /* Section Header Table */ + (Sections * sizeof(coff_section)) + + /* .idata$2 */ + (sizeof(coff_import_directory_table_entry) + + Relocations * sizeof(coff_relocation)) + + /* .idata$6 */ + (DLLName.size() + 1) + + /* Symbol Table */ + (Symbols * sizeof(symbol)) + + /* String Table */ + (sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1 + + NullImportDescriptorSymbolName.length() + 1 + + NullThunkSymbolName.length() + 1); + + char *Buffer = Alloc.Allocate(Size); + memset(Buffer, 0, Size); + char *P = Buffer; + + /* COFF Header */ + P = writeFileHeader(P, Sections, + (sizeof(coff_import_directory_table_entry) + + Relocations * sizeof(coff_relocation)) + + (DLLName.size() + 1), + Symbols); + + /* Section Header Table */ + static const section SectionTable[Sections] = { + {{'.', 'i', 'd', 'a', 't', 'a', '$', '2'}, + 0, + 0, + sizeof(coff_import_directory_table_entry), + sizeof(coff_file_header) + Sections * sizeof(coff_section), + sizeof(coff_file_header) + Sections * sizeof(coff_section) + + sizeof(coff_import_directory_table_entry), + 0, + Relocations, + 0, + IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | + IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE}, + {{'.', 'i', 'd', 'a', 't', 'a', '$', '6'}, + 0, + 0, + static_cast(DLLName.size() + 1), + sizeof(coff_file_header) + Sections * sizeof(coff_section) + + sizeof(coff_import_directory_table_entry) + + Relocations * sizeof(coff_relocation), + 0, + 0, + 0, + 0, + IMAGE_SCN_ALIGN_2BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | + IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE}, + }; + P = writeSectionTable(P, SectionTable); + + /* .idata$2 */ + P += sizeof(coff_import_directory_table_entry); + + static const relocation RelocationTable[Relocations] = { + {offsetof(coff_import_directory_table_entry, NameRVA), 2, + getImgRelRelocation()}, + {offsetof(coff_import_directory_table_entry, ImportLookupTableRVA), 3, + getImgRelRelocation()}, + {offsetof(coff_import_directory_table_entry, ImportAddressTableRVA), 4, + getImgRelRelocation()}, + }; + P = writeRelocationTable(P, RelocationTable); + + /* .idata$6 */ + strncpy(P, DLLName.data(), DLLName.size()); + P += DLLName.size() + 1; + + /* Symbol Table */ + symbol SymbolTable[Symbols] = { + {{0, 0, 0, 0, 0, 0, 0}, 0, 1, 0, IMAGE_SYM_CLASS_EXTERNAL, 0}, + {{'.', 'i', 'd', 'a', 't', 'a', '$', '2'}, + 0, + 1, + 0, + IMAGE_SYM_CLASS_SECTION, + 0}, + {{'.', 'i', 'd', 'a', 't', 'a', '$', '6'}, + 0, + 2, + 0, + IMAGE_SYM_CLASS_STATIC, + 0}, + {{'.', 'i', 'd', 'a', 't', 'a', '$', '4'}, + 0, + 0, + 0, + IMAGE_SYM_CLASS_SECTION, + 0}, + {{'.', 'i', 'd', 'a', 't', 'a', '$', '5'}, + 0, + 0, + 0, + IMAGE_SYM_CLASS_SECTION, + 0}, + {{0, 0, 0, 0, 0, 0, 0, 0}, 0, 0, 0, IMAGE_SYM_CLASS_EXTERNAL, 0}, + {{0, 0, 0, 0, 0, 0, 0, 0}, 0, 0, 0, IMAGE_SYM_CLASS_EXTERNAL, 0}, + }; + reinterpret_cast(SymbolTable[0].Name).Offset = + sizeof(uint32_t); + reinterpret_cast(SymbolTable[5].Name).Offset = + sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1; + reinterpret_cast(SymbolTable[6].Name).Offset = + sizeof(uint32_t) + ImportDescriptorSymbolName.length() + 1 + + NullImportDescriptorSymbolName.length() + 1; + P = writeSymbolTable(P, SymbolTable); + + /* String Table */ + writeStringTable(P, {ImportDescriptorSymbolName, + NullImportDescriptorSymbolName, NullThunkSymbolName}); + + return {MemoryBufferRef(StringRef(Buffer, Size), DLLName)}; + } + + NewArchiveMember createNullImportDescriptor() { + static const uint32_t Sections = 1; + static const uint32_t Symbols = 1; + + uint32_t Size = + /* COFF Header */ + sizeof(coff_file_header) + + /* Section Header Table */ + (Sections * sizeof(coff_section)) + + /* .idata$3 */ + sizeof(coff_import_directory_table_entry) + + /* Symbol Table */ + (Symbols * + (is32bit() ? sizeof(coff_symbol16) : sizeof(coff_symbol32))) + + /* String Tablee */ + (sizeof(uint32_t) + NullImportDescriptorSymbolName.length() + 1); + + char *Buffer = Alloc.Allocate(Size); + memset(Buffer, 0, Size); + char *P = Buffer; + + /* COFF Header */ + P = writeFileHeader(P, Sections, sizeof(coff_import_directory_table_entry), + Symbols); + + /* Section Header Table */ + static const section SectionTable[Sections] = { + {{'.', 'i', 'd', 'a', 't', 'a', '$', '3'}, + 0, + 0, + sizeof(coff_import_directory_table_entry), + sizeof(coff_file_header) + (Sections * sizeof(coff_section)), + 0, + 0, + 0, + 0, + IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | + IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE}, + }; + P = writeSectionTable(P, SectionTable); + + /* .idata$3 */ + P += sizeof(coff_import_directory_table_entry); + + /* Symbol Table */ + symbol SymbolTable[Symbols] = { + {{0, 0, 0, 0, 0, 0, 0, 0}, 0, 1, 0, IMAGE_SYM_CLASS_EXTERNAL, 0}, + }; + reinterpret_cast(SymbolTable[0].Name).Offset = + sizeof(uint32_t); + P = writeSymbolTable(P, SymbolTable); + + /* String Table */ + writeStringTable(P, {NullImportDescriptorSymbolName}); + + return {MemoryBufferRef(StringRef(Buffer, Size), DLLName)}; + } + + NewArchiveMember createNullThunk() { + static const uint32_t Sections = 2; + static const uint32_t Symbols = 1; + + uint32_t Size = + /* COFF Header */ + sizeof(coff_file_header) + + /* Section Header Table */ + (Sections * sizeof(coff_section)) + + /* .idata$5 */ + sizeof(export_address_table_entry) + + /* .idata$4 */ + sizeof(export_address_table_entry) + + /* Symbol Table */ + (Symbols * + (is32bit() ? sizeof(coff_symbol16) : sizeof(coff_symbol32))) + + /* String Table */ + (sizeof(uint32_t) + NullThunkSymbolName.length() + 1); + + char *Buffer = Alloc.Allocate(Size); + memset(Buffer, 0, Size); + char *P = Buffer; + + /* COFF Header */ + P = writeFileHeader(P, Sections, sizeof(export_address_table_entry) + + sizeof(export_address_table_entry), + Symbols); + + /* Section Header Table */ + static const section SectionTable[Sections] = { + {{'.', 'i', 'd', 'a', 't', 'a', '$', '5'}, + 0, + 0, + sizeof(export_address_table_entry), + sizeof(coff_file_header) + Sections * sizeof(coff_section), + 0, + 0, + 0, + 0, + IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | + IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE}, + {{'.', 'i', 'd', 'a', 't', 'a', '$', '4'}, + 0, + 0, + sizeof(export_address_table_entry), + sizeof(coff_file_header) + Sections * sizeof(coff_section) + + sizeof(export_address_table_entry), + 0, + 0, + 0, + 0, + IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_CNT_INITIALIZED_DATA | + IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE}, + }; + P = writeSectionTable(P, SectionTable); + + /* .idata$5 */ + P += sizeof(export_address_table_entry); + + /* .idata$4 */ + P += sizeof(export_address_table_entry); + + /* Symbol Table */ + symbol SymbolTable[Symbols] = { + {{0, 0, 0, 0, 0, 0, 0, 0}, 0, 1, 0, IMAGE_SYM_CLASS_EXTERNAL, 0}, + }; + reinterpret_cast(SymbolTable[0].Name).Offset = + sizeof(uint32_t); + P = writeSymbolTable(P, SymbolTable); + + /* String Table */ + writeStringTable(P, {NullThunkSymbolName}); + + return {MemoryBufferRef(StringRef(Buffer, Size), DLLName)}; + } + + // This class creates short import files which is described in + // PE/COFF spec 7. Import Library Format. + NewArchiveMember createShortImport(StringRef Sym, uint16_t Ordinal, + ImportNameType NameType, bool isData) { + size_t ImpSize = DLLName.size() + Sym.size() + 2; // +2 for NULs + size_t Size = sizeof(coff_import_header) + ImpSize; + char *Buf = Alloc.Allocate(Size); + memset(Buf, 0, Size); + char *P = Buf; + + // Write short import library. + auto *Imp = reinterpret_cast(P); + P += sizeof(*Imp); + Imp->Sig2 = 0xFFFF; + Imp->Machine = lld::coff::Config->Machine; + Imp->SizeOfData = ImpSize; + if (Ordinal > 0) + Imp->OrdinalHint = Ordinal; + Imp->TypeInfo = (isData ? IMPORT_DATA : IMPORT_CODE); + Imp->TypeInfo |= NameType << 2; + + // Write symbol name and DLL name. + memcpy(P, Sym.data(), Sym.size()); + P += Sym.size() + 1; + memcpy(P, DLLName.data(), DLLName.size()); + + return {MemoryBufferRef(StringRef(Buf, Size), DLLName)}; + } +}; +} + +namespace lld { +namespace coff { +// Creates an import library for a DLL. In this function, we first +// create an empty import library using lib.exe and then adds short +// import files to that file. +void writeImportLibrary() { + std::vector Members; + + std::string Path = getImplibPath(); + std::string DLLName = llvm::sys::path::filename(Config->OutputFile); + ObjectFactory OF(DLLName); + + Members.push_back(OF.createImportDescriptor()); + Members.push_back(OF.createNullImportDescriptor()); + Members.push_back(OF.createNullThunk()); + + for (Export &E : Config->Exports) { + if (E.Private) + continue; + + ImportNameType Type = getNameType(E.SymbolName, E.Name); + std::string Name = E.ExtName.empty() + ? std::string(E.SymbolName) + : replace(E.SymbolName, E.Name, E.ExtName); + Members.push_back(OF.createShortImport(Name, E.Ordinal, Type, E.Data)); + } + + std::pair Result = + writeArchive(Path, Members, /*WriteSymtab*/ true, object::Archive::K_GNU, + /*Deterministic*/ true, /*Thin*/ false); + error(Result.second, Twine("Failed to write ") + Path); +} +} +} +