Skip to content

Commit 9e680d2

Browse files
authored
Rollup merge of rust-lang#154861 - mehdiakiki:fix/rlib-digest, r=petrochenkov
Add rlib digest to identify Rust object files This adds a metadata entry to `rlib` archives that lists which members are Rust object files instead of relying on the filename heuristic in `looks_like_rust_object.file`. I also added a fallback to the old behavior for `rlibs` built by older compilers. Part of rust-lang#138243.
2 parents 83adf7e + 9606b0b commit 9e680d2

7 files changed

Lines changed: 121 additions & 46 deletions

File tree

compiler/rustc_codegen_gcc/src/back/lto.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ use std::path::{Path, PathBuf};
2424
use gccjit::OutputKind;
2525
use object::read::archive::ArchiveFile;
2626
use rustc_codegen_ssa::back::lto::SerializedModule;
27+
use rustc_codegen_ssa::back::rmeta_link;
2728
use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput, SharedEmitter};
2829
use rustc_codegen_ssa::traits::*;
29-
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind, looks_like_rust_object_file};
30+
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind};
3031
use rustc_data_structures::memmap::Mmap;
3132
use rustc_data_structures::profiling::SelfProfilerRef;
3233
use rustc_errors::{DiagCtxt, DiagCtxtHandle};
@@ -63,6 +64,7 @@ fn prepare_lto(each_linked_rlib_for_lto: &[PathBuf], dcx: DiagCtxtHandle<'_>) ->
6364
let archive_data = unsafe {
6465
Mmap::map(File::open(path).expect("couldn't open rlib")).expect("couldn't map rlib")
6566
};
67+
let metadata_link = rmeta_link::read_from_data(&archive_data, path).unwrap();
6668
let archive = ArchiveFile::parse(&*archive_data).expect("wanted an rlib");
6769
let obj_files = archive
6870
.members()
@@ -71,7 +73,7 @@ fn prepare_lto(each_linked_rlib_for_lto: &[PathBuf], dcx: DiagCtxtHandle<'_>) ->
7173
.ok()
7274
.and_then(|c| std::str::from_utf8(c.name()).ok().map(|name| (name.trim(), c)))
7375
})
74-
.filter(|&(name, _)| looks_like_rust_object_file(name));
76+
.filter(|&(name, _)| metadata_link.rust_object_files.iter().any(|f| f == name));
7577
for (name, child) in obj_files {
7678
info!("adding bitcode from {}", name);
7779
let path = tmp_path.path().join(name);

compiler/rustc_codegen_llvm/src/back/lto.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ use std::{io, iter, slice};
88
use object::read::archive::ArchiveFile;
99
use object::{Object, ObjectSection};
1010
use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared};
11+
use rustc_codegen_ssa::back::rmeta_link;
1112
use rustc_codegen_ssa::back::write::{
1213
CodegenContext, FatLtoInput, SharedEmitter, TargetMachineFactoryFn, ThinLtoInput,
1314
};
1415
use rustc_codegen_ssa::traits::*;
15-
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind, looks_like_rust_object_file};
16+
use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind};
1617
use rustc_data_structures::fx::FxHashMap;
1718
use rustc_data_structures::memmap::Mmap;
1819
use rustc_data_structures::profiling::SelfProfilerRef;
@@ -96,14 +97,15 @@ fn prepare_lto(
9697
.expect("couldn't map rlib")
9798
};
9899
let archive = ArchiveFile::parse(&*archive_data).expect("wanted an rlib");
100+
let metadata_link = rmeta_link::read(&archive, &archive_data, &path).unwrap();
99101
let obj_files = archive
100102
.members()
101103
.filter_map(|child| {
102104
child
103105
.ok()
104106
.and_then(|c| std::str::from_utf8(c.name()).ok().map(|name| (name.trim(), c)))
105107
})
106-
.filter(|&(name, _)| looks_like_rust_object_file(name));
108+
.filter(|&(name, _)| metadata_link.rust_object_files.iter().any(|f| f == name));
107109
for (name, child) in obj_files {
108110
info!("adding bitcode from {}", name);
109111
match get_bitcode_slice_from_object_data(

compiler/rustc_codegen_ssa/src/back/archive.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use rustc_target::spec::Arch;
2121
use tracing::trace;
2222

2323
use super::metadata::{create_compressed_metadata_file, search_for_section};
24+
use super::rmeta_link::{self, RmetaLink};
2425
use crate::common;
2526
// Public for ArchiveBuilderBuilder::extract_bundled_libs
2627
pub use crate::errors::ExtractBundledLibsError;
@@ -313,7 +314,7 @@ pub trait ArchiveBuilder {
313314
fn add_archive(
314315
&mut self,
315316
archive: &Path,
316-
skip: Box<dyn FnMut(&str) -> bool + 'static>,
317+
skip: Option<Box<dyn FnMut(&str, Option<&RmetaLink>) -> bool + 'static>>,
317318
) -> io::Result<()>;
318319

319320
fn build(self: Box<Self>, output: &Path) -> bool;
@@ -445,7 +446,7 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> {
445446
fn add_archive(
446447
&mut self,
447448
archive_path: &Path,
448-
mut skip: Box<dyn FnMut(&str) -> bool + 'static>,
449+
mut skip: Option<Box<dyn FnMut(&str, Option<&RmetaLink>) -> bool + 'static>>,
449450
) -> io::Result<()> {
450451
let mut archive_path = archive_path.to_path_buf();
451452
if self.sess.target.llvm_target.contains("-apple-macosx")
@@ -461,6 +462,8 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> {
461462
let archive_map = unsafe { Mmap::map(File::open(&archive_path)?)? };
462463
let archive = ArchiveFile::parse(&*archive_map)
463464
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
465+
let metadata_link =
466+
skip.as_ref().and_then(|_| rmeta_link::read(&archive, &archive_map, &archive_path));
464467
let archive_index = self.src_archives.len();
465468

466469
if let Some(expected_kind) =
@@ -480,7 +483,8 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> {
480483
let entry = entry.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
481484
let file_name = String::from_utf8(entry.name().to_vec())
482485
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
483-
if !skip(&file_name) {
486+
let drop = skip.as_mut().is_some_and(|f| f(&file_name, metadata_link.as_ref()));
487+
if !drop {
484488
if entry.is_thin() {
485489
let member_path = archive_path.parent().unwrap().join(Path::new(&file_name));
486490
self.entries.push((file_name.into_bytes(), ArchiveEntry::File(member_path)));

compiler/rustc_codegen_ssa/src/back/link.rs

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,9 @@ use super::command::Command;
5858
use super::linker::{self, Linker};
5959
use super::metadata::{MetadataPosition, create_wrapper_file};
6060
use super::rpath::{self, RPathConfig};
61-
use super::{apple, versioned_llvm_target};
61+
use super::{apple, rmeta_link, versioned_llvm_target};
6262
use crate::base::needs_allocator_shim_for_linking;
63-
use crate::{
64-
CodegenLintLevels, CompiledModule, CompiledModules, CrateInfo, NativeLib, errors,
65-
looks_like_rust_object_file,
66-
};
63+
use crate::{CodegenLintLevels, CompiledModule, CompiledModules, CrateInfo, NativeLib, errors};
6764

6865
pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) {
6966
if let Err(e) = fs::remove_file(path) {
@@ -307,6 +304,27 @@ fn link_rlib<'a>(
307304
) -> Box<dyn ArchiveBuilder + 'a> {
308305
let mut ab = archive_builder_builder.new_archive_builder(sess);
309306

307+
// Pre-compute the list of Rust object filenames and materialize the rmeta-link
308+
// wrapper file before any `add_file` calls. This lets the rmeta-link member be
309+
// placed immediately after metadata in the archive, so consumers can find
310+
// it without iterating every archive member.
311+
let rust_object_files: Vec<String> = compiled_modules
312+
.modules
313+
.iter()
314+
.filter_map(|m| m.object.as_ref())
315+
.map(|obj| obj.file_name().unwrap().to_str().unwrap().to_string())
316+
.collect();
317+
318+
let metadata_link_file = if matches!(flavor, RlibFlavor::Normal) {
319+
let metadata_link = rmeta_link::RmetaLink { rust_object_files };
320+
let metadata_link_data = metadata_link.encode();
321+
let (wrapper, _) =
322+
create_wrapper_file(sess, rmeta_link::SECTION.to_string(), &metadata_link_data);
323+
Some(emit_wrapper_file(sess, &wrapper, tmpdir.as_ref(), rmeta_link::FILENAME))
324+
} else {
325+
None
326+
};
327+
310328
let trailing_metadata = match flavor {
311329
RlibFlavor::Normal => {
312330
let (metadata, metadata_position) =
@@ -320,6 +338,11 @@ fn link_rlib<'a>(
320338
// If it is possible however, placing the metadata object first improves
321339
// performance of getting metadata from rlibs.
322340
ab.add_file(&metadata);
341+
// Place the rmeta-link member immediately after metadata so consumers
342+
// can find it without iterating the whole archive.
343+
if let Some(file) = &metadata_link_file {
344+
ab.add_file(file);
345+
}
323346
None
324347
}
325348
MetadataPosition::Last => Some(metadata),
@@ -383,7 +406,7 @@ fn link_rlib<'a>(
383406
packed_bundled_libs.push(wrapper_file);
384407
} else {
385408
let path = find_native_static_library(lib.name.as_str(), lib.verbatim, sess);
386-
ab.add_archive(&path, Box::new(|_| false)).unwrap_or_else(|error| {
409+
ab.add_archive(&path, None).unwrap_or_else(|error| {
387410
sess.dcx().emit_fatal(errors::AddNativeLibrary { library_path: path, error })
388411
});
389412
}
@@ -400,7 +423,7 @@ fn link_rlib<'a>(
400423
tmpdir.as_ref(),
401424
true,
402425
) {
403-
ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| {
426+
ab.add_archive(&output_path, None).unwrap_or_else(|error| {
404427
sess.dcx()
405428
.emit_fatal(errors::AddNativeLibrary { library_path: output_path, error });
406429
});
@@ -434,6 +457,11 @@ fn link_rlib<'a>(
434457
// Basically, all this means is that this code should not move above the
435458
// code above.
436459
ab.add_file(&trailing_metadata);
460+
// Place the rmeta-link member immediately after metadata so consumers can
461+
// find it without iterating the whole archive.
462+
if let Some(file) = &metadata_link_file {
463+
ab.add_file(file);
464+
}
437465
}
438466

439467
// Add all bundled static native library dependencies.
@@ -488,14 +516,16 @@ fn link_staticlib(
488516
let bundled_libs: FxIndexSet<_> = native_libs.filter_map(|lib| lib.filename).collect();
489517
ab.add_archive(
490518
path,
491-
Box::new(move |fname: &str| {
492-
// Ignore metadata files, no matter the name.
493-
if fname == METADATA_FILENAME {
519+
Some(Box::new(move |fname: &str, metadata_link| {
520+
// Ignore metadata and rmeta-link files.
521+
if fname == METADATA_FILENAME || fname == rmeta_link::FILENAME {
494522
return true;
495523
}
496524

497-
// Don't include Rust objects if LTO is enabled
498-
if lto && looks_like_rust_object_file(fname) {
525+
// Don't include Rust objects if LTO is enabled.
526+
if lto
527+
&& metadata_link.is_some_and(|m| m.rust_object_files.iter().any(|f| f == fname))
528+
{
499529
return true;
500530
}
501531

@@ -505,7 +535,7 @@ fn link_staticlib(
505535
}
506536

507537
false
508-
}),
538+
})),
509539
)
510540
.unwrap();
511541

@@ -516,7 +546,7 @@ fn link_staticlib(
516546
for filename in relevant_libs.iter() {
517547
let joined = tempdir.as_ref().join(filename.as_str());
518548
let path = joined.as_path();
519-
ab.add_archive(path, Box::new(|_| false)).unwrap();
549+
ab.add_archive(path, None).unwrap();
520550
}
521551

522552
all_native_libs.extend(crate_info.native_libraries[&cnum].iter().cloned());
@@ -3167,23 +3197,20 @@ fn add_static_crate(
31673197
let bundled_lib_file_names = bundled_lib_file_names.clone();
31683198

31693199
sess.prof.generic_activity_with_arg("link_altering_rlib", name).run(|| {
3170-
let canonical_name = name.replace('-', "_");
31713200
let upstream_rust_objects_already_included =
31723201
are_upstream_rust_objects_already_included(sess);
31733202
let is_builtins = sess.target.no_builtins || !crate_info.is_no_builtins.contains(&cnum);
31743203

31753204
let mut archive = archive_builder_builder.new_archive_builder(sess);
31763205
if let Err(error) = archive.add_archive(
31773206
cratepath,
3178-
Box::new(move |f| {
3179-
if f == METADATA_FILENAME {
3207+
Some(Box::new(move |f, metadata_link| {
3208+
if f == METADATA_FILENAME || f == rmeta_link::FILENAME {
31803209
return true;
31813210
}
31823211

3183-
let canonical = f.replace('-', "_");
3184-
31853212
let is_rust_object =
3186-
canonical.starts_with(&canonical_name) && looks_like_rust_object_file(f);
3213+
metadata_link.is_some_and(|m| m.rust_object_files.iter().any(|rf| rf == f));
31873214

31883215
// If we're performing LTO and this is a rust-generated object
31893216
// file, then we don't need the object file as it's part of the
@@ -3203,7 +3230,7 @@ fn add_static_crate(
32033230
}
32043231

32053232
false
3206-
}),
3233+
})),
32073234
) {
32083235
sess.dcx()
32093236
.emit_fatal(errors::RlibArchiveBuildFailure { path: cratepath.clone(), error });

compiler/rustc_codegen_ssa/src/back/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub mod link;
99
pub(crate) mod linker;
1010
pub mod lto;
1111
pub mod metadata;
12+
pub mod rmeta_link;
1213
pub(crate) mod rpath;
1314
pub mod symbol_export;
1415
pub mod write;
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//! Late-metadata archive member that lists which rlib entries are Rust object files,
2+
//! and potentially other data collected and used when building or linking a rlib.
3+
//! See <https://github.com/rust-lang/rust/issues/138243>.
4+
5+
use std::path::Path;
6+
7+
use object::read::archive::ArchiveFile;
8+
use rustc_serialize::opaque::mem_encoder::MemEncoder;
9+
use rustc_serialize::opaque::{MAGIC_END_BYTES, MemDecoder};
10+
use rustc_serialize::{Decodable, Encodable};
11+
12+
use super::metadata::search_for_section;
13+
14+
pub(crate) const FILENAME: &str = "lib.rmeta-link";
15+
pub(crate) const SECTION: &str = ".rmeta-link";
16+
17+
pub struct RmetaLink {
18+
pub rust_object_files: Vec<String>,
19+
}
20+
21+
impl RmetaLink {
22+
pub(crate) fn encode(&self) -> Vec<u8> {
23+
let mut encoder = MemEncoder::new();
24+
self.rust_object_files.encode(&mut encoder);
25+
let mut data = encoder.finish();
26+
data.extend_from_slice(MAGIC_END_BYTES);
27+
data
28+
}
29+
30+
pub(crate) fn decode(data: &[u8]) -> Option<RmetaLink> {
31+
let mut decoder = MemDecoder::new(data, 0).ok()?;
32+
let rust_object_files = Vec::<String>::decode(&mut decoder);
33+
Some(RmetaLink { rust_object_files })
34+
}
35+
}
36+
37+
/// Reads the link-time metadata from an already-parsed archive.
38+
pub fn read(archive: &ArchiveFile<'_>, archive_data: &[u8], rlib_path: &Path) -> Option<RmetaLink> {
39+
for entry in archive.members() {
40+
let entry = entry.ok()?;
41+
if entry.name() == FILENAME.as_bytes() {
42+
let data = entry.data(archive_data).ok()?;
43+
let section_data = search_for_section(rlib_path, data, SECTION).ok()?;
44+
return RmetaLink::decode(section_data);
45+
}
46+
}
47+
None
48+
}
49+
50+
/// Like [`read`], but parses the archive from raw bytes.
51+
///
52+
/// Use this when the caller's `ArchiveFile` comes from a different version of the `object` crate.
53+
pub fn read_from_data(archive_data: &[u8], rlib_path: &Path) -> Option<RmetaLink> {
54+
let archive = ArchiveFile::parse(archive_data).ok()?;
55+
read(&archive, archive_data, rlib_path)
56+
}

compiler/rustc_codegen_ssa/src/lib.rs

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use rustc_middle::util::Providers;
3535
use rustc_serialize::opaque::{FileEncoder, MemDecoder};
3636
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
3737
use rustc_session::Session;
38-
use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT};
38+
use rustc_session::config::{CrateType, OutputFilenames, OutputType};
3939
use rustc_session::cstore::{self, CrateSource};
4040
use rustc_session::lint::builtin::LINKER_MESSAGES;
4141
use rustc_span::Symbol;
@@ -272,23 +272,6 @@ pub fn provide(providers: &mut Providers) {
272272
providers.queries.global_backend_features = |_tcx: TyCtxt<'_>, ()| vec![];
273273
}
274274

275-
/// Checks if the given filename ends with the `.rcgu.o` extension that `rustc`
276-
/// uses for the object files it generates.
277-
pub fn looks_like_rust_object_file(filename: &str) -> bool {
278-
let path = Path::new(filename);
279-
let ext = path.extension().and_then(|s| s.to_str());
280-
if ext != Some(OutputType::Object.extension()) {
281-
// The file name does not end with ".o", so it can't be an object file.
282-
return false;
283-
}
284-
285-
// Strip the ".o" at the end
286-
let ext2 = path.file_stem().and_then(|s| Path::new(s).extension()).and_then(|s| s.to_str());
287-
288-
// Check if the "inner" extension
289-
ext2 == Some(RUST_CGU_EXT)
290-
}
291-
292275
const RLINK_VERSION: u32 = 1;
293276
const RLINK_MAGIC: &[u8] = b"rustlink";
294277

0 commit comments

Comments
 (0)