diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 3a69b19ee609c..d6d170bef70a5 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -625,6 +625,12 @@ impl LinkSelfContainedDefault { _ => "crt-objects-fallback", } } + + /// Creates a `LinkSelfContainedDefault` enabling the self-contained linker for target specs + /// (the equivalent of `-Clink-self-contained=+linker` on the CLI). + pub fn with_linker() -> LinkSelfContainedDefault { + LinkSelfContainedDefault::WithComponents(LinkSelfContainedComponents::LINKER) + } } bitflags::bitflags! { diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs index 11fb28a9aed7f..f0598ff9c5f26 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs @@ -18,6 +18,14 @@ pub fn target() -> Target { | SanitizerSet::THREAD; base.supports_xray = true; + #[cfg(use_rust_lld)] + { + // When we're asked to use the `rust-lld` linker by default, set the appropriate lld-using + // linker flavor, and self-contained linker component. + base.linker_flavor = LinkerFlavor::Gnu(Cc::Yes, Lld::Yes); + base.link_self_contained = crate::spec::LinkSelfContainedDefault::with_linker(); + } + Target { llvm_target: "x86_64-unknown-linux-gnu".into(), metadata: crate::spec::TargetMetadata { diff --git a/config.example.toml b/config.example.toml index 0d4b4e9e7e026..60fc3a5f4454b 100644 --- a/config.example.toml +++ b/config.example.toml @@ -652,9 +652,12 @@ # when no explicit backend is specified. #codegen-backends = ["llvm"] -# Indicates whether LLD will be compiled and made available in the sysroot for -# rustc to execute. -#lld = false +# Indicates whether LLD will be compiled and made available in the sysroot for rustc to execute, and +# whether to set it as rustc's default linker on `x86_64-unknown-linux-gnu`. This will also only be +# when *not* building an external LLVM (so only when using `download-ci-llvm` or building LLVM from +# the in-tree source): setting `llvm-config` in the `[target.x86_64-unknown-linux-gnu]` section will +# make this default to false. +#lld = false in all cases, except on `x86_64-unknown-linux-gnu` as described above, where it is true # Indicates whether LLD will be used to link Rust crates during bootstrap on # supported platforms. diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 50149d370d5ee..35824a1c6d498 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1129,6 +1129,12 @@ pub fn rustc_cargo_env( cargo.env("CFG_DEFAULT_LINKER", s); } + // Enable rustc's cfgs for `rust-lld` when requested. + if builder.config.lld_enabled { + cargo.rustflag("--cfg=use_rust_lld"); + cargo.rustdocflag("--cfg=use_rust_lld"); + } + if builder.config.rust_verify_llvm_ir { cargo.env("RUSTC_VERIFY_LLVM_IR", "1"); } diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index a272d8bff005d..c39553bd6e4f9 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1551,6 +1551,7 @@ impl Config { let mut debuginfo_level_tests = None; let mut optimize = None; let mut omit_git_hash = None; + let mut lld_enabled = None; if let Some(rust) = toml.rust { let Rust { @@ -1584,7 +1585,7 @@ impl Config { dist_src, save_toolstates, codegen_backends, - lld, + lld: lld_enabled_toml, llvm_tools, llvm_bitcode_linker, deny_warnings, @@ -1639,6 +1640,7 @@ impl Config { debuginfo_level_std = debuginfo_level_std_toml; debuginfo_level_tools = debuginfo_level_tools_toml; debuginfo_level_tests = debuginfo_level_tests_toml; + lld_enabled = lld_enabled_toml; config.rust_split_debuginfo_for_build_triple = split_debuginfo .as_deref() @@ -1672,18 +1674,8 @@ impl Config { config.incremental = true; } set(&mut config.lld_mode, lld_mode); - set(&mut config.lld_enabled, lld); set(&mut config.llvm_bitcode_linker_enabled, llvm_bitcode_linker); - if matches!(config.lld_mode, LldMode::SelfContained) - && !config.lld_enabled - && flags.stage.unwrap_or(0) > 0 - { - panic!( - "Trying to use self-contained lld as a linker, but LLD is not being added to the sysroot. Enable it with rust.lld = true." - ); - } - config.llvm_tools_enabled = llvm_tools.unwrap_or(true); config.rustc_parallel = parallel_compiler.unwrap_or(config.channel == "dev" || config.channel == "nightly"); @@ -1973,6 +1965,43 @@ impl Config { config.llvm_plugins = llvm_plugins.unwrap_or(false); config.rust_optimize = optimize.unwrap_or(RustOptimize::Bool(true)); + // We make `x86_64-unknown-linux-gnu` use the self-contained linker by default, so we will + // build our internal lld and use it as the default linker, by setting the `rust.lld` config + // to true by default: + // - on the `x86_64-unknown-linux-gnu` target + // - on the `dev` and `nightly` channels + // - when building our in-tree llvm (i.e. the target has not set an `llvm-config`), so that + // we're also able to build the corresponding lld + // - or when using an external llvm that's downloaded from CI, which also contains our prebuilt + // lld + // - otherwise, we'd be using an external llvm, and lld would not necessarily available and + // thus, disabled + // - similarly, lld will not be built nor used by default when explicitly asked not to, e.g. + // when the config sets `rust.lld = false` + if config.build.triple == "x86_64-unknown-linux-gnu" + && config.hosts == &[config.build] + && (config.channel == "dev" || config.channel == "nightly") + { + let no_llvm_config = config + .target_config + .get(&config.build) + .is_some_and(|target_config| target_config.llvm_config.is_none()); + let enable_lld = config.llvm_from_ci || no_llvm_config; + // Prefer the config setting in case an explicit opt-out is needed. + config.lld_enabled = lld_enabled.unwrap_or(enable_lld); + } else { + set(&mut config.lld_enabled, lld_enabled); + } + + if matches!(config.lld_mode, LldMode::SelfContained) + && !config.lld_enabled + && flags.stage.unwrap_or(0) > 0 + { + panic!( + "Trying to use self-contained lld as a linker, but LLD is not being added to the sysroot. Enable it with rust.lld = true." + ); + } + let default = debug == Some(true); config.rust_debug_assertions = debug_assertions.unwrap_or(default); config.rust_debug_assertions_std = diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 0b6d987f87dec..5988fca4059e4 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -114,6 +114,10 @@ const EXTRA_CHECK_CFGS: &[(Option, &str, Option<&[&'static str]>)] = &[ // Needed to avoid the need to copy windows.lib into the sysroot. (Some(Mode::Rustc), "windows_raw_dylib", None), (Some(Mode::ToolRustc), "windows_raw_dylib", None), + // If rustc wants to use rust-lld as the default linker in a target spec. + (Some(Mode::Rustc), "use_rust_lld", None), + (Some(Mode::ToolRustc), "use_rust_lld", None), + (Some(Mode::Codegen), "use_rust_lld", None), ]; /// A structure representing a Rust compiler. diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index db3df598a0c63..4406ff11bd7df 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -175,4 +175,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Warning, summary: "The deprecated field `changelog-seen` has been removed. Using that field in `config.toml` from now on will result in breakage.", }, + ChangeInfo { + change_id: 124129, + severity: ChangeSeverity::Warning, + summary: "`rust.lld` has a new default value of `true` on `x86_64-unknown-linux-gnu`. Starting at stage1, `rust-lld` will thus be this target's default linker. No config changes should be necessary.", + }, ]; diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile index 538962802c96f..354fe438511c3 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-17/Dockerfile @@ -54,7 +54,8 @@ ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ --llvm-root=/usr/lib/llvm-17 \ --enable-llvm-link-shared \ - --set rust.thin-lto-import-instr-limit=10 + --set rust.thin-lto-import-instr-limit=10 \ + --set rust.lld=false COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile index 3476b10a3addc..f7d00e9af98aa 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-18/Dockerfile @@ -51,7 +51,8 @@ ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ --llvm-root=/usr/lib/llvm-18 \ --enable-llvm-link-shared \ - --set rust.thin-lto-import-instr-limit=10 + --set rust.thin-lto-import-instr-limit=10 \ + --set rust.lld=false COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/ COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/ diff --git a/tests/run-make/rust-lld-by-default/main.rs b/tests/run-make/rust-lld-by-default/main.rs new file mode 100644 index 0000000000000..e9f655fc09e4c --- /dev/null +++ b/tests/run-make/rust-lld-by-default/main.rs @@ -0,0 +1,5 @@ +// Test linking using `cc` with `rust-lld`, which is on by default on the x86_64-unknown-linux-gnu +// target. +// See https://github.com/rust-lang/compiler-team/issues/510 for more info + +fn main() {} diff --git a/tests/run-make/rust-lld-by-default/rmake.rs b/tests/run-make/rust-lld-by-default/rmake.rs new file mode 100644 index 0000000000000..876968727f3ca --- /dev/null +++ b/tests/run-make/rust-lld-by-default/rmake.rs @@ -0,0 +1,43 @@ +// Ensure that rust-lld is used as the default linker on `x86_64-unknown-linux-gnu`, and that it can +// also be turned off with a CLI flag. + +//@ needs-rust-lld +//@ only-x86_64-unknown-linux-gnu + +extern crate run_make_support; + +use run_make_support::regex::Regex; +use run_make_support::rustc; +use std::process::Output; + +fn main() { + // A regular compilation should use rust-lld by default. We'll check that by asking the linker + // to display its version number with a link-arg. + let output = rustc() + .env("RUSTC_LOG", "rustc_codegen_ssa::back::link=info") + .link_arg("-Wl,-v") + .input("main.rs") + .run(); + assert!( + find_lld_version_in_logs(output), + "the LLD version string should be present in the output logs" + ); + + // But it can still be disabled by turning the linker feature off. + let output = rustc() + .env("RUSTC_LOG", "rustc_codegen_ssa::back::link=info") + .link_arg("-Wl,-v") + .arg("-Zlinker-features=-lld") + .input("main.rs") + .run(); + assert!( + !find_lld_version_in_logs(output), + "the LLD version string should not be present in the output logs" + ); +} + +fn find_lld_version_in_logs(output: Output) -> bool { + let lld_version_re = Regex::new(r"^LLD [0-9]+\.[0-9]+\.[0-9]+").unwrap(); + let stderr = std::str::from_utf8(&output.stderr).unwrap(); + stderr.lines().any(|line| lld_version_re.is_match(line)) +}