mini-backtrace ======= [![Crates.io](https://img.shields.io/crates/v/mini-backtrace.svg)](https://crates.io/crates/mini-backtrace) [![Documentation](https://docs.rs/mini-backtrace/badge.svg)](https://docs.rs/mini-backtrace) This crate provides backtrace support for `no_std` and embedded programs. This is done through by compiling LLVM's libunwind with certain flags to remove all OS dependencies, including libc and any memory allocations. ## Usage ### Setup There are two prerequisites for using this crate: 1. Unwind tables must be built into the binary, even with unwinding is set to abort. This can be done with the `-C force-unwind-tables` flag. 2. Several `__eh_frame_*` symbols need to be defined by the linker so that the the unwinding tables can be located by libunwind. This can be done by including the [`eh_frame.ld`] linker script fragment. Both of these can be done by setting `RUSTFLAGS`: ```sh export RUSTFLAGS="-Cforce-unwind-tables -Clink-arg=-Wl,eh_frame.ld" ``` Note that these flags also apply to build-dependencies and proc macros by default. This can be worked around by explicitly specifying a target when invoking cargo: ```sh # Applies RUSTFLAGS to everything cargo build # Doesn't apply RUSTFLAGS to build dependencies and proc macros cargo build --target x86_64-unknown-linux-gnu ``` [`eh_frame.ld`]: https://github.com/Amanieu/mini-backtrace/blob/master/eh_frame.ld ### Capturing backtraces Add the `mini-backtrace` crate as a dependency to your program: ```toml [dependencies] mini-backtrace = "0.1" ``` You can capture a backtrace by using the `Backtrace` type which returns a list of frames as an `ArrayVec` of instruction pointer addresses. ```rust use mini_backtrace::Backtrace; // Capture up to 16 frames. This is returned using an ArrayVec that doesn't // perform any dynamic memory allocation. let bt = Backtrace::<16>::capture(); println!("Backtrace:"); for frame in bt.frames { println!(" {:#x}", frame); } if bt.frames_omitted { println!(" ... "); } ``` This will output: ```text Backtrace: 0x5587058c3eb1 0x5587058c3cdb 0x5587058c491e 0x5587058c38b1 0x5587058daf1a 0x5587058c3890 0x5587058c414c ``` ### Position-independent code If your code is executing at a different address than the one it is linked at then you will need to fix up the frame pointer addresses to be relative to the module base address. This can be done with the following function: ```rust fn adjust_for_pic(ip: usize) -> usize { extern "C" { // Symbol defined by the linker static __executable_start: [u8; 0]; } let base = unsafe { __executable_start.as_ptr() as usize }; ip - base } ``` After post-processing, the output should look like this: ```text Backtrace: 0x8eb1 0x8cdb 0x999e 0x88b1 0x1ffba 0x8890 0x91cc ``` Have a look at `examples/backtrace.rs` for a complete example. Note that `adjust_for_pic` should *only* be called for position-independent binaries. Statically-linked binaries should emit unadjusted addresses so that the backtraces can be correctly resolved. ### Resolving backtraces The addresses generated by `Backtrace` can be converted to human-readable function names, filenames and line numbers by using the `addr2line` tool from LLVM or binutils with [rustfilt] to demangle Rust symbol names. Simply run `addr2line -fipe /path/to/binary | rustfilt` in a terminal and then paste the addresses from the backtrace: ```text $ llvm-addr2line -fipe target/x86_64-unknown-linux-gnu/debug/examples/backtrace | rustfilt 0x8ed1 0x8ea6 0x8e96 0x8cdb 0x99be 0x88b1 0x1ffda 0x8890 0x91ec backtrace::bar at /home/amanieu/code/mini-backtrace/examples/backtrace.rs:15 backtrace::foo at /home/amanieu/code/mini-backtrace/examples/backtrace.rs:10 backtrace::main at /home/amanieu/code/mini-backtrace/examples/backtrace.rs:5 core::ops::function::FnOnce::call_once at /home/amanieu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:227 std::sys_common::backtrace::__rust_begin_short_backtrace at /home/amanieu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys_common/backtrace.rs:128 std::rt::lang_start::{{closure}} at /home/amanieu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:49 std::panicking::try at /rustc/676ee14729462585b969bbc52f32c307403f4126/library/std/src/panicking.rs:344 (inlined by) std::panic::catch_unwind at /rustc/676ee14729462585b969bbc52f32c307403f4126/library/std/src/panic.rs:431 (inlined by) std::rt::lang_start_internal at /rustc/676ee14729462585b969bbc52f32c307403f4126/library/std/src/rt.rs:34 std::rt::lang_start at /home/amanieu/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:48 main at ??:0 ``` [rustfilt]: https://github.com/luser/rustfilt ### Backtraces from signal/interrupt handlers The libunwind unwinder used by this crate is usually unable to unwind past signal handler or interrupt handler frames. Instead, you can use `Backtrace::capture_from_context` and pass in the register state at the point where the exception occurred. In a signal handler this can be obtained through the `uc_mcontext` field of `ucontext_t`. This is currently only implemented for: - AArch64 - RISC-V (RV32 & RV64) ## [Change log](CHANGELOG.md) ## License Licensed under either of: * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.