diff options
Diffstat (limited to '')
-rw-r--r-- | compiler/rustc_codegen_ssa/src/back/archive.rs | 73 | ||||
-rw-r--r-- | compiler/rustc_codegen_ssa/src/back/link.rs | 629 | ||||
-rw-r--r-- | compiler/rustc_codegen_ssa/src/back/linker.rs | 208 | ||||
-rw-r--r-- | compiler/rustc_codegen_ssa/src/back/metadata.rs | 16 | ||||
-rw-r--r-- | compiler/rustc_codegen_ssa/src/back/symbol_export.rs | 33 | ||||
-rw-r--r-- | compiler/rustc_codegen_ssa/src/back/write.rs | 90 |
6 files changed, 570 insertions, 479 deletions
diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 0d2aa483d..bb76ca5d2 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -1,44 +1,16 @@ +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::memmap::Mmap; use rustc_session::cstore::DllImport; use rustc_session::Session; +use rustc_span::symbol::Symbol; +use object::read::archive::ArchiveFile; + +use std::fmt::Display; +use std::fs::File; use std::io; use std::path::{Path, PathBuf}; -pub(super) fn find_library( - name: &str, - verbatim: bool, - search_paths: &[PathBuf], - sess: &Session, -) -> PathBuf { - // On Windows, static libraries sometimes show up as libfoo.a and other - // times show up as foo.lib - let oslibname = if verbatim { - name.to_string() - } else { - format!("{}{}{}", sess.target.staticlib_prefix, name, sess.target.staticlib_suffix) - }; - let unixlibname = format!("lib{}.a", name); - - for path in search_paths { - debug!("looking for {} inside {:?}", name, path); - let test = path.join(&oslibname); - if test.exists() { - return test; - } - if oslibname != unixlibname { - let test = path.join(&unixlibname); - if test.exists() { - return test; - } - } - } - sess.fatal(&format!( - "could not find native static library `{}`, \ - perhaps an -L flag is missing?", - name - )); -} - pub trait ArchiveBuilderBuilder { fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder<'a> + 'a>; @@ -53,7 +25,38 @@ pub trait ArchiveBuilderBuilder { lib_name: &str, dll_imports: &[DllImport], tmpdir: &Path, + is_direct_dependency: bool, ) -> PathBuf; + + fn extract_bundled_libs( + &self, + rlib: &Path, + outdir: &Path, + bundled_lib_file_names: &FxHashSet<Symbol>, + ) -> Result<(), String> { + let message = |msg: &str, e: &dyn Display| format!("{} '{}': {}", msg, &rlib.display(), e); + let archive_map = unsafe { + Mmap::map(File::open(rlib).map_err(|e| message("failed to open file", &e))?) + .map_err(|e| message("failed to mmap file", &e))? + }; + let archive = ArchiveFile::parse(&*archive_map) + .map_err(|e| message("failed to parse archive", &e))?; + + for entry in archive.members() { + let entry = entry.map_err(|e| message("failed to read entry", &e))?; + let data = entry + .data(&*archive_map) + .map_err(|e| message("failed to get data from archive member", &e))?; + let name = std::str::from_utf8(entry.name()) + .map_err(|e| message("failed to convert name", &e))?; + if !bundled_lib_file_names.contains(&Symbol::intern(name)) { + continue; // We need to extract only native libraries. + } + std::fs::write(&outdir.join(&name), data) + .map_err(|e| message("failed to write file", &e))?; + } + Ok(()) + } } pub trait ArchiveBuilder<'a> { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 63207803e..0dc0dee86 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1,15 +1,17 @@ use rustc_arena::TypedArena; use rustc_ast::CRATE_NODE_ID; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; +use rustc_data_structures::fx::FxHashSet; +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_hir::def_id::CrateNum; +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}; @@ -20,21 +22,24 @@ use rustc_session::utils::NativeLibKind; use rustc_session::{filesearch, Session}; use rustc_span::symbol::Symbol; use rustc_span::DebuggerVisualizerFile; -use rustc_target::spec::crt_objects::{CrtObjects, CrtObjectsFallback}; -use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor, SplitDebuginfo}; -use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, SanitizerSet, Target}; +use rustc_target::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault}; +use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy}; +use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, Target}; -use super::archive::{find_library, ArchiveBuilder, ArchiveBuilderBuilder}; +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; @@ -44,7 +49,7 @@ use std::io::{BufWriter, Write}; use std::ops::Deref; use std::path::{Path, PathBuf}; use std::process::{ExitStatus, Output, Stdio}; -use std::{ascii, char, env, fmt, fs, io, mem, str}; +use std::{env, fmt, fs, io, mem, str}; pub fn ensure_removed(diag_handler: &Handler, path: &Path) { if let Err(e) = fs::remove_file(path) { @@ -91,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, @@ -204,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 @@ -218,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 }); } } } @@ -307,6 +331,9 @@ fn link_rlib<'a>( } } + // Used if packed_bundled_libs flag enabled. + let mut packed_bundled_libs = Vec::new(); + // Note that in this loop we are ignoring the value of `lib.cfg`. That is, // we may not be configured to actually include a static library if we're // adding it here. That's because later when we consume this rlib we'll @@ -326,6 +353,8 @@ fn link_rlib<'a>( for lib in codegen_results.crate_info.used_libraries.iter() { match lib.kind { NativeLibKind::Static { bundle: None | Some(true), whole_archive: Some(true) } + if flavor == RlibFlavor::Normal && sess.opts.unstable_opts.packed_bundled_libs => {} + NativeLibKind::Static { bundle: None | Some(true), whole_archive: Some(true) } if flavor == RlibFlavor::Normal => { // Don't allow mixing +bundle with +whole_archive since an rlib may contain @@ -333,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), .. } @@ -348,29 +374,35 @@ fn link_rlib<'a>( } if let Some(name) = lib.name { let location = - find_library(name.as_str(), lib.verbatim.unwrap_or(false), &lib_search_paths, sess); - ab.add_archive(&location, Box::new(|_| false)).unwrap_or_else(|e| { - sess.fatal(&format!( - "failed to add native library {}: {}", - location.to_string_lossy(), - e + find_native_static_library(name.as_str(), lib.verbatim, &lib_search_paths, sess); + if sess.opts.unstable_opts.packed_bundled_libs && flavor == RlibFlavor::Normal { + packed_bundled_libs.push(find_native_static_library( + lib.filename.unwrap().as_str(), + Some(true), + &lib_search_paths, + sess, )); + continue; + } + ab.add_archive(&location, Box::new(|_| false)).unwrap_or_else(|error| { + sess.emit_fatal(errors::AddNativeLibrary { library_path: location, error }); }); } } for (raw_dylib_name, raw_dylib_imports) in - collate_raw_dylibs(sess, &codegen_results.crate_info.used_libraries)? + collate_raw_dylibs(sess, codegen_results.crate_info.used_libraries.iter())? { let output_path = archive_builder_builder.create_dll_import_lib( sess, &raw_dylib_name, &raw_dylib_imports, tmpdir.as_ref(), + 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 }); }); } @@ -403,6 +435,12 @@ fn link_rlib<'a>( ab.add_file(&trailing_metadata); } + // Add all bundled static native library dependencies. + // Archives added to the end of .rlib archive, see comment above for the reason. + for lib in packed_bundled_libs { + ab.add_file(&lib) + } + return Ok(ab); } @@ -412,9 +450,9 @@ fn link_rlib<'a>( /// then the CodegenResults value contains one NativeLib instance for each block. However, the /// linker appears to expect only a single import library for each library used, so we need to /// collate the symbols together by library name before generating the import libraries. -fn collate_raw_dylibs( - sess: &Session, - used_libraries: &[NativeLib], +fn collate_raw_dylibs<'a, 'b>( + sess: &'a Session, + used_libraries: impl IntoIterator<Item = &'b NativeLib>, ) -> Result<Vec<(String, Vec<DllImport>)>, ErrorGuaranteed> { // Use index maps to preserve original order of imports and libraries. let mut dylib_table = FxIndexMap::<String, FxIndexMap<Symbol, &DllImport>>::default(); @@ -429,14 +467,11 @@ fn collate_raw_dylibs( // 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, + }); } } } @@ -479,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]; @@ -538,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); @@ -552,14 +587,6 @@ fn link_staticlib<'a>( Ok(()) } -fn escape_stdout_stderr_string(s: &[u8]) -> String { - str::from_utf8(s).map(|s| s.to_owned()).unwrap_or_else(|_| { - let mut x = "Non-UTF-8 output: ".to_string(); - x.extend(s.iter().flat_map(|&b| ascii::escape_default(b)).map(char::from)); - x - }) -} - /// Use `thorin` (rust implementation of a dwarf packaging utility) to link DWARF objects into a /// DWARF package. fn link_dwarf_object<'a>( @@ -659,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(); } } @@ -734,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") @@ -753,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") @@ -764,15 +787,15 @@ fn link_natively<'a>( "Linker does not support -static-pie command line option. Retrying with -static instead." ); // Mirror `add_(pre,post)_link_objects` to replace CRT objects. - let self_contained = crt_objects_fallback(sess, crate_type); + let self_contained = self_contained(sess, crate_type); let opts = &sess.target; let pre_objects = if self_contained { - &opts.pre_link_objects_fallback + &opts.pre_link_objects_self_contained } else { &opts.pre_link_objects }; let post_objects = if self_contained { - &opts.post_link_objects_fallback + &opts.post_link_objects_self_contained } else { &opts.post_link_objects }; @@ -866,30 +889,21 @@ fn link_natively<'a>( if !prog.status.success() { let mut output = prog.stderr.clone(); output.extend_from_slice(&prog.stdout); - let escaped_output = escape_stdout_stderr_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(); - + let escaped_output = escape_string(&output); + // 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" @@ -934,8 +948,8 @@ fn link_natively<'a>( sess.abort_if_errors(); } - info!("linker stderr:\n{}", escape_stdout_stderr_string(&prog.stderr)); - info!("linker stdout:\n{}", escape_stdout_stderr_string(&prog.stdout)); + info!("linker stderr:\n{}", escape_string(&prog.stderr)); + info!("linker stdout:\n{}", escape_string(&prog.stdout)); } Err(e) => { let linker_not_found = e.kind() == io::ErrorKind::NotFound; @@ -965,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(); } @@ -1020,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(()) } @@ -1041,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); } @@ -1053,23 +1093,22 @@ 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)), } } fn escape_string(s: &[u8]) -> String { - str::from_utf8(s).map(|s| s.to_owned()).unwrap_or_else(|_| { - let mut x = "Non-UTF-8 output: ".to_string(); - x.extend(s.iter().flat_map(|&b| ascii::escape_default(b)).map(char::from)); - x - }) + match str::from_utf8(s) { + Ok(s) => s.to_owned(), + Err(_) => format!("Non-UTF-8 output: {}", s.escape_ascii()), + } } fn add_sanitizer_libraries(sess: &Session, crate_type: CrateType, linker: &mut dyn Linker) { @@ -1077,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; @@ -1173,14 +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::Em => { - if cfg!(windows) { - "emcc.bat" - } else { - "emcc" - } - } - 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 @@ -1193,12 +1229,23 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { "cc" } } - LinkerFlavor::Ld => "ld", - LinkerFlavor::Msvc => "link.exe", - LinkerFlavor::Lld(_) => "lld", - LinkerFlavor::PtxLinker => "rust-ptx-linker", - LinkerFlavor::BpfLinker => "bpf-linker", - LinkerFlavor::L4Bender => "l4-bender", + 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" + } else { + "emcc" + } + } + LinkerFlavor::Bpf => "bpf-linker", + LinkerFlavor::Ptx => "rust-ptx-linker", }), flavor, )), @@ -1208,21 +1255,26 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { }); let flavor = if stem == "emcc" { - LinkerFlavor::Em + LinkerFlavor::EmCc } else if stem == "gcc" || stem.ends_with("-gcc") || 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 @@ -1236,7 +1288,9 @@ 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 - if let Some(ret) = infer_from(sess, sess.opts.cg.linker.clone(), sess.opts.cg.linker_flavor) { + 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; } @@ -1306,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)) @@ -1556,26 +1610,26 @@ fn detect_self_contained_mingw(sess: &Session) -> bool { true } -/// Whether we link to our own CRT objects instead of relying on gcc to pull them. +/// Various toolchain components used during linking are used from rustc distribution +/// instead of being found somewhere on the host system. /// We only provide such support for a very limited number of targets. -fn crt_objects_fallback(sess: &Session, crate_type: CrateType) -> bool { +fn self_contained(sess: &Session, crate_type: CrateType) -> bool { if let Some(self_contained) = sess.opts.cg.link_self_contained { return self_contained; } - match sess.target.crt_objects_fallback { + match sess.target.link_self_contained { + LinkSelfContainedDefault::False => false, + LinkSelfContainedDefault::True => true, // FIXME: Find a better heuristic for "native musl toolchain is available", // based on host and linker path, for example. // (https://github.com/rust-lang/rust/pull/71769#issuecomment-626330237). - Some(CrtObjectsFallback::Musl) => sess.crt_static(Some(crate_type)), - Some(CrtObjectsFallback::Mingw) => { + LinkSelfContainedDefault::Musl => sess.crt_static(Some(crate_type)), + LinkSelfContainedDefault::Mingw => { sess.host == sess.target && sess.target.vendor != "uwp" && detect_self_contained_mingw(&sess) } - // FIXME: Figure out cases in which WASM needs to link with a native toolchain. - Some(CrtObjectsFallback::Wasm) => true, - None => false, } } @@ -1583,12 +1637,21 @@ fn crt_objects_fallback(sess: &Session, crate_type: CrateType) -> bool { fn add_pre_link_objects( cmd: &mut dyn Linker, sess: &Session, + flavor: LinkerFlavor, link_output_kind: LinkOutputKind, self_contained: bool, ) { + // FIXME: we are currently missing some infra here (per-linker-flavor CRT objects), + // so Fuchsia has to be special-cased. let opts = &sess.target; - let objects = - if self_contained { &opts.pre_link_objects_fallback } else { &opts.pre_link_objects }; + let empty = Default::default(); + let objects = if self_contained { + &opts.pre_link_objects_self_contained + } else if !(sess.target.os == "fuchsia" && matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _))) { + &opts.pre_link_objects + } else { + &empty + }; for obj in objects.get(&link_output_kind).iter().copied().flatten() { cmd.add_object(&get_object_file_path(sess, obj, self_contained)); } @@ -1601,9 +1664,11 @@ fn add_post_link_objects( link_output_kind: LinkOutputKind, self_contained: bool, ) { - let opts = &sess.target; - let objects = - if self_contained { &opts.post_link_objects_fallback } else { &opts.post_link_objects }; + let objects = if self_contained { + &sess.target.post_link_objects_self_contained + } else { + &sess.target.post_link_objects + }; for obj in objects.get(&link_output_kind).iter().copied().flatten() { cmd.add_object(&get_object_file_path(sess, obj, self_contained)); } @@ -1622,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"); } @@ -1703,6 +1768,13 @@ fn add_post_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor /// that are necessary for the linking. They are only present in symbol table but not actually /// used in any sections, so the linker will therefore pick relevant rlibs for linking, but /// unused `#[no_mangle]` or `#[used]` can still be discard by GC sections. +/// +/// There's a few internal crates in the standard library (aka libcore and +/// libstd) which actually have a circular dependence upon one another. This +/// currently arises through "weak lang items" where libcore requires things +/// like `rust_begin_unwind` but libstd ends up defining it. To get this +/// circular dependence to work correctly we declare some of these things +/// in this synthetic object. fn add_linked_symbol_object( cmd: &mut dyn Linker, sess: &Session, @@ -1858,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)); } @@ -1882,12 +1954,12 @@ fn linker_with_args<'a>( out_filename: &Path, codegen_results: &CodegenResults, ) -> Result<Command, ErrorGuaranteed> { - let crt_objects_fallback = crt_objects_fallback(sess, crate_type); + let self_contained = self_contained(sess, crate_type); let cmd = &mut *super::linker::get_linker( sess, path, flavor, - crt_objects_fallback, + self_contained, &codegen_results.crate_info.target_cpu, ); let link_output_kind = link_output_kind(sess, crate_type); @@ -1914,7 +1986,7 @@ fn linker_with_args<'a>( // ------------ Object code and libraries, order-dependent ------------ // Pre-link CRT objects. - add_pre_link_objects(cmd, sess, link_output_kind, crt_objects_fallback); + add_pre_link_objects(cmd, sess, flavor, link_output_kind, self_contained); add_linked_symbol_object( cmd, @@ -1947,7 +2019,6 @@ fn linker_with_args<'a>( // Upstream rust libraries are not supposed to depend on our local native // libraries as that would violate the structure of the DAG, in that // scenario they are required to link to them as well in a shared fashion. - // (The current implementation still doesn't prevent it though, see the FIXME below.) // // Note that upstream rust libraries may contain native dependencies as // well, but they also can't depend on what we just started to add to the @@ -1968,15 +2039,16 @@ fn linker_with_args<'a>( // and move this option back to the top. cmd.add_as_needed(); - // FIXME: Move this below to other native libraries - // (or alternatively link all native libraries after their respective crates). - // This change is somewhat breaking in practice due to local static libraries being linked - // as whole-archive (#85144), so removing whole-archive may be a pre-requisite. + // Local native libraries of all kinds. + // + // If `-Zlink-native-libraries=false` is set, then the assumption is that an + // external build system already has the native dependencies defined, and it + // will provide them to the linker itself. if sess.opts.unstable_opts.link_native_libraries { add_local_native_libraries(cmd, sess, codegen_results); } - // Upstream rust libraries and their non-bundled static libraries + // Upstream rust libraries and their (possibly bundled) static native libraries. add_upstream_rust_crates( cmd, sess, @@ -1986,24 +2058,54 @@ fn linker_with_args<'a>( tmpdir, ); - // Upstream dynamic native libraries linked with `#[link]` attributes at and `-l` - // command line options. - // If -Zlink-native-libraries=false is set, then the assumption is that an - // external build system already has the native dependencies defined, and it - // will provide them to the linker itself. + // Dynamic native libraries from upstream crates. + // + // FIXME: Merge this to `add_upstream_rust_crates` so that all native libraries are linked + // together with their respective upstream crates, and in their originally specified order. + // This may be slightly breaking due to our use of `--as-needed` and needs a crater run. if sess.opts.unstable_opts.link_native_libraries { add_upstream_native_libraries(cmd, sess, codegen_results); } // Link with the import library generated for any raw-dylib functions. for (raw_dylib_name, raw_dylib_imports) in - collate_raw_dylibs(sess, &codegen_results.crate_info.used_libraries)? + collate_raw_dylibs(sess, codegen_results.crate_info.used_libraries.iter())? + { + cmd.add_object(&archive_builder_builder.create_dll_import_lib( + sess, + &raw_dylib_name, + &raw_dylib_imports, + tmpdir, + true, + )); + } + // As with add_upstream_native_libraries, we need to add the upstream raw-dylib symbols in case + // they are used within inlined functions or instantiated generic functions. We do this *after* + // handling the raw-dylib symbols in the current crate to make sure that those are chosen first + // by the linker. + let (_, dependency_linkage) = codegen_results + .crate_info + .dependency_formats + .iter() + .find(|(ty, _)| *ty == crate_type) + .expect("failed to find crate type in dependency format list"); + let native_libraries_from_nonstatics = codegen_results + .crate_info + .native_libraries + .iter() + .filter_map(|(cnum, libraries)| { + (dependency_linkage[cnum.as_usize() - 1] != Linkage::Static).then(|| libraries) + }) + .flatten(); + for (raw_dylib_name, raw_dylib_imports) in + collate_raw_dylibs(sess, native_libraries_from_nonstatics)? { cmd.add_object(&archive_builder_builder.create_dll_import_lib( sess, &raw_dylib_name, &raw_dylib_imports, tmpdir, + false, )); } @@ -2024,7 +2126,7 @@ fn linker_with_args<'a>( cmd, sess, link_output_kind, - crt_objects_fallback, + self_contained, flavor, crate_type, codegen_results, @@ -2040,7 +2142,7 @@ fn linker_with_args<'a>( // ------------ Object code and libraries, order-dependent ------------ // Post-link CRT objects. - add_post_link_objects(cmd, sess, link_output_kind, crt_objects_fallback); + add_post_link_objects(cmd, sess, link_output_kind, self_contained); // ------------ Late order-dependent options ------------ @@ -2057,7 +2159,7 @@ fn add_order_independent_options( cmd: &mut dyn Linker, sess: &Session, link_output_kind: LinkOutputKind, - crt_objects_fallback: bool, + self_contained: bool, flavor: LinkerFlavor, crate_type: CrateType, codegen_results: &CodegenResults, @@ -2070,7 +2172,10 @@ fn add_order_independent_options( add_link_script(cmd, sess, tmpdir, crate_type); - if sess.target.os == "fuchsia" && crate_type == CrateType::Executable { + if sess.target.os == "fuchsia" + && crate_type == CrateType::Executable + && !matches!(flavor, LinkerFlavor::Gnu(Cc::Yes, _)) + { let prefix = if sess.opts.unstable_opts.sanitizer.contains(SanitizerSet::ADDRESS) { "asan/" } else { @@ -2086,7 +2191,7 @@ fn add_order_independent_options( // Make the binary compatible with data execution prevention schemes. cmd.add_no_exec(); - if crt_objects_fallback { + if self_contained { cmd.no_crt_objects(); } @@ -2099,11 +2204,11 @@ fn add_order_independent_options( }); } - if flavor == LinkerFlavor::PtxLinker { + if flavor == LinkerFlavor::Ptx { // Provide the linker with fallback to internal `target-cpu`. cmd.arg("--fallback-arch"); cmd.arg(&codegen_results.crate_info.target_cpu); - } else if flavor == LinkerFlavor::BpfLinker { + } else if flavor == LinkerFlavor::Bpf { cmd.arg("--cpu"); cmd.arg(&codegen_results.crate_info.target_cpu); cmd.arg("--cpu-features"); @@ -2115,7 +2220,7 @@ fn add_order_independent_options( cmd.linker_plugin_lto(); - add_library_search_dirs(cmd, sess, crt_objects_fallback); + add_library_search_dirs(cmd, sess, self_contained); cmd.output_filename(out_filename); @@ -2319,72 +2424,25 @@ fn add_upstream_rust_crates<'a>( // crates. let deps = &codegen_results.crate_info.used_crates; - // There's a few internal crates in the standard library (aka libcore and - // libstd) which actually have a circular dependence upon one another. This - // currently arises through "weak lang items" where libcore requires things - // like `rust_begin_unwind` but libstd ends up defining it. To get this - // circular dependence to work correctly in all situations we'll need to be - // sure to correctly apply the `--start-group` and `--end-group` options to - // GNU linkers, otherwise if we don't use any other symbol from the standard - // library it'll get discarded and the whole application won't link. - // - // In this loop we're calculating the `group_end`, after which crate to - // pass `--end-group` and `group_start`, before which crate to pass - // `--start-group`. We currently do this by passing `--end-group` after - // the first crate (when iterating backwards) that requires a lang item - // defined somewhere else. Once that's set then when we've defined all the - // necessary lang items we'll pass `--start-group`. - // - // Note that this isn't amazing logic for now but it should do the trick - // for the current implementation of the standard library. - let mut group_end = None; - let mut group_start = None; - // Crates available for linking thus far. - let mut available = FxHashSet::default(); - // Crates required to satisfy dependencies discovered so far. - let mut required = FxHashSet::default(); - - let info = &codegen_results.crate_info; - for &cnum in deps.iter().rev() { - if let Some(missing) = info.missing_lang_items.get(&cnum) { - let missing_crates = missing.iter().map(|i| info.lang_item_to_crate.get(i).copied()); - required.extend(missing_crates); - } - - required.insert(Some(cnum)); - available.insert(Some(cnum)); - - if required.len() > available.len() && group_end.is_none() { - group_end = Some(cnum); - } - if required.len() == available.len() && group_end.is_some() { - group_start = Some(cnum); - break; - } - } - - // If we didn't end up filling in all lang items from upstream crates then - // we'll be filling it in with our crate. This probably means we're the - // standard library itself, so skip this for now. - if group_end.is_some() && group_start.is_none() { - group_end = None; - } - let mut compiler_builtins = None; let search_path = OnceCell::new(); for &cnum in deps.iter() { - if group_start == Some(cnum) { - cmd.group_start(); - } - // We may not pass all crates through to the linker. Some crates may // appear statically in an existing dylib, meaning we'll pick up all the // symbols from the dylib. let src = &codegen_results.crate_info.used_crate_source[&cnum]; match data[cnum.as_usize() - 1] { _ if codegen_results.crate_info.profiler_runtime == Some(cnum) => { - add_static_crate(cmd, sess, archive_builder_builder, codegen_results, tmpdir, cnum); + add_static_crate( + cmd, + sess, + archive_builder_builder, + codegen_results, + tmpdir, + cnum, + &Default::default(), + ); } // compiler-builtins are always placed last to ensure that they're // linked correctly. @@ -2394,17 +2452,41 @@ fn add_upstream_rust_crates<'a>( } Linkage::NotLinked | Linkage::IncludedFromDylib => {} Linkage::Static => { - add_static_crate(cmd, sess, archive_builder_builder, codegen_results, tmpdir, cnum); + let bundled_libs = if sess.opts.unstable_opts.packed_bundled_libs { + codegen_results.crate_info.native_libraries[&cnum] + .iter() + .filter_map(|lib| lib.filename) + .collect::<FxHashSet<_>>() + } else { + Default::default() + }; + add_static_crate( + cmd, + sess, + archive_builder_builder, + codegen_results, + tmpdir, + cnum, + &bundled_libs, + ); // Link static native libs with "-bundle" modifier only if the crate they originate from // is being linked statically to the current crate. If it's linked dynamically // or is an rlib already included via some other dylib crate, the symbols from // native libs will have already been included in that dylib. // - // If -Zlink-native-libraries=false is set, then the assumption is that an + // If `-Zlink-native-libraries=false` is set, then the assumption is that an // external build system already has the native dependencies defined, and it // will provide them to the linker itself. if sess.opts.unstable_opts.link_native_libraries { + if sess.opts.unstable_opts.packed_bundled_libs { + // If rlib contains native libs as archives, unpack them to tmpdir. + let rlib = &src.rlib.as_ref().unwrap().0; + archive_builder_builder + .extract_bundled_libs(rlib, tmpdir, &bundled_libs) + .unwrap_or_else(|e| sess.fatal(e)); + } + let mut last = (None, NativeLibKind::Unspecified, None); for lib in &codegen_results.crate_info.native_libraries[&cnum] { let Some(name) = lib.name else { @@ -2437,6 +2519,14 @@ fn add_upstream_rust_crates<'a>( bundle: Some(false), whole_archive: Some(false) | None, } => { + // HACK/FIXME: Fixup a circular dependency between libgcc and libc + // with glibc. This logic should be moved to the libc crate. + if sess.target.os == "linux" + && sess.target.env == "gnu" + && name == "c" + { + cmd.link_staticlib("gcc", false); + } cmd.link_staticlib(name, lib.verbatim.unwrap_or(false)); } NativeLibKind::LinkArg => { @@ -2446,20 +2536,23 @@ fn add_upstream_rust_crates<'a>( | NativeLibKind::Framework { .. } | NativeLibKind::Unspecified | NativeLibKind::RawDylib => {} - NativeLibKind::Static { - bundle: Some(true) | None, - whole_archive: _, - } => {} + NativeLibKind::Static { bundle: Some(true) | None, whole_archive } => { + if sess.opts.unstable_opts.packed_bundled_libs { + // If rlib contains native libs as archives, they are unpacked to tmpdir. + let path = tmpdir.join(lib.filename.unwrap().as_str()); + if whole_archive == Some(true) { + cmd.link_whole_rlib(&path); + } else { + cmd.link_rlib(&path); + } + } + } } } } } Linkage::Dynamic => add_dynamic_crate(cmd, sess, &src.dylib.as_ref().unwrap().0), } - - if group_end == Some(cnum) { - cmd.group_end(); - } } // compiler-builtins are always placed last to ensure that they're @@ -2468,7 +2561,15 @@ fn add_upstream_rust_crates<'a>( // was already "included" in a dylib (e.g., `libstd` when `-C prefer-dynamic` // is used) if let Some(cnum) = compiler_builtins { - add_static_crate(cmd, sess, archive_builder_builder, codegen_results, tmpdir, cnum); + add_static_crate( + cmd, + sess, + archive_builder_builder, + codegen_results, + tmpdir, + cnum, + &Default::default(), + ); } // Converts a library file-stem into a cc -l argument @@ -2501,6 +2602,7 @@ fn add_upstream_rust_crates<'a>( codegen_results: &CodegenResults, tmpdir: &Path, cnum: CrateNum, + bundled_lib_file_names: &FxHashSet<Symbol>, ) { let src = &codegen_results.crate_info.used_crate_source[&cnum]; let cratepath = &src.rlib.as_ref().unwrap().0; @@ -2529,6 +2631,7 @@ fn add_upstream_rust_crates<'a>( let dst = tmpdir.join(cratepath.file_name().unwrap()); let name = cratepath.file_name().unwrap().to_str().unwrap(); let name = &name[3..name.len() - 5]; // chop off lib/.rlib + let bundled_lib_file_names = bundled_lib_file_names.clone(); sess.prof.generic_activity_with_arg("link_altering_rlib", name).run(|| { let canonical_name = name.replace('-', "_"); @@ -2562,6 +2665,15 @@ fn add_upstream_rust_crates<'a>( let skip_because_lto = upstream_rust_objects_already_included && is_rust_object && is_builtins; + // We skip native libraries because: + // 1. This native libraries won't be used from the generated rlib, + // so we can throw them away to avoid the copying work. + // 2. We can't allow it to be a single remaining entry in archive + // as some linkers may complain on that. + if bundled_lib_file_names.contains(&Symbol::intern(f)) { + return true; + } + if skip_because_cfg_say_so || skip_because_lto { return true; } @@ -2657,7 +2769,7 @@ fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool { } } -fn are_upstream_rust_objects_already_included(sess: &Session) -> bool { +pub(crate) fn are_upstream_rust_objects_already_included(sess: &Session) -> bool { match sess.lto() { config::Lto::Fat => true, config::Lto::Thin => { @@ -2674,11 +2786,16 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { let os = &sess.target.os; let llvm_target = &sess.target.llvm_target; if sess.target.vendor != "apple" - || !matches!(os.as_ref(), "ios" | "tvos" | "watchos") - || (flavor != LinkerFlavor::Gcc && flavor != LinkerFlavor::Lld(LldFlavor::Ld64)) + || !matches!(os.as_ref(), "ios" | "tvos" | "watchos" | "macos") + || !matches!(flavor, LinkerFlavor::Darwin(..)) { return; } + + if os == "macos" && !matches!(flavor, LinkerFlavor::Darwin(Cc::No, _)) { + return; + } + let sdk_name = match (arch.as_ref(), os.as_ref()) { ("aarch64", "tvos") => "appletvos", ("x86_64", "tvos") => "appletvsimulator", @@ -2694,6 +2811,7 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { ("aarch64", "watchos") if llvm_target.ends_with("-simulator") => "watchsimulator", ("aarch64", "watchos") => "watchos", ("arm", "watchos") => "watchos", + (_, "macos") => "macosx", _ => { sess.err(&format!("unsupported arch `{}` for os `{}`", arch, os)); return; @@ -2708,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!(), @@ -2774,23 +2892,30 @@ 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 => { - let tools_path = sess.get_tools_search_paths(false); - let gcc_ld_dir = tools_path - .into_iter() - .map(|p| p.join("gcc-ld")) - .find(|p| { - p.join(if sess.host.is_like_windows { "ld.exe" } else { "ld" }).exists() - }) - .unwrap_or_else(|| sess.fatal("rust-lld (as ld) not found")); - cmd.arg({ - let mut arg = OsString::from("-B"); - arg.push(gcc_ld_dir); - arg - }); - cmd.arg(format!("-Wl,-rustc-lld-flavor={}", sess.target.lld_flavor.as_str())); + // 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). + cmd.arg(format!("--target={}", sess.target.llvm_target)); + } } } } else { diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index ce51b2e95..c49b19bdf 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1,6 +1,6 @@ -use super::archive; use super::command::Command; use super::symbol_export; +use crate::errors; use rustc_span::symbol::sym; use std::ffi::{OsStr, OsString}; @@ -11,12 +11,13 @@ use std::path::{Path, PathBuf}; use std::{env, mem, str}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; +use rustc_metadata::find_native_static_library; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind}; use rustc_middle::ty::TyCtxt; use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip}; use rustc_session::Session; -use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor}; +use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld}; use cc::windows_registry; @@ -56,8 +57,13 @@ pub fn get_linker<'a>( let mut cmd = match linker.to_str() { Some(linker) if cfg!(windows) && linker.ends_with(".bat") => Command::bat_script(linker), _ => match flavor { - LinkerFlavor::Lld(f) => Command::lld(linker, f), - LinkerFlavor::Msvc if sess.opts.cg.linker.is_none() && sess.target.linker.is_none() => { + LinkerFlavor::Gnu(Cc::No, Lld::Yes) + | LinkerFlavor::Darwin(Cc::No, Lld::Yes) + | LinkerFlavor::WasmLld(Cc::No) + | LinkerFlavor::Msvc(Lld::Yes) => Command::lld(linker, flavor.lld_flavor()), + LinkerFlavor::Msvc(Lld::No) + if sess.opts.cg.linker.is_none() && sess.target.linker.is_none() => + { Command::new(msvc_tool.as_ref().map_or(linker, |t| t.path())) } _ => Command::new(linker), @@ -68,9 +74,7 @@ pub fn get_linker<'a>( // To comply with the Windows App Certification Kit, // MSVC needs to link with the Store versions of the runtime libraries (vcruntime, msvcrt, etc). let t = &sess.target; - if (flavor == LinkerFlavor::Msvc || flavor == LinkerFlavor::Lld(LldFlavor::Link)) - && t.vendor == "uwp" - { + if matches!(flavor, LinkerFlavor::Msvc(..)) && t.vendor == "uwp" { if let Some(ref tool) = msvc_tool { let original_path = tool.path(); if let Some(ref root_lib_path) = original_path.ancestors().nth(4) { @@ -126,29 +130,25 @@ pub fn get_linker<'a>( // to the linker args construction. assert!(cmd.get_args().is_empty() || sess.target.vendor == "uwp"); match flavor { - LinkerFlavor::Lld(LldFlavor::Link) | LinkerFlavor::Msvc => { - Box::new(MsvcLinker { cmd, sess }) as Box<dyn Linker> - } - LinkerFlavor::Em => Box::new(EmLinker { cmd, sess }) as Box<dyn Linker>, - LinkerFlavor::Gcc => { - Box::new(GccLinker { cmd, sess, target_cpu, hinted_static: false, is_ld: false }) - as Box<dyn Linker> - } - - LinkerFlavor::Lld(LldFlavor::Ld) - | LinkerFlavor::Lld(LldFlavor::Ld64) - | LinkerFlavor::Ld => { - Box::new(GccLinker { cmd, sess, target_cpu, hinted_static: false, is_ld: true }) - as Box<dyn Linker> + LinkerFlavor::Unix(Cc::No) if sess.target.os == "l4re" => { + Box::new(L4Bender::new(cmd, sess)) as Box<dyn Linker> } - - LinkerFlavor::Lld(LldFlavor::Wasm) => Box::new(WasmLd::new(cmd, sess)) as Box<dyn Linker>, - - LinkerFlavor::PtxLinker => Box::new(PtxLinker { cmd, sess }) as Box<dyn Linker>, - - LinkerFlavor::BpfLinker => Box::new(BpfLinker { cmd, sess }) as Box<dyn Linker>, - - LinkerFlavor::L4Bender => Box::new(L4Bender::new(cmd, sess)) as Box<dyn Linker>, + LinkerFlavor::WasmLld(Cc::No) => Box::new(WasmLd::new(cmd, sess)) as Box<dyn Linker>, + LinkerFlavor::Gnu(cc, _) + | LinkerFlavor::Darwin(cc, _) + | LinkerFlavor::WasmLld(cc) + | LinkerFlavor::Unix(cc) => Box::new(GccLinker { + cmd, + sess, + target_cpu, + hinted_static: false, + is_ld: cc == Cc::No, + is_gnu: flavor.is_gnu(), + }) as Box<dyn Linker>, + LinkerFlavor::Msvc(..) => Box::new(MsvcLinker { cmd, sess }) as Box<dyn Linker>, + LinkerFlavor::EmCc => Box::new(EmLinker { cmd, sess }) as Box<dyn Linker>, + LinkerFlavor::Bpf => Box::new(BpfLinker { cmd, sess }) as Box<dyn Linker>, + LinkerFlavor::Ptx => Box::new(PtxLinker { cmd, sess }) as Box<dyn Linker>, } } @@ -186,8 +186,6 @@ pub trait Linker { fn no_default_libraries(&mut self); fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]); fn subsystem(&mut self, subsystem: &str); - fn group_start(&mut self); - fn group_end(&mut self); fn linker_plugin_lto(&mut self); fn add_eh_frame_header(&mut self) {} fn add_no_exec(&mut self) {} @@ -216,6 +214,7 @@ pub struct GccLinker<'a> { hinted_static: bool, // Keeps track of the current hinting mode. // Link as ld is_ld: bool, + is_gnu: bool, } impl<'a> GccLinker<'a> { @@ -364,7 +363,7 @@ impl<'a> Linker for GccLinker<'a> { fn set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path) { match output_kind { LinkOutputKind::DynamicNoPicExe => { - if !self.is_ld && self.sess.target.linker_is_gnu { + if !self.is_ld && self.is_gnu { self.cmd.arg("-no-pie"); } } @@ -378,7 +377,7 @@ impl<'a> Linker for GccLinker<'a> { LinkOutputKind::StaticNoPicExe => { // `-static` works for both gcc wrapper and ld. self.cmd.arg("-static"); - if !self.is_ld && self.sess.target.linker_is_gnu { + if !self.is_ld && self.is_gnu { self.cmd.arg("-no-pie"); } } @@ -436,26 +435,26 @@ impl<'a> Linker for GccLinker<'a> { // FIXME(81490): ld64 doesn't support these flags but macOS 11 // has -needed-l{} / -needed_library {} // but we have no way to detect that here. - self.sess.warn("`as-needed` modifier not implemented yet for ld64"); - } else if self.sess.target.linker_is_gnu && !self.sess.target.is_like_windows { + self.sess.emit_warning(errors::Ld64UnimplementedModifier); + } else if self.is_gnu && !self.sess.target.is_like_windows { self.linker_arg("--no-as-needed"); } else { - self.sess.warn("`as-needed` modifier not supported for current linker"); + self.sess.emit_warning(errors::LinkerUnsupportedModifier); } } self.hint_dynamic(); - self.cmd.arg(format!("-l{}{}", if verbatim { ":" } else { "" }, lib)); + self.cmd.arg(format!("-l{}{lib}", if verbatim && self.is_gnu { ":" } else { "" },)); if !as_needed { if self.sess.target.is_like_osx { // See above FIXME comment - } else if self.sess.target.linker_is_gnu && !self.sess.target.is_like_windows { + } else if self.is_gnu && !self.sess.target.is_like_windows { self.linker_arg("--as-needed"); } } } fn link_staticlib(&mut self, lib: &str, verbatim: bool) { self.hint_static(); - self.cmd.arg(format!("-l{}{}", if verbatim { ":" } else { "" }, lib)); + self.cmd.arg(format!("-l{}{lib}", if verbatim && self.is_gnu { ":" } else { "" },)); } fn link_rlib(&mut self, lib: &Path) { self.hint_static(); @@ -494,7 +493,7 @@ impl<'a> Linker for GccLinker<'a> { // FIXME(81490): ld64 as of macOS 11 supports the -needed_framework // flag but we have no way to detect that here. // self.cmd.arg("-needed_framework").arg(framework); - self.sess.warn("`as-needed` modifier not implemented yet for ld64"); + self.sess.emit_warning(errors::Ld64UnimplementedModifier); } self.cmd.arg("-framework").arg(framework); } @@ -509,17 +508,14 @@ impl<'a> Linker for GccLinker<'a> { self.hint_static(); let target = &self.sess.target; if !target.is_like_osx { - self.linker_arg("--whole-archive").cmd.arg(format!( - "-l{}{}", - if verbatim { ":" } else { "" }, - lib - )); + self.linker_arg("--whole-archive"); + self.cmd.arg(format!("-l{}{lib}", if verbatim && self.is_gnu { ":" } else { "" },)); self.linker_arg("--no-whole-archive"); } else { // -force_load is the macOS equivalent of --whole-archive, but it // involves passing the full path to the library to link. self.linker_arg("-force_load"); - let lib = archive::find_library(lib, verbatim, search_path, &self.sess); + let lib = find_native_static_library(lib, Some(verbatim), search_path, &self.sess); self.linker_arg(&lib); } } @@ -558,21 +554,19 @@ impl<'a> Linker for GccLinker<'a> { // eliminate the metadata. If we're building an executable, however, // --gc-sections drops the size of hello world from 1.8MB to 597K, a 67% // reduction. - } else if (self.sess.target.linker_is_gnu || self.sess.target.is_like_wasm) - && !keep_metadata - { + } else if (self.is_gnu || self.sess.target.is_like_wasm) && !keep_metadata { self.linker_arg("--gc-sections"); } } fn no_gc_sections(&mut self) { - if self.sess.target.linker_is_gnu || self.sess.target.is_like_wasm { + if self.is_gnu || self.sess.target.is_like_wasm { self.linker_arg("--no-gc-sections"); } } fn optimize(&mut self) { - if !self.sess.target.linker_is_gnu && !self.sess.target.is_like_wasm { + if !self.is_gnu && !self.sess.target.is_like_wasm { return; } @@ -586,7 +580,7 @@ impl<'a> Linker for GccLinker<'a> { } fn pgo_gen(&mut self) { - if !self.sess.target.linker_is_gnu { + if !self.is_gnu { return; } @@ -616,7 +610,13 @@ impl<'a> Linker for GccLinker<'a> { match strip { Strip::None => {} Strip::Debuginfo => { - self.linker_arg("--strip-debug"); + // The illumos linker does not support --strip-debug although + // it does support --strip-all as a compatibility alias for -s. + // The --strip-debug case is handled by running an external + // `strip` utility as a separate step after linking. + if self.sess.target.os != "illumos" { + self.linker_arg("--strip-debug"); + } } Strip::Symbols => { self.linker_arg("--strip-all"); @@ -672,8 +672,8 @@ impl<'a> Linker for GccLinker<'a> { writeln!(f, "_{}", sym)?; } }; - if let Err(e) = res { - self.sess.fatal(&format!("failed to write lib.def file: {}", e)); + if let Err(error) = res { + self.sess.emit_fatal(errors::LibDefWriteFailure { error }); } } else if is_windows { let res: io::Result<()> = try { @@ -687,8 +687,8 @@ impl<'a> Linker for GccLinker<'a> { writeln!(f, " {}", symbol)?; } }; - if let Err(e) = res { - self.sess.fatal(&format!("failed to write list.def file: {}", e)); + if let Err(error) = res { + self.sess.emit_fatal(errors::LibDefWriteFailure { error }); } } else { // Write an LD version script @@ -704,8 +704,8 @@ impl<'a> Linker for GccLinker<'a> { } writeln!(f, "\n local:\n *;\n}};")?; }; - if let Err(e) = res { - self.sess.fatal(&format!("failed to write version script: {}", e)); + if let Err(error) = res { + self.sess.emit_fatal(errors::VersionScriptWriteFailure { error }); } } @@ -733,18 +733,6 @@ impl<'a> Linker for GccLinker<'a> { self.hint_dynamic(); // Reset to default before returning the composed command line. } - fn group_start(&mut self) { - if self.takes_hints() { - self.linker_arg("--start-group"); - } - } - - fn group_end(&mut self) { - if self.takes_hints() { - self.linker_arg("--end-group"); - } - } - fn linker_plugin_lto(&mut self) { match self.sess.opts.cg.linker_plugin_lto { LinkerPluginLto::Disabled => { @@ -769,13 +757,13 @@ impl<'a> Linker for GccLinker<'a> { fn add_no_exec(&mut self) { if self.sess.target.is_like_windows { self.linker_arg("--nxcompat"); - } else if self.sess.target.linker_is_gnu { + } else if self.is_gnu { self.linker_arg("-znoexecstack"); } } fn add_as_needed(&mut self) { - if self.sess.target.linker_is_gnu && !self.sess.target.is_like_windows { + if self.is_gnu && !self.sess.target.is_like_windows { self.linker_arg("--as-needed"); } else if self.sess.target.is_like_solaris { // -z ignore is the Solaris equivalent to the GNU ld --as-needed option @@ -934,9 +922,8 @@ impl<'a> Linker for MsvcLinker<'a> { self.cmd.arg(arg); } } - Err(err) => { - self.sess - .warn(&format!("error enumerating natvis directory: {}", err)); + Err(error) => { + self.sess.emit_warning(errors::NoNatvisDirectory { error }); } } } @@ -990,8 +977,8 @@ impl<'a> Linker for MsvcLinker<'a> { writeln!(f, " {}", symbol)?; } }; - if let Err(e) = res { - self.sess.fatal(&format!("failed to write lib.def file: {}", e)); + if let Err(error) = res { + self.sess.emit_fatal(errors::LibDefWriteFailure { error }); } let mut arg = OsString::from("/DEF:"); arg.push(path); @@ -1022,10 +1009,6 @@ impl<'a> Linker for MsvcLinker<'a> { } } - // MSVC doesn't need group indicators - fn group_start(&mut self) {} - fn group_end(&mut self) {} - fn linker_plugin_lto(&mut self) { // Do nothing } @@ -1168,10 +1151,6 @@ impl<'a> Linker for EmLinker<'a> { // noop } - // Appears not necessary on Emscripten - fn group_start(&mut self) {} - fn group_end(&mut self) {} - fn linker_plugin_lto(&mut self) { // Do nothing } @@ -1199,22 +1178,19 @@ impl<'a> WasmLd<'a> { // sharing memory and instantiating the module multiple times. As a // result if it were exported then we'd just have no sharing. // - // * `--export=__wasm_init_memory` - when using `--passive-segments` the - // linker will synthesize this function, and so we need to make sure - // that our usage of `--export` below won't accidentally cause this - // function to get deleted. - // - // * `--export=*tls*` - when `#[thread_local]` symbols are used these - // symbols are how the TLS segments are initialized and configured. + // On wasm32-unknown-unknown, we also export symbols for glue code to use: + // * `--export=*tls*` - when `#[thread_local]` symbols are used these + // symbols are how the TLS segments are initialized and configured. if sess.target_features.contains(&sym::atomics) { cmd.arg("--shared-memory"); cmd.arg("--max-memory=1073741824"); cmd.arg("--import-memory"); - cmd.arg("--export=__wasm_init_memory"); - cmd.arg("--export=__wasm_init_tls"); - cmd.arg("--export=__tls_size"); - cmd.arg("--export=__tls_align"); - cmd.arg("--export=__tls_base"); + if sess.target.os == "unknown" { + cmd.arg("--export=__wasm_init_tls"); + cmd.arg("--export=__tls_size"); + cmd.arg("--export=__tls_align"); + cmd.arg("--export=__tls_base"); + } } WasmLd { cmd, sess } } @@ -1339,18 +1315,16 @@ impl<'a> Linker for WasmLd<'a> { // LLD will hide these otherwise-internal symbols since it only exports // symbols explicitly passed via the `--export` flags above and hides all - // others. Various bits and pieces of tooling use this, so be sure these - // symbols make their way out of the linker as well. - self.cmd.arg("--export=__heap_base"); - self.cmd.arg("--export=__data_end"); + // others. Various bits and pieces of wasm32-unknown-unknown tooling use + // this, so be sure these symbols make their way out of the linker as well. + if self.sess.target.os == "unknown" { + self.cmd.arg("--export=__heap_base"); + self.cmd.arg("--export=__data_end"); + } } fn subsystem(&mut self, _subsystem: &str) {} - // Not needed for now with LLD - fn group_start(&mut self) {} - fn group_end(&mut self) {} - fn linker_plugin_lto(&mut self) { // Do nothing for now } @@ -1467,7 +1441,7 @@ impl<'a> Linker for L4Bender<'a> { fn export_symbols(&mut self, _: &Path, _: CrateType, _: &[String]) { // ToDo, not implemented, copy from GCC - self.sess.warn("exporting symbols not implemented yet for L4Bender"); + self.sess.emit_warning(errors::L4BenderExportingSymbolsUnimplemented); return; } @@ -1479,14 +1453,6 @@ impl<'a> Linker for L4Bender<'a> { self.hint_static(); // Reset to default before returning the composed command line. } - fn group_start(&mut self) { - self.cmd.arg("--start-group"); - } - - fn group_end(&mut self) { - self.cmd.arg("--end-group"); - } - fn linker_plugin_lto(&mut self) {} fn control_flow_guard(&mut self) {} @@ -1667,10 +1633,6 @@ impl<'a> Linker for PtxLinker<'a> { fn subsystem(&mut self, _subsystem: &str) {} - fn group_start(&mut self) {} - - fn group_end(&mut self) {} - fn linker_plugin_lto(&mut self) {} } @@ -1771,8 +1733,8 @@ impl<'a> Linker for BpfLinker<'a> { writeln!(f, "{}", sym)?; } }; - if let Err(e) = res { - self.sess.fatal(&format!("failed to write symbols file: {}", e)); + if let Err(error) = res { + self.sess.emit_fatal(errors::SymbolFileWriteFailure { error }); } else { self.cmd.arg("--export-symbols").arg(&path); } @@ -1780,9 +1742,5 @@ impl<'a> Linker for BpfLinker<'a> { fn subsystem(&mut self, _subsystem: &str) {} - fn group_start(&mut self) {} - - fn group_end(&mut self) {} - fn linker_plugin_lto(&mut self) {} } diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 0302c2881..99ddd1764 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -117,6 +117,10 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static "riscv32" => Architecture::Riscv32, "riscv64" => Architecture::Riscv64, "sparc64" => Architecture::Sparc64, + "avr" => Architecture::Avr, + "msp430" => Architecture::Msp430, + "hexagon" => Architecture::Hexagon, + "bpf" => Architecture::Bpf, // Unsupported architecture. _ => return None, }; @@ -187,12 +191,12 @@ pub enum MetadataPosition { Last, } -// For rlibs we "pack" rustc metadata into a dummy object file. When rustc -// creates a dylib crate type it will pass `--whole-archive` (or the -// platform equivalent) to include all object files from an rlib into the -// final dylib itself. This causes linkers to iterate and try to include all -// files located in an archive, so if metadata is stored in an archive then -// it needs to be of a form that the linker will be able to process. +// For rlibs we "pack" rustc metadata into a dummy object file. +// +// Historically it was needed because rustc linked rlibs as whole-archive in some cases. +// In that case linkers try to include all files located in an archive, so if metadata is stored +// in an archive then it needs to be of a form that the linker is able to process. +// Now it's not clear whether metadata still needs to be wrapped into an object file or not. // // Note, though, that we don't actually want this metadata to show up in any // final output of the compiler. Instead this is purely for rustc's own diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index e6b605575..c2ecc4160 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -13,7 +13,7 @@ use rustc_middle::ty::query::{ExternProviders, Providers}; use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; use rustc_middle::ty::Instance; use rustc_middle::ty::{self, SymbolName, TyCtxt}; -use rustc_session::config::CrateType; +use rustc_session::config::{CrateType, OomStrategy}; use rustc_target::spec::SanitizerSet; pub fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel { @@ -76,7 +76,7 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, cnum: CrateNum) -> DefIdMap< // let it through if it's included statically. match tcx.hir().get_by_def_id(def_id) { Node::ForeignItem(..) => { - tcx.is_statically_included_foreign_item(def_id).then_some(def_id) + tcx.native_library(def_id).map_or(false, |library| library.kind.is_statically_included()).then_some(def_id) } // Only consider nodes that actually have exported symbols. @@ -103,18 +103,14 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, cnum: CrateNum) -> DefIdMap< } }) .map(|def_id| { - let (export_level, used) = if special_runtime_crate { - let name = tcx.symbol_name(Instance::mono(tcx, def_id.to_def_id())).name; - // We won't link right if these symbols are stripped during LTO. - let used = match name { - "rust_eh_personality" - | "rust_eh_register_frames" - | "rust_eh_unregister_frames" => true, - _ => false, - }; - (SymbolExportLevel::Rust, used) + // We won't link right if this symbol is stripped during LTO. + let name = tcx.symbol_name(Instance::mono(tcx, def_id.to_def_id())).name; + let used = name == "rust_eh_personality"; + + let export_level = if special_runtime_crate { + SymbolExportLevel::Rust } else { - (symbol_export_level(tcx, def_id.to_def_id()), false) + symbol_export_level(tcx, def_id.to_def_id()) }; let codegen_attrs = tcx.codegen_fn_attrs(def_id.to_def_id()); debug!( @@ -210,6 +206,15 @@ fn exported_symbols_provider_local<'tcx>( }, )); } + + symbols.push(( + ExportedSymbol::NoDefId(SymbolName::new(tcx, OomStrategy::SYMBOL)), + SymbolExportInfo { + level: SymbolExportLevel::Rust, + kind: SymbolExportKind::Text, + used: false, + }, + )); } if tcx.sess.instrument_coverage() || tcx.sess.opts.cg.profile_generate.enabled() { @@ -544,7 +549,7 @@ pub fn linking_symbol_name_for_instance_in_crate<'tcx>( .map(|fnabi| (fnabi.conv, &fnabi.args[..])) .unwrap_or((Conv::Rust, &[])); - // Decorate symbols with prefices, suffices and total number of bytes of arguments. + // Decorate symbols with prefixes, suffixes and total number of bytes of arguments. // Reference: https://docs.microsoft.com/en-us/cpp/build/reference/decorated-names?view=msvc-170 let (prefix, suffix) = match conv { Conv::X86Fastcall => ("@", "@"), diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 1b5ad8710..d0ac016b0 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -2,11 +2,11 @@ use super::link::{self, ensure_removed}; use super::lto::{self, SerializedModule}; use super::symbol_export::symbol_name_for_instance_in_crate; +use crate::errors; +use crate::traits::*; use crate::{ CachedModuleCodegen, CodegenResults, CompiledModule, CrateInfo, ModuleCodegen, ModuleKind, }; - -use crate::traits::*; use jobserver::{Acquired, Client}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::memmap::Mmap; @@ -15,7 +15,10 @@ use rustc_data_structures::profiling::TimingGuard; use rustc_data_structures::profiling::VerboseTimingGuard; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::Emitter; -use rustc_errors::{DiagnosticId, FatalError, Handler, Level}; +use rustc_errors::{ + translation::{to_fluent_args, Translate}, + DiagnosticId, FatalError, Handler, Level, +}; use rustc_fs_util::link_or_copy; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_incremental::{ @@ -113,7 +116,6 @@ pub struct ModuleConfig { pub vectorize_slp: bool, pub merge_functions: bool, pub inline_threshold: Option<u32>, - pub new_llvm_pass_manager: Option<bool>, pub emit_lifetime_markers: bool, pub llvm_plugins: Vec<String>, } @@ -256,13 +258,15 @@ impl ModuleConfig { { MergeFunctions::Disabled => false, MergeFunctions::Trampolines | MergeFunctions::Aliases => { - sess.opts.optimize == config::OptLevel::Default - || sess.opts.optimize == config::OptLevel::Aggressive + use config::OptLevel::*; + match sess.opts.optimize { + Aggressive | Default | SizeMin | Size => true, + Less | No => false, + } } }, inline_threshold: sess.opts.cg.inline_threshold, - new_llvm_pass_manager: sess.opts.unstable_opts.new_llvm_pass_manager, emit_lifetime_markers: sess.emit_lifetime_markers(), llvm_plugins: if_regular!(sess.opts.unstable_opts.llvm_plugins.clone(), vec![]), } @@ -529,7 +533,7 @@ fn produce_final_output_artifacts( // Produce final compile outputs. let copy_gracefully = |from: &Path, to: &Path| { if let Err(e) = fs::copy(from, to) { - sess.err(&format!("could not copy {:?} to {:?}: {}", from, to, e)); + sess.emit_err(errors::CopyPath::new(from, to, e)); } }; @@ -545,7 +549,7 @@ fn produce_final_output_artifacts( ensure_removed(sess.diagnostic(), &path); } } else { - let ext = crate_output + let extension = crate_output .temp_path(output_type, None) .extension() .unwrap() @@ -556,19 +560,11 @@ fn produce_final_output_artifacts( if crate_output.outputs.contains_key(&output_type) { // 2) Multiple codegen units, with `--emit foo=some_name`. We have // no good solution for this case, so warn the user. - sess.warn(&format!( - "ignoring emit path because multiple .{} files \ - were produced", - ext - )); + sess.emit_warning(errors::IgnoringEmitPath { extension }); } else if crate_output.single_output_file.is_some() { // 3) Multiple codegen units, with `-o some_name`. We have // no good solution for this case, so warn the user. - sess.warn(&format!( - "ignoring -o because multiple .{} files \ - were produced", - ext - )); + sess.emit_warning(errors::IgnoringOutput { extension }); } else { // 4) Multiple codegen units, but no explicit name. We // just leave the `foo.0.x` files in place. @@ -879,14 +875,12 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>( ); match link_or_copy(&source_file, &output_path) { Ok(_) => Some(output_path), - Err(err) => { - let diag_handler = cgcx.create_diag_handler(); - diag_handler.err(&format!( - "unable to copy {} to {}: {}", - source_file.display(), - output_path.display(), - err - )); + Err(error) => { + cgcx.create_diag_handler().emit_err(errors::CopyPathBuf { + source_file, + output_path, + error, + }); None } } @@ -1005,6 +999,14 @@ fn start_executing_work<B: ExtraBackendMethods>( let coordinator_send = tx_to_llvm_workers; let sess = tcx.sess; + let mut each_linked_rlib_for_lto = Vec::new(); + drop(link::each_linked_rlib(sess, crate_info, &mut |cnum, path| { + if link::ignored_for_lto(sess, crate_info, cnum) { + return; + } + each_linked_rlib_for_lto.push((cnum, path.to_path_buf())); + })); + // Compute the set of symbols we need to retain when doing LTO (if we need to) let exported_symbols = { let mut exported_symbols = FxHashMap::default(); @@ -1026,7 +1028,7 @@ fn start_executing_work<B: ExtraBackendMethods>( } Lto::Fat | Lto::Thin => { exported_symbols.insert(LOCAL_CRATE, copy_symbols(LOCAL_CRATE)); - for &cnum in tcx.crates(()).iter() { + for &(cnum, ref _path) in &each_linked_rlib_for_lto { exported_symbols.insert(cnum, copy_symbols(cnum)); } Some(Arc::new(exported_symbols)) @@ -1046,14 +1048,6 @@ fn start_executing_work<B: ExtraBackendMethods>( }) .expect("failed to spawn helper thread"); - let mut each_linked_rlib_for_lto = Vec::new(); - drop(link::each_linked_rlib(crate_info, &mut |cnum, path| { - if link::ignored_for_lto(sess, crate_info, cnum) { - return; - } - each_linked_rlib_for_lto.push((cnum, path.to_path_buf())); - })); - let ol = if tcx.sess.opts.unstable_opts.no_codegen || !tcx.sess.opts.output_types.should_codegen() { // If we know that we won’t be doing codegen, create target machines without optimisation. @@ -1636,7 +1630,7 @@ fn start_executing_work<B: ExtraBackendMethods>( llvm_start_time: &mut Option<VerboseTimingGuard<'a>>, ) { if config.time_module && llvm_start_time.is_none() { - *llvm_start_time = Some(prof.extra_verbose_generic_activity("LLVM_passes", "crate")); + *llvm_start_time = Some(prof.verbose_generic_activity("LLVM_passes")); } } } @@ -1737,9 +1731,19 @@ impl SharedEmitter { } } +impl Translate for SharedEmitter { + fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> { + None + } + + fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle { + panic!("shared emitter attempted to translate a diagnostic"); + } +} + impl Emitter for SharedEmitter { fn emit_diagnostic(&mut self, diag: &rustc_errors::Diagnostic) { - let fluent_args = self.to_fluent_args(diag.args()); + let fluent_args = to_fluent_args(diag.args()); drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { msg: self.translate_messages(&diag.message, &fluent_args).to_string(), code: diag.code.clone(), @@ -1758,14 +1762,6 @@ impl Emitter for SharedEmitter { fn source_map(&self) -> Option<&Lrc<SourceMap>> { None } - - fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> { - None - } - - fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle { - panic!("shared emitter attempted to translate a diagnostic"); - } } impl SharedEmitterMain { @@ -1887,7 +1883,7 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> { } }); - sess.cgu_reuse_tracker.check_expected_reuse(sess.diagnostic()); + sess.cgu_reuse_tracker.check_expected_reuse(sess); sess.abort_if_errors(); |