krytarowski created this revision.
krytarowski added reviewers: ruiu, joerg, mgorny, MaskRay.
krytarowski added a project: lld.
Herald added subscribers: llvm-commits, cfe-commits, fedor.sergeev, aheejin, 
emaste, dschuff.
Herald added projects: clang, LLVM.

The NetBSD target wraps the default Linux/ELF target with OS specific
customization. It is implemented as a light nb.lld program that
mutates arguments in argv[] and spawns ld.lld.

This flavor detects the native/current and target Triple based on
argv[0] parsing. This is prerequisite for cross-compilation, in
particular the NetBSD distribution is cross-built always.

The default configuration of the ELF target is tuned for Linux and
there is no way to costomize in-place for the NetBSD target in the
same way as FreeBSD/OpenBSD. FreeBSD whenever needed can check
emulation name ("*_fbsd") and OpenBSD calls its extensions
"PT_OPENBSD_*".

This distinct flavor is needed for NetBSD as:

- the linker MUST work in the standalone mode
- it must be useful with gcc/pcc/others out of the box
- clang NetBSD driver shall not hardcode LLD specific options
- the linker must have support for cross-building
- LLD shall be a drop-in replacement for (NetBSD-patched) GNU ld

nb.lld calls internally ld.lld and there is no code-duplication
between nb.lld and ld.lld. There is no functional or code
maintenance change for other Operating Systems, especially the ELF
ones.

Equivalent customization is already done for the Darwin mode. For
instance there are hardcoded default search paths such as "/usr/lib"
and "/Library/Frameworks" in DarwinLdDriver.cpp.

This change is a starting point for development of NetBSD support
in LLD. This is also another approach to achieve the same goal as
mutating the default LLD/ELF target based on Triple, but in a
less invasive way as everything is moved out of LLD to nb.lld now.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D69755

Files:
  clang/lib/Driver/ToolChain.cpp
  lld/CMakeLists.txt
  lld/tools/lld/lld.cpp
  lld/tools/nb.lld/CMakeLists.txt
  lld/tools/nb.lld/Options.td
  lld/tools/nb.lld/nb.lld.cpp

Index: lld/tools/nb.lld/nb.lld.cpp
===================================================================
--- /dev/null
+++ 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<StringRef> &args, const char *path) {
+  args.push_back("--library-path");
+  args.push_back(path);
+}
+
+static void appendTargetCustomization(std::vector<StringRef> &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<const char *> 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<StringRef> 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;
+}
Index: lld/tools/nb.lld/Options.td
===================================================================
--- /dev/null
+++ lld/tools/nb.lld/Options.td
@@ -0,0 +1,7 @@
+include "llvm/Option/OptParser.td"
+
+class F<string name>: 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<version>, HelpText<"Alias for --version">;
Index: lld/tools/nb.lld/CMakeLists.txt
===================================================================
--- /dev/null
+++ 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
+  )
Index: lld/tools/lld/lld.cpp
===================================================================
--- lld/tools/lld/lld.cpp
+++ 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 <cstdlib>
 
 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<Flavor>(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<StringRef, 3> 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<StringRef> 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");
   }
 }
Index: lld/CMakeLists.txt
===================================================================
--- lld/CMakeLists.txt
+++ 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)
Index: clang/lib/Driver/ToolChain.cpp
===================================================================
--- clang/lib/Driver/ToolChain.cpp
+++ 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);
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to