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 @@ -222,4 +222,5 @@ add_subdirectory(COFF) add_subdirectory(ELF) add_subdirectory(MinGW) +add_subdirectory(NetBSD) add_subdirectory(wasm) diff --git a/lld/NetBSD/CMakeLists.txt b/lld/NetBSD/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/lld/NetBSD/CMakeLists.txt @@ -0,0 +1,24 @@ +set(LLVM_TARGET_DEFINITIONS Options.td) +tablegen(LLVM Options.inc -gen-opt-parser-defs) +add_public_tablegen_target(NetBSDOptionsTableGen) + +if(NOT LLD_BUILT_STANDALONE) + set(tablegen_deps intrinsics_gen) +endif() + +add_lld_library(lldNetBSD + Driver.cpp + + LINK_COMPONENTS + Option + Support + Target + + LINK_LIBS + lldELF + lldCommon + + DEPENDS + NetBSDOptionsTableGen + ${tablegen_deps} +) diff --git a/lld/NetBSD/Driver.cpp b/lld/NetBSD/Driver.cpp new file mode 100644 --- /dev/null +++ b/lld/NetBSD/Driver.cpp @@ -0,0 +1,187 @@ +//===- 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 driver wraps the default ELF/GNU lld linker with NetBSD specific +// customization. +// +// This code supports standalone linker usage. +// +//===----------------------------------------------------------------------===// + +#include "lld/Common/Driver.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" + +using namespace lld; +using namespace llvm; +using namespace llvm::sys; + +namespace { +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID, +#include "Options.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Options.inc" +#undef PREFIX + +// Create table mapping all options defined in Options.td +static const opt::OptTable::Info infoTable[] = { +#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \ + {X1, X2, X10, X11, OPT_##ID, opt::Option::KIND##Class, \ + X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12}, +#include "Options.inc" +#undef OPTION +}; + +class NetBSDOptTable : public opt::OptTable { +public: + NetBSDOptTable() : 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 prependTargetCustomization(std::vector &args) { + // force-disable RO segment due to ld.elf_so limitations + args.push_back("--no-rosegment"); + + // force-disable superfluous RUNPATH + args.push_back("--disable-new-dtags"); + + // force-disable superfluous GNU stack + args.push_back("-znognustack"); + + // 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: + args.push_back("-L=/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: + args.push_back("-L=/usr/lib/eabi"); + break; + case llvm::Triple::EABIHF: + case llvm::Triple::GNUEABIHF: + args.push_back("-L=/usr/lib/eabihf"); + break; + default: + args.push_back("-L=/usr/lib/oabi"); + break; + } + break; +#if 0 // TODO + case llvm::Triple::mips64: + case llvm::Triple::mips64el: + if (tools::mips::hasMipsAbiArg(Args, "o32")) { + args.push_back("-L=/usr/lib/o32"); + } else if (tools::mips::hasMipsAbiArg(Args, "64")) { + args.push_back("-L=/usr/lib/64"); + } + break; +#endif + case llvm::Triple::ppc: + args.push_back("-L=/usr/lib/powerpc"); + break; + case llvm::Triple::sparc: + args.push_back("-L=/usr/lib/sparc"); + break; + default: + break; + } + + args.push_back("-L=/usr/lib"); +} + +// Adapt command line arguments for NetBSD and then call elf::link. +bool netbsd::link(ArrayRef argsArr, bool canExitEarly, + raw_ostream &diag) { + bool printTarget = false; + + NetBSDOptTable 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()) { + error("invalid NetBSD target: " + targetTriple.str()); + return false; + } + + // Construct new argv[] that is passed to elf::link(). + // It will contain NetBSD specific customization. + std::vector Argv; + + // Pretend that we were executed as the GNU/ELF target. + Argv.push_back("ld.lld"); + + // Prepend original arguments with the target options. + prependTargetCustomization(Argv); + + // Append original arguments, skipping the program name. + for (auto I = argsArr.begin() + 1, E = argsArr.end(); I != E; ++I) + Argv.push_back(*I); + + if (printTarget) + message("Target: " + targetTriple.str()); + + return elf::link(Argv, canExitEarly, diag); +} diff --git a/lld/NetBSD/Options.td b/lld/NetBSD/Options.td new file mode 100644 --- /dev/null +++ b/lld/NetBSD/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/include/lld/Common/Driver.h b/lld/include/lld/Common/Driver.h --- a/lld/include/lld/Common/Driver.h +++ b/lld/include/lld/Common/Driver.h @@ -28,6 +28,11 @@ llvm::raw_ostream &diag = llvm::errs()); } +namespace netbsd { +bool link(llvm::ArrayRef args, bool canExitEarly, + llvm::raw_ostream &diag = llvm::errs()); +} + namespace mach_o { bool link(llvm::ArrayRef args, bool canExitEarly, llvm::raw_ostream &diag = llvm::errs()); 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 @@ -12,6 +12,7 @@ lldCOFF lldDriver lldELF + lldNetBSD lldMinGW lldWasm ) @@ -20,7 +21,7 @@ RUNTIME DESTINATION bin) if(NOT LLD_SYMLINKS_TO_CREATE) - set(LLD_SYMLINKS_TO_CREATE lld-link ld.lld ld64.lld wasm-ld) + set(LLD_SYMLINKS_TO_CREATE lld-link ld.lld nb.lld ld64.lld wasm-ld) endif() foreach(link ${LLD_SYMLINKS_TO_CREATE}) 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 @@ -45,6 +46,7 @@ enum Flavor { Invalid, Gnu, // -flavor gnu + NetBSD, // -flavor netbsd WinLink, // -flavor link Darwin, // -flavor darwin Wasm, // -flavor wasm @@ -58,6 +60,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 +103,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; @@ -141,7 +149,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 +160,8 @@ if (isPETarget(args)) return !mingw::link(args); return !elf::link(args, canExitEarly()); + case NetBSD: + return !netbsd::link(args, canExitEarly()); case WinLink: return !coff::link(args, canExitEarly()); case Darwin: @@ -160,7 +170,7 @@ 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" - " (WebAssembly) instead"); + "Invoke ld.lld (Unix), nb.lld (NetBSD), ld64.lld (macOS), lld-link " + "(Windows), wasm-ld (WebAssembly) instead"); } }