diff options
Diffstat (limited to 'compiler/rustc_codegen_ssa/src/back/link.rs')
-rw-r--r-- | compiler/rustc_codegen_ssa/src/back/link.rs | 241 |
1 files changed, 142 insertions, 99 deletions
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 6cce95427..0dc0dee86 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -11,7 +11,7 @@ use rustc_metadata::find_native_static_library; use rustc_metadata::fs::{emit_metadata, METADATA_FILENAME}; 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, LdImpl, Lto, 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,20 +23,23 @@ use rustc_session::{filesearch, Session}; use rustc_span::symbol::Symbol; use rustc_span::DebuggerVisualizerFile; use rustc_target::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault}; -use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor, SplitDebuginfo}; -use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, SanitizerSet, Target}; +use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy}; +use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, Target}; use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder}; use super::command::Command; use super::linker::{self, Linker}; use super::metadata::{create_rmeta_file, MetadataPosition}; use super::rpath::{self, RPathConfig}; -use crate::{looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib}; +use crate::{ + errors, looks_like_rust_object_file, CodegenResults, CompiledModule, CrateInfo, NativeLib, +}; use cc::windows_registry; use regex::Regex; use tempfile::Builder as TempFileBuilder; +use itertools::Itertools; use std::borrow::Borrow; use std::cell::OnceCell; use std::collections::BTreeSet; @@ -93,7 +96,7 @@ pub fn link_binary<'a>( let tmpdir = TempFileBuilder::new() .prefix("rustc") .tempdir() - .unwrap_or_else(|err| sess.fatal(&format!("couldn't create a temp dir: {}", err))); + .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( sess, @@ -206,11 +209,29 @@ pub fn link_binary<'a>( } pub fn each_linked_rlib( + sess: &Session, info: &CrateInfo, f: &mut dyn FnMut(CrateNum, &Path), -) -> Result<(), String> { +) -> Result<(), errors::LinkRlibError> { let crates = info.used_crates.iter(); let mut fmts = None; + + let lto_active = matches!(sess.lto(), Lto::Fat | Lto::Thin); + if lto_active { + for combination in info.dependency_formats.iter().combinations(2) { + let (ty1, list1) = &combination[0]; + let (ty2, list2) = &combination[1]; + if list1 != list2 { + return Err(errors::LinkRlibError::IncompatibleDependencyFormats { + ty1: format!("{ty1:?}"), + ty2: format!("{ty2:?}"), + list1: format!("{list1:?}"), + list2: format!("{list2:?}"), + }); + } + } + } + for (ty, list) in info.dependency_formats.iter() { match ty { CrateType::Executable @@ -220,30 +241,31 @@ pub fn each_linked_rlib( fmts = Some(list); break; } + CrateType::Dylib if lto_active => { + fmts = Some(list); + break; + } _ => {} } } let Some(fmts) = fmts else { - return Err("could not find formats for rlibs".to_string()); + return Err(errors::LinkRlibError::MissingFormat); }; for &cnum in crates { match fmts.get(cnum.as_usize() - 1) { Some(&Linkage::NotLinked | &Linkage::IncludedFromDylib) => continue, Some(_) => {} - None => return Err("could not find formats for rlibs".to_string()), + None => return Err(errors::LinkRlibError::MissingFormat), } - let name = info.crate_name[&cnum]; + let crate_name = info.crate_name[&cnum]; let used_crate_source = &info.used_crate_source[&cnum]; if let Some((path, _)) = &used_crate_source.rlib { f(cnum, &path); } else { if used_crate_source.rmeta.is_some() { - return Err(format!( - "could not find rlib for: `{}`, found rmeta (metadata) file", - name - )); + return Err(errors::LinkRlibError::OnlyRmetaFound { crate_name }); } else { - return Err(format!("could not find rlib for: `{}`", name)); + return Err(errors::LinkRlibError::NotFound { crate_name }); } } } @@ -340,10 +362,7 @@ fn link_rlib<'a>( // -whole-archive and it isn't clear how we can currently handle such a // situation correctly. // See https://github.com/rust-lang/rust/issues/88085#issuecomment-901050897 - sess.err( - "the linking modifiers `+bundle` and `+whole-archive` are not compatible \ - with each other when generating rlibs", - ); + sess.emit_err(errors::IncompatibleLinkingModifiers); } NativeLibKind::Static { bundle: None | Some(true), .. } => {} NativeLibKind::Static { bundle: Some(false), .. } @@ -365,12 +384,8 @@ fn link_rlib<'a>( )); continue; } - ab.add_archive(&location, Box::new(|_| false)).unwrap_or_else(|e| { - sess.fatal(&format!( - "failed to add native library {}: {}", - location.to_string_lossy(), - e - )); + ab.add_archive(&location, Box::new(|_| false)).unwrap_or_else(|error| { + sess.emit_fatal(errors::AddNativeLibrary { library_path: location, error }); }); } } @@ -386,8 +401,8 @@ fn link_rlib<'a>( true, ); - ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|e| { - sess.fatal(&format!("failed to add native library {}: {}", output_path.display(), e)); + ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| { + sess.emit_fatal(errors::AddNativeLibrary { library_path: output_path, error }); }); } @@ -452,14 +467,11 @@ fn collate_raw_dylibs<'a, 'b>( // FIXME: when we add support for ordinals, figure out if we need to do anything // if we have two DllImport values with the same name but different ordinals. if import.calling_convention != old_import.calling_convention { - sess.span_err( - import.span, - &format!( - "multiple declarations of external function `{}` from \ - library `{}` have different calling conventions", - import.name, name, - ), - ); + sess.emit_err(errors::MultipleExternalFuncDecl { + span: import.span, + function: import.name, + library_name: &name, + }); } } } @@ -502,7 +514,7 @@ fn link_staticlib<'a>( )?; let mut all_native_libs = vec![]; - let res = each_linked_rlib(&codegen_results.crate_info, &mut |cnum, path| { + let res = each_linked_rlib(sess, &codegen_results.crate_info, &mut |cnum, path| { let name = codegen_results.crate_info.crate_name[&cnum]; let native_libs = &codegen_results.crate_info.native_libraries[&cnum]; @@ -561,7 +573,7 @@ fn link_staticlib<'a>( all_native_libs.extend(codegen_results.crate_info.native_libraries[&cnum].iter().cloned()); }); if let Err(e) = res { - sess.fatal(&e); + sess.emit_fatal(e); } ab.build(out_filename); @@ -674,9 +686,7 @@ fn link_dwarf_object<'a>( }) { Ok(()) => {} Err(e) => { - sess.struct_err("linking dwarf objects with thorin failed") - .note(&format!("{:?}", e)) - .emit(); + sess.emit_err(errors::ThorinErrorWrapper(e)); sess.abort_if_errors(); } } @@ -749,8 +759,7 @@ fn link_natively<'a>( // then it should not default to linking executables as pie. Different // versions of gcc seem to use different quotes in the error message so // don't check for them. - if sess.target.linker_is_gnu - && flavor != LinkerFlavor::Ld + if matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _)) && unknown_arg_regex.is_match(&out) && out.contains("-no-pie") && cmd.get_args().iter().any(|e| e.to_string_lossy() == "-no-pie") @@ -768,8 +777,7 @@ fn link_natively<'a>( // Detect '-static-pie' used with an older version of gcc or clang not supporting it. // Fallback from '-static-pie' to '-static' in that case. - if sess.target.linker_is_gnu - && flavor != LinkerFlavor::Ld + if matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _)) && unknown_arg_regex.is_match(&out) && (out.contains("-static-pie") || out.contains("--no-dynamic-linker")) && cmd.get_args().iter().any(|e| e.to_string_lossy() == "-static-pie") @@ -882,29 +890,20 @@ fn link_natively<'a>( let mut output = prog.stderr.clone(); output.extend_from_slice(&prog.stdout); let escaped_output = escape_string(&output); - let mut err = sess.struct_err(&format!( - "linking with `{}` failed: {}", - linker_path.display(), - prog.status - )); - err.note(&format!("{:?}", &cmd)).note(&escaped_output); - if escaped_output.contains("undefined reference to") { - err.help( - "some `extern` functions couldn't be found; some native libraries may \ - need to be installed or have their path specified", - ); - err.note("use the `-l` flag to specify native libraries to link"); - err.note("use the `cargo:rustc-link-lib` directive to specify the native \ - libraries to link with Cargo (see https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorustc-link-libkindname)"); - } - err.emit(); - + // FIXME: Add UI tests for this error. + let err = errors::LinkingFailed { + linker_path: &linker_path, + exit_status: prog.status, + command: &cmd, + escaped_output: &escaped_output, + }; + sess.diagnostic().emit_err(err); // If MSVC's `link.exe` was expected but the return code // is not a Microsoft LNK error then suggest a way to fix or // install the Visual Studio build tools. if let Some(code) = prog.status.code() { if sess.target.is_like_msvc - && flavor == LinkerFlavor::Msvc + && flavor == LinkerFlavor::Msvc(Lld::No) // Respect the command line override && sess.opts.cg.linker.is_none() // Match exactly "link.exe" @@ -980,9 +979,10 @@ fn link_natively<'a>( but `link.exe` was not found", ); sess.note_without_error( - "please ensure that VS 2013, VS 2015, VS 2017, VS 2019 or VS 2022 \ - was installed with the Visual C++ option", + "please ensure that Visual Studio 2017 or later, or Build Tools \ + for Visual Studio were installed with the Visual C++ option.", ); + sess.note_without_error("VS Code is a different product, and is not sufficient."); } sess.abort_if_errors(); } @@ -1035,16 +1035,36 @@ fn link_natively<'a>( if sess.target.is_like_osx { match (strip, crate_type) { - (Strip::Debuginfo, _) => strip_symbols_in_osx(sess, &out_filename, Some("-S")), + (Strip::Debuginfo, _) => { + strip_symbols_with_external_utility(sess, "strip", &out_filename, Some("-S")) + } // Per the manpage, `-x` is the maximum safe strip level for dynamic libraries. (#93988) (Strip::Symbols, CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro) => { - strip_symbols_in_osx(sess, &out_filename, Some("-x")) + strip_symbols_with_external_utility(sess, "strip", &out_filename, Some("-x")) + } + (Strip::Symbols, _) => { + strip_symbols_with_external_utility(sess, "strip", &out_filename, None) } - (Strip::Symbols, _) => strip_symbols_in_osx(sess, &out_filename, None), (Strip::None, _) => {} } } + if sess.target.os == "illumos" { + // Many illumos systems will have both the native 'strip' utility and + // the GNU one. Use the native version explicitly and do not rely on + // what's in the path. + let stripcmd = "/usr/bin/strip"; + match strip { + // Always preserve the symbol table (-x). + Strip::Debuginfo => { + strip_symbols_with_external_utility(sess, stripcmd, &out_filename, Some("-x")) + } + // Strip::Symbols is handled via the --strip-all linker option. + Strip::Symbols => {} + Strip::None => {} + } + } + Ok(()) } @@ -1056,8 +1076,13 @@ fn strip_value(sess: &Session) -> Strip { } } -fn strip_symbols_in_osx<'a>(sess: &'a Session, out_filename: &Path, option: Option<&str>) { - let mut cmd = Command::new("strip"); +fn strip_symbols_with_external_utility<'a>( + sess: &'a Session, + util: &str, + out_filename: &Path, + option: Option<&str>, +) { + let mut cmd = Command::new(util); if let Some(option) = option { cmd.arg(option); } @@ -1068,14 +1093,14 @@ fn strip_symbols_in_osx<'a>(sess: &'a Session, out_filename: &Path, option: Opti let mut output = prog.stderr.clone(); output.extend_from_slice(&prog.stdout); sess.struct_warn(&format!( - "stripping debug info with `strip` failed: {}", - prog.status + "stripping debug info with `{}` failed: {}", + util, prog.status )) .note(&escape_string(&output)) .emit(); } } - Err(e) => sess.fatal(&format!("unable to run `strip`: {}", e)), + Err(e) => sess.fatal(&format!("unable to run `{}`: {}", util, e)), } } @@ -1091,11 +1116,12 @@ fn add_sanitizer_libraries(sess: &Session, crate_type: CrateType, linker: &mut d // both executables and dynamic shared objects. Everywhere else the runtimes // are currently distributed as static libraries which should be linked to // executables only. - let needs_runtime = match crate_type { - CrateType::Executable => true, - CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro => sess.target.is_like_osx, - CrateType::Rlib | CrateType::Staticlib => false, - }; + let needs_runtime = !sess.target.is_like_android + && match crate_type { + CrateType::Executable => true, + CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro => sess.target.is_like_osx, + CrateType::Rlib | CrateType::Staticlib => false, + }; if !needs_runtime { return; @@ -1187,7 +1213,10 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { // only the linker flavor is known; use the default linker for the selected flavor (None, Some(flavor)) => Some(( PathBuf::from(match flavor { - LinkerFlavor::Gcc => { + LinkerFlavor::Gnu(Cc::Yes, _) + | LinkerFlavor::Darwin(Cc::Yes, _) + | LinkerFlavor::WasmLld(Cc::Yes) + | LinkerFlavor::Unix(Cc::Yes) => { if cfg!(any(target_os = "solaris", target_os = "illumos")) { // On historical Solaris systems, "cc" may have // been Sun Studio, which is not flag-compatible @@ -1200,9 +1229,14 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { "cc" } } - LinkerFlavor::Ld => "ld", - LinkerFlavor::Lld(_) => "lld", - LinkerFlavor::Msvc => "link.exe", + LinkerFlavor::Gnu(_, Lld::Yes) + | LinkerFlavor::Darwin(_, Lld::Yes) + | LinkerFlavor::WasmLld(..) + | LinkerFlavor::Msvc(Lld::Yes) => "lld", + LinkerFlavor::Gnu(..) | LinkerFlavor::Darwin(..) | LinkerFlavor::Unix(..) => { + "ld" + } + LinkerFlavor::Msvc(..) => "link.exe", LinkerFlavor::EmCc => { if cfg!(windows) { "emcc.bat" @@ -1227,15 +1261,20 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { || stem == "clang" || stem.ends_with("-clang") { - LinkerFlavor::Gcc + LinkerFlavor::from_cli(LinkerFlavorCli::Gcc, &sess.target) } else if stem == "wasm-ld" || stem.ends_with("-wasm-ld") { - LinkerFlavor::Lld(LldFlavor::Wasm) - } else if stem == "ld" || stem == "ld.lld" || stem.ends_with("-ld") { - LinkerFlavor::Ld - } else if stem == "link" || stem == "lld-link" { - LinkerFlavor::Msvc + 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" { - LinkerFlavor::Lld(sess.target.lld_flavor) + 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 @@ -1249,7 +1288,8 @@ 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(LinkerFlavor::from_cli); + let linker_flavor = + sess.opts.cg.linker_flavor.map(|flavor| LinkerFlavor::from_cli(flavor, &sess.target)); if let Some(ret) = infer_from(sess, sess.opts.cg.linker.clone(), linker_flavor) { return ret; } @@ -1320,7 +1360,7 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLib]) { let verbatim = lib.verbatim.unwrap_or(false); if sess.target.is_like_msvc { Some(format!("{}{}", name, if verbatim { "" } else { ".lib" })) - } else if sess.target.linker_is_gnu { + } else if sess.target.linker_flavor.is_gnu() { Some(format!("-l{}{}", if verbatim { ":" } else { "" }, name)) } else { Some(format!("-l{}", name)) @@ -1607,7 +1647,7 @@ fn add_pre_link_objects( let empty = Default::default(); let objects = if self_contained { &opts.pre_link_objects_self_contained - } else if !(sess.target.os == "fuchsia" && flavor == LinkerFlavor::Gcc) { + } else if !(sess.target.os == "fuchsia" && matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _))) { &opts.pre_link_objects } else { &empty @@ -1647,7 +1687,7 @@ fn add_pre_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) fn add_link_script(cmd: &mut dyn Linker, sess: &Session, tmpdir: &Path, crate_type: CrateType) { match (crate_type, &sess.target.link_script) { (CrateType::Cdylib | CrateType::Executable, Some(script)) => { - if !sess.target.linker_is_gnu { + if !sess.target.linker_flavor.is_gnu() { sess.fatal("can only use link script when linking with GNU-like linker"); } @@ -1890,7 +1930,7 @@ fn add_rpath_args( out_filename: out_filename.to_path_buf(), has_rpath: sess.target.has_rpath, is_like_osx: sess.target.is_like_osx, - linker_is_gnu: sess.target.linker_is_gnu, + linker_is_gnu: sess.target.linker_flavor.is_gnu(), }; cmd.args(&rpath::get_rpath_flags(&mut rpath_config)); } @@ -2134,7 +2174,7 @@ fn add_order_independent_options( if sess.target.os == "fuchsia" && crate_type == CrateType::Executable - && flavor != LinkerFlavor::Gcc + && !matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _)) { let prefix = if sess.opts.unstable_opts.sanitizer.contains(SanitizerSet::ADDRESS) { "asan/" @@ -2747,12 +2787,12 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { let llvm_target = &sess.target.llvm_target; if sess.target.vendor != "apple" || !matches!(os.as_ref(), "ios" | "tvos" | "watchos" | "macos") - || (flavor != LinkerFlavor::Gcc && flavor != LinkerFlavor::Lld(LldFlavor::Ld64)) + || !matches!(flavor, LinkerFlavor::Darwin(..)) { return; } - if os == "macos" && flavor != LinkerFlavor::Lld(LldFlavor::Ld64) { + if os == "macos" && !matches!(flavor, LinkerFlavor::Darwin(Cc::No, _)) { return; } @@ -2786,10 +2826,10 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { }; match flavor { - LinkerFlavor::Gcc => { + LinkerFlavor::Darwin(Cc::Yes, _) => { cmd.args(&["-isysroot", &sdk_root, "-Wl,-syslibroot", &sdk_root]); } - LinkerFlavor::Lld(LldFlavor::Ld64) => { + LinkerFlavor::Darwin(Cc::No, _) => { cmd.args(&["-syslibroot", &sdk_root]); } _ => unreachable!(), @@ -2852,7 +2892,10 @@ fn get_apple_sdk_root(sdk_name: &str) -> Result<String, String> { 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::Gcc = flavor { + 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 @@ -2867,7 +2910,7 @@ fn add_gcc_ld_path(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { // Implement the "linker flavor" part of -Zgcc-ld // by asking cc to use some kind of lld. cmd.arg("-fuse-ld=lld"); - if sess.target.lld_flavor != LldFlavor::Ld { + 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). |