Add rlib digest to identify Rust object files

This commit is contained in:
mehdiakiki
2026-04-05 19:59:30 -04:00
parent 338dff3e3a
commit 9606b0bd77
7 changed files with 121 additions and 46 deletions
+4 -2
View File
@@ -24,9 +24,10 @@ use std::path::{Path, PathBuf};
use gccjit::OutputKind;
use object::read::archive::ArchiveFile;
use rustc_codegen_ssa::back::lto::SerializedModule;
use rustc_codegen_ssa::back::rmeta_link;
use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput, SharedEmitter};
use rustc_codegen_ssa::traits::*;
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind, looks_like_rust_object_file};
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind};
use rustc_data_structures::memmap::Mmap;
use rustc_data_structures::profiling::SelfProfilerRef;
use rustc_errors::{DiagCtxt, DiagCtxtHandle};
@@ -63,6 +64,7 @@ fn prepare_lto(each_linked_rlib_for_lto: &[PathBuf], dcx: DiagCtxtHandle<'_>) ->
let archive_data = unsafe {
Mmap::map(File::open(path).expect("couldn't open rlib")).expect("couldn't map rlib")
};
let metadata_link = rmeta_link::read_from_data(&archive_data, path).unwrap();
let archive = ArchiveFile::parse(&*archive_data).expect("wanted an rlib");
let obj_files = archive
.members()
@@ -71,7 +73,7 @@ fn prepare_lto(each_linked_rlib_for_lto: &[PathBuf], dcx: DiagCtxtHandle<'_>) ->
.ok()
.and_then(|c| std::str::from_utf8(c.name()).ok().map(|name| (name.trim(), c)))
})
.filter(|&(name, _)| looks_like_rust_object_file(name));
.filter(|&(name, _)| metadata_link.rust_object_files.iter().any(|f| f == name));
for (name, child) in obj_files {
info!("adding bitcode from {}", name);
let path = tmp_path.path().join(name);
+4 -2
View File
@@ -8,11 +8,12 @@ use std::{io, iter, slice};
use object::read::archive::ArchiveFile;
use object::{Object, ObjectSection};
use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared};
use rustc_codegen_ssa::back::rmeta_link;
use rustc_codegen_ssa::back::write::{
CodegenContext, FatLtoInput, SharedEmitter, TargetMachineFactoryFn, ThinLtoInput,
};
use rustc_codegen_ssa::traits::*;
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind, looks_like_rust_object_file};
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::memmap::Mmap;
use rustc_data_structures::profiling::SelfProfilerRef;
@@ -96,6 +97,7 @@ fn prepare_lto(
.expect("couldn't map rlib")
};
let archive = ArchiveFile::parse(&*archive_data).expect("wanted an rlib");
let metadata_link = rmeta_link::read(&archive, &archive_data, &path).unwrap();
let obj_files = archive
.members()
.filter_map(|child| {
@@ -103,7 +105,7 @@ fn prepare_lto(
.ok()
.and_then(|c| std::str::from_utf8(c.name()).ok().map(|name| (name.trim(), c)))
})
.filter(|&(name, _)| looks_like_rust_object_file(name));
.filter(|&(name, _)| metadata_link.rust_object_files.iter().any(|f| f == name));
for (name, child) in obj_files {
info!("adding bitcode from {}", name);
match get_bitcode_slice_from_object_data(
@@ -21,6 +21,7 @@ use rustc_target::spec::Arch;
use tracing::trace;
use super::metadata::{create_compressed_metadata_file, search_for_section};
use super::rmeta_link::{self, RmetaLink};
use crate::common;
// Public for ArchiveBuilderBuilder::extract_bundled_libs
pub use crate::errors::ExtractBundledLibsError;
@@ -314,7 +315,7 @@ pub trait ArchiveBuilder {
fn add_archive(
&mut self,
archive: &Path,
skip: Box<dyn FnMut(&str) -> bool + 'static>,
skip: Option<Box<dyn FnMut(&str, Option<&RmetaLink>) -> bool + 'static>>,
) -> io::Result<()>;
fn build(self: Box<Self>, output: &Path) -> bool;
@@ -402,7 +403,7 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> {
fn add_archive(
&mut self,
archive_path: &Path,
mut skip: Box<dyn FnMut(&str) -> bool + 'static>,
mut skip: Option<Box<dyn FnMut(&str, Option<&RmetaLink>) -> bool + 'static>>,
) -> io::Result<()> {
let mut archive_path = archive_path.to_path_buf();
if self.sess.target.llvm_target.contains("-apple-macosx")
@@ -418,13 +419,16 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> {
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 metadata_link =
skip.as_ref().and_then(|_| rmeta_link::read(&archive, &archive_map, &archive_path));
let archive_index = self.src_archives.len();
for entry in archive.members() {
let entry = entry.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
let file_name = String::from_utf8(entry.name().to_vec())
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
if !skip(&file_name) {
let drop = skip.as_mut().is_some_and(|f| f(&file_name, metadata_link.as_ref()));
if !drop {
if entry.is_thin() {
let member_path = archive_path.parent().unwrap().join(Path::new(&file_name));
self.entries.push((file_name.into_bytes(), ArchiveEntry::File(member_path)));
+48 -21
View File
@@ -58,12 +58,9 @@ 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,
looks_like_rust_object_file,
};
use crate::{CodegenLintLevels, CompiledModule, CompiledModules, CrateInfo, NativeLib, errors};
pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) {
if let Err(e) = fs::remove_file(path) {
@@ -307,6 +304,27 @@ fn link_rlib<'a>(
) -> Box<dyn ArchiveBuilder + 'a> {
let mut ab = archive_builder_builder.new_archive_builder(sess);
// Pre-compute the list of Rust object filenames and materialize the rmeta-link
// wrapper file before any `add_file` calls. This lets the rmeta-link member be
// placed immediately after metadata in the archive, so consumers can find
// it without iterating every archive member.
let rust_object_files: Vec<String> = compiled_modules
.modules
.iter()
.filter_map(|m| m.object.as_ref())
.map(|obj| obj.file_name().unwrap().to_str().unwrap().to_string())
.collect();
let metadata_link_file = if matches!(flavor, RlibFlavor::Normal) {
let metadata_link = rmeta_link::RmetaLink { rust_object_files };
let metadata_link_data = metadata_link.encode();
let (wrapper, _) =
create_wrapper_file(sess, rmeta_link::SECTION.to_string(), &metadata_link_data);
Some(emit_wrapper_file(sess, &wrapper, tmpdir.as_ref(), rmeta_link::FILENAME))
} else {
None
};
let trailing_metadata = match flavor {
RlibFlavor::Normal => {
let (metadata, metadata_position) =
@@ -320,6 +338,11 @@ fn link_rlib<'a>(
// If it is possible however, placing the metadata object first improves
// performance of getting metadata from rlibs.
ab.add_file(&metadata);
// Place the rmeta-link member immediately after metadata so consumers
// can find it without iterating the whole archive.
if let Some(file) = &metadata_link_file {
ab.add_file(file);
}
None
}
MetadataPosition::Last => Some(metadata),
@@ -383,7 +406,7 @@ fn link_rlib<'a>(
packed_bundled_libs.push(wrapper_file);
} else {
let path = find_native_static_library(lib.name.as_str(), lib.verbatim, sess);
ab.add_archive(&path, Box::new(|_| false)).unwrap_or_else(|error| {
ab.add_archive(&path, None).unwrap_or_else(|error| {
sess.dcx().emit_fatal(errors::AddNativeLibrary { library_path: path, error })
});
}
@@ -400,7 +423,7 @@ fn link_rlib<'a>(
tmpdir.as_ref(),
true,
) {
ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| {
ab.add_archive(&output_path, None).unwrap_or_else(|error| {
sess.dcx()
.emit_fatal(errors::AddNativeLibrary { library_path: output_path, error });
});
@@ -434,6 +457,11 @@ fn link_rlib<'a>(
// Basically, all this means is that this code should not move above the
// code above.
ab.add_file(&trailing_metadata);
// Place the rmeta-link member immediately after metadata so consumers can
// find it without iterating the whole archive.
if let Some(file) = &metadata_link_file {
ab.add_file(file);
}
}
// Add all bundled static native library dependencies.
@@ -488,14 +516,16 @@ fn link_staticlib(
let bundled_libs: FxIndexSet<_> = native_libs.filter_map(|lib| lib.filename).collect();
ab.add_archive(
path,
Box::new(move |fname: &str| {
// Ignore metadata files, no matter the name.
if fname == METADATA_FILENAME {
Some(Box::new(move |fname: &str, metadata_link| {
// Ignore metadata and rmeta-link 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
&& metadata_link.is_some_and(|m| m.rust_object_files.iter().any(|f| f == fname))
{
return true;
}
@@ -505,7 +535,7 @@ fn link_staticlib(
}
false
}),
})),
)
.unwrap();
@@ -516,7 +546,7 @@ fn link_staticlib(
for filename in relevant_libs.iter() {
let joined = tempdir.as_ref().join(filename.as_str());
let path = joined.as_path();
ab.add_archive(path, Box::new(|_| false)).unwrap();
ab.add_archive(path, None).unwrap();
}
all_native_libs.extend(crate_info.native_libraries[&cnum].iter().cloned());
@@ -3146,7 +3176,6 @@ fn add_static_crate(
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('-', "_");
let upstream_rust_objects_already_included =
are_upstream_rust_objects_already_included(sess);
let is_builtins = sess.target.no_builtins || !crate_info.is_no_builtins.contains(&cnum);
@@ -3154,15 +3183,13 @@ fn add_static_crate(
let mut archive = archive_builder_builder.new_archive_builder(sess);
if let Err(error) = archive.add_archive(
cratepath,
Box::new(move |f| {
if f == METADATA_FILENAME {
Some(Box::new(move |f, metadata_link| {
if f == METADATA_FILENAME || f == rmeta_link::FILENAME {
return true;
}
let canonical = f.replace('-', "_");
let is_rust_object =
canonical.starts_with(&canonical_name) && looks_like_rust_object_file(f);
metadata_link.is_some_and(|m| m.rust_object_files.iter().any(|rf| rf == f));
// If we're performing LTO and this is a rust-generated object
// file, then we don't need the object file as it's part of the
@@ -3182,7 +3209,7 @@ fn add_static_crate(
}
false
}),
})),
) {
sess.dcx()
.emit_fatal(errors::RlibArchiveBuildFailure { path: cratepath.clone(), error });
@@ -9,6 +9,7 @@ pub mod link;
pub(crate) mod linker;
pub mod lto;
pub mod metadata;
pub mod rmeta_link;
pub(crate) mod rpath;
pub mod symbol_export;
pub mod write;
@@ -0,0 +1,56 @@
//! Late-metadata archive member that lists which rlib entries are Rust object files,
//! and potentially other data collected and used when building or linking a rlib.
//! See <https://github.com/rust-lang/rust/issues/138243>.
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 struct RmetaLink {
pub rust_object_files: Vec<String>,
}
impl RmetaLink {
pub(crate) fn encode(&self) -> Vec<u8> {
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<RmetaLink> {
let mut decoder = MemDecoder::new(data, 0).ok()?;
let rust_object_files = Vec::<String>::decode(&mut decoder);
Some(RmetaLink { rust_object_files })
}
}
/// Reads the link-time metadata from an already-parsed archive.
pub fn read(archive: &ArchiveFile<'_>, archive_data: &[u8], rlib_path: &Path) -> Option<RmetaLink> {
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
}
/// Like [`read`], but parses the archive from raw bytes.
///
/// Use this when the caller's `ArchiveFile` comes from a different version of the `object` crate.
pub fn read_from_data(archive_data: &[u8], rlib_path: &Path) -> Option<RmetaLink> {
let archive = ArchiveFile::parse(archive_data).ok()?;
read(&archive, archive_data, rlib_path)
}
+1 -18
View File
@@ -35,7 +35,7 @@ use rustc_middle::util::Providers;
use rustc_serialize::opaque::{FileEncoder, MemDecoder};
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use rustc_session::Session;
use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT};
use rustc_session::config::{CrateType, OutputFilenames, OutputType};
use rustc_session::cstore::{self, CrateSource};
use rustc_session::lint::builtin::LINKER_MESSAGES;
use rustc_span::Symbol;
@@ -272,23 +272,6 @@ pub fn provide(providers: &mut Providers) {
providers.queries.global_backend_features = |_tcx: TyCtxt<'_>, ()| vec![];
}
/// Checks if the given filename ends with the `.rcgu.o` extension that `rustc`
/// uses for the object files it generates.
pub fn looks_like_rust_object_file(filename: &str) -> bool {
let path = Path::new(filename);
let ext = path.extension().and_then(|s| s.to_str());
if ext != Some(OutputType::Object.extension()) {
// The file name does not end with ".o", so it can't be an object file.
return false;
}
// Strip the ".o" at the end
let ext2 = path.file_stem().and_then(|s| Path::new(s).extension()).and_then(|s| s.to_str());
// Check if the "inner" extension
ext2 == Some(RUST_CGU_EXT)
}
const RLINK_VERSION: u32 = 1;
const RLINK_MAGIC: &[u8] = b"rustlink";