diff options
Diffstat (limited to 'compiler/rustc_codegen_ssa/src/back/link.rs')
-rw-r--r-- | compiler/rustc_codegen_ssa/src/back/link.rs | 289 |
1 files changed, 182 insertions, 107 deletions
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 8a00c42a0..b603a8787 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -5,14 +5,14 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_errors::{ErrorGuaranteed, Handler}; -use rustc_fs_util::fix_windows_verbatim_for_gcc; +use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_metadata::find_native_static_library; -use rustc_metadata::fs::{emit_wrapper_file, METADATA_FILENAME}; +use rustc_metadata::fs::{copy_to_stdout, emit_wrapper_file, METADATA_FILENAME}; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::SymbolExportKind; -use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip}; +use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, Strip}; use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, SplitDwarfKind}; use rustc_session::cstore::DllImport; use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename}; @@ -23,7 +23,7 @@ use rustc_session::utils::NativeLibKind; use rustc_session::{filesearch, Session}; use rustc_span::symbol::Symbol; use rustc_target::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault}; -use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy}; +use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld, PanicStrategy}; use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo}; use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder}; @@ -68,6 +68,7 @@ pub fn link_binary<'a>( ) -> Result<(), ErrorGuaranteed> { let _timer = sess.timer("link_binary"); let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); + let mut tempfiles_for_stdout_output: Vec<PathBuf> = Vec::new(); for &crate_type in sess.crate_types().iter() { // Ignore executable crates if we have -Z no-codegen, as they will error. if (sess.opts.unstable_opts.no_codegen || !sess.opts.output_types.should_codegen()) @@ -97,12 +98,15 @@ pub fn link_binary<'a>( .tempdir() .unwrap_or_else(|error| sess.emit_fatal(errors::CreateTempDir { error })); let path = MaybeTempDir::new(tmpdir, sess.opts.cg.save_temps); - let out_filename = out_filename( + let output = out_filename( sess, crate_type, outputs, codegen_results.crate_info.local_crate_name, ); + let crate_name = format!("{}", codegen_results.crate_info.local_crate_name); + let out_filename = + output.file_for_writing(outputs, OutputType::Exe, Some(crate_name.as_str())); match crate_type { CrateType::Rlib => { let _timer = sess.timer("link_rlib"); @@ -152,6 +156,17 @@ pub fn link_binary<'a>( ); } } + + if output.is_stdout() { + if output.is_tty() { + sess.emit_err(errors::BinaryOutputToTty { + shorthand: OutputType::Exe.shorthand(), + }); + } else if let Err(e) = copy_to_stdout(&out_filename) { + sess.emit_err(errors::CopyPath::new(&out_filename, output.as_path(), e)); + } + tempfiles_for_stdout_output.push(out_filename); + } } } @@ -189,6 +204,11 @@ pub fn link_binary<'a>( remove_temps_from_module(allocator_module); } + // Remove the temporary files if output goes to stdout + for temp in tempfiles_for_stdout_output { + ensure_removed(sess.diagnostic(), &temp); + } + // If no requested outputs require linking, then the object temporaries should // be kept. if !sess.opts.output_types.should_link() { @@ -893,7 +913,7 @@ fn link_natively<'a>( linker_path: &linker_path, exit_status: prog.status, command: &cmd, - escaped_output: &escaped_output, + escaped_output, }; sess.diagnostic().emit_err(err); // If MSVC's `link.exe` was expected but the return code @@ -1188,6 +1208,9 @@ fn add_sanitizer_libraries(sess: &Session, crate_type: CrateType, linker: &mut d if sanitizer.contains(SanitizerSet::HWADDRESS) { link_sanitizer_runtime(sess, linker, "hwasan"); } + if sanitizer.contains(SanitizerSet::SAFESTACK) { + link_sanitizer_runtime(sess, linker, "safestack"); + } } fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) { @@ -1299,44 +1322,7 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { let stem = linker.file_stem().and_then(|stem| stem.to_str()).unwrap_or_else(|| { sess.emit_fatal(errors::LinkerFileStem); }); - - // Remove any version postfix. - let stem = stem - .rsplit_once('-') - .and_then(|(lhs, rhs)| rhs.chars().all(char::is_numeric).then_some(lhs)) - .unwrap_or(stem); - - // GCC/Clang can have an optional target prefix. - let flavor = if stem == "emcc" { - LinkerFlavor::EmCc - } else if stem == "gcc" - || stem.ends_with("-gcc") - || stem == "g++" - || stem.ends_with("-g++") - || stem == "clang" - || stem.ends_with("-clang") - || stem == "clang++" - || stem.ends_with("-clang++") - { - LinkerFlavor::from_cli(LinkerFlavorCli::Gcc, &sess.target) - } else if stem == "wasm-ld" || stem.ends_with("-wasm-ld") { - LinkerFlavor::WasmLld(Cc::No) - } else if stem == "ld" || stem.ends_with("-ld") { - LinkerFlavor::from_cli(LinkerFlavorCli::Ld, &sess.target) - } else if stem == "ld.lld" { - LinkerFlavor::Gnu(Cc::No, Lld::Yes) - } else if stem == "link" { - LinkerFlavor::Msvc(Lld::No) - } else if stem == "lld-link" { - LinkerFlavor::Msvc(Lld::Yes) - } else if stem == "lld" || stem == "rust-lld" { - let lld_flavor = sess.target.linker_flavor.lld_flavor(); - LinkerFlavor::from_cli(LinkerFlavorCli::Lld(lld_flavor), &sess.target) - } else { - // fall back to the value in the target spec - sess.target.linker_flavor - }; - + let flavor = sess.target.linker_flavor.with_linker_hints(stem); Some((linker, flavor)) } (None, None) => None, @@ -1346,7 +1332,7 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { // linker and linker flavor specified via command line have precedence over what the target // specification specifies let linker_flavor = - sess.opts.cg.linker_flavor.map(|flavor| LinkerFlavor::from_cli(flavor, &sess.target)); + sess.opts.cg.linker_flavor.map(|flavor| sess.target.linker_flavor.with_cli_hints(flavor)); if let Some(ret) = infer_from(sess, sess.opts.cg.linker.clone(), linker_flavor) { return ret; } @@ -1702,7 +1688,7 @@ fn detect_self_contained_mingw(sess: &Session) -> bool { /// instead of being found somewhere on the host system. /// We only provide such support for a very limited number of targets. fn self_contained(sess: &Session, crate_type: CrateType) -> bool { - if let Some(self_contained) = sess.opts.cg.link_self_contained { + if let Some(self_contained) = sess.opts.cg.link_self_contained.explicitly_set { if sess.target.link_self_contained == LinkSelfContainedDefault::False { sess.emit_err(errors::UnsupportedLinkSelfContained); } @@ -2131,7 +2117,14 @@ fn linker_with_args<'a>( cmd.add_as_needed(); // Local native libraries of all kinds. - add_local_native_libraries(cmd, sess, archive_builder_builder, codegen_results, tmpdir); + add_local_native_libraries( + cmd, + sess, + archive_builder_builder, + codegen_results, + tmpdir, + link_output_kind, + ); // Upstream rust crates and their non-dynamic native libraries. add_upstream_rust_crates( @@ -2141,10 +2134,18 @@ fn linker_with_args<'a>( codegen_results, crate_type, tmpdir, + link_output_kind, ); // Dynamic native libraries from upstream crates. - add_upstream_native_libraries(cmd, sess, archive_builder_builder, codegen_results, tmpdir); + add_upstream_native_libraries( + cmd, + sess, + archive_builder_builder, + codegen_results, + tmpdir, + link_output_kind, + ); // Link with the import library generated for any raw-dylib functions. for (raw_dylib_name, raw_dylib_imports) in @@ -2245,7 +2246,8 @@ fn add_order_independent_options( out_filename: &Path, tmpdir: &Path, ) { - add_gcc_ld_path(cmd, sess, flavor); + // Take care of the flavors and CLI options requesting the `lld` linker. + add_lld_args(cmd, sess, flavor); add_apple_sdk(cmd, sess, flavor); @@ -2290,11 +2292,13 @@ fn add_order_independent_options( } else if flavor == LinkerFlavor::Bpf { cmd.arg("--cpu"); cmd.arg(&codegen_results.crate_info.target_cpu); - cmd.arg("--cpu-features"); - cmd.arg(match &sess.opts.cg.target_feature { - feat if !feat.is_empty() => feat.as_ref(), - _ => sess.target.options.features.as_ref(), - }); + if let Some(feat) = [sess.opts.cg.target_feature.as_str(), &sess.target.options.features] + .into_iter() + .find(|feat| !feat.is_empty()) + { + cmd.arg("--cpu-features"); + cmd.arg(feat); + } } cmd.linker_plugin_lto(); @@ -2399,6 +2403,7 @@ fn add_native_libs_from_crate( cnum: CrateNum, link_static: bool, link_dynamic: bool, + link_output_kind: LinkOutputKind, ) { if !sess.opts.unstable_opts.link_native_libraries { // If `-Zlink-native-libraries=false` is set, then the assumption is that an @@ -2478,8 +2483,16 @@ fn add_native_libs_from_crate( } } NativeLibKind::Unspecified => { - if link_dynamic { - cmd.link_dylib(name, verbatim, true); + // If we are generating a static binary, prefer static library when the + // link kind is unspecified. + if !link_output_kind.can_link_dylib() && !sess.target.crt_static_allows_dylibs { + if link_static { + cmd.link_staticlib(name, verbatim) + } + } else { + if link_dynamic { + cmd.link_dylib(name, verbatim, true); + } } } NativeLibKind::Framework { as_needed } => { @@ -2506,6 +2519,7 @@ fn add_local_native_libraries( archive_builder_builder: &dyn ArchiveBuilderBuilder, codegen_results: &CodegenResults, tmpdir: &Path, + link_output_kind: LinkOutputKind, ) { if sess.opts.unstable_opts.link_native_libraries { // User-supplied library search paths (-L on the command line). These are the same paths @@ -2535,6 +2549,7 @@ fn add_local_native_libraries( LOCAL_CRATE, link_static, link_dynamic, + link_output_kind, ); } @@ -2545,6 +2560,7 @@ fn add_upstream_rust_crates<'a>( codegen_results: &CodegenResults, crate_type: CrateType, tmpdir: &Path, + link_output_kind: LinkOutputKind, ) { // All of the heavy lifting has previously been accomplished by the // dependency_format module of the compiler. This is just crawling the @@ -2622,6 +2638,7 @@ fn add_upstream_rust_crates<'a>( cnum, link_static, link_dynamic, + link_output_kind, ); } } @@ -2632,6 +2649,7 @@ fn add_upstream_native_libraries( archive_builder_builder: &dyn ArchiveBuilderBuilder, codegen_results: &CodegenResults, tmpdir: &Path, + link_output_kind: LinkOutputKind, ) { let search_path = OnceCell::new(); for &cnum in &codegen_results.crate_info.used_crates { @@ -2660,10 +2678,35 @@ fn add_upstream_native_libraries( cnum, link_static, link_dynamic, + link_output_kind, ); } } +// Rehome lib paths (which exclude the library file name) that point into the sysroot lib directory +// to be relative to the sysroot directory, which may be a relative path specified by the user. +// +// If the sysroot is a relative path, and the sysroot libs are specified as an absolute path, the +// linker command line can be non-deterministic due to the paths including the current working +// directory. The linker command line needs to be deterministic since it appears inside the PDB +// file generated by the MSVC linker. See https://github.com/rust-lang/rust/issues/112586. +// +// The returned path will always have `fix_windows_verbatim_for_gcc()` applied to it. +fn rehome_sysroot_lib_dir<'a>(sess: &'a Session, lib_dir: &Path) -> PathBuf { + let sysroot_lib_path = sess.target_filesearch(PathKind::All).get_lib_path(); + let canonical_sysroot_lib_path = + { try_canonicalize(&sysroot_lib_path).unwrap_or_else(|_| sysroot_lib_path.clone()) }; + + let canonical_lib_dir = try_canonicalize(lib_dir).unwrap_or_else(|_| lib_dir.to_path_buf()); + if canonical_lib_dir == canonical_sysroot_lib_path { + // This path, returned by `target_filesearch().get_lib_path()`, has + // already had `fix_windows_verbatim_for_gcc()` applied if needed. + sysroot_lib_path + } else { + fix_windows_verbatim_for_gcc(&lib_dir) + } +} + // Adds the static "rlib" versions of all crates to the command line. // There's a bit of magic which happens here specifically related to LTO, // namely that we remove upstream object files. @@ -2695,7 +2738,13 @@ fn add_static_crate<'a>( let cratepath = &src.rlib.as_ref().unwrap().0; let mut link_upstream = |path: &Path| { - cmd.link_rlib(&fix_windows_verbatim_for_gcc(path)); + let rlib_path = if let Some(dir) = path.parent() { + let file_name = path.file_name().expect("rlib path has no file name path component"); + rehome_sysroot_lib_dir(sess, &dir).join(file_name) + } else { + fix_windows_verbatim_for_gcc(path) + }; + cmd.link_rlib(&rlib_path); }; if !are_upstream_rust_objects_already_included(sess) @@ -2764,7 +2813,7 @@ fn add_dynamic_crate(cmd: &mut dyn Linker, sess: &Session, cratepath: &Path) { // what its name is let parent = cratepath.parent(); if let Some(dir) = parent { - cmd.include_path(&fix_windows_verbatim_for_gcc(dir)); + cmd.include_path(&rehome_sysroot_lib_dir(sess, dir)); } let stem = cratepath.file_stem().unwrap().to_str().unwrap(); // Convert library file-stem into a cc -l argument. @@ -2900,55 +2949,81 @@ fn get_apple_sdk_root(sdk_name: &str) -> Result<String, errors::AppleSdkRootErro } } -fn add_gcc_ld_path(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { - if let Some(ld_impl) = sess.opts.unstable_opts.gcc_ld { - if let LinkerFlavor::Gnu(Cc::Yes, _) - | LinkerFlavor::Darwin(Cc::Yes, _) - | LinkerFlavor::WasmLld(Cc::Yes) = flavor - { - match ld_impl { - LdImpl::Lld => { - // Implement the "self-contained" part of -Zgcc-ld - // by adding rustc distribution directories to the tool search path. - for path in sess.get_tools_search_paths(false) { - cmd.arg({ - let mut arg = OsString::from("-B"); - arg.push(path.join("gcc-ld")); - arg - }); - } - // Implement the "linker flavor" part of -Zgcc-ld - // by asking cc to use some kind of lld. - cmd.arg("-fuse-ld=lld"); - - if !flavor.is_gnu() { - // Tell clang to use a non-default LLD flavor. - // Gcc doesn't understand the target option, but we currently assume - // that gcc is not used for Apple and Wasm targets (#97402). - // - // Note that we don't want to do that by default on macOS: e.g. passing a - // 10.7 target to LLVM works, but not to recent versions of clang/macOS, as - // shown in issue #101653 and the discussion in PR #101792. - // - // It could be required in some cases of cross-compiling with - // `-Zgcc-ld=lld`, but this is generally unspecified, and we don't know - // which specific versions of clang, macOS SDK, host and target OS - // combinations impact us here. - // - // So we do a simple first-approximation until we know more of what the - // Apple targets require (and which would be handled prior to hitting this - // `-Zgcc-ld=lld` codepath anyway), but the expectation is that until then - // this should be manually passed if needed. We specify the target when - // targeting a different linker flavor on macOS, and that's also always - // the case when targeting WASM. - if sess.target.linker_flavor != sess.host.linker_flavor { - cmd.arg(format!("--target={}", sess.target.llvm_target)); - } - } - } - } - } else { - sess.emit_fatal(errors::OptionGccOnly); +/// When using the linker flavors opting in to `lld`, or the unstable `-Zgcc-ld=lld` flag, add the +/// necessary paths and arguments to invoke it: +/// - when the self-contained linker flag is active: the build of `lld` distributed with rustc, +/// - or any `lld` available to `cc`. +fn add_lld_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { + let unstable_use_lld = sess.opts.unstable_opts.gcc_ld.is_some(); + debug!("add_lld_args requested, flavor: '{flavor:?}', `-Zgcc-ld=lld`: {unstable_use_lld}"); + + // Sanity check: using the old unstable `-Zgcc-ld=lld` option requires a `cc`-using flavor. + let flavor_uses_cc = flavor.uses_cc(); + if unstable_use_lld && !flavor_uses_cc { + sess.emit_fatal(errors::OptionGccOnly); + } + + // If the flavor doesn't use a C/C++ compiler to invoke the linker, or doesn't opt in to `lld`, + // we don't need to do anything. + let use_lld = flavor.uses_lld() || unstable_use_lld; + if !flavor_uses_cc || !use_lld { + return; + } + + let self_contained_linker = sess.opts.cg.link_self_contained.linker(); + + // FIXME: some targets default to using `lld`, but users can only override the linker on the CLI + // and cannot yet select the precise linker flavor to opt out of that. See for example issue + // #113597 for the `thumbv6m-none-eabi` target: a driver is used, and its default linker + // conflicts with the target's flavor, causing unexpected arguments being passed. + // + // Until the new `LinkerFlavor`-like CLI options are stabilized, we only adopt MCP510's behavior + // if its dedicated unstable CLI flags are used, to keep the current sub-optimal stable + // behavior. + let using_mcp510 = + self_contained_linker || sess.opts.cg.linker_flavor.is_some_and(|f| f.is_unstable()); + if !using_mcp510 && !unstable_use_lld { + return; + } + + // 1. Implement the "self-contained" part of this feature by adding rustc distribution + // directories to the tool's search path. + if self_contained_linker || unstable_use_lld { + for path in sess.get_tools_search_paths(false) { + cmd.arg({ + let mut arg = OsString::from("-B"); + arg.push(path.join("gcc-ld")); + arg + }); + } + } + + // 2. Implement the "linker flavor" part of this feature by asking `cc` to use some kind of + // `lld` as the linker. + cmd.arg("-fuse-ld=lld"); + + if !flavor.is_gnu() { + // Tell clang to use a non-default LLD flavor. + // Gcc doesn't understand the target option, but we currently assume + // that gcc is not used for Apple and Wasm targets (#97402). + // + // Note that we don't want to do that by default on macOS: e.g. passing a + // 10.7 target to LLVM works, but not to recent versions of clang/macOS, as + // shown in issue #101653 and the discussion in PR #101792. + // + // It could be required in some cases of cross-compiling with + // `-Zgcc-ld=lld`, but this is generally unspecified, and we don't know + // which specific versions of clang, macOS SDK, host and target OS + // combinations impact us here. + // + // So we do a simple first-approximation until we know more of what the + // Apple targets require (and which would be handled prior to hitting this + // `-Zgcc-ld=lld` codepath anyway), but the expectation is that until then + // this should be manually passed if needed. We specify the target when + // targeting a different linker flavor on macOS, and that's also always + // the case when targeting WASM. + if sess.target.linker_flavor != sess.host.linker_flavor { + cmd.arg(format!("--target={}", sess.target.llvm_target)); } } } |