Skip to content

Commit 9da4644

Browse files
committed
Auto 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 0df1ddc + ffa8359 commit 9da4644

File tree

23 files changed

+328
-54
lines changed

23 files changed

+328
-54
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

+89-9
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
use rustc_arena::TypedArena;
22
use rustc_ast::CRATE_NODE_ID;
3+
use rustc_data_structures::fx::FxHashSet;
34
use rustc_data_structures::fx::FxIndexMap;
45
use rustc_data_structures::memmap::Mmap;
56
use rustc_data_structures::temp_dir::MaybeTempDir;
67
use rustc_errors::{ErrorGuaranteed, Handler};
78
use rustc_fs_util::fix_windows_verbatim_for_gcc;
89
use rustc_hir::def_id::CrateNum;
10+
use rustc_metadata::find_native_static_library;
911
use rustc_metadata::fs::{emit_metadata, METADATA_FILENAME};
1012
use rustc_middle::middle::dependency_format::Linkage;
1113
use rustc_middle::middle::exported_symbols::SymbolExportKind;
@@ -24,7 +26,7 @@ use rustc_target::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault};
2426
use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor, SplitDebuginfo};
2527
use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, SanitizerSet, Target};
2628

27-
use super::archive::{find_library, ArchiveBuilder, ArchiveBuilderBuilder};
29+
use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
2830
use super::command::Command;
2931
use super::linker::{self, Linker};
3032
use super::metadata::{create_rmeta_file, MetadataPosition};
@@ -307,6 +309,9 @@ fn link_rlib<'a>(
307309
}
308310
}
309311

312+
// Used if packed_bundled_libs flag enabled.
313+
let mut packed_bundled_libs = Vec::new();
314+
310315
// Note that in this loop we are ignoring the value of `lib.cfg`. That is,
311316
// we may not be configured to actually include a static library if we're
312317
// adding it here. That's because later when we consume this rlib we'll
@@ -325,6 +330,8 @@ fn link_rlib<'a>(
325330
// metadata of the rlib we're generating somehow.
326331
for lib in codegen_results.crate_info.used_libraries.iter() {
327332
match lib.kind {
333+
NativeLibKind::Static { bundle: None | Some(true), whole_archive: Some(true) }
334+
if flavor == RlibFlavor::Normal && sess.opts.unstable_opts.packed_bundled_libs => {}
328335
NativeLibKind::Static { bundle: None | Some(true), whole_archive: Some(true) }
329336
if flavor == RlibFlavor::Normal =>
330337
{
@@ -348,7 +355,16 @@ fn link_rlib<'a>(
348355
}
349356
if let Some(name) = lib.name {
350357
let location =
351-
find_library(name.as_str(), lib.verbatim.unwrap_or(false), &lib_search_paths, sess);
358+
find_native_static_library(name.as_str(), lib.verbatim, &lib_search_paths, sess);
359+
if sess.opts.unstable_opts.packed_bundled_libs && flavor == RlibFlavor::Normal {
360+
packed_bundled_libs.push(find_native_static_library(
361+
lib.filename.unwrap().as_str(),
362+
Some(true),
363+
&lib_search_paths,
364+
sess,
365+
));
366+
continue;
367+
}
352368
ab.add_archive(&location, Box::new(|_| false)).unwrap_or_else(|e| {
353369
sess.fatal(&format!(
354370
"failed to add native library {}: {}",
@@ -403,6 +419,12 @@ fn link_rlib<'a>(
403419
ab.add_file(&trailing_metadata);
404420
}
405421

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

@@ -2341,7 +2363,15 @@ fn add_upstream_rust_crates<'a>(
23412363
let src = &codegen_results.crate_info.used_crate_source[&cnum];
23422364
match data[cnum.as_usize() - 1] {
23432365
_ if codegen_results.crate_info.profiler_runtime == Some(cnum) => {
2344-
add_static_crate(cmd, sess, archive_builder_builder, codegen_results, tmpdir, cnum);
2366+
add_static_crate(
2367+
cmd,
2368+
sess,
2369+
archive_builder_builder,
2370+
codegen_results,
2371+
tmpdir,
2372+
cnum,
2373+
&Default::default(),
2374+
);
23452375
}
23462376
// compiler-builtins are always placed last to ensure that they're
23472377
// linked correctly.
@@ -2351,7 +2381,23 @@ fn add_upstream_rust_crates<'a>(
23512381
}
23522382
Linkage::NotLinked | Linkage::IncludedFromDylib => {}
23532383
Linkage::Static => {
2354-
add_static_crate(cmd, sess, archive_builder_builder, codegen_results, tmpdir, cnum);
2384+
let bundled_libs = if sess.opts.unstable_opts.packed_bundled_libs {
2385+
codegen_results.crate_info.native_libraries[&cnum]
2386+
.iter()
2387+
.filter_map(|lib| lib.filename)
2388+
.collect::<FxHashSet<_>>()
2389+
} else {
2390+
Default::default()
2391+
};
2392+
add_static_crate(
2393+
cmd,
2394+
sess,
2395+
archive_builder_builder,
2396+
codegen_results,
2397+
tmpdir,
2398+
cnum,
2399+
&bundled_libs,
2400+
);
23552401

23562402
// Link static native libs with "-bundle" modifier only if the crate they originate from
23572403
// is being linked statically to the current crate. If it's linked dynamically
@@ -2362,6 +2408,14 @@ fn add_upstream_rust_crates<'a>(
23622408
// external build system already has the native dependencies defined, and it
23632409
// will provide them to the linker itself.
23642410
if sess.opts.unstable_opts.link_native_libraries {
2411+
if sess.opts.unstable_opts.packed_bundled_libs {
2412+
// If rlib contains native libs as archives, unpack them to tmpdir.
2413+
let rlib = &src.rlib.as_ref().unwrap().0;
2414+
archive_builder_builder
2415+
.extract_bundled_libs(rlib, tmpdir, &bundled_libs)
2416+
.unwrap_or_else(|e| sess.fatal(e));
2417+
}
2418+
23652419
let mut last = (None, NativeLibKind::Unspecified, None);
23662420
for lib in &codegen_results.crate_info.native_libraries[&cnum] {
23672421
let Some(name) = lib.name else {
@@ -2411,10 +2465,17 @@ fn add_upstream_rust_crates<'a>(
24112465
| NativeLibKind::Framework { .. }
24122466
| NativeLibKind::Unspecified
24132467
| NativeLibKind::RawDylib => {}
2414-
NativeLibKind::Static {
2415-
bundle: Some(true) | None,
2416-
whole_archive: _,
2417-
} => {}
2468+
NativeLibKind::Static { bundle: Some(true) | None, whole_archive } => {
2469+
if sess.opts.unstable_opts.packed_bundled_libs {
2470+
// If rlib contains native libs as archives, they are unpacked to tmpdir.
2471+
let path = tmpdir.join(lib.filename.unwrap().as_str());
2472+
if whole_archive == Some(true) {
2473+
cmd.link_whole_rlib(&path);
2474+
} else {
2475+
cmd.link_rlib(&path);
2476+
}
2477+
}
2478+
}
24182479
}
24192480
}
24202481
}
@@ -2429,7 +2490,15 @@ fn add_upstream_rust_crates<'a>(
24292490
// was already "included" in a dylib (e.g., `libstd` when `-C prefer-dynamic`
24302491
// is used)
24312492
if let Some(cnum) = compiler_builtins {
2432-
add_static_crate(cmd, sess, archive_builder_builder, codegen_results, tmpdir, cnum);
2493+
add_static_crate(
2494+
cmd,
2495+
sess,
2496+
archive_builder_builder,
2497+
codegen_results,
2498+
tmpdir,
2499+
cnum,
2500+
&Default::default(),
2501+
);
24332502
}
24342503

24352504
// Converts a library file-stem into a cc -l argument
@@ -2462,6 +2531,7 @@ fn add_upstream_rust_crates<'a>(
24622531
codegen_results: &CodegenResults,
24632532
tmpdir: &Path,
24642533
cnum: CrateNum,
2534+
bundled_lib_file_names: &FxHashSet<Symbol>,
24652535
) {
24662536
let src = &codegen_results.crate_info.used_crate_source[&cnum];
24672537
let cratepath = &src.rlib.as_ref().unwrap().0;
@@ -2490,6 +2560,7 @@ fn add_upstream_rust_crates<'a>(
24902560
let dst = tmpdir.join(cratepath.file_name().unwrap());
24912561
let name = cratepath.file_name().unwrap().to_str().unwrap();
24922562
let name = &name[3..name.len() - 5]; // chop off lib/.rlib
2563+
let bundled_lib_file_names = bundled_lib_file_names.clone();
24932564

24942565
sess.prof.generic_activity_with_arg("link_altering_rlib", name).run(|| {
24952566
let canonical_name = name.replace('-', "_");
@@ -2523,6 +2594,15 @@ fn add_upstream_rust_crates<'a>(
25232594
let skip_because_lto =
25242595
upstream_rust_objects_already_included && is_rust_object && is_builtins;
25252596

2597+
// We skip native libraries because:
2598+
// 1. This native libraries won't be used from the generated rlib,
2599+
// so we can throw them away to avoid the copying work.
2600+
// 2. We can't allow it to be a single remaining entry in archive
2601+
// as some linkers may complain on that.
2602+
if bundled_lib_file_names.contains(&Symbol::intern(f)) {
2603+
return true;
2604+
}
2605+
25262606
if skip_because_cfg_say_so || skip_because_lto {
25272607
return true;
25282608
}

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;
@@ -514,7 +514,7 @@ impl<'a> Linker for GccLinker<'a> {
514514
// -force_load is the macOS equivalent of --whole-archive, but it
515515
// involves passing the full path to the library to link.
516516
self.linker_arg("-force_load");
517-
let lib = archive::find_library(lib, verbatim, search_path, &self.sess);
517+
let lib = find_native_static_library(lib, Some(verbatim), search_path, &self.sess);
518518
self.linker_arg(&lib);
519519
}
520520
}

compiler/rustc_codegen_ssa/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ bitflags::bitflags! {
112112
pub struct NativeLib {
113113
pub kind: NativeLibKind,
114114
pub name: Option<Symbol>,
115+
pub filename: Option<Symbol>,
115116
pub cfg: Option<ast::MetaItem>,
116117
pub verbatim: Option<bool>,
117118
pub dll_imports: Vec<cstore::DllImport>,
@@ -121,6 +122,7 @@ impl From<&cstore::NativeLib> for NativeLib {
121122
fn from(lib: &cstore::NativeLib) -> Self {
122123
NativeLib {
123124
kind: lib.kind,
125+
filename: lib.filename,
124126
name: lib.name,
125127
cfg: lib.cfg.clone(),
126128
verbatim: lib.verbatim,

compiler/rustc_error_messages/locales/en-US/metadata.ftl

+3
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ metadata_no_transitive_needs_dep =
162162
metadata_failed_write_error =
163163
failed to write {$filename}: {$err}
164164
165+
metadata_missing_native_library =
166+
could not find native static library `{$libname}`, perhaps an -L flag is missing?
167+
165168
metadata_failed_create_tempdir =
166169
couldn't create a temp dir: {$err}
167170

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/errors.rs

+6
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,12 @@ pub struct FailedWriteError {
369369
pub err: Error,
370370
}
371371

372+
#[derive(SessionDiagnostic)]
373+
#[diag(metadata::missing_native_library)]
374+
pub struct MissingNativeLibrary<'a> {
375+
pub libname: &'a str,
376+
}
377+
372378
#[derive(SessionDiagnostic)]
373379
#[diag(metadata::failed_create_tempdir)]
374380
pub struct FailedCreateTempdir {

compiler/rustc_metadata/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,5 @@ pub mod fs;
4444
pub mod locator;
4545

4646
pub use fs::{emit_metadata, METADATA_FILENAME};
47+
pub use native_libs::find_native_static_library;
4748
pub use rmeta::{encode_metadata, EncodedMetadata, METADATA_HEADER};

0 commit comments

Comments
 (0)