diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 8b05af7bed909..8d5ca06f96e52 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -53,8 +53,17 @@ fn prepare_lto( Lto::No => panic!("didn't request LTO but we're doing LTO"), }; + let export_for_undefined_symbols = + &|name: &str| match &cgcx.undefined_symbols_from_ignored_for_lto { + Some(undefined_symbols) => undefined_symbols.contains(name), + None => false, + }; + let symbol_filter = &|&(ref name, info): &(String, SymbolExportInfo)| { - if info.level.is_below_threshold(export_threshold) || info.used { + if info.level.is_below_threshold(export_threshold) + || info.used + || export_for_undefined_symbols(name) + { Some(CString::new(name.as_str()).unwrap()) } else { None diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 1c464b3eca497..0418ed6bc0392 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -1,3 +1,4 @@ +use object::{Object, ObjectSymbol}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::memmap::Mmap; use rustc_session::cstore::DllImport; @@ -308,3 +309,32 @@ impl<'a> ArArchiveBuilder<'a> { fn io_error_context(context: &str, err: io::Error) -> io::Error { io::Error::new(io::ErrorKind::Other, format!("{context}: {err}")) } + +pub fn read_archive_file_undefined_symbols(archive_path: &Path) -> io::Result> { + let archive_map = unsafe { Mmap::map(File::open(&archive_path)?)? }; + let archive = ArchiveFile::parse(&*archive_map) + .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; + let mut undefined_symbols = Vec::new(); + for entry in archive.members() { + let entry = entry.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; + if entry.name().ends_with(b".rmeta") { + continue; + } + let data = entry + .data(&*archive_map) + .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; + let file = object::File::parse(&*data) + .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; + for symbol in file.symbols() { + if symbol.is_undefined() { + undefined_symbols.push( + symbol + .name() + .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))? + .to_string(), + ); + } + } + } + Ok(undefined_symbols) +} diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 4ccef98afc3f6..90852d084debd 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -2,6 +2,7 @@ use super::link::{self, ensure_removed}; use super::lto::{self, SerializedModule}; use super::symbol_export::symbol_name_for_instance_in_crate; +use crate::back::archive::read_archive_file_undefined_symbols; use crate::errors; use crate::traits::*; use crate::{ @@ -9,7 +10,7 @@ use crate::{ }; use jobserver::{Acquired, Client}; use rustc_ast::attr; -use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard}; use rustc_data_structures::sync::Lrc; @@ -327,6 +328,7 @@ pub struct CodegenContext { pub fewer_names: bool, pub time_trace: bool, pub exported_symbols: Option>, + pub undefined_symbols_from_ignored_for_lto: Option>, pub opts: Arc, pub crate_types: Vec, pub each_linked_rlib_for_lto: Vec<(CrateNum, PathBuf)>, @@ -989,13 +991,36 @@ fn start_executing_work( let sess = tcx.sess; let mut each_linked_rlib_for_lto = Vec::new(); + let mut ignored_cnum_for_lto = Vec::new(); drop(link::each_linked_rlib(crate_info, None, &mut |cnum, path| { if link::ignored_for_lto(sess, crate_info, cnum) { + ignored_cnum_for_lto.push(cnum); return; } each_linked_rlib_for_lto.push((cnum, path.to_path_buf())); })); + let undefined_symbols_from_ignored_for_lto = { + match sess.lto() { + Lto::Fat | Lto::Thin => { + let mut undefined_symbols_from_ignored_for_lto = FxHashSet::default(); + if !sess.target.is_like_wasm { + // FIXME: Add an undefined symbol lookup for wasm. https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md + for cnum in ignored_cnum_for_lto { + let c_src = tcx.used_crate_source(cnum); + if let Some((path, _)) = &c_src.rlib { + let undefined_symbols = read_archive_file_undefined_symbols(path) + .expect("failed to read undefined symbols"); + undefined_symbols_from_ignored_for_lto.extend(undefined_symbols); + } + } + } + Some(undefined_symbols_from_ignored_for_lto) + } + Lto::No | Lto::ThinLocal => None, + } + }; + // 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(); @@ -1049,6 +1074,7 @@ fn start_executing_work( backend: backend.clone(), crate_types: sess.crate_types().to_vec(), each_linked_rlib_for_lto, + undefined_symbols_from_ignored_for_lto, lto: sess.lto(), fewer_names: sess.fewer_names(), save_temps: sess.opts.cg.save_temps,