Index: COFF/DriverUtils.cpp =================================================================== --- COFF/DriverUtils.cpp +++ COFF/DriverUtils.cpp @@ -34,6 +34,7 @@ #include using namespace llvm::COFF; +using namespace llvm::object; using namespace llvm; using llvm::cl::ExpandResponseFiles; using llvm::cl::TokenizeWindowsCommandLine; @@ -572,18 +573,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; @@ -592,50 +581,375 @@ 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); +class ObjectFactory { + BumpPtrAllocator Alloc; + StringRef DLLName; + StringRef Library; + std::string ImportDescriptorSymbolName; + std::string NullImportDescriptorSymbolName; + std::string NullThunkSymbolName; + + static bool is32bit() { + switch (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; + } + } - 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(); + static uint16_t getImgRelRelocation() { + switch (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; + } + } - 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)); + static char *writeFileHeader(char *P, uint32_t Sections, + uint32_t RawDataSize, + uint32_t Symbols) { + auto *Header = reinterpret_cast(P); + Header->Machine = 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; } - 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) {} + ObjectFactory(StringRef S) + : DLLName(S), Library(S.drop_back(4)), + ImportDescriptorSymbolName(("__IMPORT_DESCRIPTOR_" + Library).str()), + NullImportDescriptorSymbolName("__NULL_IMPORT_DESCRIPTOR"), + 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)}; + } - NewArchiveMember create(StringRef Sym, uint16_t Ordinal, - ImportNameType NameType, bool isData) { + // 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); @@ -658,12 +972,8 @@ P += Sym.size() + 1; memcpy(P, DLLName.data(), DLLName.size()); - return NewArchiveMember(MemoryBufferRef(StringRef(Buf, Size), DLLName)); + return {MemoryBufferRef(StringRef(Buf, Size), DLLName)}; } - -private: - BumpPtrAllocator Alloc; - StringRef DLLName; }; static ImportNameType getNameType(StringRef Sym, StringRef ExtName) { @@ -684,28 +994,27 @@ // 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::vector Members; + std::string Path = getImplibPath(); std::string DLLName = llvm::sys::path::filename(Config->OutputFile); - ShortImportCreator ShortImport(DLLName); + 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; - 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)); - } + + 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::string Path = getImplibPath(); std::pair Result = writeArchive(Path, Members, /*WriteSymtab*/ true, object::Archive::K_GNU, /*Deterministic*/ true, /*Thin*/ false);