mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-06 08:36:52 -04:00
Add rlib digest to identify Rust object files
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user