Skip to content
Open
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
121 changes: 104 additions & 17 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use rustc_lint_defs::builtin::LINKER_INFO;
use rustc_macros::Diagnostic;
use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file};
use rustc_metadata::{
EncodedMetadata, NativeLibSearchFallback, find_native_static_library,
EncodedMetadata, NativeLibSearchFallback, find_bundled_library, find_native_static_library,
walk_native_lib_search_dirs,
};
use rustc_middle::bug;
Expand Down Expand Up @@ -310,8 +310,25 @@ fn link_rlib<'a>(
.map(|obj| obj.file_name().unwrap().to_str().unwrap().to_string())
.collect();

let native_lib_filenames: Vec<Option<Symbol>> = crate_info
.used_libraries
.iter()
.map(|lib| {
find_bundled_library(
lib.name,
Some(lib.verbatim),
lib.kind,
lib.cfg.is_some(),
sess,
&crate_info.crate_types,
)
})
.collect();

let metadata_link_file = if matches!(flavor, RlibFlavor::Normal) {
let metadata_link = rmeta_link::RmetaLink { rust_object_files };
let native_lib_filenames: Vec<Option<String>> =
native_lib_filenames.iter().map(|f| f.map(|s| s.to_string())).collect();
let metadata_link = rmeta_link::RmetaLink { rust_object_files, native_lib_filenames };
let metadata_link_data = metadata_link.encode();
let (wrapper, _) =
create_wrapper_file(sess, rmeta_link::SECTION.to_string(), &metadata_link_data);
Expand Down Expand Up @@ -386,12 +403,12 @@ fn link_rlib<'a>(
// feature then we'll need to figure out how to record what objects were
// loaded from the libraries found here and then encode that into the
// metadata of the rlib we're generating somehow.
for lib in crate_info.used_libraries.iter() {
for (i, lib) in crate_info.used_libraries.iter().enumerate() {
let NativeLibKind::Static { bundle: None | Some(true), .. } = lib.kind else {
continue;
};
if flavor == RlibFlavor::Normal
&& let Some(filename) = lib.filename
&& let Some(filename) = native_lib_filenames[i]
{
let path = find_native_static_library(filename.as_str(), true, sess);
let src = read(path)
Expand Down Expand Up @@ -504,11 +521,31 @@ fn link_staticlib(
let lto = are_upstream_rust_objects_already_included(sess)
&& !ignored_for_lto(sess, crate_info, cnum);

let native_libs = crate_info.native_libraries[&cnum].iter();
let relevant = native_libs.clone().filter(|lib| relevant_lib(sess, lib));
let relevant_libs: FxIndexSet<_> = relevant.filter_map(|lib| lib.filename).collect();
let native_libs = &crate_info.native_libraries[&cnum];
let filenames: Vec<Option<Symbol>> = if crate_may_have_bundled_libs(native_libs) {
rmeta_link::read_from_path(&sess.target, path)
.map(|rl| {
rl.native_lib_filenames
.iter()
.map(|f| f.as_deref().map(Symbol::intern))
.collect()
})
.unwrap_or_default()
} else {
Vec::new()
};
let relevant_libs: FxIndexSet<_> = native_libs
.iter()
.enumerate()
.filter(|(_, lib)| relevant_lib(sess, lib))
.filter_map(|(i, _)| filenames.get(i).copied().flatten())
.collect();

let bundled_libs: FxIndexSet<_> = native_libs.filter_map(|lib| lib.filename).collect();
let bundled_libs: FxIndexSet<_> = native_libs
.iter()
.enumerate()
.filter_map(|(i, _)| filenames.get(i).copied().flatten())
.collect();
ab.add_archive(
path,
Some(Box::new(move |fname: &str, metadata_link| {
Expand Down Expand Up @@ -2866,6 +2903,11 @@ fn collect_natvis_visualizers(
visualizer_paths
}

fn crate_may_have_bundled_libs(libs: &[NativeLib]) -> bool {
libs.iter()
.any(|lib| matches!(lib.kind, NativeLibKind::Static { bundle: Some(true) | None, .. }))
}

fn add_native_libs_from_crate(
cmd: &mut dyn Linker,
sess: &Session,
Expand Down Expand Up @@ -2893,13 +2935,47 @@ fn add_native_libs_from_crate(
.unwrap_or_else(|e| sess.dcx().emit_fatal(e));
}

let native_libs = match cnum {
LOCAL_CRATE => &crate_info.used_libraries,
_ => &crate_info.native_libraries[&cnum],
let (native_libs, filenames): (&Vec<NativeLib>, Vec<Option<Symbol>>) = match cnum {
LOCAL_CRATE => {
let libs = &crate_info.used_libraries;
let filenames = libs
.iter()
.map(|lib| {
find_bundled_library(
lib.name,
Some(lib.verbatim),
lib.kind,
lib.cfg.is_some(),
sess,
&crate_info.crate_types,
)
})
.collect();
(libs, filenames)
}
_ => {
let native_libs = &crate_info.native_libraries[&cnum];
let filenames: Vec<Option<Symbol>> = if crate_may_have_bundled_libs(native_libs) {
crate_info.used_crate_source[&cnum]
.rlib
.as_ref()
.and_then(|rlib_path| rmeta_link::read_from_path(&sess.target, rlib_path))
.map(|rl| {
rl.native_lib_filenames
.iter()
.map(|f| f.as_deref().map(Symbol::intern))
.collect()
})
.unwrap_or_default()
} else {
Vec::new()
};
(native_libs, filenames)
}
};

let mut last = (None, NativeLibKind::Unspecified, false);
for lib in native_libs {
for (i, lib) in native_libs.iter().enumerate() {
if !relevant_lib(sess, lib) {
continue;
}
Expand All @@ -2919,7 +2995,7 @@ fn add_native_libs_from_crate(
let bundle = bundle.unwrap_or(true);
let whole_archive = whole_archive == Some(true);
if bundle && cnum != LOCAL_CRATE {
if let Some(filename) = lib.filename {
if let Some(filename) = filenames.get(i).copied().flatten() {
// If rlib contains native libs as archives, they are unpacked to tmpdir.
let path = tmpdir.join(filename.as_str());
cmd.link_staticlib_by_path(&path, whole_archive);
Expand Down Expand Up @@ -3040,10 +3116,21 @@ fn add_upstream_rust_crates(
match linkage {
Linkage::Static | Linkage::IncludedFromDylib | Linkage::NotLinked => {
if link_static_crate {
bundled_libs = crate_info.native_libraries[&cnum]
.iter()
.filter_map(|lib| lib.filename)
.collect();
if crate_may_have_bundled_libs(&crate_info.native_libraries[&cnum]) {
bundled_libs = crate_info.used_crate_source[&cnum]
.rlib
.as_ref()
.and_then(|rlib_path| {
rmeta_link::read_from_path(&sess.target, rlib_path)
})
.map(|rl| {
rl.native_lib_filenames
.iter()
.filter_map(|f| f.as_deref().map(Symbol::intern))
.collect()
})
.unwrap_or_default();
}
add_static_crate(
cmd,
sess,
Expand Down
40 changes: 38 additions & 2 deletions compiler/rustc_codegen_ssa/src/back/rmeta_link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,34 @@
//! and potentially other data collected and used when building or linking a rlib.
//! 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, Encodable};
use rustc_target::spec::Target;
use tracing::debug;

use super::metadata::search_for_section;
use super::metadata::{get_metadata_xcoff, 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>,
/// Positionally aligned with `native_libraries` in regular metadata: index `i` is the
/// bundled filename for native library `i`, or `None` if that library needs no bundling.
pub native_lib_filenames: Vec<Option<String>>,
Comment thread
petrochenkov marked this conversation as resolved.
}

impl RmetaLink {
pub(crate) fn encode(&self) -> Vec<u8> {
let mut encoder = MemEncoder::new();
self.rust_object_files.encode(&mut encoder);
self.native_lib_filenames.encode(&mut encoder);
let mut data = encoder.finish();
data.extend_from_slice(MAGIC_END_BYTES);
data
Expand All @@ -30,7 +38,8 @@ impl RmetaLink {
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 })
let native_lib_filenames = Vec::<Option<String>>::decode(&mut decoder);
Some(RmetaLink { rust_object_files, native_lib_filenames })
}
}

Expand All @@ -54,3 +63,30 @@ 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)
}

// FIXME: this is mostly a copy-paste of `DefaultMetadataLoader::get_rlib_metadata`.
pub fn read_from_path(target: &Target, path: &Path) -> Option<RmetaLink> {

@petrochenkov petrochenkov Jun 3, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a FIXME comment saying that this is mostly a copypaste of DefaultMetadataLoader::get_rlib_metadata?

View changes since the review

let Ok(file) = File::open(path) else {
debug!("failed to open rlib for rmeta-link: {}", path.display());
return None;
};
let Ok(mmap) = (unsafe { Mmap::map(file) }) else {
debug!("failed to mmap rlib for rmeta-link: {}", path.display());
return None;
};

if target.is_like_aix {
let archive = ArchiveFile::parse(&*mmap).ok()?;
for entry in archive.members() {
let entry = entry.ok()?;
if entry.name() == FILENAME.as_bytes() {
let member_data = entry.data(&*mmap).ok()?;
let section_data = get_metadata_xcoff(path, member_data).ok()?;
return RmetaLink::decode(section_data);
}
}
return None;
}

read_from_data(&mmap, path)
}
2 changes: 0 additions & 2 deletions compiler/rustc_codegen_ssa/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,6 @@ bitflags::bitflags! {
pub struct NativeLib {
pub kind: NativeLibKind,
pub name: Symbol,
pub filename: Option<Symbol>,
pub cfg: Option<CfgEntry>,
pub verbatim: bool,
pub dll_imports: Vec<cstore::DllImport>,
Expand All @@ -218,7 +217,6 @@ impl From<&cstore::NativeLib> for NativeLib {
fn from(lib: &cstore::NativeLib) -> Self {
NativeLib {
kind: lib.kind,
filename: lib.filename,
name: lib.name,
cfg: lib.cfg.clone(),
verbatim: lib.verbatim.unwrap_or(false),
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_metadata/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub mod locator;
pub use creader::{DylibError, load_symbol_from_dylib};
pub use fs::{METADATA_FILENAME, emit_wrapper_file};
pub use native_libs::{
NativeLibSearchFallback, find_native_static_library, try_find_native_dynamic_library,
try_find_native_static_library, walk_native_lib_search_dirs,
NativeLibSearchFallback, find_bundled_library, find_native_static_library,
try_find_native_dynamic_library, try_find_native_static_library, walk_native_lib_search_dirs,
};
pub use rmeta::{EncodedMetadata, METADATA_HEADER, encode_metadata, rendered_const};
24 changes: 4 additions & 20 deletions compiler/rustc_metadata/src/native_libs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,16 +163,16 @@ pub fn find_native_static_library(name: &str, verbatim: bool, sess: &Session) ->
.unwrap_or_else(|| sess.dcx().emit_fatal(errors::MissingNativeLibrary::new(name, verbatim)))
}

fn find_bundled_library(
pub fn find_bundled_library(
name: Symbol,
verbatim: Option<bool>,
kind: NativeLibKind,
has_cfg: bool,
tcx: TyCtxt<'_>,
sess: &Session,
crate_types: &[CrateType],
) -> Option<Symbol> {
let sess = tcx.sess;
if let NativeLibKind::Static { bundle: Some(true) | None, whole_archive, .. } = kind
&& tcx.crate_types().iter().any(|t| matches!(t, &CrateType::Rlib | CrateType::StaticLib))
&& crate_types.iter().any(|t| matches!(t, &CrateType::Rlib | CrateType::StaticLib))
&& (sess.opts.unstable_opts.packed_bundled_libs || has_cfg || whole_archive == Some(true))
{
let verbatim = verbatim.unwrap_or(false);
Expand Down Expand Up @@ -245,16 +245,8 @@ impl<'tcx> Collector<'tcx> {
}
};

let filename = find_bundled_library(
attr.name,
attr.verbatim,
attr.kind,
attr.cfg.is_some(),
self.tcx,
);
self.libs.push(NativeLib {
name: attr.name,
filename,
kind: attr.kind,
cfg: attr.cfg.clone(),
foreign_module: Some(def_id.to_def_id()),
Expand Down Expand Up @@ -334,16 +326,8 @@ impl<'tcx> Collector<'tcx> {
// Add if not found
let new_name: Option<&str> = passed_lib.new_name.as_deref();
let name = Symbol::intern(new_name.unwrap_or(&passed_lib.name));
let filename = find_bundled_library(
name,
passed_lib.verbatim,
passed_lib.kind,
false,
self.tcx,
);
self.libs.push(NativeLib {
name,
filename,
kind: passed_lib.kind,
cfg: None,
foreign_module: None,
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_session/src/cstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,6 @@ pub enum LinkagePreference {
pub struct NativeLib {
pub kind: NativeLibKind,
pub name: Symbol,
/// If packed_bundled_libs enabled, actual filename of library is stored.
pub filename: Option<Symbol>,
pub cfg: Option<CfgEntry>,
pub foreign_module: Option<DefId>,
pub verbatim: Option<bool>,
Expand Down
Loading