I am compiling the Rust code of an rlib to LLVM IR, and then using Clang to compile & link it with a C program. This works until my code contains panics, at which point I get linker errors:
ld.lld: error: undefined symbol: core::panicking::panic_bounds_check::hc3a71010bf41c72d
>>> referenced by ld-temp.o
>>> lto.tmp:(run)
>>> referenced by ld-temp.o
>>> lto.tmp:(run)
>>> referenced by ld-temp.o
>>> lto.tmp:(run)
>>> referenced 11 more times
ld.lld: error: undefined symbol: core::panicking::panic::hd695e3b1d0dd4ef4
>>> referenced by ld-temp.o
>>> lto.tmp:(run)
clang: error: ld.lld command failed with exit code 1 (use -v to see invocation)
I have tried two things to mitigate this:
I have added a panic_handler
to my library:
use core::panic::PanicInfo;
#[panic_handler]
pub extern fn panic(_: &PanicInfo<'_>) -> ! {
loop{}
}
I have set the panic
mode to abort
in my Cargo.toml
:
[profile.release]
panic = "abort"
Neither on its own, nor combined, solves the issue.
In the comments, @Solomon Ucko requested more details on the whole compilation pipeline. As I wrote in the tags, this is with no_std
; also, the compilation target is MOS 6502. This is the full list of commands to compile and (try to) link:
llvm-mos/bin/clang --config llvm-mos-sdk/build/commodore/64.cfg \
-O2 -c \
-o _build/main.c.o \
src/main.c
cargo rustc --release -- \
-C debuginfo=0 -C opt-level=1 --emit=llvm-ir
llvm-mos/bin/clang --config llvm-mos-sdk/build/commodore/64.cfg \
-O2 \
-o _build/charset.prg \
_build/main.c.o \
target/release/deps/chip8_c64-e21ff59526dd729a.ll
Based on this answer, I worked around this by passing -Z build-std
to Cargo, thereby getting the LLVM IR from Rust's core
library, and linking it in.
But then I realized I can do one better, and avoid the long compilation time imposed by -Z std
, by just taking the definitions of
core::panicking::panic_bounds_check
and core::panicking::panic
from these IR files, simplifying their body, and adding them to a hand-written panic.ll
file:
%"panic::location::Location" = type { { [0 x i8]*, i64 }, i32, i32 }
; core::panicking::panic
; Function Attrs: cold noinline noreturn nounwind nonlazybind
define void @_ZN4core9panicking5panic17hd695e3b1d0dd4ef4E([0 x i8]* noalias nonnull readonly align 1 %expr.0, i64 %expr.1, %"panic::location::Location"* noalias readonly align 8 dereferenceable(24) %0) unnamed_addr #22 {
start:
unreachable
}
; core::panicking::panic_bounds_check
; Function Attrs: cold noinline noreturn nounwind nonlazybind
define void @_ZN4core9panicking18panic_bounds_check17hc3a71010bf41c72dE(i64 %0, i64 %1, %"panic::location::Location"* noalias readonly align 8 dereferenceable(24) %2) unnamed_addr #22 {
start:
unreachable
}
TBH I'm not sure how robust that is (maybe they should loop by branchin to start
before `unreachable?), but at least it got me to a fully linked and runnable program.