diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index f0d320c7c21c9..483fa74221eee 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -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 @@ -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 => {} + }; } } } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 21d9eaccf67e7..506bf20e0f06c 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -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)); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 501997679f4bf..09962bdd3c301 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -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 = (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`) @@ -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 = (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 = (None, parse_symbol_mangling_version, [TRACKED], "which mangling version to use for symbol names ('legacy' (default) or 'v0')"), diff --git a/src/test/run-make/native-link-modifier-bundle/Makefile b/src/test/run-make/native-link-modifier-bundle/Makefile index e4b0f74ac26ac..b9340457b34dc 100644 --- a/src/test/run-make/native-link-modifier-bundle/Makefile +++ b/src/test/run-make/native-link-modifier-bundle/Makefile @@ -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 @@ -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" \ No newline at end of file diff --git a/src/test/run-make/native-link-modifier-bundle/cdylib-bundled-split.rs b/src/test/run-make/native-link-modifier-bundle/cdylib-bundled-split.rs new file mode 100644 index 0000000000000..f586f701cc5ba --- /dev/null +++ b/src/test/run-make/native-link-modifier-bundle/cdylib-bundled-split.rs @@ -0,0 +1 @@ +extern crate bundled_split; diff --git a/src/test/rustdoc-ui/z-help.stdout b/src/test/rustdoc-ui/z-help.stdout index 86e42440bd0bc..93665579b306b 100644 --- a/src/test/rustdoc-ui/z-help.stdout +++ b/src/test/rustdoc-ui/z-help.stdout @@ -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`) @@ -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