From e5d350c5e56d218b425529e2207050066861c913 Mon Sep 17 00:00:00 2001 From: Noratrieb <48135649+Noratrieb@users.noreply.github.com> Date: Sun, 2 Nov 2025 21:19:29 +0100 Subject: [PATCH 1/3] Mangle personality symbol Previously, std-internal symbols (like `__rust_alloc` or `rust_panic`) were changed to now be mangled via a v0 scheme with the compiler version hash (`_RNvCs4CIB29Id3dw_7___rustc10rust_panic`) to make it possible to have multiple staticlibs (as long as they have different version hashes) in the same program without symbol conflicts. But the personality function remained unmangled, because LLVM hardcodes that name. I made a change in LLVM that makes LLVM now check the suffix instead of an exact match, so we can mangle the personality function with this scheme. This removes the last completely unmangled symbol from Rust staticlibs! (though the mangling is still quite weak, so it's still not as nice as it would ideally be). Before, on a trivial staticlib: ``` readelf -s liblib.a | rg GLOBAL | rg -v " UND | HIDDEN " | rg -v ' _ZN' 8: 0000000000000000 5 FUNC GLOBAL DEFAULT 3 _RNvCseCSg29WUqSe_7___rustc12___rust_alloc 10: 0000000000000000 5 FUNC GLOBAL DEFAULT 5 _RNvCseCSg29WUqSe_7___rustc14___rust_dealloc 12: 0000000000000000 5 FUNC GLOBAL DEFAULT 7 _RNvCseCSg29WUqSe_7___rustc14___rust_realloc 14: 0000000000000000 5 FUNC GLOBAL DEFAULT 9 _RNvCseCSg29WUqSe_7___rustc19___rust_alloc_zeroed 16: 0000000000000000 3 FUNC GLOBAL DEFAULT 11 _RNvCseCSg29WUqSe_7___rustc42___rust_alloc_error_handler_should_panic_v2 17: 0000000000000000 1 FUNC GLOBAL DEFAULT 12 _RNvCseCSg29WUqSe_7___rustc35___rust_no_alloc_shim_is_unstable_v2 3383: 0000000000000000 111 FUNC GLOBAL DEFAULT 5 _RNvCseCSg29WUqSe_7___rustc10rust_panic 3387: 0000000000000000 93 FUNC GLOBAL DEFAULT 7 _RNvCseCSg29WUqSe_7___rustc11___rdl_alloc 3390: 0000000000000000 10 FUNC GLOBAL DEFAULT 9 _RNvCseCSg29WUqSe_7___rustc12___rust_abort 3391: 0000000000000000 6 FUNC GLOBAL DEFAULT 11 _RNvCseCSg29WUqSe_7___rustc13___rdl_dealloc 3393: 0000000000000000 167 FUNC GLOBAL DEFAULT 13 _RNvCseCSg29WUqSe_7___rustc13___rdl_realloc 3396: 0000000000000000 203 FUNC GLOBAL DEFAULT 15 _RNvCseCSg29WUqSe_7___rustc17___rust_drop_panic 3399: 0000000000000000 29 FUNC GLOBAL DEFAULT 18 _RNvCseCSg29WUqSe_7___rustc17rust_begin_unwind 3401: 0000000000000000 140 FUNC GLOBAL DEFAULT 20 _RNvCseCSg29WUqSe_7___rustc18___rdl_alloc_zeroed 3404: 0000000000000000 203 FUNC GLOBAL DEFAULT 22 _RNvCseCSg29WUqSe_7___rustc24___rust_foreign_exception 3405: 0000000000000000 19 FUNC GLOBAL DEFAULT 25 _RNvCseCSg29WUqSe_7___rustc26___rust_alloc_error_handler 4410: 0000000000000000 1471 FUNC GLOBAL DEFAULT 2752 rust_eh_personality 25: 0000000000000000 161 FUNC GLOBAL DEFAULT 5 _RNvCseCSg29WUqSe_7___rustc18___rust_start_panic 32: 0000000000000000 82 FUNC GLOBAL DEFAULT 8 _RNvCseCSg29WUqSe_7___rustc20___rust_panic_cleanup 249: 0000000000000000 182 FUNC GLOBAL DEFAULT 5 _RNvCseCSg29WUqSe_7___rustc25___rdl_alloc_error_handler 3: 0000000000000000 57 FUNC GLOBAL DEFAULT 3 _RNvCseCSg29WUqSe_7___rustc17___rust_probestack ``` After: ``` 8: 0000000000000000 5 FUNC GLOBAL DEFAULT 3 _RNvCs4CIB29Id3dw_7___rustc12___rust_alloc 10: 0000000000000000 5 FUNC GLOBAL DEFAULT 5 _RNvCs4CIB29Id3dw_7___rustc14___rust_dealloc 12: 0000000000000000 5 FUNC GLOBAL DEFAULT 7 _RNvCs4CIB29Id3dw_7___rustc14___rust_realloc 14: 0000000000000000 5 FUNC GLOBAL DEFAULT 9 _RNvCs4CIB29Id3dw_7___rustc19___rust_alloc_zeroed 16: 0000000000000000 3 FUNC GLOBAL DEFAULT 11 _RNvCs4CIB29Id3dw_7___rustc42___rust_alloc_error_handler_should_panic_v2 17: 0000000000000000 1 FUNC GLOBAL DEFAULT 12 _RNvCs4CIB29Id3dw_7___rustc35___rust_no_alloc_shim_is_unstable_v2 422: 0000000000000000 19 FUNC GLOBAL DEFAULT 5 _RNvCs4CIB29Id3dw_7___rustc26___rust_alloc_error_handler 429: 0000000000000000 308 FUNC GLOBAL DEFAULT 5 _RNvCs4CIB29Id3dw_7___rustc19rust_eh_personality 349: 0000000000000000 10 FUNC GLOBAL DEFAULT 5 _RNvCs4CIB29Id3dw_7___rustc12___rust_abort 164: 0000000000000000 93 FUNC GLOBAL DEFAULT 5 _RNvCs4CIB29Id3dw_7___rustc11___rdl_alloc 167: 0000000000000000 11 FUNC GLOBAL DEFAULT 7 _RNvCs4CIB29Id3dw_7___rustc13___rdl_dealloc 169: 0000000000000000 167 FUNC GLOBAL DEFAULT 9 _RNvCs4CIB29Id3dw_7___rustc13___rdl_realloc 172: 0000000000000000 140 FUNC GLOBAL DEFAULT 11 _RNvCs4CIB29Id3dw_7___rustc18___rdl_alloc_zeroed 688: 0000000000000000 106 FUNC GLOBAL DEFAULT 5 _RNvCs4CIB29Id3dw_7___rustc10rust_panic 693: 0000000000000000 207 FUNC GLOBAL DEFAULT 7 _RNvCs4CIB29Id3dw_7___rustc17___rust_drop_panic 696: 0000000000000000 29 FUNC GLOBAL DEFAULT 10 _RNvCs4CIB29Id3dw_7___rustc17rust_begin_unwind 698: 0000000000000000 207 FUNC GLOBAL DEFAULT 12 _RNvCs4CIB29Id3dw_7___rustc24___rust_foreign_exception 26: 0000000000000000 161 FUNC GLOBAL DEFAULT 5 _RNvCs4CIB29Id3dw_7___rustc18___rust_start_panic 33: 0000000000000000 82 FUNC GLOBAL DEFAULT 8 _RNvCs4CIB29Id3dw_7___rustc20___rust_panic_cleanup 88: 0000000000000000 182 FUNC GLOBAL DEFAULT 5 _RNvCs4CIB29Id3dw_7___rustc25___rdl_alloc_error_handler 3: 0000000000000000 57 FUNC GLOBAL DEFAULT 3 _RNvCs4CIB29Id3dw_7___rustc17___rust_probestack ``` --- .../src/debuginfo/unwind.rs | 4 +- .../rustc_codegen_cranelift/src/driver/aot.rs | 2 +- .../src/unwind_module.rs | 5 +- compiler/rustc_codegen_gcc/src/context.rs | 4 +- compiler/rustc_codegen_gcc/src/declare.rs | 2 +- compiler/rustc_codegen_gcc/src/lib.rs | 15 +++++- compiler/rustc_codegen_llvm/src/back/lto.rs | 2 +- compiler/rustc_codegen_llvm/src/context.rs | 9 +++- compiler/rustc_codegen_llvm/src/lib.rs | 5 ++ compiler/rustc_codegen_ssa/src/back/write.rs | 2 + .../rustc_codegen_ssa/src/traits/backend.rs | 6 +++ compiler/rustc_interface/src/interface.rs | 2 + compiler/rustc_session/src/session.rs | 5 ++ compiler/rustc_symbol_mangling/src/lib.rs | 2 +- compiler/rustc_symbol_mangling/src/v0.rs | 9 +++- src/tools/compiletest/src/runtest/run_make.rs | 4 ++ .../src/external_deps/llvm.rs | 13 +++++ src/tools/run-make-support/src/lib.rs | 2 +- tests/codegen-llvm/gdb_debug_script_load.rs | 2 +- tests/run-make/no-alloc-shim/foo.rs | 4 +- tests/run-make/repr128-dwarf/rmake.rs | 2 +- tests/run-make/symbols-all-mangled/rmake.rs | 54 ++++++++++--------- .../raw-dylib/elf/glibc-x86_64.rs | 4 +- 23 files changed, 113 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs index 4b0260a8abc74..094fd0f0d1abe 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs @@ -25,7 +25,7 @@ pub(crate) struct UnwindContext { } impl UnwindContext { - pub(crate) fn new(module: &mut dyn Module, pic_eh_frame: bool) -> Self { + pub(crate) fn new(module: &mut dyn Module, tcx: TyCtxt<'_>, pic_eh_frame: bool) -> Self { let endian = match module.isa().endianness() { Endianness::Little => RunTimeEndian::Little, Endianness::Big => RunTimeEndian::Big, @@ -70,7 +70,7 @@ impl UnwindContext { // FIXME use eh_personality lang item instead let personality = module .declare_function( - "rust_eh_personality", + &rustc_symbol_mangling::eh_personality_symbol(tcx), Linkage::Import, &Signature { params: vec![ diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index d6c25cf524a5c..f21d0df8461f6 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -62,7 +62,7 @@ fn make_module(tcx: TyCtxt<'_>, cgu_name: &str) -> AotModule { tcx.sess.opts.unstable_opts.function_sections.unwrap_or(default_function_sections), ); - let module = UnwindModule::new(ObjectModule::new(builder), true); + let module = UnwindModule::new(ObjectModule::new(builder), tcx, true); let producer = crate::debuginfo::producer(tcx.sess); let global_asm_config = GlobalAsmConfig::new(tcx.sess); diff --git a/compiler/rustc_codegen_cranelift/src/unwind_module.rs b/compiler/rustc_codegen_cranelift/src/unwind_module.rs index b4eb939cf2560..b294122d1a964 100644 --- a/compiler/rustc_codegen_cranelift/src/unwind_module.rs +++ b/compiler/rustc_codegen_cranelift/src/unwind_module.rs @@ -7,6 +7,7 @@ use cranelift_module::{ ModuleReloc, ModuleResult, }; use cranelift_object::{ObjectModule, ObjectProduct}; +use rustc_middle::ty::TyCtxt; use crate::UnwindContext; @@ -17,8 +18,8 @@ pub(crate) struct UnwindModule { } impl UnwindModule { - pub(crate) fn new(mut module: T, pic_eh_frame: bool) -> Self { - let unwind_context = UnwindContext::new(&mut module, pic_eh_frame); + pub(crate) fn new(mut module: T, tcx: TyCtxt<'_>, pic_eh_frame: bool) -> Self { + let unwind_context = UnwindContext::new(&mut module, tcx, pic_eh_frame); UnwindModule { module, unwind_context } } } diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 184db4cb25778..3d647e308a440 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -462,10 +462,12 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { self.declare_fn(symbol_name, fn_abi) } _ => { + let rust_name; let name = if wants_msvc_seh(self.sess()) { "__CxxFrameHandler3" } else { - "rust_eh_personality" + rust_name = rustc_symbol_mangling::eh_personality_symbol(tcx); + &rust_name }; self.declare_func(name, self.type_i32(), &[], true) } diff --git a/compiler/rustc_codegen_gcc/src/declare.rs b/compiler/rustc_codegen_gcc/src/declare.rs index 4174eebcf7b02..376890f0ccfd0 100644 --- a/compiler/rustc_codegen_gcc/src/declare.rs +++ b/compiler/rustc_codegen_gcc/src/declare.rs @@ -179,7 +179,7 @@ fn declare_raw_fn<'gcc>( cx.functions.borrow_mut().insert(name.to_string(), func); #[cfg(feature = "master")] - if name == "rust_eh_personality" { + if name.ends_with("rust_eh_personality") { // NOTE: GCC will sometimes change the personality function set on a function from // rust_eh_personality to __gcc_personality_v0 as an optimization. // As such, we need to create a weak alias from __gcc_personality_v0 to diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 4cc4a2d258d14..c6ee957141438 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -248,8 +248,6 @@ impl CodegenBackend for GccCodegenBackend { { let lto_supported = gccjit::is_lto_supported(); self.lto_supported.store(lto_supported, Ordering::SeqCst); - - gccjit::set_global_personality_function_name(b"rust_eh_personality\0"); } #[cfg(not(feature = "master"))] @@ -290,6 +288,8 @@ impl CodegenBackend for GccCodegenBackend { } fn codegen_crate(&self, tcx: TyCtxt<'_>) -> Box { + #[cfg(feature = "master")] + self.set_personality_function(tcx); Box::new(codegen_crate(self.clone(), tcx)) } @@ -315,6 +315,17 @@ impl CodegenBackend for GccCodegenBackend { } } +impl GccCodegenBackend { + #[cfg(feature = "master")] + fn set_personality_function(&self, tcx: TyCtxt<'_>) { + let personality_symbol = + CString::new(rustc_symbol_mangling::eh_personality_symbol(tcx)).unwrap(); + // FIXME: Change gccjit to store an owned string internally (https://github.com/rust-lang/rust/pull/148413#discussion_r2749777937) + let personality_symbol = Box::leak(personality_symbol.into_boxed_c_str()); + gccjit::set_global_personality_function_name(personality_symbol.to_bytes_with_nul()); + } +} + fn new_context<'gcc, 'tcx>(tcx: TyCtxt<'tcx>) -> Context<'gcc> { let context = Context::default(); if matches!(tcx.sess.target.arch, Arch::X86 | Arch::X86_64) { diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index b2d22876c1858..f1dc6b9cda79a 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -82,7 +82,7 @@ fn prepare_lto( symbols_below_threshold.push(c"__llvm_profile_counter_bias".to_owned()); // LTO seems to discard this otherwise under certain circumstances. - symbols_below_threshold.push(c"rust_eh_personality".to_owned()); + symbols_below_threshold.push(CString::new(cgcx.rust_eh_personality_symbol.clone()).unwrap()); // If we're performing LTO for the entire crate graph, then for each of our // upstream dependencies, find the corresponding rlib and load the bitcode diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index c018ab23c849a..7e4311850003c 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -984,7 +984,14 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { tcx.sess.pointer_authentication_functions(), ), _ => { - let name = name.unwrap_or("rust_eh_personality"); + let mangled_symbol; + let name = match name { + Some(name) => name, + None => { + mangled_symbol = rustc_symbol_mangling::eh_personality_symbol(tcx); + mangled_symbol.as_str() + } + }; if let Some(llfn) = self.get_declared_value(name) { llfn } else { diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 1dd460c409737..dbd003d9730d1 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -320,6 +320,11 @@ impl CodegenBackend for LlvmCodegenBackend { llvm_util::target_has_mnemonic(sess, mnemonic) } + fn can_mangle_eh_personality(&self) -> bool { + // https://github.com/llvm/llvm-project/pull/166095 + llvm_util::get_version() > (22, 0, 0) + } + fn target_config(&self, sess: &Session) -> TargetConfig { target_config(sess) } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 1db2321f7b249..29b563ecfd7d0 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -343,6 +343,7 @@ pub struct CodegenContext { pub split_debuginfo: rustc_target::spec::SplitDebuginfo, pub split_dwarf_kind: rustc_session::config::SplitDwarfKind, pub pointer_size: Size, + pub rust_eh_personality_symbol: String, /// LLVM optimizations for which we want to print remarks. pub remark: Passes, @@ -1298,6 +1299,7 @@ fn start_executing_work( target_is_like_darwin: tcx.sess.target.is_like_darwin, target_is_like_aix: tcx.sess.target.is_like_aix, target_is_like_gpu: tcx.sess.target.is_like_gpu, + rust_eh_personality_symbol: rustc_symbol_mangling::eh_personality_symbol(tcx), split_debuginfo: tcx.sess.split_debuginfo(), split_dwarf_kind: tcx.sess.opts.unstable_opts.split_dwarf_kind, parallel, diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 6014f1af4bfc3..824b0f0a484fa 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -104,6 +104,12 @@ pub trait CodegenBackend { false } + /// Whether the `rust_eh_personality` symbol can be mangled (this is false if the codegen + /// backend hardcodes the name for certain checks). + fn can_mangle_eh_personality(&self) -> bool { + true + } + /// The metadata loader used to load rlib and dylib metadata. /// /// Alternative codegen backends may want to use different rlib or dylib formats than the diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index b0539b973c201..c723a00728df6 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -442,6 +442,8 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se sess.replaced_intrinsics = FxHashSet::from_iter(codegen_backend.replaced_intrinsics()); sess.fallback_intrinsics = FxHashSet::from_iter(codegen_backend.fallback_intrinsics()); sess.thin_lto_supported = codegen_backend.thin_lto_supported(); + sess.codegen_backend_supports_eh_personality_mangling = + codegen_backend.can_mangle_eh_personality(); let cfg = parse_cfg(sess.dcx(), config.crate_cfg); let mut cfg = config::build_configuration(&sess, cfg); diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index eebead6fc1f47..00ce87a2c03be 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -372,6 +372,10 @@ pub struct Session { /// drown everything else in noise. miri_unleashed_features: Lock)>>, + /// Whether the codegen backend supports mangling of the personality. + /// See `CodegenBackend::can_mangle_eh_personality`. + pub codegen_backend_supports_eh_personality_mangling: bool, + /// Architecture to use for interpreting asm!. pub asm_arch: Option, @@ -1363,6 +1367,7 @@ pub fn build_session( driver_lint_caps, ctfe_backtrace, miri_unleashed_features: Lock::new(Default::default()), + codegen_backend_supports_eh_personality_mangling: false, // filled by `run_compiler` asm_arch, target_features: Default::default(), unstable_target_features: Default::default(), diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index d61437212a266..572c54a63c686 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -103,7 +103,7 @@ mod v0; pub mod test; -pub use v0::mangle_internal_symbol; +pub use v0::{eh_personality_symbol, mangle_internal_symbol}; /// This function computes the symbol name for the given `instance` and the /// given instantiating crate. That is, if you know that instance X is diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index a0fe69ca2c59c..5eca538c512ae 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -90,10 +90,15 @@ pub(super) fn mangle<'tcx>( std::mem::take(&mut p.out) } +pub fn eh_personality_symbol<'tcx>(tcx: TyCtxt<'tcx>) -> String { + mangle_internal_symbol(tcx, "rust_eh_personality") +} + pub fn mangle_internal_symbol<'tcx>(tcx: TyCtxt<'tcx>, item_name: &str) -> String { match item_name { - // rust_eh_personality must not be renamed as LLVM hard-codes the name - "rust_eh_personality" => return item_name.to_owned(), + "rust_eh_personality" if !tcx.sess.codegen_backend_supports_eh_personality_mangling => { + return item_name.to_owned(); + } // Apple availability symbols need to not be mangled to be usable by // C/Objective-C code. "__isPlatformVersionAtLeast" | "__isOSVersionAtLeast" => return item_name.to_owned(), diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs index e1aaa2a03880f..6694a6568fdc1 100644 --- a/src/tools/compiletest/src/runtest/run_make.rs +++ b/src/tools/compiletest/src/runtest/run_make.rs @@ -227,6 +227,10 @@ impl TestCx<'_> { cmd.env("LLVM_BIN_DIR", llvm_bin_dir); } + if let Some(ref llvm_version) = self.config.llvm_version { + cmd.env("LLVM_VERSION", llvm_version.to_string()); + } + if let Some(ref remote_test_client) = self.config.remote_test_client { cmd.env("REMOTE_TEST_CLIENT", remote_test_client); } diff --git a/src/tools/run-make-support/src/external_deps/llvm.rs b/src/tools/run-make-support/src/external_deps/llvm.rs index 70bed5a471ab2..29752c0e7653c 100644 --- a/src/tools/run-make-support/src/external_deps/llvm.rs +++ b/src/tools/run-make-support/src/external_deps/llvm.rs @@ -3,6 +3,19 @@ use std::path::{Path, PathBuf}; use crate::command::Command; use crate::env::env_var; +pub fn llvm_version() -> (u32, u32, u32) { + let version_string = env_var("LLVM_VERSION"); + let mut parts = version_string.split("."); + let mut part = || { + parts + .next() + .expect(&format!("invalid LLVM version: {version_string}")) + .parse::() + .expect(&format!("invalid LLVM version: {version_string}")) + }; + (part(), part(), part()) +} + /// Construct a new `llvm-readobj` invocation with the `GNU` output style. /// This assumes that `llvm-readobj` is available at `$LLVM_BIN_DIR/llvm-readobj`. #[track_caller] diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index a686b25c34322..5ea2ad7a5118b 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -76,7 +76,7 @@ pub use crate::external_deps::llvm::{ self, LlvmAr, LlvmBcanalyzer, LlvmDis, LlvmDwarfdump, LlvmFilecheck, LlvmNm, LlvmObjcopy, LlvmObjdump, LlvmProfdata, LlvmReadobj, llvm_ar, llvm_as, llvm_bcanalyzer, llvm_dis, llvm_dwarfdump, llvm_filecheck, llvm_nm, llvm_objcopy, llvm_objdump, llvm_profdata, - llvm_readobj, + llvm_readobj, llvm_version, }; pub use crate::external_deps::python::python_command; pub use crate::external_deps::rustc::{self, Rustc, bare_rustc, rustc, rustc_minicore, rustc_path}; diff --git a/tests/codegen-llvm/gdb_debug_script_load.rs b/tests/codegen-llvm/gdb_debug_script_load.rs index 3e92eba10b121..044d8577df6a9 100644 --- a/tests/codegen-llvm/gdb_debug_script_load.rs +++ b/tests/codegen-llvm/gdb_debug_script_load.rs @@ -14,7 +14,7 @@ fn panic_handler(_: &core::panic::PanicInfo) -> ! { loop {} } -#[no_mangle] +#[lang = "eh_personality"] extern "C" fn rust_eh_personality() { loop {} } diff --git a/tests/run-make/no-alloc-shim/foo.rs b/tests/run-make/no-alloc-shim/foo.rs index a22307f41b39e..247b73f47c9c6 100644 --- a/tests/run-make/no-alloc-shim/foo.rs +++ b/tests/run-make/no-alloc-shim/foo.rs @@ -1,4 +1,4 @@ -#![feature(rustc_attrs)] +#![feature(rustc_attrs, lang_items)] #![no_std] #![no_main] @@ -11,7 +11,7 @@ fn panic_handler(_: &core::panic::PanicInfo) -> ! { loop {} } -#[no_mangle] +#[lang = "eh_personality"] extern "C" fn rust_eh_personality( _version: i32, _actions: i32, diff --git a/tests/run-make/repr128-dwarf/rmake.rs b/tests/run-make/repr128-dwarf/rmake.rs index 38c69d5107337..dead49265797f 100644 --- a/tests/run-make/repr128-dwarf/rmake.rs +++ b/tests/run-make/repr128-dwarf/rmake.rs @@ -10,7 +10,7 @@ use std::rc::Rc; use gimli::read::DebuggingInformationEntry; use gimli::{AttributeValue, EndianRcSlice, Reader, RunTimeEndian}; use object::{Object, ObjectSection}; -use run_make_support::{gimli, object, rfs, rustc}; +use run_make_support::{gimli, llvm_version, object, rfs, rustc}; fn main() { let output = PathBuf::from("repr128"); diff --git a/tests/run-make/symbols-all-mangled/rmake.rs b/tests/run-make/symbols-all-mangled/rmake.rs index 2cf579758002a..4bfee6a136297 100644 --- a/tests/run-make/symbols-all-mangled/rmake.rs +++ b/tests/run-make/symbols-all-mangled/rmake.rs @@ -4,7 +4,9 @@ //@ ignore-cross-compile (host-only) use run_make_support::object::read::{Object, ObjectSymbol}; -use run_make_support::{bin_name, dynamic_lib_name, object, rfs, rustc, static_lib_name}; +use run_make_support::{ + bin_name, dynamic_lib_name, llvm_version, object, rfs, rustc, static_lib_name, +}; fn main() { let staticlib_name = static_lib_name("a_lib"); @@ -19,13 +21,35 @@ fn main() { symbols_check(&exe_name); } +fn is_symbol_ok(sym: &str) -> bool { + let sym = strip_underscore_if_apple(sym); + // There are debuginfo helper symbols that get prefixed with DW.ref. + let sym = sym.strip_prefix("DW.ref.").unwrap_or(sym); + + if sym.starts_with("_ZN") || sym.starts_with("_R") { + return true; // Correctly mangled + } + + if sym.contains(".llvm.") { + // Starting in LLVM 21 we get various implementation-detail functions which + // contain .llvm. that are not a problem. + return true; + } + + if llvm_version() < (22, 0, 0) && sym.contains("rust_eh_personality") { + return true; // LLVM before 22 doesn't allow us to mangle this symbol + } + + false +} + fn symbols_check_archive(path: &str) { let binary_data = rfs::read(path); let file = object::read::archive::ArchiveFile::parse(&*binary_data).unwrap(); for symbol in file.symbols().unwrap().unwrap() { let symbol = symbol.unwrap(); - let name = strip_underscore_if_apple(std::str::from_utf8(symbol.name()).unwrap()); - if name.starts_with("_ZN") || name.starts_with("_R") { + let name = std::str::from_utf8(symbol.name()).unwrap(); + if is_symbol_ok(name) { continue; // Correctly mangled } @@ -35,16 +59,6 @@ fn symbols_check_archive(path: &str) { continue; // All compiler-builtins symbols must remain unmangled } - if name.contains("rust_eh_personality") { - continue; // Unfortunately LLVM doesn't allow us to mangle this symbol - } - - if name.contains(".llvm.") { - // Starting in LLVM 21 we get various implementation-detail functions which - // contain .llvm. that are not a problem. - continue; - } - panic!("Unmangled symbol found in {path}: {name}"); } } @@ -59,8 +73,8 @@ fn symbols_check(path: &str) { if symbol.is_weak() { continue; // Likely an intrinsic from compiler-builtins } - let name = strip_underscore_if_apple(symbol.name().unwrap()); - if name.starts_with("_ZN") || name.starts_with("_R") { + let name = symbol.name().unwrap(); + if is_symbol_ok(name) { continue; // Correctly mangled } @@ -71,16 +85,6 @@ fn symbols_check(path: &str) { continue; } - if name.contains("rust_eh_personality") { - continue; // Unfortunately LLVM doesn't allow us to mangle this symbol - } - - if name.contains(".llvm.") { - // Starting in LLVM 21 we get various implementation-detail functions which - // contain .llvm. that are not a problem. - continue; - } - panic!("Unmangled symbol found in {path}: {name}"); } } diff --git a/tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs b/tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs index 62d352facd178..52256cd834da7 100644 --- a/tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs +++ b/tests/ui/linkage-attr/raw-dylib/elf/glibc-x86_64.rs @@ -6,7 +6,7 @@ //@ ignore-backends: gcc #![allow(incomplete_features)] -#![feature(raw_dylib_elf)] +#![feature(raw_dylib_elf, lang_items)] #![no_std] #![no_main] @@ -69,7 +69,7 @@ fn panic_handler(_: &core::panic::PanicInfo<'_>) -> ! { exit(1); } -#[unsafe(no_mangle)] +#[lang = "eh_personality"] extern "C" fn rust_eh_personality( _version: i32, _actions: i32, From c66c7f180c7fa8160ab484f9f5f1c82f2c39526c Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 9 Jul 2026 17:31:57 +0200 Subject: [PATCH 2/3] Fix LLVM version test The PR in question was already included in LLVM 22. --- compiler/rustc_codegen_llvm/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index dbd003d9730d1..3afe942cdccc7 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -322,7 +322,7 @@ impl CodegenBackend for LlvmCodegenBackend { fn can_mangle_eh_personality(&self) -> bool { // https://github.com/llvm/llvm-project/pull/166095 - llvm_util::get_version() > (22, 0, 0) + llvm_util::get_version() >= (22, 0, 0) } fn target_config(&self, sess: &Session) -> TargetConfig { From 0b190650850bb8d4ce869fc56af7d8c82b518237 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 24 Jul 2026 15:15:26 +0200 Subject: [PATCH 3/3] Move cg_clif eh_personality_symbol call --- compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs | 6 +++--- compiler/rustc_codegen_cranelift/src/driver/aot.rs | 6 +++++- compiler/rustc_codegen_cranelift/src/driver/jit.rs | 6 +++++- compiler/rustc_codegen_cranelift/src/unwind_module.rs | 5 ++--- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs index 094fd0f0d1abe..9599664f9a4ac 100644 --- a/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs +++ b/compiler/rustc_codegen_cranelift/src/debuginfo/unwind.rs @@ -25,7 +25,7 @@ pub(crate) struct UnwindContext { } impl UnwindContext { - pub(crate) fn new(module: &mut dyn Module, tcx: TyCtxt<'_>, pic_eh_frame: bool) -> Self { + pub(crate) fn new(module: &mut dyn Module, eh_personality: &str, pic_eh_frame: bool) -> Self { let endian = match module.isa().endianness() { Endianness::Little => RunTimeEndian::Little, Endianness::Big => RunTimeEndian::Big, @@ -70,7 +70,7 @@ impl UnwindContext { // FIXME use eh_personality lang item instead let personality = module .declare_function( - &rustc_symbol_mangling::eh_personality_symbol(tcx), + eh_personality, Linkage::Import, &Signature { params: vec![ @@ -89,7 +89,7 @@ impl UnwindContext { // Use indirection here to support PIC the case where rust_eh_personality is defined in // another DSO. let personality_ref = module - .declare_data("DW.ref.rust_eh_personality", Linkage::Local, false, false) + .declare_data(&format!("DW.ref.{eh_personality}"), Linkage::Local, false, false) .unwrap(); let mut personality_ref_data = DataDescription::new(); diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index f21d0df8461f6..1512e063f85da 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -62,7 +62,11 @@ fn make_module(tcx: TyCtxt<'_>, cgu_name: &str) -> AotModule { tcx.sess.opts.unstable_opts.function_sections.unwrap_or(default_function_sections), ); - let module = UnwindModule::new(ObjectModule::new(builder), tcx, true); + let module = UnwindModule::new( + ObjectModule::new(builder), + &rustc_symbol_mangling::eh_personality_symbol(tcx), + true, + ); let producer = crate::debuginfo::producer(tcx.sess); let global_asm_config = GlobalAsmConfig::new(tcx.sess); diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index 32f60615844bc..22f1186930924 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -25,7 +25,11 @@ fn create_jit_module( let mut jit_builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names()); crate::compiler_builtins::register_functions_for_jit(&mut jit_builder); jit_builder.symbol_lookup_fn(dep_symbol_lookup_fn(tcx.sess, crate_info.clone())); - let mut jit_module = UnwindModule::new(JITModule::new(jit_builder), false); + let mut jit_module = UnwindModule::new( + JITModule::new(jit_builder), + &rustc_symbol_mangling::eh_personality_symbol(tcx), + false, + ); let cx = DebugContext::new(tcx, jit_module.isa(), false, "dummy_cgu_name"); diff --git a/compiler/rustc_codegen_cranelift/src/unwind_module.rs b/compiler/rustc_codegen_cranelift/src/unwind_module.rs index b294122d1a964..d79960f418128 100644 --- a/compiler/rustc_codegen_cranelift/src/unwind_module.rs +++ b/compiler/rustc_codegen_cranelift/src/unwind_module.rs @@ -7,7 +7,6 @@ use cranelift_module::{ ModuleReloc, ModuleResult, }; use cranelift_object::{ObjectModule, ObjectProduct}; -use rustc_middle::ty::TyCtxt; use crate::UnwindContext; @@ -18,8 +17,8 @@ pub(crate) struct UnwindModule { } impl UnwindModule { - pub(crate) fn new(mut module: T, tcx: TyCtxt<'_>, pic_eh_frame: bool) -> Self { - let unwind_context = UnwindContext::new(&mut module, tcx, pic_eh_frame); + pub(crate) fn new(mut module: T, eh_personality: &str, pic_eh_frame: bool) -> Self { + let unwind_context = UnwindContext::new(&mut module, eh_personality, pic_eh_frame); UnwindModule { module, unwind_context } } }