Skip to content
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

[WIP] add -Z split_bundled_libs #99773

Closed
wants to merge 14 commits into from
112 changes: 76 additions & 36 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,37 +318,56 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(
// metadata of the rlib we're generating somehow.
for lib in codegen_results.crate_info.used_libraries.iter() {
match lib.kind {
NativeLibKind::Static { bundle: None | Some(true), whole_archive: Some(true) }
if flavor == RlibFlavor::Normal =>
{
// Don't allow mixing +bundle with +whole_archive since an rlib may contain
// multiple native libs, some of which are +whole-archive and some of which are
// -whole-archive and it isn't clear how we can currently handle such a
// situation correctly.
// See https://github.com/rust-lang/rust/issues/88085#issuecomment-901050897
sess.err(
"the linking modifiers `+bundle` and `+whole-archive` are not compatible \
with each other when generating rlibs",
NativeLibKind::Static { bundle: None | Some(true), whole_archive } => {
if flavor == RlibFlavor::Normal
&& whole_archive == Some(true)
&& !sess.opts.unstable_opts.split_bundled_libs
{
// Don't allow mixing +bundle with +whole_archive since an rlib may contain
// multiple native libs, some of which are +whole-archive and some of which are
// -whole-archive and it isn't clear how we can currently handle such a
// situation correctly.
// See https://github.com/rust-lang/rust/issues/88085#issuecomment-901050897
sess.err(
"the linking modifiers `+bundle` and `+whole-archive` are not compatible \
with each other when generating rlibs",
);
}

let Some(name) = lib.name else {
continue;
};

let location = find_library(
name.as_str(),
lib.verbatim.unwrap_or(false),
&lib_search_paths,
sess,
);

if flavor == RlibFlavor::Normal && sess.opts.unstable_opts.split_bundled_libs {
let suffix = &sess.target.staticlib_suffix;
let bundle_lib =
PathBuf::from(format!("{}.bundle.{name}{suffix}", out_filename.display()));
fs::copy(location, bundle_lib).unwrap_or_else(|e| {
sess.fatal(format!("Unable to create bundle lib: {}", e));
});
} else {
ab.add_archive(&location, |_| false).unwrap_or_else(|e| {
sess.fatal(&format!(
"failed to add native library {}: {}",
location.to_string_lossy(),
e
));
});
}
}
NativeLibKind::Static { bundle: None | Some(true), .. } => {}
NativeLibKind::Static { bundle: Some(false), .. }
| NativeLibKind::Dylib { .. }
| NativeLibKind::Framework { .. }
| NativeLibKind::RawDylib
| NativeLibKind::Unspecified => continue,
}
if let Some(name) = lib.name {
let location =
find_library(name.as_str(), lib.verbatim.unwrap_or(false), &lib_search_paths, sess);
ab.add_archive(&location, |_| false).unwrap_or_else(|e| {
sess.fatal(&format!(
"failed to add native library {}: {}",
location.to_string_lossy(),
e
));
});
}
}

for (raw_dylib_name, raw_dylib_imports) in
Expand Down Expand Up @@ -2380,20 +2399,41 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
(lib.name, lib.kind, lib.verbatim)
};

if let NativeLibKind::Static { bundle: Some(false), whole_archive } =
lib.kind
{
let verbatim = lib.verbatim.unwrap_or(false);
if whole_archive == Some(true) {
cmd.link_whole_staticlib(
name,
verbatim,
search_path.get_or_init(|| archive_search_paths(sess)),
);
} else {
cmd.link_staticlib(name, verbatim);
match lib.kind {
NativeLibKind::Static { bundle: None | Some(true), whole_archive }
if sess.opts.unstable_opts.split_bundled_libs =>
{
let suffix = &sess.target.staticlib_suffix;
let cratepath = &src.rlib.as_ref().unwrap().0;
let bundle_lib = PathBuf::from(format!(
"{}.bundle.{name}{suffix}",
cratepath.display()
));
if whole_archive == Some(true) {
cmd.link_whole_rlib(&fix_windows_verbatim_for_gcc(&bundle_lib));
} else {
cmd.link_rlib(&fix_windows_verbatim_for_gcc(&bundle_lib));
}
}
}

NativeLibKind::Static { bundle: Some(false), whole_archive } => {
let verbatim = lib.verbatim.unwrap_or(false);
if whole_archive == Some(true) {
cmd.link_whole_staticlib(
name,
verbatim,
search_path.get_or_init(|| archive_search_paths(sess)),
);
} else {
cmd.link_staticlib(name, verbatim);
}
}
NativeLibKind::Static { bundle: None | Some(true), .. }
| NativeLibKind::Dylib { .. }
| NativeLibKind::Framework { .. }
| NativeLibKind::RawDylib
| NativeLibKind::Unspecified => {}
};
}
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_interface/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,7 @@ fn test_unstable_options_tracking_hash() {
tracked!(share_generics, Some(true));
tracked!(show_span, Some(String::from("abc")));
tracked!(simulate_remapped_rust_src_base, Some(PathBuf::from("/rustc/abc")));
tracked!(split_bundled_libs, true);
tracked!(src_hash_algorithm, Some(SourceFileHashAlgorithm::Sha1));
tracked!(stack_protector, StackProtector::All);
tracked!(symbol_mangling_version, Some(SymbolManglingVersion::V0));
Expand Down
20 changes: 11 additions & 9 deletions compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1519,15 +1519,8 @@ options! {
/// o/w tests have closure@path
span_free_formats: bool = (false, parse_bool, [UNTRACKED],
"exclude spans when debug-printing compiler state (default: no)"),
src_hash_algorithm: Option<SourceFileHashAlgorithm> = (None, parse_src_file_hash, [TRACKED],
"hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"),
#[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::stack_protector` instead of this field"))]
stack_protector: StackProtector = (StackProtector::None, parse_stack_protector, [TRACKED],
"control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)"),
strict_init_checks: bool = (false, parse_bool, [TRACKED],
"control if mem::uninitialized and mem::zeroed panic on more UB"),
strip: Strip = (Strip::None, parse_strip, [UNTRACKED],
"tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"),
split_bundled_libs: bool = (false, parse_bool, [TRACKED],
"if libfoo.rlib is the rlib, then libfoo.rlib.bundle.* are the corresponding bundled static libraries"),
split_dwarf_kind: SplitDwarfKind = (SplitDwarfKind::Split, parse_split_dwarf_kind, [TRACKED],
"split dwarf variant (only if -Csplit-debuginfo is enabled and on relevant platform)
(default: `split`)
Expand All @@ -1539,6 +1532,15 @@ options! {
split_dwarf_inlining: bool = (true, parse_bool, [TRACKED],
"provide minimal debug info in the object/executable to facilitate online \
symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF"),
src_hash_algorithm: Option<SourceFileHashAlgorithm> = (None, parse_src_file_hash, [TRACKED],
"hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"),
#[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::stack_protector` instead of this field"))]
stack_protector: StackProtector = (StackProtector::None, parse_stack_protector, [TRACKED],
"control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)"),
strict_init_checks: bool = (false, parse_bool, [TRACKED],
"control if mem::uninitialized and mem::zeroed panic on more UB"),
strip: Strip = (Strip::None, parse_strip, [UNTRACKED],
"tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)"),
symbol_mangling_version: Option<SymbolManglingVersion> = (None,
parse_symbol_mangling_version, [TRACKED],
"which mangling version to use for symbol names ('legacy' (default) or 'v0')"),
Expand Down
15 changes: 15 additions & 0 deletions src/test/run-make/native-link-modifier-bundle/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
# We're using the llvm-nm instead of the system nm to ensure it is compatible
# with the LLVM bitcode generated by rustc.
NM = "$(LLVM_BIN_DIR)"/llvm-nm
SPLIT = "-Zsplit-bundled-libs"
BUNDLED_LIB = "libbundled_split.rlib.bundle.native-staticlib.a"


all: $(call NATIVE_STATICLIB,native-staticlib)
# Build a staticlib and a rlib, the `native_func` symbol will be bundled into them
Expand All @@ -31,3 +34,15 @@ all: $(call NATIVE_STATICLIB,native-staticlib)
# The cdylib will contain the `native_func` symbol in the end
$(RUSTC) cdylib-non-bundled.rs --crate-type=cdylib --print link-args | $(CGREP) -e '-l[" ]*native-staticlib'
$(NM) $(call DYLIB,cdylib_non_bundled) | $(CGREP) -e "[Tt] _*native_func"

# Build a staticlib and a rlib, the `native_func` symbol will be bundled only into staticlib
$(RUSTC) bundled.rs --crate-type=staticlib --crate-type=rlib $(SPLIT) --crate-name=bundled_split
$(NM) $(TMPDIR)/libbundled_split.a | $(CGREP) -e "T _*native_func"
$(NM) $(TMPDIR)/libbundled_split.a | $(CGREP) -e "U _*native_func"
$(NM) $(TMPDIR)/libbundled_split.rlib | $(CGREP) -ve "T _*native_func"
$(NM) $(TMPDIR)/libbundled_split.rlib | $(CGREP) -e "U _*native_func"

# Build a cdylib, 'BUNDLED_LIB' will appear on the linker line
# The cdylib will contain the `native_func` symbol in the end
$(RUSTC) cdylib-bundled-split.rs --crate-type=cdylib --print link-args $(SPLIT) | $(CGREP) -e $(BUNDLED_LIB)
$(NM) $(call DYLIB,cdylib_bundled_split) | $(CGREP) -e "[Tt] _*native_func"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
extern crate bundled_split;
9 changes: 5 additions & 4 deletions src/test/rustdoc-ui/z-help.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,7 @@
-Z show-span=val -- show spans for compiler debugging (expr|pat|ty)
-Z span-debug=val -- forward proc_macro::Span's `Debug` impl to `Span`
-Z span-free-formats=val -- exclude spans when debug-printing compiler state (default: no)
-Z src-hash-algorithm=val -- hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)
-Z stack-protector=val -- control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)
-Z strict-init-checks=val -- control if mem::uninitialized and mem::zeroed panic on more UB
-Z strip=val -- tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)
-Z split-bundled-libs=val -- if libfoo.rlib is the rlib, then libfoo.rlib.bundle.* are the corresponding bundled static libraries
-Z split-dwarf-kind=val -- split dwarf variant (only if -Csplit-debuginfo is enabled and on relevant platform)
(default: `split`)

Expand All @@ -156,6 +153,10 @@
`single`: sections which do not require relocation are written into object file but ignored
by the linker
-Z split-dwarf-inlining=val -- provide minimal debug info in the object/executable to facilitate online symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF
-Z src-hash-algorithm=val -- hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)
-Z stack-protector=val -- control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)
-Z strict-init-checks=val -- control if mem::uninitialized and mem::zeroed panic on more UB
-Z strip=val -- tell the linker which information to strip (`none` (default), `debuginfo` or `symbols`)
-Z symbol-mangling-version=val -- which mangling version to use for symbol names ('legacy' (default) or 'v0')
-Z teach=val -- show extended diagnostic help (default: no)
-Z temps-dir=val -- the directory the intermediate files are written to
Expand Down