From fc7de9979e418b0ea467bb2833b07486d7c54940 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 28 Aug 2025 13:36:10 +0000 Subject: [PATCH 1/4] Ensure we emit an allocator shim when only some crate types need one --- compiler/rustc_codegen_ssa/src/back/link.rs | 17 ++++++++++--- compiler/rustc_codegen_ssa/src/base.rs | 27 +++++++++++++++++---- tests/ui/linking/mixed-allocator-shim.rs | 15 ++++++++++++ 3 files changed, 50 insertions(+), 9 deletions(-) create mode 100644 tests/ui/linking/mixed-allocator-shim.rs diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 19c919c0e4efe..48b01ea2df197 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -58,6 +58,7 @@ use super::linker::{self, Linker}; use super::metadata::{MetadataPosition, create_wrapper_file}; use super::rpath::{self, RPathConfig}; use super::{apple, versioned_llvm_target}; +use crate::base::needs_allocator_shim_for_linking; use crate::{ CodegenResults, CompiledModule, CrateInfo, NativeLib, errors, looks_like_rust_object_file, }; @@ -2080,9 +2081,17 @@ fn add_local_crate_regular_objects(cmd: &mut dyn Linker, codegen_results: &Codeg } /// Add object files for allocator code linked once for the whole crate tree. -fn add_local_crate_allocator_objects(cmd: &mut dyn Linker, codegen_results: &CodegenResults) { - if let Some(obj) = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref()) { - cmd.add_object(obj); +fn add_local_crate_allocator_objects( + cmd: &mut dyn Linker, + codegen_results: &CodegenResults, + crate_type: CrateType, +) { + if needs_allocator_shim_for_linking(&codegen_results.crate_info.dependency_formats, crate_type) + { + if let Some(obj) = codegen_results.allocator_module.as_ref().and_then(|m| m.object.as_ref()) + { + cmd.add_object(obj); + } } } @@ -2281,7 +2290,7 @@ fn linker_with_args( codegen_results, metadata, ); - add_local_crate_allocator_objects(cmd, codegen_results); + add_local_crate_allocator_objects(cmd, codegen_results, crate_type); // Avoid linking to dynamic libraries unless they satisfy some undefined symbols // at the point at which they are specified on the command line. diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 8abaf201abae2..97cdf8b697348 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -17,6 +17,7 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::{ItemId, Target}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::middle::debugger_visualizer::{DebuggerVisualizerFile, DebuggerVisualizerType}; +use rustc_middle::middle::dependency_format::Dependencies; use rustc_middle::middle::exported_symbols::{self, SymbolExportKind}; use rustc_middle::middle::lang_items; use rustc_middle::mir::BinOp; @@ -630,14 +631,30 @@ pub fn allocator_kind_for_codegen(tcx: TyCtxt<'_>) -> Option { // If the crate doesn't have an `allocator_kind` set then there's definitely // no shim to generate. Otherwise we also check our dependency graph for all // our output crate types. If anything there looks like its a `Dynamic` - // linkage, then it's already got an allocator shim and we'll be using that - // one instead. If nothing exists then it's our job to generate the - // allocator! - let any_dynamic_crate = tcx.dependency_formats(()).iter().any(|(_, list)| { + // linkage for all crate types we may link as, then it's already got an + // allocator shim and we'll be using that one instead. If nothing exists + // then it's our job to generate the allocator! If crate types disagree + // about whether an allocator shim is necessary or not, we generate one + // and let needs_allocator_shim_for_linking decide at link time whether or + // not to use it for any particular linker invocation. + let all_crate_types_any_dynamic_crate = tcx.dependency_formats(()).iter().all(|(_, list)| { use rustc_middle::middle::dependency_format::Linkage; list.iter().any(|&linkage| linkage == Linkage::Dynamic) }); - if any_dynamic_crate { None } else { tcx.allocator_kind(()) } + if all_crate_types_any_dynamic_crate { None } else { tcx.allocator_kind(()) } +} + +/// Decide if this particular crate type needs an allocator shim linked in. +/// This may return true even when allocator_kind_for_codegen returns false. In +/// this case no allocator shim shall be linked. +pub(crate) fn needs_allocator_shim_for_linking( + dependency_formats: &Dependencies, + crate_type: CrateType, +) -> bool { + use rustc_middle::middle::dependency_format::Linkage; + let any_dynamic_crate = + dependency_formats[&crate_type].iter().any(|&linkage| linkage == Linkage::Dynamic); + !any_dynamic_crate } pub fn codegen_crate( diff --git a/tests/ui/linking/mixed-allocator-shim.rs b/tests/ui/linking/mixed-allocator-shim.rs new file mode 100644 index 0000000000000..b81fbdc5205e7 --- /dev/null +++ b/tests/ui/linking/mixed-allocator-shim.rs @@ -0,0 +1,15 @@ +//@ build-pass +//@ compile-flags: --crate-type staticlib,dylib -Zstaticlib-prefer-dynamic +//@ no-prefer-dynamic + +// Test that compiling for multiple crate types in a single compilation with +// mismatching allocator shim requirements doesn't result in the allocator shim +// missing entirely. +// In this particular test the dylib crate type will statically link libstd and +// thus need an allocator shim, while the staticlib crate type will dynamically +// link libstd and thus not need an allocator shim. +// The -Zstaticlib-prefer-dynamic flag could be avoided by doing it the other +// way around, but testing that the staticlib correctly has the allocator shim +// in that case would require a run-make test instead. + +pub fn foo() {} From 1d309008306f6695bdbbd535245067202ab44802 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 28 Aug 2025 15:16:48 +0000 Subject: [PATCH 2/4] Fix typo in comment --- compiler/rustc_codegen_ssa/src/back/linker.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index df1e91b12f904..03c30dc0c5929 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1827,7 +1827,7 @@ fn exported_symbols_for_non_proc_macro( let export_threshold = symbol_export::crates_export_threshold(&[crate_type]); for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| { // Do not export mangled symbols from cdylibs and don't attempt to export compiler-builtins - // from any cdylib. The latter doesn't work anyway as we use hidden visibility for + // from any dylib. The latter doesn't work anyway as we use hidden visibility for // compiler-builtins. Most linkers silently ignore it, but ld64 gives a warning. if info.level.is_below_threshold(export_threshold) && !tcx.is_compiler_builtins(cnum) { symbols.push(( From f4888c2a680aff0bd191704a8d44716eee94103e Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 28 Aug 2025 16:59:22 +0000 Subject: [PATCH 3/4] Correctly handle different crate types disagreeing if the allocator shim is exported Previously it would attempt to export the allocator shim even linking for a crate type which pulls in the allocator shim from a dylib rather than locally defining it. --- compiler/rustc_codegen_ssa/src/back/linker.rs | 15 +++++- compiler/rustc_codegen_ssa/src/back/lto.rs | 8 ++- .../src/back/symbol_export.rs | 52 +++++++++---------- 3 files changed, 46 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 03c30dc0c5929..a2efd420a327a 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -11,8 +11,9 @@ use rustc_metadata::{ }; use rustc_middle::bug; use rustc_middle::middle::dependency_format::Linkage; -use rustc_middle::middle::exported_symbols; -use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind}; +use rustc_middle::middle::exported_symbols::{ + self, ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel, +}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip}; @@ -22,6 +23,8 @@ use tracing::{debug, warn}; use super::command::Command; use super::symbol_export; +use crate::back::symbol_export::allocator_shim_symbols; +use crate::base::needs_allocator_shim_for_linking; use crate::errors; #[cfg(test)] @@ -1838,6 +1841,14 @@ fn exported_symbols_for_non_proc_macro( } }); + // Mark allocator shim symbols as exported only if they were generated. + if export_threshold == SymbolExportLevel::Rust + && needs_allocator_shim_for_linking(tcx.dependency_formats(()), crate_type) + && tcx.allocator_kind(()).is_some() + { + symbols.extend(allocator_shim_symbols(tcx)); + } + symbols } diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index c95038375a1bd..e6df6a2469f37 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -8,8 +8,9 @@ use rustc_middle::ty::TyCtxt; use rustc_session::config::{CrateType, Lto}; use tracing::info; -use crate::back::symbol_export::{self, symbol_name_for_instance_in_crate}; +use crate::back::symbol_export::{self, allocator_shim_symbols, symbol_name_for_instance_in_crate}; use crate::back::write::CodegenContext; +use crate::base::allocator_kind_for_codegen; use crate::errors::{DynamicLinkingWithLTO, LtoDisallowed, LtoDylib, LtoProcMacro}; use crate::traits::*; @@ -115,6 +116,11 @@ pub(super) fn exported_symbols_for_lto( } } + // Mark allocator shim symbols as exported only if they were generated. + if export_threshold == SymbolExportLevel::Rust && allocator_kind_for_codegen(tcx).is_some() { + symbols_below_threshold.extend(allocator_shim_symbols(tcx).map(|(name, _kind)| name)); + } + symbols_below_threshold } diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index d8a1480e911fe..b49e67217fb01 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -18,7 +18,7 @@ use rustc_symbol_mangling::mangle_internal_symbol; use rustc_target::spec::TlsModel; use tracing::debug; -use crate::base::allocator_kind_for_codegen; +use crate::back::symbol_export; fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel { crates_export_threshold(tcx.crate_types()) @@ -217,31 +217,6 @@ fn exported_non_generic_symbols_provider_local<'tcx>( )); } - // Mark allocator shim symbols as exported only if they were generated. - if allocator_kind_for_codegen(tcx).is_some() { - for symbol_name in ALLOCATOR_METHODS - .iter() - .map(|method| mangle_internal_symbol(tcx, global_fn_name(method.name).as_str())) - .chain([ - mangle_internal_symbol(tcx, "__rust_alloc_error_handler"), - mangle_internal_symbol(tcx, OomStrategy::SYMBOL), - mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE), - ]) - { - let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name)); - - symbols.push(( - exported_symbol, - SymbolExportInfo { - level: SymbolExportLevel::Rust, - kind: SymbolExportKind::Text, - used: false, - rustc_std_internal_symbol: true, - }, - )); - } - } - // Sort so we get a stable incr. comp. hash. symbols.sort_by_cached_key(|s| s.0.symbol_name_for_local_instance(tcx)); @@ -516,6 +491,31 @@ pub(crate) fn provide(providers: &mut Providers) { upstream_monomorphizations_for_provider; } +pub(crate) fn allocator_shim_symbols( + tcx: TyCtxt<'_>, +) -> impl Iterator { + ALLOCATOR_METHODS + .iter() + .map(move |method| mangle_internal_symbol(tcx, global_fn_name(method.name).as_str())) + .chain([ + mangle_internal_symbol(tcx, "__rust_alloc_error_handler"), + mangle_internal_symbol(tcx, OomStrategy::SYMBOL), + mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE), + ]) + .map(move |symbol_name| { + let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name)); + + ( + symbol_export::exporting_symbol_name_for_instance_in_crate( + tcx, + exported_symbol, + LOCAL_CRATE, + ), + SymbolExportKind::Text, + ) + }) +} + fn symbol_export_level(tcx: TyCtxt<'_>, sym_def_id: DefId) -> SymbolExportLevel { // We export anything that's not mangled at the "C" layer as it probably has // to do with ABI concerns. We do not, however, apply such treatment to From 0711bba9ea368204b1a7f833e0e44c529ce140c8 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 31 Aug 2025 20:43:44 +0000 Subject: [PATCH 4/4] Ignore test when dylibs are not supported --- tests/ui/linking/mixed-allocator-shim.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/ui/linking/mixed-allocator-shim.rs b/tests/ui/linking/mixed-allocator-shim.rs index b81fbdc5205e7..e4f20a11ebb37 100644 --- a/tests/ui/linking/mixed-allocator-shim.rs +++ b/tests/ui/linking/mixed-allocator-shim.rs @@ -1,6 +1,7 @@ //@ build-pass //@ compile-flags: --crate-type staticlib,dylib -Zstaticlib-prefer-dynamic //@ no-prefer-dynamic +//@ needs-crate-type: dylib // Test that compiling for multiple crate types in a single compilation with // mismatching allocator shim requirements doesn't result in the allocator shim