Skip to content

Commit 91fcbbb

Browse files
committed
Auto merge of #154861 - mehdiakiki:fix/rlib-digest, r=<try>
Add rlib digest to identify Rust object files
2 parents 818811b + d4c808c commit 91fcbbb

7 files changed

Lines changed: 118 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 digest = 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, _)| digest.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 digest = 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, _)| digest.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 digest =
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, digest.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: 45 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,26 @@ 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 rlib
308+
// digest wrapper file before any `add_file` calls. This lets the digest 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 digest_file = if matches!(flavor, RlibFlavor::Normal) {
319+
let digest = rmeta_link::RmetaLink { rust_object_files };
320+
let digest_data = digest.encode();
321+
let (wrapper, _) = create_wrapper_file(sess, rmeta_link::SECTION.to_string(), &digest_data);
322+
Some(emit_wrapper_file(sess, &wrapper, tmpdir.as_ref(), rmeta_link::FILENAME))
323+
} else {
324+
None
325+
};
326+
310327
let trailing_metadata = match flavor {
311328
RlibFlavor::Normal => {
312329
let (metadata, metadata_position) =
@@ -320,6 +337,11 @@ fn link_rlib<'a>(
320337
// If it is possible however, placing the metadata object first improves
321338
// performance of getting metadata from rlibs.
322339
ab.add_file(&metadata);
340+
// Place the rlib digest immediately after metadata so consumers
341+
// can find it without iterating the whole archive.
342+
if let Some(df) = &digest_file {
343+
ab.add_file(df);
344+
}
323345
None
324346
}
325347
MetadataPosition::Last => Some(metadata),
@@ -383,7 +405,7 @@ fn link_rlib<'a>(
383405
packed_bundled_libs.push(wrapper_file);
384406
} else {
385407
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| {
408+
ab.add_archive(&path, None).unwrap_or_else(|error| {
387409
sess.dcx().emit_fatal(errors::AddNativeLibrary { library_path: path, error })
388410
});
389411
}
@@ -400,7 +422,7 @@ fn link_rlib<'a>(
400422
tmpdir.as_ref(),
401423
true,
402424
) {
403-
ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| {
425+
ab.add_archive(&output_path, None).unwrap_or_else(|error| {
404426
sess.dcx()
405427
.emit_fatal(errors::AddNativeLibrary { library_path: output_path, error });
406428
});
@@ -434,6 +456,11 @@ fn link_rlib<'a>(
434456
// Basically, all this means is that this code should not move above the
435457
// code above.
436458
ab.add_file(&trailing_metadata);
459+
// Place the rlib digest immediately after metadata so consumers can find
460+
// it without iterating the whole archive.
461+
if let Some(df) = &digest_file {
462+
ab.add_file(df);
463+
}
437464
}
438465

439466
// Add all bundled static native library dependencies.
@@ -488,14 +515,14 @@ fn link_staticlib(
488515
let bundled_libs: FxIndexSet<_> = native_libs.filter_map(|lib| lib.filename).collect();
489516
ab.add_archive(
490517
path,
491-
Box::new(move |fname: &str| {
492-
// Ignore metadata files, no matter the name.
493-
if fname == METADATA_FILENAME {
518+
Some(Box::new(move |fname: &str, digest| {
519+
// Ignore metadata and rlib digest files.
520+
if fname == METADATA_FILENAME || fname == rmeta_link::FILENAME {
494521
return true;
495522
}
496523

497-
// Don't include Rust objects if LTO is enabled
498-
if lto && looks_like_rust_object_file(fname) {
524+
// Don't include Rust objects if LTO is enabled.
525+
if lto && digest.is_some_and(|d| d.rust_object_files.iter().any(|f| f == fname)) {
499526
return true;
500527
}
501528

@@ -505,7 +532,7 @@ fn link_staticlib(
505532
}
506533

507534
false
508-
}),
535+
})),
509536
)
510537
.unwrap();
511538

@@ -516,7 +543,7 @@ fn link_staticlib(
516543
for filename in relevant_libs.iter() {
517544
let joined = tempdir.as_ref().join(filename.as_str());
518545
let path = joined.as_path();
519-
ab.add_archive(path, Box::new(|_| false)).unwrap();
546+
ab.add_archive(path, None).unwrap();
520547
}
521548

522549
all_native_libs.extend(crate_info.native_libraries[&cnum].iter().cloned());
@@ -3167,23 +3194,20 @@ fn add_static_crate(
31673194
let bundled_lib_file_names = bundled_lib_file_names.clone();
31683195

31693196
sess.prof.generic_activity_with_arg("link_altering_rlib", name).run(|| {
3170-
let canonical_name = name.replace('-', "_");
31713197
let upstream_rust_objects_already_included =
31723198
are_upstream_rust_objects_already_included(sess);
31733199
let is_builtins = sess.target.no_builtins || !crate_info.is_no_builtins.contains(&cnum);
31743200

31753201
let mut archive = archive_builder_builder.new_archive_builder(sess);
31763202
if let Err(error) = archive.add_archive(
31773203
cratepath,
3178-
Box::new(move |f| {
3179-
if f == METADATA_FILENAME {
3204+
Some(Box::new(move |f, digest| {
3205+
if f == METADATA_FILENAME || f == rmeta_link::FILENAME {
31803206
return true;
31813207
}
31823208

3183-
let canonical = f.replace('-', "_");
3184-
31853209
let is_rust_object =
3186-
canonical.starts_with(&canonical_name) && looks_like_rust_object_file(f);
3210+
digest.is_some_and(|d| d.rust_object_files.iter().any(|rf| rf == f));
31873211

31883212
// If we're performing LTO and this is a rust-generated object
31893213
// file, then we don't need the object file as it's part of the
@@ -3203,7 +3227,7 @@ fn add_static_crate(
32033227
}
32043228

32053229
false
3206-
}),
3230+
})),
32073231
) {
32083232
sess.dcx()
32093233
.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 digest 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)