From 395ec4458fb7fc700551f7017c0a395d68c55873 Mon Sep 17 00:00:00 2001 From: "Vadim Paretsky (Intel Americas Inc)" Date: Wed, 1 Mar 2023 20:32:35 -0800 Subject: [PATCH] [llvm-lib] 'llvm-lib' currently cannot generate an import library from a Windows .def file, functionality supported by 'lib'. This incompatibility is breaking clang based Windows openmp builds. This revision adds basic support for this feature to llvm-lib by cloning the corresponding code from 'dlltool'. Differential Revision:https://reviews.llvm.org/D144765 --- llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp | 102 +++++++++++++++----- llvm/lib/ToolDrivers/llvm-lib/Options.td | 1 + llvm/test/tools/llvm-lib/implibs.test | 7 ++ 3 files changed, 88 insertions(+), 22 deletions(-) diff --git a/llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp b/llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp index f92ff3d3873e0..3a609eefcb10e 100644 --- a/llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp +++ b/llvm/lib/ToolDrivers/llvm-lib/LibDriver.cpp @@ -19,6 +19,7 @@ #include "llvm/Bitcode/BitcodeReader.h" #include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/COFF.h" +#include "llvm/Object/COFFModuleDefinition.h" #include "llvm/Object/WindowsMachineFlag.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" @@ -31,6 +32,7 @@ #include using namespace llvm; +using namespace llvm::object; namespace { @@ -60,7 +62,7 @@ class LibOptTable : public opt::GenericOptTable { public: LibOptTable() : opt::GenericOptTable(InfoTable, true) {} }; -} +} // namespace static std::string getDefaultOutputPath(const NewArchiveMember &FirstMember) { SmallString<128> Val = StringRef(FirstMember.Buf->getBufferIdentifier()); @@ -91,6 +93,18 @@ static std::vector getSearchPaths(opt::InputArgList *Args, return Ret; } +// Opens a file. Path has to be resolved already. (used for def file) +std::unique_ptr openFile(const Twine &Path) { + ErrorOr> MB = MemoryBuffer::getFile(Path); + + if (std::error_code EC = MB.getError()) { + llvm::errs() << "cannot open file " << Path << ": " << EC.message() << "\n"; + return nullptr; + } + + return std::move(*MB); +} + static std::string findInputFile(StringRef File, ArrayRef Paths) { for (StringRef Dir : Paths) { SmallString<128> Path = Dir; @@ -110,7 +124,7 @@ static void fatalOpenError(llvm::Error E, Twine File) { }); } -static void doList(opt::InputArgList& Args) { +static void doList(opt::InputArgList &Args) { // lib.exe prints the contents of the first archive file. std::unique_ptr B; for (auto *Arg : Args.filtered(OPT_INPUT)) { @@ -302,6 +316,63 @@ int llvm::libDriverMain(ArrayRef ArgsArr) { for (auto *Arg : Args.filtered(OPT_ignore)) IgnoredWarnings.insert(Arg->getValue()); + // get output library path, if any + std::string OutputPath; + if (auto *Arg = Args.getLastArg(OPT_out)) { + OutputPath = Arg->getValue(); + } + + COFF::MachineTypes LibMachine = COFF::IMAGE_FILE_MACHINE_UNKNOWN; + std::string LibMachineSource; + if (auto *Arg = Args.getLastArg(OPT_machine)) { + LibMachine = getMachineType(Arg->getValue()); + if (LibMachine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) { + llvm::errs() << "unknown /machine: arg " << Arg->getValue() << '\n'; + return 1; + } + LibMachineSource = + std::string(" (from '/machine:") + Arg->getValue() + "' flag)"; + } + + // create an import library + if (Args.hasArg(OPT_deffile)) { + + if (OutputPath.empty()) { + llvm::errs() << "no output path given\n"; + return 1; + } + + if (LibMachine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) { + llvm::errs() << "/def option requires /machine to be specified" << '\n'; + return 1; + } + + std::unique_ptr MB = + openFile(Args.getLastArg(OPT_deffile)->getValue()); + if (!MB) + return 1; + + if (!MB->getBufferSize()) { + llvm::errs() << "definition file empty\n"; + return 1; + } + + Expected Def = + parseCOFFModuleDefinition(*MB, LibMachine, true); + + if (!Def) { + llvm::errs() << "error parsing definition\n" + << errorToErrorCode(Def.takeError()).message(); + return 1; + } + + return writeImportLibrary(Def->OutputFile, OutputPath, Def->Exports, + LibMachine, + /*MinGW=*/false) + ? 1 + : 0; + } + // If no input files and not told otherwise, silently do nothing to match // lib.exe if (!Args.hasArgNoClaim(OPT_INPUT) && !Args.hasArg(OPT_llvmlibempty)) { @@ -324,18 +395,6 @@ int llvm::libDriverMain(ArrayRef ArgsArr) { std::vector SearchPaths = getSearchPaths(&Args, Saver); - COFF::MachineTypes LibMachine = COFF::IMAGE_FILE_MACHINE_UNKNOWN; - std::string LibMachineSource; - if (auto *Arg = Args.getLastArg(OPT_machine)) { - LibMachine = getMachineType(Arg->getValue()); - if (LibMachine == COFF::IMAGE_FILE_MACHINE_UNKNOWN) { - llvm::errs() << "unknown /machine: arg " << Arg->getValue() << '\n'; - return 1; - } - LibMachineSource = - std::string(" (from '/machine:") + Arg->getValue() + "' flag)"; - } - std::vector> MBs; StringSet<> Seen; std::vector Members; @@ -373,14 +432,13 @@ int llvm::libDriverMain(ArrayRef ArgsArr) { } // Create an archive file. - std::string OutputPath; - if (auto *Arg = Args.getLastArg(OPT_out)) { - OutputPath = Arg->getValue(); - } else if (!Members.empty()) { - OutputPath = getDefaultOutputPath(Members[0]); - } else { - llvm::errs() << "no output path given, and cannot infer with no inputs\n"; - return 1; + if (OutputPath.empty()) { + if (!Members.empty()) { + OutputPath = getDefaultOutputPath(Members[0]); + } else { + llvm::errs() << "no output path given, and cannot infer with no inputs\n"; + return 1; + } } // llvm-lib uses relative paths for both regular and thin archives, unlike // standard GNU ar, which only uses relative paths for thin archives and diff --git a/llvm/lib/ToolDrivers/llvm-lib/Options.td b/llvm/lib/ToolDrivers/llvm-lib/Options.td index 4af250e8ad737..22ac1fb842e4d 100644 --- a/llvm/lib/ToolDrivers/llvm-lib/Options.td +++ b/llvm/lib/ToolDrivers/llvm-lib/Options.td @@ -22,6 +22,7 @@ def libpath: P<"libpath", "Object file search path">; // Can't be called "list" since that's a keyword. def lst : F<"list">, HelpText<"List contents of .lib file on stdout">; def out : P<"out", "Path to file to write output">; +def deffile : P<"def", "def file to use to generate import library">; def llvmlibthin : F<"llvmlibthin">, HelpText<"Make .lib point to .obj files instead of copying their contents">; diff --git a/llvm/test/tools/llvm-lib/implibs.test b/llvm/test/tools/llvm-lib/implibs.test index ebff4bb4608f2..e98c158b6f594 100644 --- a/llvm/test/tools/llvm-lib/implibs.test +++ b/llvm/test/tools/llvm-lib/implibs.test @@ -10,3 +10,10 @@ RUN: llvm-lib -out:%t/newlib.lib %t/lib.lib RUN: llvm-ar t %t/newlib.lib | FileCheck %s CHECK: lib.dll + +Test that import libraries can be created from a def file + +RUN: echo -e "NAME lib.dll\nEXPORTS\nMyFunc" > %t/implib.def +RUN: llvm-lib -out:%t/implib.lib -def:%t/implib.def -machine:x64 + +RUN: llvm-ar t %t/implib.lib | FileCheck %s