Skip to content

Support -Clink-self-contained=+linker for ld.lld linker flavor #140813

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
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
25 changes: 23 additions & 2 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1369,8 +1369,29 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
"cc"
}
}
LinkerFlavor::Gnu(_, Lld::Yes)
| LinkerFlavor::Darwin(_, Lld::Yes)
LinkerFlavor::Gnu(_, Lld::Yes) => {
// Here, we're asked to use lld without a linker driver, so we'll check
// whether the self-contained linker is enabled on the CLI or explicitly by
// the target, to launch `rust-lld` instead of the system `lld`.
let self_contained_cli = &sess.opts.cg.link_self_contained;
let self_contained_target = match sess.target.link_self_contained {
LinkSelfContainedDefault::WithComponents(components) => {
components.is_linker_enabled()
}
_ => {
// We don't try to infer whether the self-contained linker component
// is enabled: on some targets (mingw), the component inference
// actually uses the linker to compute whether self-contained
// linking is enabled, but we're computing the linker to use here.
false
}
};
let self_contained_linker = (self_contained_cli.is_linker_enabled()
|| self_contained_target)
&& !self_contained_cli.is_linker_disabled();
if self_contained_linker { "rust-lld" } else { "lld" }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With mingw in particular this logic + logic in detect_self_contained_mingw mean that self-contained=+linker always turns into self-contained=yes :D

}
LinkerFlavor::Darwin(_, Lld::Yes)
| LinkerFlavor::WasmLld(..)
| LinkerFlavor::Msvc(Lld::Yes) => "lld",
LinkerFlavor::Gnu(..) | LinkerFlavor::Darwin(..) | LinkerFlavor::Unix(..) => {
Expand Down
6 changes: 6 additions & 0 deletions tests/run-make/rust-lld-self-contained-flavor/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// We're just trying to launch the linker to see which one rustc picks between system lld and
// rust-lld. We don't need to link anything successfully though, so this is just a stub.

#![feature(no_core)]
#![no_core]
#![no_main]
57 changes: 57 additions & 0 deletions tests/run-make/rust-lld-self-contained-flavor/rmake.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// When using an `ld.lld` linker flavor, rustc will invoke lld directly. This test ensures that
// turning on the self-contained linker will result in rustc choosing `rust-lld` instead of the
// system lld.
//
// This is not straigthforward to test, so we make linking fail and look for the linker name
// appearing in the failure.

//@ needs-rust-lld
//@ only-x86_64-unknown-linux-gnu

use run_make_support::{Rustc, rustc};

// Make linking fail because of an incorrect flag. In case there's an issue on CI or locally, we
// also ask for rustc's linking debug logging, in order to have more information in the test logs.
fn make_linking_fail(linker_flavor: &str) -> Rustc {
let mut rustc = rustc();
rustc
.env("RUSTC_LOG", "rustc_codegen_ssa::back::link") // ask for linking debug logs
.linker_flavor(linker_flavor)
.link_arg("--baguette") // ensures linking failure
.input("main.rs");
rustc
}

fn main() {
// 1. Using `ld` directly via the linker flavor.
make_linking_fail("ld").run_fail().assert_stderr_contains("error: linking with `ld` failed");

// 2. Using `lld` via the linker flavor. We ensure the self-contained linker is disabled to use
// the system lld.
//
// This could fail in two ways:
// - the most likely case: `lld` rejects our incorrect link arg
// - or there may not be an `lld` on the $PATH. The testing/run-make infrastructure runs tests
// with llvm tools in the path and there is an `lld` executable there most of the time (via
// `ci-llvm`). But since one can turn that off in the config, we also look for the usual
// "-fuse-ld=lld" failure.
let system_lld_failure = make_linking_fail("ld.lld")
.arg("-Clink-self-contained=-linker")
.arg("-Zunstable-options")
.run_fail();
let lld_stderr = system_lld_failure.stderr_utf8();
assert!(
lld_stderr.contains("error: linking with `lld` failed")
|| lld_stderr.contains("error: linker `lld` not found"),
"couldn't find `lld` failure in stderr: {}",
lld_stderr,
);

// 3. Using the same lld linker flavor and enabling the self-contained linker should use
// `rust-lld`.
make_linking_fail("ld.lld")
.arg("-Clink-self-contained=+linker")
.arg("-Zunstable-options")
.run_fail()
.assert_stderr_contains("error: linking with `rust-lld` failed");
}
Loading