diff --git a/lld/tools/lld/CMakeLists.txt b/lld/tools/lld/CMakeLists.txt --- a/lld/tools/lld/CMakeLists.txt +++ b/lld/tools/lld/CMakeLists.txt @@ -3,12 +3,17 @@ ) add_lld_tool(lld - lld.cpp - + lld_main.cpp SUPPORT_PLUGINS - ) + PARTIAL_SOURCES_INTENDED) + export_executable_symbols_for_plugins(lld) + +add_lld_library(lldLib + lld_lib.cpp + PARTIAL_SOURCES_INTENDED) + target_link_libraries(lld PRIVATE lldCommon @@ -17,6 +22,7 @@ lldMachO lldMinGW lldWasm + lldLib ) install(TARGETS lld diff --git a/lld/tools/lld/lld.cpp b/lld/tools/lld/lld_lib.cpp rename from lld/tools/lld/lld.cpp rename to lld/tools/lld/lld_lib.cpp --- a/lld/tools/lld/lld.cpp +++ b/lld/tools/lld/lld_lib.cpp @@ -1,29 +1,10 @@ -//===- tools/lld/lld.cpp - Linker Driver Dispatcher -----------------------===// +//===- tools/lld/lld_lib.cpp - Use LLD as a library -----------------------===// // // 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 contains the main function of the lld executable. The main -// function is a thin wrapper which dispatches to the platform specific -// driver. -// -// lld is a single executable that contains four different linkers for ELF, -// COFF, WebAssembly and Mach-O. The main function dispatches according to -// argv[0] (i.e. command name). The most common name for each target is shown -// below: -// -// - ld.lld: ELF (Unix) -// - ld64: Mach-O (macOS) -// - lld-link: COFF (Windows) -// - ld-wasm: WebAssembly -// -// lld can be invoked as "lld" along with "-flavor" option. This is for -// backward compatibility and not recommended. -// -//===----------------------------------------------------------------------===// #include "lld/Common/Driver.h" #include "lld/Common/ErrorHandler.h" @@ -38,7 +19,6 @@ #include "llvm/Support/Host.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/Path.h" -#include "llvm/Support/PluginLoader.h" #include "llvm/Support/Process.h" #include @@ -136,12 +116,13 @@ return parseProgname(arg0); } +namespace lld { bool inTestOutputDisabled = false; /// Universal linker main(). This linker emulates the gnu, darwin, or /// windows linker based on the argv[0] or -flavor option. -static int lldMain(int argc, const char **argv, llvm::raw_ostream &stdoutOS, - llvm::raw_ostream &stderrOS, bool exitEarly = true) { +int lldMain(int argc, const char **argv, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly) { std::vector args(argv, argv + argc); auto link = [&args]() { Flavor f = parseFlavor(args); @@ -175,9 +156,8 @@ } // Similar to lldMain except that exceptions are caught. -SafeReturn lld::safeLldMain(int argc, const char **argv, - llvm::raw_ostream &stdoutOS, - llvm::raw_ostream &stderrOS) { +SafeReturn safeLldMain(int argc, const char **argv, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS) { int r = 0; { // The crash recovery is here only to be able to recover from arbitrary @@ -200,43 +180,67 @@ return {r, /*canRunAgain=*/true}; } -// When in lit tests, tells how many times the LLD tool should re-execute the -// main loop with the same inputs. When not in test, returns a value of 0 which -// signifies that LLD shall not release any memory after execution, to speed up -// process destruction. -static unsigned inTestVerbosity() { - unsigned v = 0; - StringRef(getenv("LLD_IN_TEST")).getAsInteger(10, v); - return v; -} +#define STR(x) #x +#ifdef _MSC_VER +#define BUILD(x) STR(comment(linker, x)) +#define LLVM_WEAK_SYMBOL(weak, alt) \ + /* NOTE - Please beware that clang-format doesn't break the line below */ \ + _Pragma(BUILD(STR(/alternatename:weak=alt))) +#else +#define BUILD(x) STR(weak x) +#define LLVM_WEAK_SYMBOL(weak, alt) _Pragma(BUILD(STR(weak) = STR(alt))) +#endif -int main(int argc, const char **argv) { - InitLLVM x(argc, argv); - sys::Process::UseANSIEscapeCodes(true); - - // Not running in lit tests, just take the shortest codepath with global - // exception handling and no memory cleanup on exit. - if (!inTestVerbosity()) - return lldMain(argc, argv, llvm::outs(), llvm::errs()); - - Optional mainRet; - CrashRecoveryContext::Enable(); - - for (unsigned i = inTestVerbosity(); i > 0; --i) { - // Disable stdout/stderr for all iterations but the last one. - inTestOutputDisabled = (i != 1); - - // Execute one iteration. - auto r = safeLldMain(argc, argv, llvm::outs(), llvm::errs()); - if (!r.canRunAgain) - exitLld(r.ret); // Exit now, can't re-execute again. - - if (!mainRet) { - mainRet = r.ret; - } else if (r.ret != *mainRet) { - // Exit now, to fail the tests if the result is different between runs. - return r.ret; - } - } - return *mainRet; +// Provide empty implementations for all drivers, to allow on-demand usage for +// any of them. For example, users of the LLD library mode can use only the ELF +// driver, by only linking lldELF in their target executable. +namespace coff { +bool alt_link(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput) { + die("The LLD COFF driver is not available in this build."); +} +} // namespace coff +LLVM_WEAK_SYMBOL( + ?link@coff@lld@@YA_NV?$ArrayRef@PEBD@llvm@@AEAVraw_ostream@4@1_N2@Z, + ?alt_link@coff@lld@@YA_NV?$ArrayRef@PEBD@llvm@@AEAVraw_ostream@4@1_N2@Z) + +namespace mingw { +bool alt_link(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput) { + die("The LLD MinGW driver is not available in this build."); +} +LLVM_WEAK_SYMBOL( + ?link@mingw@lld@@YA_NV?$ArrayRef@PEBD@llvm@@AEAVraw_ostream@4@1_N2@Z, + ?alt_link@mingw@lld@@YA_NV?$ArrayRef@PEBD@llvm@@AEAVraw_ostream@4@1_N2@Z) +} // namespace mingw + +namespace elf { +bool alt_link(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput) { + die("The LLD ELF driver is not available in this build."); +} +LLVM_WEAK_SYMBOL( + ?link@elf@lld@@YA_NV?$ArrayRef@PEBD@llvm@@AEAVraw_ostream@4@1_N2@Z, + ?alt_link@elf@lld@@YA_NV?$ArrayRef@PEBD@llvm@@AEAVraw_ostream@4@1_N2@Z) +} // namespace elf + +namespace macho { +bool alt_link(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput) { + die("The LLD Mach-O driver is not available in this build."); +} +LLVM_WEAK_SYMBOL( + ?link@macho@lld@@YA_NV?$ArrayRef@PEBD@llvm@@AEAVraw_ostream@4@1_N2@Z, + ?alt_link@macho@lld@@YA_NV?$ArrayRef@PEBD@llvm@@AEAVraw_ostream@4@1_N2@Z) +} // namespace macho + +namespace wasm { +bool alt_link(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput) { + die("The LLD Wasm driver is not available in this build."); } +LLVM_WEAK_SYMBOL( + ?link@wasm@lld@@YA_NV?$ArrayRef@PEBD@llvm@@AEAVraw_ostream@4@1_N2@Z, + ?alt_link@wasm@lld@@YA_NV?$ArrayRef@PEBD@llvm@@AEAVraw_ostream@4@1_N2@Z) +} // namespace wasm +} // namespace lld \ No newline at end of file diff --git a/lld/tools/lld/lld_main.cpp b/lld/tools/lld/lld_main.cpp new file mode 100644 --- /dev/null +++ b/lld/tools/lld/lld_main.cpp @@ -0,0 +1,95 @@ +//===- tools/lld/lld.cpp - Linker Driver Dispatcher -----------------------===// +// +// 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 contains the main function of the lld executable. The main +// function is a thin wrapper which dispatches to the platform specific +// driver. +// +// lld is a single executable that contains four different linkers for ELF, +// COFF, WebAssembly and Mach-O. The main function dispatches according to +// argv[0] (i.e. command name). The most common name for each target is shown +// below: +// +// - ld.lld: ELF (Unix) +// - ld64: Mach-O (macOS) +// - lld-link: COFF (Windows) +// - ld-wasm: WebAssembly +// +// lld can be invoked as "lld" along with "-flavor" option. This is for +// backward compatibility and not recommended. +// +//===----------------------------------------------------------------------===// + +#include "lld/Common/Driver.h" +#include "lld/Common/ErrorHandler.h" +#include "lld/Common/Memory.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PluginLoader.h" +#include "llvm/Support/Process.h" +#include + +using namespace lld; +using namespace llvm; +using namespace llvm::sys; + +namespace lld { +extern bool inTestOutputDisabled; + +int lldMain(int argc, const char **argv, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly); +} // namespace lld + +// When in lit tests, tells how many times the LLD tool should re-execute the +// main loop with the same inputs. When not in test, returns a value of 0 which +// signifies that LLD shall not release any memory after execution, to speed up +// process destruction. +static unsigned inTestVerbosity() { + unsigned v = 0; + StringRef(getenv("LLD_IN_TEST")).getAsInteger(10, v); + return v; +} + +int main(int argc, const char **argv) { + InitLLVM x(argc, argv); + sys::Process::UseANSIEscapeCodes(true); + + // Not running in lit tests, just take the shortest codepath with global + // exception handling and no memory cleanup on exit. + if (!inTestVerbosity()) + return lldMain(argc, argv, llvm::outs(), llvm::errs(), /*exitEarly=*/true); + + Optional mainRet; + CrashRecoveryContext::Enable(); + + for (unsigned i = inTestVerbosity(); i > 0; --i) { + // Disable stdout/stderr for all iterations but the last one. + inTestOutputDisabled = (i != 1); + + // Execute one iteration. + auto r = safeLldMain(argc, argv, llvm::outs(), llvm::errs()); + if (!r.canRunAgain) + exitLld(r.ret); // Exit now, can't re-execute again. + + if (!mainRet) { + mainRet = r.ret; + } else if (r.ret != *mainRet) { + // Exit now, to fail the tests if the result is different between runs. + return r.ret; + } + } + return *mainRet; +}