I encountered an issue where I tried to compile a Rust static library with #![no_std] and -C panic=abort. However, when linking the resulting library with a C program, I received the following error:
/usr/bin/ld: libadd-withoutopt.a(core-d453bab70303062c.core.51655a4ef8d536d2-cgu.0.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
Steps to Reproduce
Rust source (add.rs):
#![no_std]
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
#[no_mangle]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
C code (test.c):
#include <stdio.h>
extern int add(int a, int b);
int main() {
int result = add(1, 2);
printf("The result is: %d\n", result);
return 0;
}
/usr/bin/ld: libadd-withoutopt.a(core-d453bab70303062c.core.51655a4ef8d536d2-cgu.0.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
However, if I add -C opt-level=3, the linking succeeds:
$ rustc -C panic=abort -C opt-level=3 --crate-type staticlib add.rs -o libadd-withopt.a
$ gcc test.c libadd-withopt.a -o main
$ ./main
The result is: 3
Questions:
Why does using -C panic=abort without optimization still lead to an undefined reference to rust_eh_personality? My understanding was that this setting should prevent any references to Rust's panic machinery.
Does setting -C opt-level=3 optimize out the unused rust_eh_personality symbol and make gcc runs fine, or is there another mechanism at play here?
If you are compiling for a target which by default uses panic=unwind, the precompiled standard library is compiled with panic=unwind and thus references rust_eh_personality even when your own code uses panic=abort. If you compile for a target which is always panic=abort you won't have this issue. And if you use the unstable -Zbuild-std flag of cargo, the standard library will be compiled with panic=abort and thus doesn'5 have the issue either.
Hello bjorn3, Thank you for the detailed explanation!
Following your advice, I recompiled the add library with -Zbuild-std and set panic=abort to avoid linking against the unwind features in the standard library.
Here's what I did:
I created a new Cargo library project with the same code:
cargo new --lib add
and replaced the content of src/lib.rs with:
#![no_std]
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
#[no_mangle]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
I attempted to link the resulting libadd.a with test.c:
gcc ../test.c target/x86_64-unknown-linux-gnu/debug/libadd.a -o main
However, I still encountered the following error:
/usr/bin/ld: target/x86_64-unknown-linux-gnu/debug/libadd.a(core-69a355373b02aaef.core.b9105654efbf3ff8-cgu.04.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
From your previous message, recompiling core with panic=abort should avoid pulling in unwind dependencies, but why it still give a undefined reference
If you do cargo rustc -- -Cpanic=abort only your own crate will use panic=abort, not any of your dependencies (including the standard library) You need to use CARGO_PROFILE_DEV_PANIC=abort (or CARGO_PROFILE_RELEASE_PANIC=abort if doing a release build) instead.
Hello bjorn3, thanks again! Using CARGO_PROFILE_DEV_PANIC=abort with cargo rustc as you suggested worked perfectly. I was able to link test.c successfully, and the custom panic handler (infinite loop) was invoked as expected when adding 2147483647(2^31 - 1) and 1, resulting in a panic condition.
I do have an additional question:
When I compile the library with cargo rustc --release --crate-type=staticlib -- -C panic=abort (without recompiling core to get rid of unwind but with --release, which seems to strip out unused symbols), the linking with test.c is also successful. However, when running the output, the behavior deviates from the custom panic handler I defined. Instead of the expected infinite loop, it appears to use wrapping_add and outputs -2147483648.
Is this behavior expected? Since it bypasses my custom panic handler, would this be considered undefined behavior (UB)? Would this be something worth opening an issue for?
By default overflow checks are disabled in release builds, and the behaviour is defined to be wrapping for both signed and unsigned (so no risk of UB, unlike C). You can change this in your profile in your Cargo.toml. Or via environment variables as well probably.