Skip to content

Commit 1718c16

Browse files
committed
Auto merge of rust-lang#140813 - lqd:self-contained-lld-flavor, r=<try>
Support `-Clink-self-contained=+linker` for `ld.lld` linker flavor When using an `ld.lld` linker flavor, rustc will invoke lld directly. This PR adds support to choose `rust-lld` instead of the system lld when the self-contained linker is enabled (on the CLI or by the target). There's some slight wrinkle/cycle here: inferring whether self-contained linking is enabled needs to know the linker (on mingw), but to choose the linker (rust-lld instead of lld) we need to know if the self-contained linker is enabled... So I only check for explicit self-contained linking components in the target (without the inference implied by the `LinkSelfContainedDefault` infra), as well as the CLI. That seems fine?. The linker command and binary is usually hidden by rustc so while I'm not enamored by the test, it feels slightly cleaner than parsing rustc's debug logs looking for the linker that is invoked. r? ghost try-job: dist-x86_64-linux
2 parents e964cca + d74189c commit 1718c16

File tree

3 files changed

+86
-2
lines changed

3 files changed

+86
-2
lines changed

compiler/rustc_codegen_ssa/src/back/link.rs

+23-2
Original file line numberDiff line numberDiff line change
@@ -1369,8 +1369,29 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
13691369
"cc"
13701370
}
13711371
}
1372-
LinkerFlavor::Gnu(_, Lld::Yes)
1373-
| LinkerFlavor::Darwin(_, Lld::Yes)
1372+
LinkerFlavor::Gnu(_, Lld::Yes) => {
1373+
// Here, we're asked to use lld without a linker driver, so we'll check
1374+
// whether the self-contained linker is enabled on the CLI or explicitly by
1375+
// the target, to launch `rust-lld` instead of the system `lld`.
1376+
let self_contained_cli = &sess.opts.cg.link_self_contained;
1377+
let self_contained_target = match sess.target.link_self_contained {
1378+
LinkSelfContainedDefault::WithComponents(components) => {
1379+
components.is_linker_enabled()
1380+
}
1381+
_ => {
1382+
// We don't try to infer whether the self-contained linker component
1383+
// is enabled: on some targets (mingw), the component inference
1384+
// actually uses the linker to compute whether self-contained
1385+
// linking is enabled, but we're computing the linker to use here.
1386+
false
1387+
}
1388+
};
1389+
let self_contained_linker = (self_contained_cli.is_linker_enabled()
1390+
|| self_contained_target)
1391+
&& !self_contained_cli.is_linker_disabled();
1392+
if self_contained_linker { "rust-lld" } else { "lld" }
1393+
}
1394+
LinkerFlavor::Darwin(_, Lld::Yes)
13741395
| LinkerFlavor::WasmLld(..)
13751396
| LinkerFlavor::Msvc(Lld::Yes) => "lld",
13761397
LinkerFlavor::Gnu(..) | LinkerFlavor::Darwin(..) | LinkerFlavor::Unix(..) => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// We're just trying to launch the linker to see which one rustc picks between system lld and
2+
// rust-lld. We don't need to link anything successfully though, so this is just a stub.
3+
4+
#![feature(no_core)]
5+
#![no_core]
6+
#![no_main]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// When using an `ld.lld` linker flavor, rustc will invoke lld directly. This test ensures that
2+
// turning on the self-contained linker will result in rustc choosing `rust-lld` instead of the
3+
// system lld.
4+
//
5+
// This is not straigthforward to test, so we make linking fail and look for the linker name
6+
// appearing in the failure.
7+
8+
//@ needs-rust-lld
9+
//@ only-x86_64-unknown-linux-gnu
10+
11+
use run_make_support::{Rustc, rustc};
12+
13+
// Make linking fail because of an incorrect flag. In case there's an issue on CI or locally, we
14+
// also ask for rustc's linking debug logging, in order to have more information in the test logs.
15+
fn make_linking_fail(linker_flavor: &str) -> Rustc {
16+
let mut rustc = rustc();
17+
rustc
18+
.env("RUSTC_LOG", "rustc_codegen_ssa::back::link") // ask for linking debug logs
19+
.linker_flavor(linker_flavor)
20+
.link_arg("--baguette") // ensures linking failure
21+
.input("main.rs");
22+
rustc
23+
}
24+
25+
fn main() {
26+
// 1. Using `ld` directly via the linker flavor.
27+
make_linking_fail("ld").run_fail().assert_stderr_contains("error: linking with `ld` failed");
28+
29+
// 2. Using `lld` via the linker flavor. We ensure the self-contained linker is disabled to use
30+
// the system lld.
31+
//
32+
// This could fail in two ways:
33+
// - the most likely case: `lld` rejects our incorrect link arg
34+
// - or there may not be an `lld` on the $PATH. The testing/run-make infrastructure runs tests
35+
// with llvm tools in the path and there is an `lld` executable there most of the time (via
36+
// `ci-llvm`). But since one can turn that off in the config, we also look for the usual
37+
// "-fuse-ld=lld" failure.
38+
let system_lld_failure = make_linking_fail("ld.lld")
39+
.arg("-Clink-self-contained=-linker")
40+
.arg("-Zunstable-options")
41+
.run_fail();
42+
let lld_stderr = system_lld_failure.stderr_utf8();
43+
assert!(
44+
lld_stderr.contains("error: linking with `lld` failed")
45+
|| lld_stderr.contains("error: linker `lld` not found"),
46+
"couldn't find `lld` failure in stderr: {}",
47+
lld_stderr,
48+
);
49+
50+
// 3. Using the same lld linker flavor and enabling the self-contained linker should use
51+
// `rust-lld`.
52+
make_linking_fail("ld.lld")
53+
.arg("-Clink-self-contained=+linker")
54+
.arg("-Zunstable-options")
55+
.run_fail()
56+
.assert_stderr_contains("error: linking with `rust-lld` failed");
57+
}

0 commit comments

Comments
 (0)