https://github.com/daniel-levin updated 
https://github.com/llvm/llvm-project/pull/163000

>From dd443a162de6f14f923d67efbb4a082840ad3e08 Mon Sep 17 00:00:00 2001
From: Daniel Levin <[email protected]>
Date: Wed, 22 Oct 2025 14:31:04 +0000
Subject: [PATCH] [Clang] [Driver] Support --ld-path flag on Illumos/Solaris

---
 clang/include/clang/Driver/ToolChain.h     |   2 +
 clang/lib/Driver/ToolChains/CommonArgs.cpp |   8 +-
 clang/lib/Driver/ToolChains/Solaris.cpp    | 110 ++++++++++++++-------
 clang/lib/Driver/ToolChains/Solaris.h      |  25 ++++-
 clang/test/Driver/solaris-ld.c             |  37 +++++++
 5 files changed, 141 insertions(+), 41 deletions(-)

diff --git a/clang/include/clang/Driver/ToolChain.h 
b/clang/include/clang/Driver/ToolChain.h
index 1425714d34110..753742067fe8c 100644
--- a/clang/include/clang/Driver/ToolChain.h
+++ b/clang/include/clang/Driver/ToolChain.h
@@ -491,6 +491,8 @@ class ToolChain {
   }
 
   /// GetDefaultLinker - Get the default linker to use.
+  /// Note: this is distinct from the 'preferred' linker, which is optionally
+  /// set at compile time using CLANG_DEFAULT_LINKER.
   virtual const char *getDefaultLinker() const { return "ld"; }
 
   /// GetDefaultRuntimeLibType - Get the default runtime library variant to 
use.
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp 
b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 99400ac701fbe..036f8d4b59313 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -1485,11 +1485,11 @@ static void addSanitizerRuntime(const ToolChain &TC, 
const ArgList &Args,
 static bool addSanitizerDynamicList(const ToolChain &TC, const ArgList &Args,
                                     ArgStringList &CmdArgs,
                                     StringRef Sanitizer) {
-  bool LinkerIsGnuLd = solaris::isLinkerGnuLd(TC, Args);
+  bool LinkerIsSolarisLinkEditor = solaris::isLinkerSolarisLinkEditor(TC, 
Args);
 
   // Solaris ld defaults to --export-dynamic behaviour but doesn't support
   // the option, so don't try to pass it.
-  if (TC.getTriple().isOSSolaris() && !LinkerIsGnuLd)
+  if (TC.getTriple().isOSSolaris() && LinkerIsSolarisLinkEditor)
     return true;
   SmallString<128> SanRT(TC.getCompilerRT(Args, Sanitizer));
   if (llvm::sys::fs::exists(SanRT + ".syms")) {
@@ -1505,14 +1505,14 @@ void tools::addAsNeededOption(const ToolChain &TC,
                               bool as_needed) {
   assert(!TC.getTriple().isOSAIX() &&
          "AIX linker does not support any form of --as-needed option yet.");
-  bool LinkerIsGnuLd = solaris::isLinkerGnuLd(TC, Args);
+  bool LinkerIsSolarisLinkEditor = solaris::isLinkerSolarisLinkEditor(TC, 
Args);
 
   // While the Solaris 11.2 ld added --as-needed/--no-as-needed as aliases
   // for the native forms -z ignore/-z record, they are missing in Illumos,
   // so always use the native form.
   // GNU ld doesn't support -z ignore/-z record, so don't use them even on
   // Solaris.
-  if (TC.getTriple().isOSSolaris() && !LinkerIsGnuLd) {
+  if (TC.getTriple().isOSSolaris() && LinkerIsSolarisLinkEditor) {
     CmdArgs.push_back("-z");
     CmdArgs.push_back(as_needed ? "ignore" : "record");
   } else {
diff --git a/clang/lib/Driver/ToolChains/Solaris.cpp 
b/clang/lib/Driver/ToolChains/Solaris.cpp
index 02aa59817449d..7e727d816e560 100644
--- a/clang/lib/Driver/ToolChains/Solaris.cpp
+++ b/clang/lib/Driver/ToolChains/Solaris.cpp
@@ -36,11 +36,11 @@ void solaris::Assembler::ConstructJob(Compilation &C, const 
JobAction &JA,
   gnutools::Assembler::ConstructJob(C, JA, Output, Inputs, Args, 
LinkingOutput);
 }
 
-bool solaris::isLinkerGnuLd(const ToolChain &TC, const ArgList &Args) {
-  // Only used if targetting Solaris.
-  const Arg *A = Args.getLastArg(options::OPT_fuse_ld_EQ);
-  StringRef UseLinker = A ? A->getValue() : 
TC.getDriver().getPreferredLinker();
-  return UseLinker == "bfd" || UseLinker == "gld";
+bool solaris::isLinkerSolarisLinkEditor(const ToolChain &TC,
+                                        const ArgList &Args) {
+  auto Determination =
+      solaris::LinkerDetermination::make(TC, Args, /* EmitDiagnostics */ 
false);
+  return Determination.IsSolarisLd;
 }
 
 static bool getPIE(const ArgList &Args, const ToolChain &TC) {
@@ -52,30 +52,11 @@ static bool getPIE(const ArgList &Args, const ToolChain 
&TC) {
                       TC.isPIEDefault(Args));
 }
 
-// FIXME: Need to handle PreferredLinker here?
 std::string solaris::Linker::getLinkerPath(const ArgList &Args) const {
-  const ToolChain &ToolChain = getToolChain();
-  if (const Arg *A = Args.getLastArg(options::OPT_fuse_ld_EQ)) {
-    StringRef UseLinker = A->getValue();
-    if (!UseLinker.empty()) {
-      if (llvm::sys::path::is_absolute(UseLinker) &&
-          llvm::sys::fs::can_execute(UseLinker))
-        return std::string(UseLinker);
-
-      // Accept 'bfd' and 'gld' as aliases for the GNU linker.
-      if (UseLinker == "bfd" || UseLinker == "gld")
-        // FIXME: Could also use /usr/bin/gld here.
-        return "/usr/gnu/bin/ld";
-
-      // Accept 'ld' as alias for the default linker
-      if (UseLinker != "ld")
-        ToolChain.getDriver().Diag(diag::err_drv_invalid_linker_name)
-            << A->getAsString(Args);
-    }
-  }
+  auto Determination =
+      solaris::LinkerDetermination::make(getToolChain(), Args, true);
 
-  // getDefaultLinker() always returns an absolute path.
-  return ToolChain.getDefaultLinker();
+  return Determination.Linker;
 }
 
 void solaris::Linker::ConstructJob(Compilation &C, const JobAction &JA,
@@ -87,11 +68,11 @@ void solaris::Linker::ConstructJob(Compilation &C, const 
JobAction &JA,
   const Driver &D = ToolChain.getDriver();
   const llvm::Triple::ArchType Arch = ToolChain.getArch();
   const bool IsPIE = getPIE(Args, ToolChain);
-  const bool LinkerIsGnuLd = isLinkerGnuLd(ToolChain, Args);
+  const bool LinkerIsSolarisLd = isLinkerSolarisLinkEditor(ToolChain, Args);
   ArgStringList CmdArgs;
 
   // Demangle C++ names in errors.  GNU ld already defaults to --demangle.
-  if (!LinkerIsGnuLd)
+  if (LinkerIsSolarisLd)
     CmdArgs.push_back("-C");
 
   if (!Args.hasArg(options::OPT_nostdlib, options::OPT_shared,
@@ -101,7 +82,7 @@ void solaris::Linker::ConstructJob(Compilation &C, const 
JobAction &JA,
   }
 
   if (IsPIE) {
-    if (LinkerIsGnuLd) {
+    if (!LinkerIsSolarisLd) {
       CmdArgs.push_back("-pie");
     } else {
       CmdArgs.push_back("-z");
@@ -122,7 +103,7 @@ void solaris::Linker::ConstructJob(Compilation &C, const 
JobAction &JA,
     Args.ClaimAllArgs(options::OPT_pthreads);
   }
 
-  if (LinkerIsGnuLd) {
+  if (!LinkerIsSolarisLd) {
     // Set the correct linker emulation for 32- and 64-bit Solaris.
     switch (Arch) {
     case llvm::Triple::x86:
@@ -256,7 +237,7 @@ void solaris::Linker::ConstructJob(Compilation &C, const 
JobAction &JA,
       if (Arch == llvm::Triple::x86_64 &&
           (SA.needsAsanRt() || SA.needsStatsRt() ||
            (SA.needsUbsanRt() && !SA.requiresMinimalRuntime())) &&
-          !LinkerIsGnuLd) {
+          LinkerIsSolarisLd) {
         CmdArgs.push_back("-z");
         CmdArgs.push_back("relax=transtls");
       }
@@ -344,10 +325,10 @@ SanitizerMask Solaris::getSupportedSanitizers() const {
 }
 
 const char *Solaris::getDefaultLinker() const {
-  // FIXME: Only handle Solaris ld and GNU ld here.
-  return llvm::StringSwitch<const char *>(getDriver().getPreferredLinker())
-      .Cases("bfd", "gld", "/usr/gnu/bin/ld")
-      .Default("/usr/bin/ld");
+  // The default linker on Solaris is _always_ the Solaris Link Editor.
+  // Recall that the driver's _default_ linker is distinct from the 
compile-time
+  // _preferred_ linker setting CLANG_DEFAULT_LINKER, which may even be empty.
+  return "/usr/bin/ld";
 }
 
 Tool *Solaris::buildAssembler() const {
@@ -423,3 +404,60 @@ void Solaris::addLibStdCxxIncludePaths(
                            TripleStr, Multilib.includeSuffix(), DriverArgs,
                            CC1Args);
 }
+
+solaris::LinkerDetermination
+solaris::LinkerDetermination::make(const ToolChain &TC, const ArgList &Args,
+                                   bool EmitDiagnostics) {
+  // First, check --ld-path, then -fuse-ld, then the compile-time
+  // preferred linker (CLANG_DEFAULT_LINKER), then finally fall back to the
+  // platform's default - the Solaris Link Editor. This behavior is consonant
+  // with the other platforms' drivers.
+
+  auto GuessIfSolarisLd = [](const std::string &s) -> bool {
+    return (s == "ld" || s == "/bin/ld" || s == "/usr/bin/ld");
+  };
+
+  if (const Arg *A = Args.getLastArg(options::OPT_ld_path_EQ)) {
+    StringRef UseLinker = A->getValue();
+    if (!UseLinker.empty()) {
+      auto LinkerPath = std::string(UseLinker);
+      if (llvm::sys::fs::can_execute(LinkerPath))
+        return solaris::LinkerDetermination(LinkerPath,
+                                            GuessIfSolarisLd(LinkerPath));
+    }
+    if (EmitDiagnostics)
+      TC.getDriver().Diag(diag::err_drv_invalid_linker_name)
+          << A->getAsString(Args);
+  } else if (const Arg *A = Args.getLastArg(options::OPT_fuse_ld_EQ)) {
+    StringRef UseLinker = A->getValue();
+    if (!UseLinker.empty()) {
+      if (llvm::sys::path::is_absolute(UseLinker) &&
+          llvm::sys::fs::can_execute(UseLinker)) {
+        auto LinkerPath = std::string(UseLinker);
+        return solaris::LinkerDetermination(LinkerPath,
+                                            GuessIfSolarisLd(LinkerPath));
+      }
+
+      // Accept 'bfd' and 'gld' as aliases for the GNU linker.
+      if (UseLinker == "bfd" || UseLinker == "gld")
+        return solaris::LinkerDetermination("/usr/gnu/bin/ld", false);
+
+      // Accept 'ld' as an alias for the default linker
+      if (UseLinker == "ld")
+        return solaris::LinkerDetermination("/usr/bin/ld", true);
+
+      if (EmitDiagnostics)
+        TC.getDriver().Diag(diag::err_drv_invalid_linker_name)
+            << A->getAsString(Args);
+    }
+  }
+
+  auto CompileTimePreferredLinker = TC.getDriver().getPreferredLinker();
+  if (!CompileTimePreferredLinker.empty()) {
+    auto LinkerPath = std::string(CompileTimePreferredLinker);
+    return solaris::LinkerDetermination(LinkerPath,
+                                        GuessIfSolarisLd(LinkerPath));
+  }
+
+  return solaris::LinkerDetermination(std::string("/usr/bin/ld"), true);
+}
diff --git a/clang/lib/Driver/ToolChains/Solaris.h 
b/clang/lib/Driver/ToolChains/Solaris.h
index 9ec83b773da44..394ba648b53f2 100644
--- a/clang/lib/Driver/ToolChains/Solaris.h
+++ b/clang/lib/Driver/ToolChains/Solaris.h
@@ -31,7 +31,8 @@ class LLVM_LIBRARY_VISIBILITY Assembler final : public 
gnutools::Assembler {
                     const char *LinkingOutput) const override;
 };
 
-bool isLinkerGnuLd(const ToolChain &TC, const llvm::opt::ArgList &Args);
+bool isLinkerSolarisLinkEditor(const ToolChain &TC,
+                               const llvm::opt::ArgList &Args);
 
 class LLVM_LIBRARY_VISIBILITY Linker final : public Tool {
 public:
@@ -46,6 +47,28 @@ class LLVM_LIBRARY_VISIBILITY Linker final : public Tool {
                     const llvm::opt::ArgList &TCArgs,
                     const char *LinkingOutput) const override;
 };
+
+/// We use Solaris's built-in linker by default. It has a unique command line
+/// syntax and specific limitations. By contrast, other linkers such as lld,
+/// Mold, and Wild are compatible with GNU ld's command line syntax. Knowing
+/// _which_ linker to use is sufficient to determine the expectations of that
+/// linker. Rather than spread ad-hoc string comparisons all over the driver, 
we
+/// encapsulate the details of differences in the chosen linker here.
+class LinkerDetermination final {
+  LinkerDetermination(std::string Linker, bool IsSolarisLd)
+      : Linker(Linker), IsSolarisLd(IsSolarisLd) {}
+
+public:
+  std::string Linker;
+  bool IsSolarisLd;
+
+  /// Choose the correct linker based on arguments and compile-time options
+  /// recorded in the ToolChain.
+  static LinkerDetermination make(const ToolChain &TC,
+                                  const llvm::opt::ArgList &Args,
+                                  bool EmitDiagnostics);
+};
+
 } // end namespace solaris
 } // end namespace tools
 
diff --git a/clang/test/Driver/solaris-ld.c b/clang/test/Driver/solaris-ld.c
index 5940ee2add769..b0434bb128118 100644
--- a/clang/test/Driver/solaris-ld.c
+++ b/clang/test/Driver/solaris-ld.c
@@ -275,3 +275,40 @@
 // CHECK-CRTFASTMATH-X64: "-isysroot" "[[SYSROOT:[^"]+]]"
 // CHECK-CRTFASTMATH-X64: 
"[[SYSROOT]]/usr/gcc/4.9/lib/gcc/i386-pc-solaris2.11/4.9.4/amd64{{/|\\\\}}crtfastmath.o"
 // CHECK-NOCRTFASTMATH-X64-NOT: crtfastmath.o
+
+// Check --ld-path flag with GNU ld
+// RUN: test -f /usr/gnu/bin/ld && %clang --ld-path=/usr/gnu/bin/ld -### %s \
+// RUN:        -rtlib=platform --unwindlib=platform \
+// RUN:        --sysroot=%S/Inputs/solaris_x86_tree 2>&1 \
+// RUN:   | FileCheck --check-prefix=CHECK-GNU-LD %s
+// CHECK-GNU-LD: /usr/gnu/bin/ld
+// CHECK-GNU-LD-NOT: "-C"
+
+// Check --ld-path flag with system linker
+// RUN: %clang --ld-path=/bin/ld -### %s \
+// RUN:        -rtlib=platform --unwindlib=platform \
+// RUN:        --sysroot=%S/Inputs/solaris_x86_tree 2>&1 \
+// RUN:   | FileCheck --check-prefix=CHECK-SYSLINKER %s
+// CHECK-SYSLINKER: /bin/ld
+// CHECK-SYSLINKER: "-C"
+// CHECK-SYSLINKER-NOT: /usr/gnu/bin/ld
+
+// Check --ld-path flag with empty args errors out
+// RUN: not %clang --ld-path= -### %s \
+// RUN:        -rtlib=platform --unwindlib=platform \
+// RUN:        --sysroot=%S/Inputs/solaris_x86_tree 2>&1 \
+// RUN:   | FileCheck --check-prefix=NOT_EXIST %s
+// NOT_EXIST: error: invalid linker name in argument '--ld-path='
+
+// Check --ld-path flag with non-existent linker errors out
+// RUN: not %clang --ld-path=bogus-nonsense -### %s \
+// RUN:        -rtlib=platform --unwindlib=platform \
+// RUN:        --sysroot=%S/Inputs/solaris_x86_tree 2>&1 \
+// RUN:   | FileCheck --check-prefix=NOT_EXIST_BOGUS %s
+// NOT_EXIST_BOGUS: error: invalid linker name in argument 
'--ld-path=bogus-nonsense'
+
+// Check --ld-path flag takes precedence over -fuse-ld
+// RUN: %clang --ld-path=/bin/ld -fuse-ld=bfd -### %s \
+// RUN:        -rtlib=platform --unwindlib=platform \
+// RUN:        --sysroot=%S/Inputs/solaris_x86_tree 2>&1 \
+// RUN:   | FileCheck --check-prefix=CHECK-SYSLINKER %s

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to