Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/include/clang/Driver/ToolChain.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 4 additions & 4 deletions clang/lib/Driver/ToolChains/CommonArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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")) {
Expand All @@ -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 {
Expand Down
110 changes: 74 additions & 36 deletions clang/lib/Driver/ToolChains/Solaris.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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");
Expand All @@ -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:
Expand Down Expand Up @@ -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");
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
}
25 changes: 24 additions & 1 deletion clang/lib/Driver/ToolChains/Solaris.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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

Expand Down
37 changes: 37 additions & 0 deletions clang/test/Driver/solaris-ld.c
Original file line number Diff line number Diff line change
Expand Up @@ -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