diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 20e75f0fdfd20..f223441663d5d 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -58,7 +58,7 @@ use super::command::Command; use super::linker::{self, Linker}; use super::metadata::{MetadataPosition, create_wrapper_file}; use super::rpath::{self, RPathConfig}; -use super::{apple, versioned_llvm_target}; +use super::{apple, rmeta_link, versioned_llvm_target}; use crate::base::needs_allocator_shim_for_linking; use crate::{ CodegenLintLevels, CompiledModule, CompiledModules, CrateInfo, NativeLib, errors, @@ -329,8 +329,11 @@ fn link_rlib<'a>( RlibFlavor::StaticlibBase => None, }; + let mut rust_object_files: Vec = Vec::new(); + for m in &compiled_modules.modules { if let Some(obj) = m.object.as_ref() { + rust_object_files.push(obj.file_name().unwrap().to_str().unwrap().to_string()); ab.add_file(obj); } @@ -442,6 +445,16 @@ fn link_rlib<'a>( ab.add_file(&lib) } + // Add the rlib digest as the very last member. This records which archive + // members are Rust object files, replacing filename-based heuristics. + if matches!(flavor, RlibFlavor::Normal) { + let digest = rmeta_link::RmetaLink { rust_object_files }; + let digest_data = digest.encode(); + let (wrapper, _) = create_wrapper_file(sess, rmeta_link::SECTION.to_string(), &digest_data); + let digest_file = emit_wrapper_file(sess, &wrapper, tmpdir.as_ref(), rmeta_link::FILENAME); + ab.add_file(&digest_file); + } + ab } @@ -486,16 +499,18 @@ fn link_staticlib( let relevant_libs: FxIndexSet<_> = relevant.filter_map(|lib| lib.filename).collect(); let bundled_libs: FxIndexSet<_> = native_libs.filter_map(|lib| lib.filename).collect(); + let archive_map = unsafe { Mmap::map(File::open(path).unwrap()).unwrap() }; + let digest = rmeta_link::read(&archive_map, path).unwrap(); ab.add_archive( path, Box::new(move |fname: &str| { - // Ignore metadata files, no matter the name. - if fname == METADATA_FILENAME { + // Ignore metadata and rlib digest files. + if fname == METADATA_FILENAME || fname == rmeta_link::FILENAME { return true; } - // Don't include Rust objects if LTO is enabled - if lto && looks_like_rust_object_file(fname) { + // Don't include Rust objects if LTO is enabled. + if lto && digest.rust_object_files.iter().any(|f| f == fname) { return true; } @@ -3153,7 +3168,7 @@ fn add_static_crate( if let Err(error) = archive.add_archive( cratepath, Box::new(move |f| { - if f == METADATA_FILENAME { + if f == METADATA_FILENAME || f == rmeta_link::FILENAME { return true; } diff --git a/compiler/rustc_codegen_ssa/src/back/mod.rs b/compiler/rustc_codegen_ssa/src/back/mod.rs index 8d1adb9993038..7b34d80852769 100644 --- a/compiler/rustc_codegen_ssa/src/back/mod.rs +++ b/compiler/rustc_codegen_ssa/src/back/mod.rs @@ -9,6 +9,7 @@ pub mod link; pub(crate) mod linker; pub mod lto; pub mod metadata; +pub(crate) mod rmeta_link; pub(crate) mod rpath; pub mod symbol_export; pub mod write; diff --git a/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs b/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs new file mode 100644 index 0000000000000..d10c860676355 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs @@ -0,0 +1,50 @@ +//! Late-metadata archive member that lists which rlib entries are Rust object files, +//! replacing the `looks_like_rust_object_file` filename heuristic. +//! See . + +use std::path::Path; + +use object::read::archive::ArchiveFile; +use rustc_serialize::opaque::mem_encoder::MemEncoder; +use rustc_serialize::opaque::{MAGIC_END_BYTES, MemDecoder}; +use rustc_serialize::{Decodable, Encodable}; + +use super::metadata::search_for_section; + +pub(crate) const FILENAME: &str = "lib.rmeta-link"; +pub(crate) const SECTION: &str = ".rmeta-link"; + +pub(crate) struct RmetaLink { + pub rust_object_files: Vec, +} + +impl RmetaLink { + pub(crate) fn encode(&self) -> Vec { + let mut encoder = MemEncoder::new(); + self.rust_object_files.encode(&mut encoder); + let mut data = encoder.finish(); + data.extend_from_slice(MAGIC_END_BYTES); + data + } + + pub(crate) fn decode(data: &[u8]) -> Option { + let mut decoder = MemDecoder::new(data, 0).ok()?; + let rust_object_files = Vec::::decode(&mut decoder); + Some(RmetaLink { rust_object_files }) + } +} + +/// Reads the digest from already-mapped archive data. +pub(crate) fn read(archive_data: &[u8], rlib_path: &Path) -> Option { + let archive = ArchiveFile::parse(archive_data).ok()?; + + for entry in archive.members() { + let entry = entry.ok()?; + if entry.name() == FILENAME.as_bytes() { + let data = entry.data(archive_data).ok()?; + let section_data = search_for_section(rlib_path, data, SECTION).ok()?; + return RmetaLink::decode(section_data); + } + } + None +}