Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions compiler/rustc_codegen_gcc/src/back/lto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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()
Expand All @@ -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);
Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_codegen_llvm/src/back/lto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -96,14 +97,15 @@ 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| {
child
.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(
Expand Down
10 changes: 7 additions & 3 deletions compiler/rustc_codegen_ssa/src/back/archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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")
Expand All @@ -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)));
Expand Down
69 changes: 48 additions & 21 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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) =
Expand All @@ -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),
Expand Down Expand Up @@ -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 })
});
}
Expand All @@ -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 });
});
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
}

Expand All @@ -505,7 +535,7 @@ fn link_staticlib(
}

false
}),
})),
)
.unwrap();

Expand All @@ -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());
Expand Down Expand Up @@ -3146,23 +3176,20 @@ 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);

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
Expand All @@ -3182,7 +3209,7 @@ fn add_static_crate(
}

false
}),
})),
) {
sess.dcx()
.emit_fatal(errors::RlibArchiveBuildFailure { path: cratepath.clone(), error });
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_ssa/src/back/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
56 changes: 56 additions & 0 deletions compiler/rustc_codegen_ssa/src/back/rmeta_link.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//! Late-metadata archive member that lists which rlib entries are Rust object files,
Comment thread
petrochenkov marked this conversation as resolved.
//! 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)
}
19 changes: 1 addition & 18 deletions compiler/rustc_codegen_ssa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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";

Expand Down
Loading