Index: include/llvm/DlltoolDriver/DlltoolDriver.h =================================================================== --- /dev/null +++ include/llvm/DlltoolDriver/DlltoolDriver.h @@ -0,0 +1,24 @@ +//===- DlltoolDriver.h - dlltool.exe-compatible driver ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines an interface to a dlltool.exe-compatible driver. +// Used by llvm-dlltool. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_DLLTOOLDRIVER_DLLTOOLDRIVER_H +#define LLVM_DLLTOOLDRIVER_DLLTOOLDRIVER_H + +namespace llvm { +template class ArrayRef; + +int dlltoolDriverMain(ArrayRef ArgsArr); +} + +#endif \ No newline at end of file Index: include/llvm/Object/COFFImportFile.h =================================================================== --- include/llvm/Object/COFFImportFile.h +++ include/llvm/Object/COFFImportFile.h @@ -82,6 +82,12 @@ std::string SymbolName; + bool isWeak() { + if(ExtName.size() && Name != ExtName) + return true; + return false; + } + friend bool operator==(const COFFShortExport &L, const COFFShortExport &R) { return L.Name == R.Name && L.ExtName == R.ExtName && L.Ordinal == R.Ordinal && L.Noname == R.Noname && Index: include/llvm/Object/COFFModuleDefinition.h =================================================================== --- include/llvm/Object/COFFModuleDefinition.h +++ include/llvm/Object/COFFModuleDefinition.h @@ -41,7 +41,8 @@ }; Expected -parseCOFFModuleDefinition(MemoryBufferRef MB, COFF::MachineTypes Machine); +parseCOFFModuleDefinition(MemoryBufferRef MB, COFF::MachineTypes Machine, + bool MingwDef = false); } // End namespace object. } // End namespace llvm. Index: lib/CMakeLists.txt =================================================================== --- lib/CMakeLists.txt +++ lib/CMakeLists.txt @@ -21,5 +21,6 @@ add_subdirectory(ProfileData) add_subdirectory(Fuzzer) add_subdirectory(Passes) +add_subdirectory(DlltoolDriver) add_subdirectory(LibDriver) add_subdirectory(XRay) Index: lib/DlltoolDriver/CMakeLists.txt =================================================================== --- /dev/null +++ lib/DlltoolDriver/CMakeLists.txt @@ -0,0 +1,9 @@ +set(LLVM_TARGET_DEFINITIONS Options.td) +tablegen(LLVM Options.inc -gen-opt-parser-defs) +add_public_tablegen_target(DllOptionsTableGen) + +add_llvm_library(LLVMDlltoolDriver + DlltoolDriver.cpp + ) + +add_dependencies(LLVMDlltoolDriver DllOptionsTableGen) \ No newline at end of file Index: lib/DlltoolDriver/DlltoolDriver.cpp =================================================================== --- /dev/null +++ lib/DlltoolDriver/DlltoolDriver.cpp @@ -0,0 +1,180 @@ +//===- DlltoolDriver.cpp - dlltool.exe-compatible driver ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines an interface to a dlltool.exe-compatible driver. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DlltoolDriver/DlltoolDriver.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Object/ArchiveWriter.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Object/COFFImportFile.h" +#include "llvm/Object/COFFModuleDefinition.h" +#include "llvm/Object/COFF.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/StringSaver.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include +#include +#include + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::COFF; + +namespace { + +enum { + OPT_INVALID = 0, +#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11) OPT_##ID, +#include "Options.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Options.inc" +#undef PREFIX + +static const llvm::opt::OptTable::Info infoTable[] = { +#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X6, X7, X8, X9, X10) \ + { \ + X1, X2, X9, X10, OPT_##ID, llvm::opt::Option::KIND##Class, X8, X7, \ + OPT_##GROUP, OPT_##ALIAS, X6 \ + }, +#include "Options.inc" +#undef OPTION +}; + +class DllOptTable : public llvm::opt::OptTable { +public: + DllOptTable() : OptTable(infoTable, false) {} +}; + +} + +std::vector> OwningMBs; + +// Opens a file. Path has to be resolved already. +// Newly created memory buffers are owned by this driver. +MemoryBufferRef openFile(StringRef Path) { + llvm::ErrorOr> MB = + MemoryBuffer::getFile(Path); + + if (std::error_code EC = MB.getError()) + llvm::errs() << "fail openFile: " << EC.message() << "\n"; + + MemoryBufferRef MBRef = MB.get()->getMemBufferRef(); + OwningMBs.push_back(std::move(MB.get())); // take ownership + return MBRef; +} + +static MachineTypes getEmulation(StringRef S) { + return StringSwitch(S) + .Case("x86", IMAGE_FILE_MACHINE_I386) + .Case("x86_64", IMAGE_FILE_MACHINE_AMD64) + .Case("arm", IMAGE_FILE_MACHINE_ARMNT) + .Default(IMAGE_FILE_MACHINE_UNKNOWN); +} + +static std::string getImplibPath(std::string Path) { + SmallString<128> Out = StringRef("lib"); + Out.append(Path); + sys::path::replace_extension(Out, ".a"); + return Out.str(); +} + +int writeImportLibrary(std::string &Path); + +int llvm::dlltoolDriverMain(llvm::ArrayRef ArgsArr) { + SmallVector NewArgs(ArgsArr.begin(), ArgsArr.end()); + BumpPtrAllocator Alloc; + StringSaver Saver(Alloc); + COFF::MachineTypes Machine = IMAGE_FILE_MACHINE_UNKNOWN; + cl::ExpandResponseFiles(Saver, cl::TokenizeWindowsCommandLine, NewArgs); + ArgsArr = NewArgs; + + DllOptTable Table; + unsigned MissingIndex; + unsigned MissingCount; + llvm::opt::InputArgList Args = + Table.ParseArgs(ArgsArr.slice(1), MissingIndex, MissingCount); + if (MissingCount) { + llvm::errs() << "missing arg value for \"" + << Args.getArgString(MissingIndex) << "\", expected " + << MissingCount + << (MissingCount == 1 ? " argument.\n" : " arguments.\n"); + return 1; + } + for (auto *Arg : Args.filtered(OPT_UNKNOWN)) + llvm::errs() << "ignoring unknown argument: " << Arg->getSpelling() << "\n"; + + if (!Args.hasArgNoClaim(OPT_INPUT)) + return 0; + + if (!Args.hasArgNoClaim(OPT_emu)) { + llvm::errs() << "Need to set an emulation mode\n"; + return -1; + } + + std::string Path; + MemoryBufferRef MB; + int i=0; + for (auto *Arg : Args.filtered(OPT_INPUT)) { + if(i==0) + MB = openFile(Arg->getValue()); + if(i==1) + Path = Arg->getValue(); + i++; + } + + if (auto *Arg = Args.getLastArg(OPT_out)) + Path = Arg->getValue(); + + if (auto *Arg = Args.getLastArg(OPT_emu)) + Machine = getEmulation(Arg->getValue()); + + if(Machine == IMAGE_FILE_MACHINE_UNKNOWN) { + llvm::errs() << "unknown emulation mode\n"; + return -2; + } + + Expected ImportCOFFMod = + parseCOFFModuleDefinition(MB, Machine, true); + + if (!ImportCOFFMod) { + llvm::errs() << "error parsing definition\n" << + errorToErrorCode(ImportCOFFMod.takeError()).message(); + return -2; + } + + // has to happen after the parser + if (auto *Arg = Args.getLastArg(OPT_dll)) + ImportCOFFMod->OutputFile = Arg->getValue(); + + if(!Path.length()) + Path = getImplibPath(ImportCOFFMod->OutputFile); + + std::error_code import_error = + writeImportLibrary(ImportCOFFMod->OutputFile, + Path, + ImportCOFFMod->Exports, + Machine); + + if(!import_error) + return 0; + return -1; +} \ No newline at end of file Index: lib/DlltoolDriver/LLVMBuild.txt =================================================================== --- /dev/null +++ lib/DlltoolDriver/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./lib/DlltoolDriver/LLVMBuild.txt ------------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = DlltoolDriver +parent = Libraries +required_libraries = Object Option Support \ No newline at end of file Index: lib/DlltoolDriver/Options.td =================================================================== --- /dev/null +++ lib/DlltoolDriver/Options.td @@ -0,0 +1,5 @@ +include "llvm/Option/OptParser.td" + +def emu: JoinedOrSeparate<["-"], "a">, HelpText<"Set target arch">; +def out: JoinedOrSeparate<["-"], "o">, HelpText<"Set output file">; +def dll: JoinedOrSeparate<["-"], "d">, HelpText<"Set dll name">; \ No newline at end of file Index: lib/Object/ArchiveWriter.cpp =================================================================== --- lib/Object/ArchiveWriter.cpp +++ lib/Object/ArchiveWriter.cpp @@ -314,7 +314,8 @@ continue; if (!(Symflags & object::SymbolRef::SF_Global)) continue; - if (Symflags & object::SymbolRef::SF_Undefined) + if (Symflags & object::SymbolRef::SF_Undefined && + !(Symflags & object::SymbolRef::SF_Weak)) continue; unsigned NameOffset = NameOS.tell(); Index: lib/Object/COFFImportFile.cpp =================================================================== --- lib/Object/COFFImportFile.cpp +++ lib/Object/COFFImportFile.cpp @@ -162,6 +162,10 @@ // 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(std::vector &Buffer, + StringRef Sym, StringRef Weak, bool imp); }; } // namespace @@ -476,6 +480,89 @@ return {MemoryBufferRef(StringRef(Buf, Size), DLLName)}; } +NewArchiveMember ObjectFactory::createWeakExternal(std::vector &Buffer, + StringRef Sym, StringRef Weak, bool imp) { + static const uint32_t NumberOfSections = 1; + static const uint32_t NumberOfSymbols = 5; + + // COFF Header + coff_file_header Header{ + u16(0), u16(NumberOfSections), u32(0), + u32(sizeof(Header) + (NumberOfSections * sizeof(coff_section))), + u32(NumberOfSymbols), u16(0), + u16(0), + }; + append(Buffer, Header); + + // Section Header Table + static 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, 3, 0, 0, 0}}, + u32(0), + u16(0), + u16(0), + uint8_t(0), + 0}, + }; + reinterpret_cast(SymbolTable[2].Name).Offset = + sizeof(uint32_t); + + //__imp_ String Table + if(imp) { + reinterpret_cast(SymbolTable[3].Name).Offset = + sizeof(uint32_t) + Sym.size() + 1 + 6; + writeStringTable(Buffer, {std::string("__imp_").append(Sym), + std::string("__imp_").append(Weak)}); + } else { + reinterpret_cast(SymbolTable[3].Name).Offset = + sizeof(uint32_t) + Sym.size() + 1; + writeStringTable(Buffer, {Sym, Weak}); + } + append(Buffer, SymbolTable); + + StringRef F{reinterpret_cast(Buffer.data()), Buffer.size()}; + return {MemoryBufferRef{F, DLLName}}; + +} + std::error_code writeImportLibrary(StringRef DLLName, StringRef Path, ArrayRef Exports, MachineTypes Machine) { @@ -496,6 +583,14 @@ if (E.Private) continue; + if (E.isWeak()) { + std::vector *WeakBuffer1 = new std::vector(); + std::vector *WeakBuffer2 = new std::vector(); + Members.push_back(OF.createWeakExternal(*WeakBuffer1, E.Name, E.ExtName, false)); + Members.push_back(OF.createWeakExternal(*WeakBuffer2, E.Name, E.ExtName, true)); + continue; + } + ImportType ImportType = IMPORT_CODE; if (E.Data) ImportType = IMPORT_DATA; Index: lib/Object/COFFModuleDefinition.cpp =================================================================== --- lib/Object/COFFModuleDefinition.cpp +++ lib/Object/COFFModuleDefinition.cpp @@ -55,8 +55,10 @@ StringRef Value; }; -static bool isDecorated(StringRef Sym) { - return Sym.startswith("_") || Sym.startswith("@") || Sym.startswith("?"); +static bool isDecorated(StringRef Sym, bool MingwDef) { + // Disable Sym.startswith("_") for mingw-w64 + return (!MingwDef && Sym.startswith("_")) || + Sym.startswith("@") || Sym.startswith("?"); } static Error createError(const Twine &Err) { @@ -82,6 +84,10 @@ return lex(); } case '=': + // This lets us handle "==" which is a dlltool ism + if (Buf[1] == '=') + Buf = Buf.drop_front(); + Buf = Buf.drop_front(); return Token(Equal, "="); case ',': @@ -120,7 +126,8 @@ class Parser { public: - explicit Parser(StringRef S, MachineTypes M) : Lex(S), Machine(M) {} + explicit Parser(StringRef S, MachineTypes M, + bool B) : Lex(S), Machine(M), MingwDef(B) {} Expected parse() { do { @@ -213,9 +220,9 @@ } if (Machine == IMAGE_FILE_MACHINE_I386) { - if (!isDecorated(E.Name)) + if (!isDecorated(E.Name, MingwDef)) E.Name = (std::string("_").append(E.Name)); - if (!E.ExtName.empty() && !isDecorated(E.ExtName)) + if (!E.ExtName.empty() && !isDecorated(E.ExtName, MingwDef)) E.ExtName = (std::string("_").append(E.ExtName)); } @@ -244,6 +251,12 @@ continue; } unget(); + + if (E.isWeak()) + E.SymbolName = E.ExtName; + else + E.SymbolName = E.Name; + Info.Exports.push_back(E); return Error::success(); } @@ -308,11 +321,13 @@ std::vector Stack; MachineTypes Machine; COFFModuleDefinition Info; + bool MingwDef; }; Expected parseCOFFModuleDefinition(MemoryBufferRef MB, - MachineTypes Machine) { - return Parser(MB.getBuffer(), Machine).parse(); + MachineTypes Machine, + bool MingwDef) { + return Parser(MB.getBuffer(), Machine, MingwDef).parse(); } } // namespace object Index: test/DllTool/coff-exports.def =================================================================== --- /dev/null +++ test/DllTool/coff-exports.def @@ -0,0 +1,13 @@ +; RUN: llvm-dlltool -a x86_64 %s %t.a +; RUN: llvm-readobj -coff-exports %t.a | FileCheck %s + +LIBRARY test.dll +EXPORTS +TestFunction + +; CHECK: File: test.dll +; CHECK: Format: COFF-import-file +; CHECK: Type: code +; CHECK: Name type: name +; CHECK: Symbol: __imp_TestFunction +; CHECK: Symbol: TestFunction Index: test/DllTool/lit.local.cfg =================================================================== --- /dev/null +++ test/DllTool/lit.local.cfg @@ -0,0 +1 @@ +config.suffixes = ['.def'] Index: tools/llvm-ar/CMakeLists.txt =================================================================== --- tools/llvm-ar/CMakeLists.txt +++ tools/llvm-ar/CMakeLists.txt @@ -1,6 +1,7 @@ set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} Core + DlltoolDriver LibDriver Object Support @@ -15,3 +16,4 @@ add_llvm_tool_symlink(llvm-ranlib llvm-ar) add_llvm_tool_symlink(llvm-lib llvm-ar) +add_llvm_tool_symlink(llvm-dlltool llvm-ar) Index: tools/llvm-ar/llvm-ar.cpp =================================================================== --- tools/llvm-ar/llvm-ar.cpp +++ tools/llvm-ar/llvm-ar.cpp @@ -16,6 +16,7 @@ #include "llvm/ADT/Triple.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" +#include "llvm/DlltoolDriver/DlltoolDriver.h" #include "llvm/LibDriver/LibDriver.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ArchiveWriter.h" @@ -859,6 +860,9 @@ llvm::InitializeAllAsmParsers(); StringRef Stem = sys::path::stem(ToolName); + if (Stem.find("dlltool") != StringRef::npos) + return dlltoolDriverMain(makeArrayRef(argv, argc)); + if (Stem.find("ranlib") == StringRef::npos && Stem.find("lib") != StringRef::npos) return libDriverMain(makeArrayRef(argv, argc)); @@ -874,5 +878,5 @@ return ranlib_main(); if (Stem.find("ar") != StringRef::npos) return ar_main(); - fail("Not ranlib, ar or lib!"); + fail("Not ranlib, ar, lib or dlltool!"); }