-
-
Notifications
You must be signed in to change notification settings - Fork 14.8k
Add rlib digest to identify Rust object files #154861
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -58,7 +58,7 @@ 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, rlib_digest, versioned_llvm_target}; | ||
| use crate::base::needs_allocator_shim_for_linking; | ||
| use crate::{ | ||
| CodegenLintLevels, CompiledModule, CompiledModules, CrateInfo, NativeLib, errors, | ||
|
|
@@ -329,8 +329,13 @@ fn link_rlib<'a>( | |
| RlibFlavor::StaticlibBase => None, | ||
| }; | ||
|
|
||
| let mut rust_object_files: Vec<String> = Vec::new(); | ||
|
|
||
| for m in &compiled_modules.modules { | ||
| if let Some(obj) = m.object.as_ref() { | ||
| if let Some(name) = obj.file_name().and_then(|n| n.to_str()) { | ||
| rust_object_files.push(name.to_string()); | ||
| } | ||
| ab.add_file(obj); | ||
| } | ||
|
|
||
|
|
@@ -442,6 +447,17 @@ fn link_rlib<'a>( | |
| ab.add_file(&lib) | ||
| } | ||
|
|
||
| // Add the rlib digest as the very last member. This records which archive | ||
| // members are Rust object files, replacing filename-based heuristics. | ||
| if matches!(flavor, RlibFlavor::Normal) { | ||
| let digest = rlib_digest::RlibDigest { rust_object_files }; | ||
| let digest_data = digest.encode(); | ||
| let (wrapper, _) = | ||
| create_wrapper_file(sess, rlib_digest::SECTION.to_string(), &digest_data); | ||
| let digest_file = emit_wrapper_file(sess, &wrapper, tmpdir.as_ref(), rlib_digest::FILENAME); | ||
| ab.add_file(&digest_file); | ||
| } | ||
|
|
||
| ab | ||
| } | ||
|
|
||
|
|
@@ -486,6 +502,7 @@ fn link_staticlib( | |
| let relevant_libs: FxIndexSet<_> = relevant.filter_map(|lib| lib.filename).collect(); | ||
|
|
||
| let bundled_libs: FxIndexSet<_> = native_libs.filter_map(|lib| lib.filename).collect(); | ||
| let digest = rlib_digest::read(path); | ||
| ab.add_archive( | ||
| path, | ||
| Box::new(move |fname: &str| { | ||
|
|
@@ -494,11 +511,22 @@ fn link_staticlib( | |
| return true; | ||
| } | ||
|
|
||
| // Don't include Rust objects if LTO is enabled | ||
| if lto && looks_like_rust_object_file(fname) { | ||
| // Ignore the rlib digest file. | ||
| if fname == rlib_digest::FILENAME { | ||
| return true; | ||
| } | ||
|
|
||
| // Don't include Rust objects if LTO is enabled. | ||
| if lto { | ||
| let is_rust_object = match &digest { | ||
| Some(d) => d.rust_object_files.iter().any(|f| f == fname), | ||
| None => looks_like_rust_object_file(fname), | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this fallback necessary? Wouldn't all rlibs contain the digest? |
||
| }; | ||
| if is_rust_object { | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| // Skip objects for bundled libs. | ||
| if bundled_libs.contains(&Symbol::intern(fname)) { | ||
| return true; | ||
|
|
@@ -3157,6 +3185,10 @@ fn add_static_crate( | |
| return true; | ||
| } | ||
|
|
||
| if f == rlib_digest::FILENAME { | ||
| return true; | ||
| } | ||
|
|
||
| let canonical = f.replace('-', "_"); | ||
|
|
||
| let is_rust_object = | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| //! Late-metadata archive member that lists which rlib entries are Rust object files, | ||
| //! replacing the `looks_like_rust_object_file` filename heuristic. | ||
| //! See <https://github.com/rust-lang/rust/issues/138243>. | ||
|
|
||
| use std::fs::File; | ||
| use std::path::Path; | ||
|
|
||
| use object::read::archive::ArchiveFile; | ||
| use rustc_data_structures::memmap::Mmap; | ||
| use rustc_serialize::opaque::mem_encoder::MemEncoder; | ||
| use rustc_serialize::opaque::{MAGIC_END_BYTES, MemDecoder}; | ||
| use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; | ||
|
|
||
| use super::metadata::search_for_section; | ||
|
|
||
| pub(crate) const FILENAME: &str = "lib.rlib-digest"; | ||
| pub(crate) const SECTION: &str = ".rlib-digest"; | ||
| const VERSION: u8 = 1; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This version is not necessary. By the time the linker code is reached, we already know that the rustc version as embedded in the crate metadata matches exactly. |
||
|
|
||
| pub(crate) struct RlibDigest { | ||
| pub rust_object_files: Vec<String>, | ||
| } | ||
|
|
||
| impl RlibDigest { | ||
| pub(crate) fn encode(&self) -> Vec<u8> { | ||
| let mut encoder = MemEncoder::new(); | ||
| encoder.emit_u8(VERSION); | ||
| 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<RlibDigest> { | ||
| let mut decoder = MemDecoder::new(data, 0).ok()?; | ||
| let version = decoder.read_u8(); | ||
| if version != VERSION { | ||
| return None; | ||
| } | ||
| let rust_object_files = Vec::<String>::decode(&mut decoder); | ||
| Some(RlibDigest { rust_object_files }) | ||
| } | ||
| } | ||
|
|
||
| /// Returns `None` for rlibs without a digest (for older compilers). | ||
| pub(crate) fn read(rlib_path: &Path) -> Option<RlibDigest> { | ||
| let file = File::open(rlib_path).ok()?; | ||
| let mmap = unsafe { Mmap::map(file).ok()? }; | ||
| let archive = ArchiveFile::parse(&*mmap).ok()?; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ideally this mmap would be combined with the one in |
||
|
|
||
| for entry in archive.members() { | ||
| let entry = entry.ok()?; | ||
| if entry.name() == FILENAME.as_bytes() { | ||
| let data = entry.data(&*mmap).ok()?; | ||
| let section_data = search_for_section(rlib_path, data, SECTION).ok()?; | ||
| return RlibDigest::decode(section_data); | ||
| } | ||
| } | ||
| None | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be added right after the crate metadata instead? That saves time iterating over the rlib at staticlib build time.