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
Seems similar to #47442. As mentioned there, projects that don't want unwinding need to compile with panic=abort to work reliably.
Neither setting panic=abort nor specifying the eh_personality, eh_unwind_resume, and panic_fmt lang_items successfully compile with debug settings for me
Even when you recompile core via xargo?
Might this be related with #47551?
@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 status
If I add -l gcc_s (since the glibc unwinding implementation is in libgcc_s) to the link command it links and runs correctly. Either:
Result's [no_std] implementation is wrong to assume libgcc_s will be present to provide unwinding support[no_std] link commands are wrong to not include libgcc_s on the XXX-unknown-linux-gnu targetAttempting to build against x86_64-unknown-linux-musl suggests it's the former, that build also fails with a much longer link failure (longer, but still all about _Unwind_XXX functions).
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 a rust_eh_unwind_resume). See https://github.com/rust-lang/rust/blob/da569fa9ddf8369a9809184d43c600dc06bd4b4d/src/librustc_trans/context.rs#L395-L422.
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 build but builds fine with cargo +nightly build --release. x86_64-unknown-linux-gnu target.
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.
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 of g++ 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" in Cargo.toml. I don't know enough about C++ or g++ 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.
I suspect this is (loosely) similar to #55352 where Rustc creates landing pads for unwinding even if those pads are unreachable. A --release build will strip these (since they are unreachable) but a --debug build will not. If those landing pads are explicitly calling _Unwind_Resume rather than eh_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_frames section 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...).
Same problem. this code works with panic=abort and --release but not on debug.
https://github.com/rust-bitcoin/rust-secp256k1/pull/173
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 include crt0/crt1/etc. as I'm not linking libc at allerror: 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 status
So there's still references to _Unwind_Resume and rust_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 + 0
So there's relocation instructions for both functions:
_Unwind_Resume is inserted into address 0x00000020 (becoming the destination of the callq instruction)rust_eh_personality is inserted into a .data.DW.ref.rust_eh_personality section, and then that address is written into the .eh_frame section as the personality function to use for unwinding.Performing code-flow analysis on the generated code shows that the relocation site for _Unwind_Resume is unreachable (since core::result::unwrap is 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 marked nounwind and thus no .eh_frame section is generated, thus removing the need for the rust_eh_personality relocation.
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 libcore rlib was 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 pass panic=abort in RUSTFLAGS, as [profile.dev] doesn't seem to be applied to core) and the link was successful.
I'm not sure what the right answer is here, either require compiling ones own (non-unwinding) libcore, or rustup shipping two (or more) libcores, or deferring codegen for unwinding until the final build.
_Edit_ - xargo builds libcore in release mode, regardless of passing --release or not. So you can add [profile.dev] panic=abort to Cargo.toml. Support for -nostartfiles is being discussed in https://github.com/rust-lang/rfcs/pull/2735.
I hit this, too, with no_std, panic=abort and using alloc with x86_64-unknown-linux-musl
rustc 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.rlib
a quickfix for now is to add lto = true in your profile:
[profile.release]
overflow-checks = false
debug-assertions = false
lto = true
incremental = false
panic = "abort"
Most helpful comment
a quickfix for now is to add
lto = truein your profile: