diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -534,6 +534,8 @@ llvm::SmallString<8> LinkerName; if (Triple.isOSDarwin()) LinkerName.append("ld64."); + else if (Triple.isOSNetBSD()) + LinkerName.append("nb."); else LinkerName.append("ld."); LinkerName.append(UseLinker); diff --git a/lld/CMakeLists.txt b/lld/CMakeLists.txt --- a/lld/CMakeLists.txt +++ b/lld/CMakeLists.txt @@ -212,6 +212,7 @@ add_subdirectory(Common) add_subdirectory(lib) add_subdirectory(tools/lld) +add_subdirectory(tools/nb.lld) if (LLVM_INCLUDE_TESTS) add_subdirectory(test) diff --git a/lld/tools/lld/lld.cpp b/lld/tools/lld/lld.cpp --- a/lld/tools/lld/lld.cpp +++ b/lld/tools/lld/lld.cpp @@ -10,12 +10,13 @@ // 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 +// lld is a single executable that contains five different linkers for ELF, +// NetBSD, 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) +// - nb.lld: ELF (NetBSD) // - ld64: Mach-O (macOS) // - lld-link: COFF (Windows) // - ld-wasm: WebAssembly @@ -36,6 +37,9 @@ #include "llvm/Support/Host.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" #include using namespace lld; @@ -45,6 +49,7 @@ enum Flavor { Invalid, Gnu, // -flavor gnu + NetBSD, // -flavor netbsd WinLink, // -flavor link Darwin, // -flavor darwin Wasm, // -flavor wasm @@ -58,6 +63,7 @@ static Flavor getFlavor(StringRef s) { return StringSwitch(s) .CasesLower("ld", "ld.lld", "gnu", Gnu) + .CasesLower("nb.lld", "netbsd", NetBSD) .CasesLower("wasm", "ld-wasm", Wasm) .CaseLower("link", WinLink) .CasesLower("ld64", "ld64.lld", "darwin", Darwin) @@ -100,10 +106,15 @@ #endif #if LLVM_ON_UNIX - // Use GNU driver for "ld" on other Unix-like system. - if (progname == "ld") + // Use GNU or NetBSD driver for "ld" on other Unix-like system. + if (progname == "ld") { +#if defined(__NetBSD__) + return NetBSD; +#else return Gnu; #endif + } +#endif // Progname may be something like "lld-gnu". Parse it. SmallVector v; @@ -133,6 +144,38 @@ return parseProgname(arg0); } +// The NetBSD linker flavor will mutate arguments and call the GNU linker. +static int exec_nb_lld(int argc, const char **argv) { + auto Program = sys::findProgramByName("nb.lld"); + if (!Program) { + WithColor::error() << "unable to find `nb.lld' in PATH: " + << Program.getError().message() << "\n"; + return 1; + } + + std::vector Argv; + Argv.push_back("nb.lld"); + + // Trim -flavor option. + if (argc > 1 && argv[0] == StringRef("-flavor")) { + if (argc <= 2) + die("missing arg value for '-flavor'"); + argc -= 2; + argv += 2; + } + + for (int i = 0; i < argc; ++i) + Argv.push_back(argv[i]); + + std::string ErrMsg; + int Result = sys::ExecuteAndWait(*Program, Argv, None, {}, 0, 0, &ErrMsg); + if (Result < 0) { + WithColor::error() << ErrMsg << "\n"; + return 1; + } + return Result; +} + // If this function returns true, lld calls _exit() so that it quickly // exits without invoking destructors of globally allocated objects. // @@ -141,7 +184,7 @@ // and we use it to detect whether we are running tests or not. static bool canExitEarly() { return StringRef(getenv("LLD_IN_TEST")) != "1"; } -/// Universal linker main(). This linker emulates the gnu, darwin, or +/// Universal linker main(). This linker emulates the gnu, netbsd, darwin, or /// windows linker based on the argv[0] or -flavor option. int main(int argc, const char **argv) { InitLLVM x(argc, argv); @@ -152,6 +195,8 @@ if (isPETarget(args)) return !mingw::link(args); return !elf::link(args, canExitEarly()); + case NetBSD: + return exec_nb_lld(argc - 1, argv + 1); case WinLink: return !coff::link(args, canExitEarly()); case Darwin: @@ -160,7 +205,8 @@ return !wasm::link(args, canExitEarly()); default: die("lld is a generic driver.\n" - "Invoke ld.lld (Unix), ld64.lld (macOS), lld-link (Windows), wasm-ld" + "Invoke ld.lld (Unix), nb.lld (NetBSD), ld64.lld (macOS), lld-link " + "(Windows), wasm-ld" " (WebAssembly) instead"); } } diff --git a/lld/tools/nb.lld/CMakeLists.txt b/lld/tools/nb.lld/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/lld/tools/nb.lld/CMakeLists.txt @@ -0,0 +1,21 @@ +set(LLVM_TARGET_DEFINITIONS Options.td) +tablegen(LLVM Options.inc -gen-opt-parser-defs) +add_public_tablegen_target(SLLDOptionsTableGen) + +set(LLVM_LINK_COMPONENTS + Support + Option + Target + ${LLVM_TARGETS_TO_BUILD} + ) + +add_lld_tool(nb.lld + nb.lld.cpp + + DEPENDS + SLLDOptionsTableGen + ) + +install(TARGETS nb.lld + RUNTIME DESTINATION bin + ) diff --git a/lld/tools/nb.lld/Options.td b/lld/tools/nb.lld/Options.td new file mode 100644 --- /dev/null +++ b/lld/tools/nb.lld/Options.td @@ -0,0 +1,7 @@ +include "llvm/Option/OptParser.td" + +class F: Flag<["--", "-"], name>; + +def version: F<"version">, HelpText<"Display the version number and exit">; +def v: Flag<["-"], "v">, HelpText<"Display the version number">; +def: Flag<["-"], "V">, Alias, HelpText<"Alias for --version">; diff --git a/lld/tools/nb.lld/nb.lld.cpp b/lld/tools/nb.lld/nb.lld.cpp new file mode 100644 --- /dev/null +++ b/lld/tools/nb.lld/nb.lld.cpp @@ -0,0 +1,218 @@ +//===- nb.lld.cpp - NetBSD LLD standalone linker --------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// The NetBSD flavor of LLD. +// +// This code wraps the default ELF/UNIX lld variation with NetBSD specific +// customization. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/Triple.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace llvm::sys; + +#define LLD_PROGNAME "ld.lld" + +namespace { +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "Options.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Options.inc" +#undef PREFIX + +const opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + { \ + PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "Options.inc" +#undef OPTION +}; + +class SLLDOptTable : public opt::OptTable { +public: + SLLDOptTable() : OptTable(InfoTable) {} +}; +} // namespace + +static Triple targetTriple; + +static void setTargetTriple(StringRef argv0, opt::InputArgList &args) { + std::string targetError; + + // Firstly, try to get it from program name prefix + std::string ProgName = llvm::sys::path::stem(argv0); + size_t lastComponent = ProgName.rfind('-'); + if (lastComponent != std::string::npos) { + std::string prefix = ProgName.substr(0, lastComponent); + if (llvm::TargetRegistry::lookupTarget(prefix, targetError)) { + targetTriple = llvm::Triple(prefix); + return; + } + } + + // Secondly, use the default target triple + targetTriple = llvm::Triple(getDefaultTargetTriple()); +} + +static void appendSearchPath(std::vector &args, const char *path) { + args.push_back("--library-path"); + args.push_back(path); +} + +static void appendTargetCustomization(std::vector &args) { + // force-disable RO segment on NetBSD due to ld.elf_so limitations + args.push_back("--no-rosegment"); + + // disable superfluous RUNPATH on NetBSD + args.push_back("--disable-new-dtags"); + + // set default image base address + switch (targetTriple.getArch()) { + case llvm::Triple::aarch64: + case llvm::Triple::aarch64_be: + args.push_back("--image-base=0x200100000"); + break; + default: + break; + } + + // NetBSD driver relies on the linker knowing the default search paths. + // Please keep this in sync with clang/lib/Driver/ToolChains/NetBSD.cpp + // (NetBSD::NetBSD constructor) + switch (targetTriple.getArch()) { + case llvm::Triple::x86: + appendSearchPath(args, "=/usr/lib/i386"); + break; + case llvm::Triple::arm: + case llvm::Triple::armeb: + case llvm::Triple::thumb: + case llvm::Triple::thumbeb: + switch (targetTriple.getEnvironment()) { + case llvm::Triple::EABI: + case llvm::Triple::GNUEABI: + appendSearchPath(args, "=/usr/lib/eabi"); + break; + case llvm::Triple::EABIHF: + case llvm::Triple::GNUEABIHF: + appendSearchPath(args, "=/usr/lib/eabihf"); + break; + default: + appendSearchPath(args, "=/usr/lib/oabi"); + break; + } + break; +#if 0 // TODO + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + if (tools::mips::hasMipsAbiArg(Args, "o32")) { + appendSearchPath(args, "=/usr/lib/o32"); + } else if (tools::mips::hasMipsAbiArg(Args, "64")) { + appendSearchPath(args, "=/usr/lib/64"); + } + break; +#endif + case llvm::Triple::ppc: + appendSearchPath(args, "=/usr/lib/powerpc"); + break; + case llvm::Triple::sparc: + appendSearchPath(args, "=/usr/lib/sparc"); + break; + default: + break; + } + + appendSearchPath(args, "=/usr/lib"); +} + +int main(int argc, const char **argv) { + bool printTarget = false; + + InitLLVM X(argc, argv); + + auto Program = sys::findProgramByName(LLD_PROGNAME); + if (!Program) { + WithColor::error() << "unable to find `" << LLD_PROGNAME + << "' in PATH: " << Program.getError().message() << "\n"; + return 1; + } + + ArrayRef argsArr = makeArrayRef(argv, argc); + + SLLDOptTable parser; + unsigned MAI; + unsigned MAC; + opt::InputArgList args = parser.ParseArgs(argsArr.slice(1), MAI, MAC); + + // Append to -v or -version the target information from slld. + if (args.hasArg(OPT_v) || args.hasArg(OPT_version)) + printTarget = true; + + InitializeAllTargets(); + setTargetTriple(argsArr[0], args); + + if (!targetTriple.isOSNetBSD()) { + WithColor::error() << "invalid NetBSD target: " << targetTriple.str() + << "\n"; + } + + argc--; + argv++; + + std::vector Argv; + Argv.push_back(*Program); + + // Prepend original arguments with the target options. + appendTargetCustomization(Argv); + + // Append original options. + // Trim -flavor option. + if (argc > 1 && argv[0] == StringRef("-flavor")) { + if (argc <= 2) + WithColor::error() << "missing arg value for '-flavor'\n"; + argc -= 2; + argv += 2; + } + + for (int i = 0; i < argc; ++i) + Argv.push_back(argv[i]); + + std::string ErrMsg; + int Result = sys::ExecuteAndWait(*Program, Argv, None, {}, 0, 0, &ErrMsg); + if (Result < 0) { + WithColor::error() << ErrMsg << "\n"; + return 1; + } + + if (printTarget) + WithColor::note() << "Target: " << targetTriple.str() << "\n"; + + return Result; +}