Skip to content
Closed
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.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, `native-staticlib` will appear on the linker line because it was not bundled previously
# The cdylib will contain the `native_func` symbol in the end
$(RUSTC) cdylib-non-bundled.rs --crate-type=cdylib --print link-args $(SPLIT) --crate-name=cdylib_non_bundled_split | $(CGREP) -e '-l[" ]*native-staticlib'
$(NM) $(call DYLIB,cdylib_non_bundled_split) | $(CGREP) -e "[Tt] _*native_func"
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