@@ -8,14 +8,16 @@ use rustc_fs_util::fix_windows_verbatim_for_gcc;
88use rustc_hir:: def_id:: CrateNum ;
99use rustc_middle:: middle:: dependency_format:: Linkage ;
1010use 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+ } ;
1214use rustc_session:: cstore:: DllImport ;
1315use rustc_session:: output:: { check_file_is_writeable, invalid_output_for_target, out_filename} ;
1416use rustc_session:: search_paths:: PathKind ;
1517use 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 } ;
1921use rustc_span:: symbol:: Symbol ;
2022use rustc_target:: spec:: crt_objects:: { CrtObjects , CrtObjectsFallback } ;
2123use 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}
0 commit comments