Skip to content

It's impossible to write a panic crate for no_std #48661

Closed
@glandium

Description

@glandium

(Originally filed as rust-lang/cargo#5106, but I think it's actually a rustc issue)

I've created a workspace and have multiple crates, all of which are no_std and need a panic_fmt implementation, so I created a separate crate that can be shared for that. Then I built a cdylib which built and linked fine, but failed at runtime with "undefined symbol: rust_begin_unwind".

Here's how to reproduce (on Linux, but I expect this should fail the same on all platforms):

$ cargo new --lib bar
$ cd bar
$ cargo new --lib panic
$ cat > panic/src/lib.rs <<EOF
#![no_std]
#![feature(lang_items)]

#[cfg(not(test))]
#[lang = "panic_fmt"]
#[no_mangle]
pub extern "C" fn panic_fmt(_: core::fmt::Arguments, _: &'static str, _: u32, _: u32) -> ! {
    loop {}
}
EOF
$ cat >> Cargo.toml <<EOF
panic = { path = "panic" }

[profile.dev]
panic = "abort"

[profile.release]
panic = "abort"

[lib]
crate-type = ["cdylib"]
EOF
$ cat > src/lib.rs <<EOF
#![no_std]
extern crate panic;

#[no_mangle]
pub fn foo() -> ! {
    panic!("");
}
EOF
$ cargo +nightly build --release
   Compiling bar v0.1.0 (file:///tmp/bar)
    Finished release [optimized] target(s) in 0.11 secs

Note how this all compiled and linked fine. But:

$ objdump -T target/release/libbar.so 

target/release/libbar.so:     file format elf64-x86-64

DYNAMIC SYMBOL TABLE:
0000000000000000  w   D  *UND*	0000000000000000 __cxa_finalize
0000000000000000  w   D  *UND*	0000000000000000 _ITM_registerTMCloneTable
0000000000000000      D  *UND*	0000000000000000 rust_begin_unwind
0000000000000000  w   D  *UND*	0000000000000000 _ITM_deregisterTMCloneTable
0000000000000000  w   D  *UND*	0000000000000000 __gmon_start__
0000000000000550 g    DF .text	000000000000000f foo

rust_begin_unwind is undefined.

The reason this is happening is because nothing is using the symbol in any library that appears before libpanic on the linker command line, and the only thing that does use the symbol is libcore, which comes after. So the linker happily removes it.

If I force libpanic to be added last on the linker command line, it works:

$ RUSTFLAGS="-C link-arg=target/release/deps/libpanic-3af7779d45985ae9.rlib" cargo +nightly build --release
   Compiling bar v0.1.0 (file:///tmp/bar)
    Finished release [optimized] target(s) in 0.11 secs

$ objdump -T target/release/libbar.so 

target/release/libbar.so:     file format elf64-x86-64

DYNAMIC SYMBOL TABLE:
0000000000000000  w   D  *UND*	0000000000000000 __cxa_finalize
0000000000000000  w   D  *UND*	0000000000000000 _ITM_registerTMCloneTable
0000000000000000  w   D  *UND*	0000000000000000 _ITM_deregisterTMCloneTable
0000000000000000  w   D  *UND*	0000000000000000 __gmon_start__
0000000000000620 g    DF .text	0000000000000002 rust_begin_unwind
0000000000000550 g    DF .text	000000000000000f foo

Relatedly, this makes me think cargo should probably add -Wl,-z,defs:

$ RUSTFLAGS="-C link-arg=-Wl,-z,defs" cargo +nightly build --release
   Compiling bar v0.1.0 (file:///tmp/bar)
error: linking with `cc` failed: exit code: 1
  |
  = note: "cc" "-Wl,--as-needed" "-Wl,-z,noexecstack" "-m64" "-L" "/home/glandium/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "/tmp/bar/target/release/deps/bar.bar0-b17b6035155785f4e639ba83018b4206.rs.rcgu.o" "-o" "/tmp/bar/target/release/deps/libbar.so" "-Wl,--version-script=/tmp/rustc.vWTWjhp8JGrI/list" "-Wl,--gc-sections" "-Wl,-z,relro,-z,now" "-Wl,-O1" "-nodefaultlibs" "-L" "/tmp/bar/target/release/deps" "-L" "/home/glandium/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-Wl,-Bstatic" "/tmp/bar/target/release/deps/libpanic-3af7779d45985ae9.rlib" "/home/glandium/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-80b45058e2fc80f9.rlib" "-shared" "-Wl,-z,defs" "-Wl,-Bdynamic"
  = note: /home/glandium/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-80b45058e2fc80f9.rlib(core-80b45058e2fc80f9.core0.rcgu.o): In function `core::panicking::panic_fmt':
          /checkout/src/libcore/num/bignum.rs:263: undefined reference to `rust_begin_unwind'
          collect2: error: ld returned 1 exit status

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-enhancementCategory: An issue proposing an enhancement or a PR with one.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions