Skip to content

Commit 285050d

Browse files
committed
Allow configuring self-contained LLD invoked through cc to be the default linker on Linux
1 parent 0d2bb35 commit 285050d

File tree

6 files changed

+82
-70
lines changed

6 files changed

+82
-70
lines changed

bootstrap.example.toml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -761,12 +761,8 @@
761761
# Currently, the only standard options supported here are `"llvm"`, `"cranelift"` and `"gcc"`.
762762
#rust.codegen-backends = ["llvm"]
763763

764-
# Indicates whether LLD will be compiled and made available in the sysroot for rustc to execute, and
765-
# whether to set it as rustc's default linker on `x86_64-unknown-linux-gnu`. This will also only be
766-
# when *not* building an external LLVM (so only when using `download-ci-llvm` or building LLVM from
767-
# the in-tree source): setting `llvm-config` in the `[target.x86_64-unknown-linux-gnu]` section will
768-
# make this default to false.
769-
#rust.lld = false in all cases, except on `x86_64-unknown-linux-gnu` as described above, where it is true
764+
# Indicates whether LLD will be compiled and made available in the sysroot for rustc to execute,
765+
#rust.lld = false
770766

771767
# Indicates if we should override the linker used to link Rust crates during bootstrap to be LLD.
772768
# If set to `true` or `"external"`, a global `lld` binary that has to be in $PATH
@@ -1070,3 +1066,11 @@
10701066
# Link the compiler and LLVM against `jemalloc` instead of the default libc allocator.
10711067
# This overrides the global `rust.jemalloc` option. See that option for more info.
10721068
#jemalloc = rust.jemalloc (bool)
1069+
1070+
# The linker configuration that will *override* the default linker used for Linux
1071+
# targets in the built compiler.
1072+
#
1073+
# Currently, there is only one possible value:
1074+
# - `self-contained-lld-cc` => override the default linker to be self-contained LLD (`rust-lld`)
1075+
# that is invoked through `cc`
1076+
#default-linker-linux = <none>

src/bootstrap/src/core/build_steps/compile.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use crate::core::builder;
2626
use crate::core::builder::{
2727
Builder, Cargo, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description,
2828
};
29+
use crate::core::config::toml::target::DefaultLinuxLinkerOverride;
2930
use crate::core::config::{
3031
CompilerBuiltins, DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection,
3132
};
@@ -1350,9 +1351,13 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS
13501351
cargo.env("CFG_DEFAULT_LINKER", s);
13511352
}
13521353

1353-
// Enable rustc's env var for `rust-lld` when requested.
1354-
if builder.config.lld_enabled {
1355-
cargo.env("CFG_DEFAULT_LINKER_SELF_CONTAINED_LLD_CC", "1");
1354+
// Enable rustc's env var to use a linker override on Linux when requested.
1355+
if let Some(linker) = target_config.and_then(|c| c.default_linker_linux.as_ref()) {
1356+
match linker {
1357+
DefaultLinuxLinkerOverride::SelfContainedLldCc => {
1358+
cargo.env("CFG_DEFAULT_LINKER_SELF_CONTAINED_LLD_CC", "1");
1359+
}
1360+
}
13561361
}
13571362

13581363
if builder.config.rust_verify_llvm_ir {

src/bootstrap/src/core/builder/tests.rs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,6 @@ mod snapshot {
514514
};
515515
use crate::core::builder::{Builder, Kind, StepDescription, StepMetadata};
516516
use crate::core::config::TargetSelection;
517-
use crate::core::config::toml::rust::with_lld_opt_in_targets;
518517
use crate::utils::cache::Cache;
519518
use crate::utils::helpers::get_host_target;
520519
use crate::utils::tests::{ConfigBuilder, TestCtx};
@@ -782,17 +781,16 @@ mod snapshot {
782781

783782
#[test]
784783
fn build_compiler_lld_opt_in() {
785-
with_lld_opt_in_targets(vec![host_target()], || {
786-
let ctx = TestCtx::new();
787-
insta::assert_snapshot!(
788-
ctx.config("build")
789-
.path("compiler")
790-
.render_steps(), @r"
791-
[build] llvm <host>
792-
[build] rustc 0 <host> -> rustc 1 <host>
793-
[build] rustc 0 <host> -> LldWrapper 1 <host>
794-
");
795-
});
784+
let ctx = TestCtx::new();
785+
insta::assert_snapshot!(
786+
ctx.config("build")
787+
.path("compiler")
788+
.args(&["--set", "rust.lld=true"])
789+
.render_steps(), @r"
790+
[build] llvm <host>
791+
[build] rustc 0 <host> -> rustc 1 <host>
792+
[build] rustc 0 <host> -> LldWrapper 1 <host>
793+
");
796794
}
797795

798796
#[test]

src/bootstrap/src/core/config/config.rs

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ use crate::core::config::toml::install::Install;
4242
use crate::core::config::toml::llvm::Llvm;
4343
use crate::core::config::toml::rust::{
4444
BootstrapOverrideLld, Rust, RustOptimize, check_incompatible_options_for_ci_rustc,
45-
default_lld_opt_in_targets, parse_codegen_backends,
45+
parse_codegen_backends,
4646
};
47-
use crate::core::config::toml::target::{Target, TomlTarget};
47+
use crate::core::config::toml::target::{DefaultLinuxLinkerOverride, Target, TomlTarget};
4848
use crate::core::config::{
4949
CompilerBuiltins, DebuginfoLevel, DryRun, GccCiMode, LlvmLibunwind, Merge, ReplaceOpt,
5050
RustcLto, SplitDebuginfo, StringOrBool, threads_from_config,
@@ -623,6 +623,8 @@ impl Config {
623623
let bootstrap_override_lld =
624624
rust_bootstrap_override_lld.or(rust_bootstrap_override_lld_legacy).unwrap_or_default();
625625

626+
let lld_enabled = rust_lld_enabled.unwrap_or(false);
627+
626628
if rust_optimize.as_ref().is_some_and(|v| matches!(v, RustOptimize::Bool(false))) {
627629
eprintln!(
628630
"WARNING: setting `optimize` to `false` is known to cause errors and \
@@ -837,6 +839,7 @@ impl Config {
837839
ar: target_ar,
838840
ranlib: target_ranlib,
839841
default_linker: target_default_linker,
842+
default_linker_linux: target_default_linker_linux,
840843
linker: target_linker,
841844
split_debuginfo: target_split_debuginfo,
842845
llvm_config: target_llvm_config,
@@ -860,6 +863,28 @@ impl Config {
860863

861864
let mut target = Target::from_triple(&triple);
862865

866+
if let Some(linux_override) = target_default_linker_linux.as_ref() {
867+
if target_default_linker.is_some() || rust_default_linker.is_some() {
868+
panic!(
869+
"cannot set both `default-linker` and `default-linker-linux` for target {triple}"
870+
);
871+
}
872+
if !triple.contains("linux-gnu") {
873+
panic!(
874+
"`default-linker-linux` can only be set for Linux GNU targets, not for {triple}"
875+
);
876+
}
877+
match linux_override {
878+
DefaultLinuxLinkerOverride::SelfContainedLldCc => {
879+
if !lld_enabled {
880+
panic!(
881+
"Trying to override the default Linux linker for {triple} to be self-contained LLD, but LLD is not being built. Enable it with rust.lld = true."
882+
);
883+
}
884+
}
885+
}
886+
}
887+
863888
if let Some(ref s) = target_llvm_config {
864889
if download_rustc_commit.is_some() && triple == *host_target.triple {
865890
panic!(
@@ -893,6 +918,7 @@ impl Config {
893918
target.linker = target_linker.map(PathBuf::from);
894919
target.crt_static = target_crt_static;
895920
target.default_linker = target_default_linker;
921+
target.default_linker_linux = target_default_linker_linux;
896922
target.musl_root = target_musl_root.map(PathBuf::from);
897923
target.musl_libdir = target_musl_libdir.map(PathBuf::from);
898924
target.wasi_root = target_wasi_root.map(PathBuf::from);
@@ -926,28 +952,6 @@ impl Config {
926952
llvm_assertions,
927953
);
928954

929-
// We make `x86_64-unknown-linux-gnu` use the self-contained linker by default, so we will
930-
// build our internal lld and use it as the default linker, by setting the `rust.lld` config
931-
// to true by default:
932-
// - on the `x86_64-unknown-linux-gnu` target
933-
// - when building our in-tree llvm (i.e. the target has not set an `llvm-config`), so that
934-
// we're also able to build the corresponding lld
935-
// - or when using an external llvm that's downloaded from CI, which also contains our prebuilt
936-
// lld
937-
// - otherwise, we'd be using an external llvm, and lld would not necessarily available and
938-
// thus, disabled
939-
// - similarly, lld will not be built nor used by default when explicitly asked not to, e.g.
940-
// when the config sets `rust.lld = false`
941-
let lld_enabled = if default_lld_opt_in_targets().contains(&host_target.triple.to_string())
942-
&& hosts == [host_target]
943-
{
944-
let no_llvm_config =
945-
target_config.get(&host_target).is_none_or(|config| config.llvm_config.is_none());
946-
rust_lld_enabled.unwrap_or(llvm_from_ci || no_llvm_config)
947-
} else {
948-
rust_lld_enabled.unwrap_or(false)
949-
};
950-
951955
if llvm_from_ci {
952956
let warn = |option: &str| {
953957
println!(

src/bootstrap/src/core/config/toml/rust.rs

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -438,28 +438,3 @@ pub(crate) fn parse_codegen_backends(
438438
}
439439
found_backends
440440
}
441-
442-
#[cfg(not(test))]
443-
pub fn default_lld_opt_in_targets() -> Vec<String> {
444-
vec!["x86_64-unknown-linux-gnu".to_string()]
445-
}
446-
447-
#[cfg(test)]
448-
thread_local! {
449-
static TEST_LLD_OPT_IN_TARGETS: std::cell::RefCell<Option<Vec<String>>> = std::cell::RefCell::new(None);
450-
}
451-
452-
#[cfg(test)]
453-
pub fn default_lld_opt_in_targets() -> Vec<String> {
454-
TEST_LLD_OPT_IN_TARGETS.with(|cell| cell.borrow().clone()).unwrap_or_default()
455-
}
456-
457-
#[cfg(test)]
458-
pub fn with_lld_opt_in_targets<R>(targets: Vec<String>, f: impl FnOnce() -> R) -> R {
459-
TEST_LLD_OPT_IN_TARGETS.with(|cell| {
460-
let prev = cell.replace(Some(targets));
461-
let result = f();
462-
cell.replace(prev);
463-
result
464-
})
465-
}

src/bootstrap/src/core/config/toml/target.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
//! * [`Target`]: This struct represents the processed and validated configuration for a
1010
//! build target, which is is stored in the main `Config` structure.
1111
12+
use serde::de::Error;
1213
use serde::{Deserialize, Deserializer};
1314

1415
use crate::core::config::{
@@ -24,6 +25,7 @@ define_config! {
2425
ar: Option<String> = "ar",
2526
ranlib: Option<String> = "ranlib",
2627
default_linker: Option<PathBuf> = "default-linker",
28+
default_linker_linux: Option<DefaultLinuxLinkerOverride> = "default-linker-linux",
2729
linker: Option<String> = "linker",
2830
split_debuginfo: Option<String> = "split-debuginfo",
2931
llvm_config: Option<String> = "llvm-config",
@@ -60,6 +62,7 @@ pub struct Target {
6062
pub ar: Option<PathBuf>,
6163
pub ranlib: Option<PathBuf>,
6264
pub default_linker: Option<PathBuf>,
65+
pub default_linker_linux: Option<DefaultLinuxLinkerOverride>,
6366
pub linker: Option<PathBuf>,
6467
pub split_debuginfo: Option<SplitDebuginfo>,
6568
pub sanitizers: Option<bool>,
@@ -89,3 +92,26 @@ impl Target {
8992
target
9093
}
9194
}
95+
96+
/// Overrides the default linker used on a Linux linker.
97+
/// On Linux, the linker is usually invoked through `cc`, therefore this exists as a separate
98+
/// configuration from simply setting `default-linker`, which corresponds to `-Clinker`.
99+
#[derive(Debug, Clone, PartialEq, Eq)]
100+
pub enum DefaultLinuxLinkerOverride {
101+
/// Use the self-contained `rust-lld` linker, invoked through `cc`.
102+
/// Corresponds to `-Clinker-features=+lld -Clink-self-contained=+linker`.
103+
SelfContainedLldCc,
104+
}
105+
106+
impl<'de> Deserialize<'de> for DefaultLinuxLinkerOverride {
107+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
108+
where
109+
D: Deserializer<'de>,
110+
{
111+
let name = String::deserialize(deserializer)?;
112+
match name.as_str() {
113+
"self-contained-lld-cc" => Ok(Self::SelfContainedLldCc),
114+
other => Err(D::Error::unknown_variant(other, &["self-contained-lld-cc"])),
115+
}
116+
}
117+
}

0 commit comments

Comments
 (0)