Undefined reference to _Unwind_Resume
I tried to compile a no_std project with Rust, but setting the eh_unwind_resume lang_item does not seem to work properly. Building in release with cargo or -O with rustc works properly, but a debug build fails with an undefined reference to '_Unwind_Resume'.
Here is the code I tried to get working.
rustc --version --verbose:
rustc 1.25.0-nightly (e6072a7b3 2018-01-13)
binary: rustc
commit-hash: e6072a7b3835f1875e81c9fd27799f9b20a0770c
commit-date: 2018-01-13
host: x86_64-unknown-linux-gnu
release: 1.25.0-nightly
LLVM version: 4.0
We had talked on Hacker News, and while this version compiles for me, it doesn't seem to for @jefftime
https://gist.github.com/steveklabnik/5abd59a8fe7e5abda3db58bd8c208fbb
Steve Klabnik at 2018-01-16 16:30:26
Seems similar to #47442. As mentioned there, projects that don't want unwinding need to compile with panic=abort to work reliably.
Hanna Kruppe at 2018-01-16 16:40:03
Neither setting panic=abort nor specifying the eh_personality, eh_unwind_resume, and panic_fmt lang_items successfully compile with debug settings for me
jefftime at 2018-01-16 19:13:09
Even when you recompile
coreviaxargo?FenrirWolf at 2018-01-17 00:58:46
Might this be related with #47551?
Pietro Albini at 2018-01-23 19:03:43
@pietroalbini Probably not, in that case the link is fine, but the generated binary is broken.
This also fails to link for me (rustc 1.25.0-nightly (79a521bb9 2018-01-15)) with:
Error: linking with `cc` failed: exit code: 1 | = note: "cc" "-Wl,--as-needed" "-Wl,-z,noexecstack" "-m64" "-L" "/home/andy/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "test.test0.rcgu.o" "test.test1.rcgu.o" "test.test2.rcgu.o" "-o" "test" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs" "-L" "/home/andy/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-l" "c" "-Wl,-Bstatic" "/home/andy/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-9d4b7130b0117f1c.rlib" "-Wl,-Bdynamic" = note: test.test0.rcgu.o: In function `_$LT$core..result..Result$LT$T$C$$u20$E$GT$$GT$::unwrap::h4cfe66017ee4cfbf': test0-317d481089b8c8fe83113de504472633.rs:(.text._ZN47_$LT$core..result..Result$LT$T$C$$u20$E$GT$$GT$6unwrap17h4cfe66017ee4cfbfE+0x3d): undefined reference to `_Unwind_Resume' test.test0.rcgu.o: In function `core::result::unwrap_failed::h8821e290e357350d': test0-317d481089b8c8fe83113de504472633.rs:(.text._ZN4core6result13unwrap_failed17h8821e290e357350dE+0x95): undefined reference to `_Unwind_Resume' collect2: error: ld returned 1 exit statusIf I add
-l gcc_s(since the glibc unwinding implementation is inlibgcc_s) to the link command it links and runs correctly. Either:Result's[no_std]implementation is wrong to assumelibgcc_swill be present to provide unwinding support- The
[no_std]link commands are wrong to not includelibgcc_son theXXX-unknown-linux-gnutarget
Attempting to build against
x86_64-unknown-linux-muslsuggests it's the former, that build also fails with a much longer link failure (longer, but still all about_Unwind_XXXfunctions).Andy Caldwell at 2018-01-24 00:25:34
Ah, sorry, my analysis at the end there is bogus, the
#[lang = "eh_unwind_resume"]function is supposed to be used in place of the "default" implementation (which is just extern-ing arust_eh_unwind_resume). See https://github.com/rust-lang/rust/blob/da569fa9ddf8369a9809184d43c600dc06bd4b4d/src/librustc_trans/context.rs#L395-L422.Andy Caldwell at 2018-01-24 01:42:46
Today I've hit the same error with the following:
#![no_std] #![no_main] #![feature(lang_items)] #[link(name="c")] extern "C" {} #[lang = "panic_fmt"] #[no_mangle] pub fn panic_fmt(_: core::fmt::Arguments, _: &'static str, _: u32, _: u32) -> ! { loop {} } #[no_mangle] pub extern "C" fn main(_argc: isize, _arg: *const *const u8) -> isize { let a: Result<(), usize> = Err(42); a.unwrap(); 0 }And with the following added to the corresponding Cargo.toml:
[profile.release] panic="abort" [profile.dev] panic="abort"The above fails to build with
cargo +nightly buildbut builds fine withcargo +nightly build --release. x86_64-unknown-linux-gnu target.Mike Hommey at 2018-03-23 06:25:12
FWIW, when looking at the llvm-ir emitted for unwrap_failed, with opt-level=0, I can see multiple
to label %somelabel unwind label %cleanup, but there are none with opt-level=1. They seem to correspond to the assembly calling_Unwind_Resume.Mike Hommey at 2018-03-23 08:18:27
Hello. I have a similar issue there.
Code here.
Dimitri Sabadie at 2018-09-06 23:26:32
I looked into this a bit more since Rust 1.30 came out. I thought with the ability for stable Rust to allow
[no_std]executables that this issue would be fixed. However, it still persists with a simple call to something like:let x = Some(5); let y = x.unwrap();After a bit more reading on the undefined reference to
_Unwind_Resume, I found out that this seems to be an issue with C++ applications when two or more objects files have been compiled with different versions ofg++and are trying to be linked.g++has different stack unwinding methods depending on how it was compiled, and the linker has trouble in situations where object files differ in how they unwind the stack.Coming back to Rust, I believe the issue lies with the
panic = "abort"inCargo.toml. I don't know enough about C++ org++stack unwinding to really say, but my guess is that by changing the panic handling method on Linux, the Rust object code has problems being linked with system libraries because there's a mismatch in stack unwinding. I'm not sure if this bug is applicable to Rust anymore. It might just be a side effect of trying to use[no_std]Rust executables that link with the C standard library.jefftime at 2018-11-21 01:50:18
I suspect this is (loosely) similar to #55352 where Rustc creates landing pads for unwinding even if those pads are unreachable. A
--releasebuild will strip these (since they are unreachable) but a--debugbuild will not. If those landing pads are explicitly calling_Unwind_Resumerather thaneh_unwind_resume(or similar) that would lead to this issue for debug builds.I doubt this is specifically related to mixing unwind implementations (the
.eh_framessection allows separate translation modules to use different unwind strategies, though there might be an issue if multiple of them are expecting to use different implementations with the same name...).Andy Caldwell at 2018-11-21 15:56:56
Same problem. this code works with
panic=abortand--releasebut not on debug. https://github.com/rust-bitcoin/rust-secp256k1/pull/173Elichai Turkel at 2019-11-03 14:29:51
Updated test case:
#![no_std] #![no_main] #[panic_handler] pub fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} } #[no_mangle] pub extern "C" fn _start() -> ! { let a: Result<(), usize> = Err(42); a.unwrap(); loop {} }Compile with
rustc foo.rs -C panic=abort -C link-args=-nostartfiles:--C panic=abort- disable unwind handling (in theory)-C link-args=-nostartfiles- Don't includecrt0/crt1/etc.as I'm not linking libc at all
error: linking with `cc` failed: exit code: 1 | = note: "cc" "-Wl,--as-needed" "-Wl,-z,noexecstack" "-m64" "-L" "/home/andy/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "foo.foo.7rcbfp3g-cgu.0.rcgu.o" "foo.foo.7rcbfp3g-cgu.1.rcgu.o" "foo.foo.7rcbfp3g-cgu.2.rcgu.o" "foo.foo.7rcbfp3g-cgu.3.rcgu.o" "-o" "foo" "-Wl,--gc-sections" "-pie" "-Wl,-zrelro" "-Wl,-znow" "-nodefaultlibs" "-L" "/home/andy/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-Wl,-Bstatic" "/home/andy/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_std_workspace_core-837ca740df32db0a.rlib" "/home/andy/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-db27c965e824589f.rlib" "/home/andy/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcompiler_builtins-68a4f8466685ed76.rlib" "-nostartfiles" "-Wl,-Bdynamic" = note: foo.foo.7rcbfp3g-cgu.3.rcgu.o: In function `core::result::Result<T,E>::unwrap': foo.7rcbfp3g-cgu.3:(.text._ZN4core6result19Result$LT$T$C$E$GT$6unwrap17hc6a8d8550c5348d4E+0x20): undefined reference to `_Unwind_Resume' foo.foo.7rcbfp3g-cgu.3.rcgu.o:(.data.DW.ref.rust_eh_personality[DW.ref.rust_eh_personality]+0x0): undefined reference to `rust_eh_personality' collect2: error: ld returned 1 exit statusSo there's still references to
_Unwind_Resumeandrust_eh_personality, but where are they? Unleash a debugger!Dump of assembler code for function _ZN4core6result19Result$LT$T$C$E$GT$6unwrap17hc6a8d8550c5348d4E: 0x0000000000000000 <+0>: 48 83 ec 28 sub $0x28,%rsp 0x0000000000000004 <+4>: 48 89 3c 24 mov %rdi,(%rsp) 0x0000000000000008 <+8>: 48 89 74 24 08 mov %rsi,0x8(%rsp) 0x000000000000000d <+13>: 48 8b 04 24 mov (%rsp),%rax 0x0000000000000011 <+17>: 48 85 c0 test %rax,%rax 0x0000000000000014 <+20>: 74 12 je 0x28 <_ZN4core6result19Result$LT$T$C$E$GT$6unwrap17hc6a8d8550c5348d4E+40> 0x0000000000000016 <+22>: eb 00 jmp 0x18 <_ZN4core6result19Result$LT$T$C$E$GT$6unwrap17hc6a8d8550c5348d4E+24> 0x0000000000000018 <+24>: eb 20 jmp 0x3a <_ZN4core6result19Result$LT$T$C$E$GT$6unwrap17hc6a8d8550c5348d4E+58> 0x000000000000001a <+26>: 48 8b 7c 24 18 mov 0x18(%rsp),%rdi 0x000000000000001f <+31>: e8 00 00 00 00 callq 0x24 <_ZN4core6result19Result$LT$T$C$E$GT$6unwrap17hc6a8d8550c5348d4E+36> 0x0000000000000024 <+36>: 0f 0b ud2 0x0000000000000026 <+38>: 0f 0b ud2 0x0000000000000028 <+40>: 48 83 3c 24 00 cmpq $0x0,(%rsp) 0x000000000000002d <+45>: 74 3c je 0x6b <_ZN4core6result19Result$LT$T$C$E$GT$6unwrap17hc6a8d8550c5348d4E+107> 0x000000000000002f <+47>: eb 3f jmp 0x70 <_ZN4core6result19Result$LT$T$C$E$GT$6unwrap17hc6a8d8550c5348d4E+112> 0x0000000000000031 <+49>: 48 83 3c 24 00 cmpq $0x0,(%rsp) 0x0000000000000036 <+54>: 74 31 je 0x69 <_ZN4core6result19Result$LT$T$C$E$GT$6unwrap17hc6a8d8550c5348d4E+105> 0x0000000000000038 <+56>: eb e0 jmp 0x1a <_ZN4core6result19Result$LT$T$C$E$GT$6unwrap17hc6a8d8550c5348d4E+26> 0x000000000000003a <+58>: 48 8b 44 24 08 mov 0x8(%rsp),%rax 0x000000000000003f <+63>: 48 89 44 24 10 mov %rax,0x10(%rsp) 0x0000000000000044 <+68>: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 0x4b <_ZN4core6result19Result$LT$T$C$E$GT$6unwrap17hc6a8d8550c5348d4E+75> 0x000000000000004b <+75>: 48 8d 0d 00 00 00 00 lea 0x0(%rip),%rcx # 0x52 <_ZN4core6result19Result$LT$T$C$E$GT$6unwrap17hc6a8d8550c5348d4E+82> 0x0000000000000052 <+82>: 48 8b 05 00 00 00 00 mov 0x0(%rip),%rax # 0x59 <_ZN4core6result19Result$LT$T$C$E$GT$6unwrap17hc6a8d8550c5348d4E+89> 0x0000000000000059 <+89>: be 2b 00 00 00 mov $0x2b,%esi 0x000000000000005e <+94>: 48 8d 54 24 10 lea 0x10(%rsp),%rdx 0x0000000000000063 <+99>: ff d0 callq *%rax 0x0000000000000065 <+101>: eb 0b jmp 0x72 <_ZN4core6result19Result$LT$T$C$E$GT$6unwrap17hc6a8d8550c5348d4E+114> 0x0000000000000067 <+103>: eb c8 jmp 0x31 <_ZN4core6result19Result$LT$T$C$E$GT$6unwrap17hc6a8d8550c5348d4E+49> 0x0000000000000069 <+105>: eb af jmp 0x1a <_ZN4core6result19Result$LT$T$C$E$GT$6unwrap17hc6a8d8550c5348d4E+26> 0x000000000000006b <+107>: 48 83 c4 28 add $0x28,%rsp 0x000000000000006f <+111>: c3 retq 0x0000000000000070 <+112>: eb f9 jmp 0x6b <_ZN4core6result19Result$LT$T$C$E$GT$6unwrap17hc6a8d8550c5348d4E+107> 0x0000000000000072 <+114>: 0f 0b ud2 0x0000000000000074 <+116>: 48 89 44 24 18 mov %rax,0x18(%rsp) 0x0000000000000079 <+121>: 89 54 24 20 mov %edx,0x20(%rsp) 0x000000000000007d <+125>: eb e8 jmp 0x67 <_ZN4core6result19Result$LT$T$C$E$GT$6unwrap17hc6a8d8550c5348d4E+103>So, no explicit references to either function, presumably they are relocated in?
$ readelf -r foo.foo.7rcbfp3g-cgu.3.rcgu.o Relocation section '.rela.text._ZN4core6result19Result$LT$T$C$E$GT$6unwrap17hc6a8d8550c5348d4E' at offset 0x2c8 contains 4 entries: Offset Info Type Sym. Value Sym. Name + Addend 000000000020 000800000004 R_X86_64_PLT32 0000000000000000 _Unwind_Resume - 4 000000000047 000500000002 R_X86_64_PC32 0000000000000000 .rodata..L__unnamed_1 - 4 00000000004e 000600000002 R_X86_64_PC32 0000000000000000 .data.rel.ro..L__unnam - 4 000000000055 000b00000009 R_X86_64_GOTPCREL 0000000000000000 _ZN4core6result13unwra - 4 Relocation section '.rela.data.rel.ro..L__unnamed_2' at offset 0x328 contains 2 entries: Offset Info Type Sym. Value Sym. Name + Addend 000000000000 000a00000001 R_X86_64_64 0000000000000000 _ZN4core3ptr18real_dro + 0 000000000018 000900000001 R_X86_64_64 0000000000000000 _ZN4core3fmt3num52_$LT + 0 Relocation section '.rela.data.DW.ref.rust_eh_personality' at offset 0x358 contains 1 entry: Offset Info Type Sym. Value Sym. Name + Addend 000000000000 000d00000001 R_X86_64_64 0000000000000000 rust_eh_personality + 0 Relocation section '.rela.eh_frame' at offset 0x370 contains 3 entries: Offset Info Type Sym. Value Sym. Name + Addend 000000000013 000700000002 R_X86_64_PC32 0000000000000000 DW.ref.rust_eh_persona + 0 000000000028 000300000002 R_X86_64_PC32 0000000000000000 .text._ZN4core6result1 + 0 000000000031 000400000002 R_X86_64_PC32 0000000000000000 .gcc_except_table + 0So there's relocation instructions for both functions:
_Unwind_Resumeis inserted into address0x00000020(becoming the destination of thecallqinstruction)rust_eh_personalityis inserted into a.data.DW.ref.rust_eh_personalitysection, and then that address is written into the.eh_framesection as the personality function to use for unwinding.
Performing code-flow analysis on the generated code shows that the relocation site for
_Unwind_Resumeis unreachable (sincecore::result::unwrapis an external symbol). This explains why the release build is fine, as the unreachable code segments (and thus the relocation instructions) are stripped by LLVM. Similarly (inspecting the LLVM-IR) the function is markednounwindand thus no.eh_framesection is generated, thus removing the need for therust_eh_personalityrelocation.Andy Caldwell at 2019-11-04 16:21:51
More analysis... looking at the LLVM-IR, same source code, new command
rustc +nightly foo.rs -C panic=abort -C link-args=-nostartfiles --emit llvm-ir.... ; core::result::Result<T,E>::unwrap ; Function Attrs: inlinehint nounwind nonlazybind define internal void @"_ZN4core6result19Result$LT$T$C$E$GT$6unwrap17h1179f71b032e9d7eE"(i64, i64) unnamed_addr #0 personality i32 (...)* @rust_eh_personality { start: ... bb5: ; preds = %start ... ; invoke core::result::unwrap_failed invoke void @_ZN4core6result13unwrap_failed17hfb1545ecedec37d1E([0 x i8]* noalias nonnull readonly align 1 bitcast (<{ [43 x i8] }>* @0 to [0 x i8]*), i64 43, {}* nonnull align 1 %22, [3 x i64]* noalias readonly align 8 dereferenceable(24) bitcast ({ void (i64*)*, i64, i64, i1 (i64*, %"core::fmt::Formatter"*)* }* @vtable.0 to [3 x i64]*)) to label %unreachable unwind label %cleanup ...So it seems that the
libcorerlibwas built with unwind on panic and thus the expanded template includes the landing pads (and thus implicit references to the stack unwinding infrastructure).I also tested with
xargo(to rebuild libcore)RUSTFLAGS='-C link-arg=-nostartfiles -C panic=abort' xargo run(I had to passpanic=abortin RUSTFLAGS, as[profile.dev]doesn't seem to be applied tocore) and the link was successful.I'm not sure what the right answer is here, either require compiling ones own (non-unwinding)
libcore, orrustupshipping two (or more) libcores, or deferring codegen for unwinding until the final build.Edit -
xargobuildslibcorein release mode, regardless of passing--releaseor not. So you can add[profile.dev] panic=aborttoCargo.toml. Support for-nostartfilesis being discussed in https://github.com/rust-lang/rfcs/pull/2735.Andy Caldwell at 2019-11-05 17:04:00
I hit this, too, with
no_std,panic=abortand usingallocwithx86_64-unknown-linux-muslrustc 1.43.0-nightly (442ae7f04 2020-02-06)ld.lld: error: undefined symbol: _Unwind_Resume >>> referenced by string.rs:579 (src/liballoc/string.rs:579) >>> alloc-6bcd594557d0587e.alloc.6cu7tpd8-cgu.0.rcgu.o:(alloc::string::String::from_utf8_lossy::h9a7aba1aef1d966f) in archive /home/harald/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-musl/lib/liballoc-6bcd594557d0587e.rlib ld.lld: error: undefined symbol: rust_eh_personality >>> referenced by alloc.6cu7tpd8-cgu.0 >>> alloc-6bcd594557d0587e.alloc.6cu7tpd8-cgu.0.rcgu.o:(DW.ref.rust_eh_personality) in archive /home/harald/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-musl/lib/liballoc-6bcd594557d0587e.rlibHarald Hoyer at 2020-02-13 10:50:08
a quickfix for now is to add
lto = truein your profile:[profile.release] overflow-checks = false debug-assertions = false lto = true incremental = false panic = "abort"Wael El Oraiby at 2020-03-31 04:02:26
lto=truedidn't work for me in a profile without other optimizations.Just creating an
_Unwind_Resumesymbol did though. I put this in the module witheh_personalityand friends:/// Workaround for rustc bug: https://github.com/rust-lang/rust/issues/47493 /// /// It shouldn't even be possible to reach this function, thanks to panic=abort, /// but libcore is compiled with unwinding enabled and that ends up making unreachable /// references to this. #[no_mangle] extern "C" fn _Unwind_Resume() -> ! { unreachable!("Unwinding not supported"); }Greg Morenz at 2024-03-30 01:02:42
Same issue as https://github.com/rust-lang/rust/issues/53301, it seems.
Ralf Jung at 2025-01-20 14:24:09
Repro in #53301 against
x86_64-gnu-nooptCI job: https://github.com/rust-lang/rust/issues/53301#issuecomment-2602557106, including that here://@ run-pass //@ compile-flags: -Cpanic=abort //@ ignore-emscripten no no_std executables #![feature(lang_items, rustc_private)] #![no_std] #![no_main] #[macro_use] extern crate alloc; use core::alloc::{GlobalAlloc, Layout}; use alloc::string::ToString; extern crate libc; struct MyAllocator; unsafe impl GlobalAlloc for MyAllocator { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { // This is unsound because we ignore alignment, but good enough for this test. libc::malloc(layout.size()) as _ } unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { libc::free(ptr as _); } } #[global_allocator] static A: MyAllocator = MyAllocator; #[panic_handler] fn panic_handler(_: &core::panic::PanicInfo) -> ! { loop {} } #[no_mangle] extern "C" fn rust_eh_personality() { loop {} } #[no_mangle] extern "C" fn main(_argc: core::ffi::c_int, _argv: *const *const u8) -> core::ffi::c_int { let s = format!("{}", 1_isize); assert_eq!(s, "1".to_string()); let s = format!("test"); assert_eq!(s, "test".to_string()); let s = format!("{test}", test=3_isize); assert_eq!(s, "3".to_string()); let s = format!("hello {}", "world"); assert_eq!(s, "hello world".to_string()); 0 }许杰友 Jieyou Xu (Joe) at 2025-01-20 14:31:34
cc @Amanieu, is this "simply" a consequence of the pre-built standard library being built with panic=unwind?
Chris Denton at 2025-01-20 19:00:52
Yes
bjorn3 at 2025-01-20 19:57:56