Skip to content

Commit 41a6a18

Browse files
committed
implement linker-flavor enrichments for MCP
1 parent bfa09e1 commit 41a6a18

File tree

2 files changed

+130
-43
lines changed

2 files changed

+130
-43
lines changed

compiler/rustc_codegen_ssa/src/back/link.rs

Lines changed: 129 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@ use rustc_fs_util::fix_windows_verbatim_for_gcc;
88
use rustc_hir::def_id::CrateNum;
99
use rustc_middle::middle::dependency_format::Linkage;
1010
use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip};
11-
use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, SplitDwarfKind};
11+
use rustc_session::config::{
12+
LinkerFlavorCli, OutputFilenames, OutputType, PrintRequest, SplitDwarfKind,
13+
};
1214
use rustc_session::cstore::DllImport;
1315
use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename};
1416
use rustc_session::search_paths::PathKind;
1517
use rustc_session::utils::NativeLibKind;
1618
/// For all the linkers we support, and information they might
1719
/// need out of the shared crate context before we get rid of it.
18-
use rustc_session::{filesearch, Session};
20+
use rustc_session::{config::InstrumentCoverage, filesearch, Session};
1921
use rustc_span::symbol::Symbol;
2022
use rustc_target::spec::crt_objects::{CrtObjects, CrtObjectsFallback};
2123
use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor, SplitDebuginfo};
@@ -1921,7 +1923,7 @@ fn add_order_independent_options(
19211923
out_filename: &Path,
19221924
tmpdir: &Path,
19231925
) {
1924-
add_gcc_ld_path(cmd, sess, flavor);
1926+
handle_cli_linker_flavors(cmd, sess, flavor, crt_objects_fallback);
19251927

19261928
add_apple_sdk(cmd, sess, flavor);
19271929

@@ -2573,46 +2575,131 @@ fn get_apple_sdk_root(sdk_name: &str) -> Result<String, String> {
25732575
}
25742576
}
25752577

2576-
fn add_gcc_ld_path(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
2577-
if let Some(ld_impl) = sess.opts.debugging_opts.gcc_ld {
2578-
if let LinkerFlavor::Gcc = flavor {
2579-
match ld_impl {
2580-
LdImpl::Lld => {
2581-
if sess.target.lld_flavor == LldFlavor::Ld64 {
2582-
let tools_path = sess.get_tools_search_paths(false);
2583-
let ld64_exe = tools_path
2584-
.into_iter()
2585-
.map(|p| p.join("gcc-ld"))
2586-
.map(|p| {
2587-
p.join(if sess.host.is_like_windows { "ld64.exe" } else { "ld64" })
2588-
})
2589-
.find(|p| p.exists())
2590-
.unwrap_or_else(|| sess.fatal("rust-lld (as ld64) not found"));
2591-
cmd.cmd().arg({
2592-
let mut arg = OsString::from("-fuse-ld=");
2593-
arg.push(ld64_exe);
2594-
arg
2595-
});
2596-
} else {
2597-
let tools_path = sess.get_tools_search_paths(false);
2598-
let lld_path = tools_path
2599-
.into_iter()
2600-
.map(|p| p.join("gcc-ld"))
2601-
.find(|p| {
2602-
p.join(if sess.host.is_like_windows { "ld.exe" } else { "ld" })
2603-
.exists()
2604-
})
2605-
.unwrap_or_else(|| sess.fatal("rust-lld (as ld) not found"));
2606-
cmd.cmd().arg({
2607-
let mut arg = OsString::from("-B");
2608-
arg.push(lld_path);
2609-
arg
2610-
});
2611-
}
2612-
}
2578+
/// This takes care of the various possible enrichments to the linking that can be requested on the
2579+
/// CLI (and emitting errors and warnings when applicable):
2580+
/// - shortcuts to `-fuse-ld` with the `gcc` flavor
2581+
/// - the unstable `-Zgcc-ld=lld` flag to use `rust-lld`, stabilized as the following item
2582+
/// - the combination of these two: opting into using `lld` and the self-contained linker, to use
2583+
/// the `rustup` distributed `rust-lld`
2584+
fn handle_cli_linker_flavors(
2585+
cmd: &mut dyn Linker,
2586+
sess: &Session,
2587+
flavor: LinkerFlavor,
2588+
crt_objects_fallback: bool,
2589+
) {
2590+
let unstable_gcc_lld = sess.opts.debugging_opts.gcc_ld == Some(LdImpl::Lld);
2591+
if unstable_gcc_lld {
2592+
// Sanity check: ensure `gcc` is the currently selected flavor.
2593+
if LinkerFlavor::Gcc != flavor {
2594+
sess.fatal("`-Zgcc-ld` is used even though the linker flavor is not `gcc`");
2595+
}
2596+
}
2597+
2598+
let cg = &sess.opts.cg;
2599+
2600+
// The `-C linker-flavor` CLI flag can optionally enrich linker-flavors. Check whether that's
2601+
// applicable, and emit errors if sanity checks fail. There's currently only one enrichment:
2602+
// adding an argument to the `cc` invocation to use the `use_ld` given linker.
2603+
let use_ld = match &cg.linker_flavor {
2604+
Some(LinkerFlavorCli::Gcc { use_ld }) => {
2605+
// Ensure `gcc` is the currently selected flavor. Error out cleanly, as `-Zgcc-ld` does
2606+
// if that happens, but this should be unreachable.
2607+
if LinkerFlavor::Gcc != flavor {
2608+
sess.fatal(
2609+
"`-Clinker-flavor=gcc:*` flag is used even though the \
2610+
linker flavor is not `gcc`",
2611+
);
2612+
}
2613+
2614+
use_ld
2615+
}
2616+
2617+
// Note: exhaustive match arm here, to avoid fallthroughs if new linker-flavor enrichments
2618+
// are added in the future.
2619+
Some(LinkerFlavorCli::WellKnown(_)) | None => {
2620+
if unstable_gcc_lld {
2621+
"lld"
2622+
} else {
2623+
// We're not in a situation needing enrichments.
2624+
return;
26132625
}
2626+
}
2627+
};
2628+
2629+
// From now, we handle the `gcc` linker-flavor enrichment.
2630+
let mut cc_arg = OsString::new();
2631+
2632+
// Except for `lld`, the given linker executable will be passed straight to `-fuse-ld`.
2633+
if use_ld == "lld" {
2634+
// Start by checking if we're in the context of a known issue that users might hit when
2635+
// using `lld`:
2636+
//
2637+
// 1. when requesting self-contained CRT linking (or on a target that does it
2638+
// automatically), and coverage/profile generation: point at #79555 "Coverage is not
2639+
// generated when using rust-lld as linker"
2640+
let instrument_coverage = cg.instrument_coverage.is_some()
2641+
&& cg.instrument_coverage != Some(InstrumentCoverage::Off);
2642+
let generate_profile = cg.profile_generate.enabled();
2643+
if crt_objects_fallback && (instrument_coverage || generate_profile) {
2644+
sess.warn(
2645+
"Using `lld`, self-contained linking, and coverage or profile generation has known \
2646+
issues. See issue #79555 for more details, at \
2647+
https://github.com/rust-lang/rust/issues/79555",
2648+
);
2649+
}
2650+
2651+
// 2. Maybe point at https://github.com/flamegraph-rs/flamegraph/pull/157 or the
2652+
// corresponding rust/LLVM issue when/if it's tracked, depending on whether we use the
2653+
// workaround argument `--no-rosegment` by default when invoking `lld`.
2654+
//
2655+
// 3. If in the future, other linker flavors and targets are eligible to a `rust-lld`
2656+
// enrichment, maybe also point at target-specific issues like:
2657+
// - MSVC + ThinLTO blocker https://github.com/rust-lang/rust/issues/81408
2658+
// - the "lld on MSVC" tracking issue https://github.com/rust-lang/rust/issues/71520
2659+
// containing a list of blocking issues
2660+
2661+
// Now, handle `rust-lld`. If both the `-Clink-self-contained=linker` and
2662+
// `-Clinker-flavor=gcc:lld` flags were provided, we use `rust-lld`, the rustup-distributed
2663+
// version of `lld` (when applicable, i.e. not in distro-builds) by:
2664+
// - checking the `lld-wrapper`s exist in the sysroot
2665+
// - adding their folder as a search path, or requesting to use a wrapper directly
2666+
//
2667+
// FIXME: make sure rust.lld config flag is turned on before adding the sysroot magic handling
2668+
if sess.opts.cg.link_self_contained.linker.is_on() || unstable_gcc_lld {
2669+
// A `gcc-ld` folder (containing the `lld-wrapper`s that will run `rust-lld`) is present in
2670+
// the sysroot's target-specific tool binaries folder.
2671+
let tools_path = sess.get_tools_search_paths(false);
2672+
let mut possible_gcc_ld_paths = tools_path.into_iter().map(|p| p.join("gcc-ld"));
2673+
2674+
// Set-up the correct flag and argument to find the wrapper:
2675+
// - a path to the `ld64` wrapper needs to be passed with `-fuse-ld` on Apple targets
2676+
// - otherwise, a `-B` search path to the `gcc-ld` folder is enough
2677+
let (cc_flag, lld_wrapper_path) = if sess.target.lld_flavor == LldFlavor::Ld64 {
2678+
let ld64_exe = if sess.host.is_like_windows { "ld64.exe" } else { "ld64" };
2679+
let ld64_path = possible_gcc_ld_paths
2680+
.map(|p| p.join(ld64_exe))
2681+
.find(|p| p.exists())
2682+
.unwrap_or_else(|| sess.fatal("rust-lld (as ld64) not found"));
2683+
("-fuse-ld=", ld64_path)
2684+
} else {
2685+
let ld_exe = if sess.host.is_like_windows { "ld.exe" } else { "ld" };
2686+
let ld_path = possible_gcc_ld_paths
2687+
.find(|p| p.join(ld_exe).exists())
2688+
.unwrap_or_else(|| sess.fatal("rust-lld (as ld) not found"));
2689+
("-B", ld_path)
2690+
};
2691+
cc_arg.push(cc_flag);
2692+
cc_arg.push(lld_wrapper_path);
26142693
} else {
2615-
sess.fatal("option `-Z gcc-ld` is used even though linker flavor is not gcc");
2694+
// We were asked to use `lld` but not `rust-lld`.
2695+
cc_arg.push("-fuse-ld=lld");
26162696
}
2617-
}
2697+
} else {
2698+
// Otherwise, we were just asked to use a linker executable, and it's expected that `cc`
2699+
// will find it on the $PATH.
2700+
cc_arg.push("-fuse-ld=");
2701+
cc_arg.push(use_ld);
2702+
};
2703+
2704+
cmd.cmd().arg(cc_arg);
26182705
}

compiler/rustc_session/src/options.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1593,7 +1593,7 @@ pub enum WasiExecModel {
15931593
Reactor,
15941594
}
15951595

1596-
#[derive(Clone, Copy, Hash)]
1596+
#[derive(Clone, Copy, Hash, PartialEq)]
15971597
pub enum LdImpl {
15981598
Lld,
15991599
}

0 commit comments

Comments
 (0)