Skip to content

Commit 6bb93a4

Browse files
authored
Rollup merge of rust-lang#100101 - BelovDV:issue-99429, r=petrochenkov
change rlib format to distinguish native dependencies Another one method to solve problem mentioned in rust-lang#99429. Changed .rlib format, it contains all bundled native libraries as archieves. At link time rlib is unpacked and native dependencies linked separately. New behavior hidden under separate_native_rlib_dependencies flag.
2 parents 9353538 + 5bf9c7b commit 6bb93a4

File tree

21 files changed

+316
-49
lines changed

21 files changed

+316
-49
lines changed

compiler/rustc_codegen_ssa/src/back/archive.rs

+37-35
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,16 @@
1+
use rustc_data_structures::fx::FxHashSet;
2+
use rustc_data_structures::memmap::Mmap;
13
use rustc_session::cstore::DllImport;
24
use rustc_session::Session;
5+
use rustc_span::symbol::Symbol;
36

7+
use object::read::archive::ArchiveFile;
8+
9+
use std::fmt::Display;
10+
use std::fs::File;
411
use std::io;
512
use std::path::{Path, PathBuf};
613

7-
pub(super) fn find_library(
8-
name: &str,
9-
verbatim: bool,
10-
search_paths: &[PathBuf],
11-
sess: &Session,
12-
) -> PathBuf {
13-
// On Windows, static libraries sometimes show up as libfoo.a and other
14-
// times show up as foo.lib
15-
let oslibname = if verbatim {
16-
name.to_string()
17-
} else {
18-
format!("{}{}{}", sess.target.staticlib_prefix, name, sess.target.staticlib_suffix)
19-
};
20-
let unixlibname = format!("lib{}.a", name);
21-
22-
for path in search_paths {
23-
debug!("looking for {} inside {:?}", name, path);
24-
let test = path.join(&oslibname);
25-
if test.exists() {
26-
return test;
27-
}
28-
if oslibname != unixlibname {
29-
let test = path.join(&unixlibname);
30-
if test.exists() {
31-
return test;
32-
}
33-
}
34-
}
35-
sess.fatal(&format!(
36-
"could not find native static library `{}`, \
37-
perhaps an -L flag is missing?",
38-
name
39-
));
40-
}
41-
4214
pub trait ArchiveBuilderBuilder {
4315
fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder<'a> + 'a>;
4416

@@ -54,6 +26,36 @@ pub trait ArchiveBuilderBuilder {
5426
dll_imports: &[DllImport],
5527
tmpdir: &Path,
5628
) -> PathBuf;
29+
30+
fn extract_bundled_libs(
31+
&self,
32+
rlib: &Path,
33+
outdir: &Path,
34+
bundled_lib_file_names: &FxHashSet<Symbol>,
35+
) -> Result<(), String> {
36+
let message = |msg: &str, e: &dyn Display| format!("{} '{}': {}", msg, &rlib.display(), e);
37+
let archive_map = unsafe {
38+
Mmap::map(File::open(rlib).map_err(|e| message("failed to open file", &e))?)
39+
.map_err(|e| message("failed to mmap file", &e))?
40+
};
41+
let archive = ArchiveFile::parse(&*archive_map)
42+
.map_err(|e| message("failed to parse archive", &e))?;
43+
44+
for entry in archive.members() {
45+
let entry = entry.map_err(|e| message("failed to read entry", &e))?;
46+
let data = entry
47+
.data(&*archive_map)
48+
.map_err(|e| message("failed to get data from archive member", &e))?;
49+
let name = std::str::from_utf8(entry.name())
50+
.map_err(|e| message("failed to convert name", &e))?;
51+
if !bundled_lib_file_names.contains(&Symbol::intern(name)) {
52+
continue; // We need to extract only native libraries.
53+
}
54+
std::fs::write(&outdir.join(&name), data)
55+
.map_err(|e| message("failed to write file", &e))?;
56+
}
57+
Ok(())
58+
}
5759
}
5860

5961
pub trait ArchiveBuilder<'a> {

compiler/rustc_codegen_ssa/src/back/link.rs

+88-9
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use rustc_data_structures::temp_dir::MaybeTempDir;
66
use rustc_errors::{ErrorGuaranteed, Handler};
77
use rustc_fs_util::fix_windows_verbatim_for_gcc;
88
use rustc_hir::def_id::CrateNum;
9+
use rustc_metadata::find_native_static_library;
910
use rustc_metadata::fs::{emit_metadata, METADATA_FILENAME};
1011
use rustc_middle::middle::dependency_format::Linkage;
1112
use rustc_middle::middle::exported_symbols::SymbolExportKind;
@@ -24,7 +25,7 @@ use rustc_target::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault};
2425
use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor, SplitDebuginfo};
2526
use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, SanitizerSet, Target};
2627

27-
use super::archive::{find_library, ArchiveBuilder, ArchiveBuilderBuilder};
28+
use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
2829
use super::command::Command;
2930
use super::linker::{self, Linker};
3031
use super::metadata::{create_rmeta_file, MetadataPosition};
@@ -307,6 +308,9 @@ fn link_rlib<'a>(
307308
}
308309
}
309310

311+
// Used if packed_bundled_libs flag enabled.
312+
let mut packed_bundled_libs = Vec::new();
313+
310314
// Note that in this loop we are ignoring the value of `lib.cfg`. That is,
311315
// we may not be configured to actually include a static library if we're
312316
// adding it here. That's because later when we consume this rlib we'll
@@ -325,6 +329,8 @@ fn link_rlib<'a>(
325329
// metadata of the rlib we're generating somehow.
326330
for lib in codegen_results.crate_info.used_libraries.iter() {
327331
match lib.kind {
332+
NativeLibKind::Static { bundle: None | Some(true), whole_archive: Some(true) }
333+
if flavor == RlibFlavor::Normal && sess.opts.unstable_opts.packed_bundled_libs => {}
328334
NativeLibKind::Static { bundle: None | Some(true), whole_archive: Some(true) }
329335
if flavor == RlibFlavor::Normal =>
330336
{
@@ -348,7 +354,16 @@ fn link_rlib<'a>(
348354
}
349355
if let Some(name) = lib.name {
350356
let location =
351-
find_library(name.as_str(), lib.verbatim.unwrap_or(false), &lib_search_paths, sess);
357+
find_native_static_library(name.as_str(), lib.verbatim, &lib_search_paths, sess);
358+
if sess.opts.unstable_opts.packed_bundled_libs && flavor == RlibFlavor::Normal {
359+
packed_bundled_libs.push(find_native_static_library(
360+
lib.filename.unwrap().as_str(),
361+
Some(true),
362+
&lib_search_paths,
363+
sess,
364+
));
365+
continue;
366+
}
352367
ab.add_archive(&location, Box::new(|_| false)).unwrap_or_else(|e| {
353368
sess.fatal(&format!(
354369
"failed to add native library {}: {}",
@@ -403,6 +418,12 @@ fn link_rlib<'a>(
403418
ab.add_file(&trailing_metadata);
404419
}
405420

421+
// Add all bundled static native library dependencies.
422+
// Archives added to the end of .rlib archive, see comment above for the reason.
423+
for lib in packed_bundled_libs {
424+
ab.add_file(&lib)
425+
}
426+
406427
return Ok(ab);
407428
}
408429

@@ -2398,7 +2419,15 @@ fn add_upstream_rust_crates<'a>(
23982419
let src = &codegen_results.crate_info.used_crate_source[&cnum];
23992420
match data[cnum.as_usize() - 1] {
24002421
_ if codegen_results.crate_info.profiler_runtime == Some(cnum) => {
2401-
add_static_crate(cmd, sess, archive_builder_builder, codegen_results, tmpdir, cnum);
2422+
add_static_crate(
2423+
cmd,
2424+
sess,
2425+
archive_builder_builder,
2426+
codegen_results,
2427+
tmpdir,
2428+
cnum,
2429+
&Default::default(),
2430+
);
24022431
}
24032432
// compiler-builtins are always placed last to ensure that they're
24042433
// linked correctly.
@@ -2408,7 +2437,23 @@ fn add_upstream_rust_crates<'a>(
24082437
}
24092438
Linkage::NotLinked | Linkage::IncludedFromDylib => {}
24102439
Linkage::Static => {
2411-
add_static_crate(cmd, sess, archive_builder_builder, codegen_results, tmpdir, cnum);
2440+
let bundled_libs = if sess.opts.unstable_opts.packed_bundled_libs {
2441+
codegen_results.crate_info.native_libraries[&cnum]
2442+
.iter()
2443+
.filter_map(|lib| lib.filename)
2444+
.collect::<FxHashSet<_>>()
2445+
} else {
2446+
Default::default()
2447+
};
2448+
add_static_crate(
2449+
cmd,
2450+
sess,
2451+
archive_builder_builder,
2452+
codegen_results,
2453+
tmpdir,
2454+
cnum,
2455+
&bundled_libs,
2456+
);
24122457

24132458
// Link static native libs with "-bundle" modifier only if the crate they originate from
24142459
// is being linked statically to the current crate. If it's linked dynamically
@@ -2419,6 +2464,14 @@ fn add_upstream_rust_crates<'a>(
24192464
// external build system already has the native dependencies defined, and it
24202465
// will provide them to the linker itself.
24212466
if sess.opts.unstable_opts.link_native_libraries {
2467+
if sess.opts.unstable_opts.packed_bundled_libs {
2468+
// If rlib contains native libs as archives, unpack them to tmpdir.
2469+
let rlib = &src.rlib.as_ref().unwrap().0;
2470+
archive_builder_builder
2471+
.extract_bundled_libs(rlib, tmpdir, &bundled_libs)
2472+
.unwrap_or_else(|e| sess.fatal(e));
2473+
}
2474+
24222475
let mut last = (None, NativeLibKind::Unspecified, None);
24232476
for lib in &codegen_results.crate_info.native_libraries[&cnum] {
24242477
let Some(name) = lib.name else {
@@ -2460,10 +2513,17 @@ fn add_upstream_rust_crates<'a>(
24602513
| NativeLibKind::Framework { .. }
24612514
| NativeLibKind::Unspecified
24622515
| NativeLibKind::RawDylib => {}
2463-
NativeLibKind::Static {
2464-
bundle: Some(true) | None,
2465-
whole_archive: _,
2466-
} => {}
2516+
NativeLibKind::Static { bundle: Some(true) | None, whole_archive } => {
2517+
if sess.opts.unstable_opts.packed_bundled_libs {
2518+
// If rlib contains native libs as archives, they are unpacked to tmpdir.
2519+
let path = tmpdir.join(lib.filename.unwrap().as_str());
2520+
if whole_archive == Some(true) {
2521+
cmd.link_whole_rlib(&path);
2522+
} else {
2523+
cmd.link_rlib(&path);
2524+
}
2525+
}
2526+
}
24672527
}
24682528
}
24692529
}
@@ -2482,7 +2542,15 @@ fn add_upstream_rust_crates<'a>(
24822542
// was already "included" in a dylib (e.g., `libstd` when `-C prefer-dynamic`
24832543
// is used)
24842544
if let Some(cnum) = compiler_builtins {
2485-
add_static_crate(cmd, sess, archive_builder_builder, codegen_results, tmpdir, cnum);
2545+
add_static_crate(
2546+
cmd,
2547+
sess,
2548+
archive_builder_builder,
2549+
codegen_results,
2550+
tmpdir,
2551+
cnum,
2552+
&Default::default(),
2553+
);
24862554
}
24872555

24882556
// Converts a library file-stem into a cc -l argument
@@ -2515,6 +2583,7 @@ fn add_upstream_rust_crates<'a>(
25152583
codegen_results: &CodegenResults,
25162584
tmpdir: &Path,
25172585
cnum: CrateNum,
2586+
bundled_lib_file_names: &FxHashSet<Symbol>,
25182587
) {
25192588
let src = &codegen_results.crate_info.used_crate_source[&cnum];
25202589
let cratepath = &src.rlib.as_ref().unwrap().0;
@@ -2543,6 +2612,7 @@ fn add_upstream_rust_crates<'a>(
25432612
let dst = tmpdir.join(cratepath.file_name().unwrap());
25442613
let name = cratepath.file_name().unwrap().to_str().unwrap();
25452614
let name = &name[3..name.len() - 5]; // chop off lib/.rlib
2615+
let bundled_lib_file_names = bundled_lib_file_names.clone();
25462616

25472617
sess.prof.generic_activity_with_arg("link_altering_rlib", name).run(|| {
25482618
let canonical_name = name.replace('-', "_");
@@ -2576,6 +2646,15 @@ fn add_upstream_rust_crates<'a>(
25762646
let skip_because_lto =
25772647
upstream_rust_objects_already_included && is_rust_object && is_builtins;
25782648

2649+
// We skip native libraries because:
2650+
// 1. This native libraries won't be used from the generated rlib,
2651+
// so we can throw them away to avoid the copying work.
2652+
// 2. We can't allow it to be a single remaining entry in archive
2653+
// as some linkers may complain on that.
2654+
if bundled_lib_file_names.contains(&Symbol::intern(f)) {
2655+
return true;
2656+
}
2657+
25792658
if skip_because_cfg_say_so || skip_because_lto {
25802659
return true;
25812660
}

compiler/rustc_codegen_ssa/src/back/linker.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use super::archive;
21
use super::command::Command;
32
use super::symbol_export;
43
use rustc_span::symbol::sym;
@@ -11,6 +10,7 @@ use std::path::{Path, PathBuf};
1110
use std::{env, mem, str};
1211

1312
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
13+
use rustc_metadata::find_native_static_library;
1414
use rustc_middle::middle::dependency_format::Linkage;
1515
use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind};
1616
use rustc_middle::ty::TyCtxt;
@@ -516,7 +516,7 @@ impl<'a> Linker for GccLinker<'a> {
516516
// -force_load is the macOS equivalent of --whole-archive, but it
517517
// involves passing the full path to the library to link.
518518
self.linker_arg("-force_load");
519-
let lib = archive::find_library(lib, verbatim, search_path, &self.sess);
519+
let lib = find_native_static_library(lib, Some(verbatim), search_path, &self.sess);
520520
self.linker_arg(&lib);
521521
}
522522
}

compiler/rustc_codegen_ssa/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ bitflags::bitflags! {
113113
pub struct NativeLib {
114114
pub kind: NativeLibKind,
115115
pub name: Option<Symbol>,
116+
pub filename: Option<Symbol>,
116117
pub cfg: Option<ast::MetaItem>,
117118
pub verbatim: Option<bool>,
118119
pub dll_imports: Vec<cstore::DllImport>,
@@ -122,6 +123,7 @@ impl From<&cstore::NativeLib> for NativeLib {
122123
fn from(lib: &cstore::NativeLib) -> Self {
123124
NativeLib {
124125
kind: lib.kind,
126+
filename: lib.filename,
125127
name: lib.name,
126128
cfg: lib.cfg.clone(),
127129
verbatim: lib.verbatim,

compiler/rustc_interface/src/tests.rs

+1
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,7 @@ fn test_unstable_options_tracking_hash() {
765765
tracked!(no_profiler_runtime, true);
766766
tracked!(oom, OomStrategy::Panic);
767767
tracked!(osx_rpath_install_name, true);
768+
tracked!(packed_bundled_libs, true);
768769
tracked!(panic_abort_tests, true);
769770
tracked!(panic_in_drop, PanicStrategy::Abort);
770771
tracked!(pick_stable_methods_before_any_unstable, false);

compiler/rustc_metadata/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,5 @@ pub mod fs;
4141
pub mod locator;
4242

4343
pub use fs::{emit_metadata, METADATA_FILENAME};
44+
pub use native_libs::find_native_static_library;
4445
pub use rmeta::{encode_metadata, EncodedMetadata, METADATA_HEADER};

0 commit comments

Comments
 (0)