#backtrace #dwarf #no-std

no-std mini-backtrace

为无std和嵌入式程序提供回溯支持

5个版本

0.1.4 2023年5月7日
0.1.3 2022年9月25日
0.1.2 2021年9月8日
0.1.1 2021年6月11日
0.1.0 2021年5月12日

#322嵌入式开发

Apache-2.0/MIT

230KB
5K SLoC

GNU Style Assembly 2K SLoC // 0.1% comments C++ 1.5K SLoC // 0.2% comments C 1K SLoC // 0.2% comments Rust 194 SLoC // 0.0% comments Python 83 SLoC // 0.7% comments

mini-backtrace

Crates.io Documentation

此crate为no_std和嵌入式程序提供回溯支持。

这是通过编译具有特定标志的LLVM的libunwind来实现的,以移除所有操作系统依赖,包括libc和任何内存分配。

使用方法

设置

使用此crate有两个前提条件

  1. 即使将unwinding设置为中止,也要将unwind表构建到二进制文件中。这可以通过使用-C force-unwind-tables标志来完成。

  2. 需要由链接器定义几个__eh_frame_*符号,以便libunwind可以定位unwind表。这可以通过包含eh_frame.ld链接器脚本片段来完成。

这两个都可以通过设置RUSTFLAGS来实现。

export RUSTFLAGS="-Cforce-unwind-tables -Clink-arg=-Wl,eh_frame.ld"

请注意,默认情况下,这些标志也适用于构建依赖项和proc宏。在调用cargo时显式指定目标可以解决此问题。

# Applies RUSTFLAGS to everything
cargo build

# Doesn't apply RUSTFLAGS to build dependencies and proc macros
cargo build --target x86_64-unknown-linux-gnu

捕获回溯

mini-backtrace crate作为依赖项添加到您的程序中

[dependencies]
mini-backtrace = "0.1"

您可以使用返回指令指针地址的Backtrace类型来捕获回溯,该类型作为ArrayVec返回一个帧列表。

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!(" ... <frames omitted>");
}

这将输出

Backtrace:
  0x5587058c3eb1
  0x5587058c3cdb
  0x5587058c491e
  0x5587058c38b1
  0x5587058daf1a
  0x5587058c3890
  0x5587058c414c

位置无关代码

如果您的代码在与其他链接地址不同的地址上执行,则需要修复帧指针地址以相对于模块基本地址。这可以通过以下函数来完成

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
}

在处理完成后,输出应如下所示

Backtrace:
  0x8eb1
  0x8cdb
  0x999e
  0x88b1
  0x1ffba
  0x8890
  0x91cc

请查看examples/backtrace.rs以获取完整示例。

请注意,adjust_for_pic 应针对位置无关的二进制文件调用。静态链接的二进制文件应发出未调整的地址,以便正确解析回溯。

解析回溯

可以通过使用来自LLVM或binutils的addr2line工具以及使用rustfilt来去混淆Rust符号名称,将Backtrace生成的地址转换为人类可读的函数名称、文件名和行号。

在终端中简单地运行 addr2line -fipe /path/to/binary | rustfilt,然后将回溯中的地址粘贴进去。

$ 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

来自信号/中断处理器的回溯

此crate使用的libunwind回溯器通常无法回溯过信号处理程序或中断处理程序帧。相反,您可以使用Backtrace::capture_from_context并传入异常发生时的寄存器状态。在信号处理程序中,这可以通过uc_mcontext字段获得。

目前仅实现于

  • AArch64
  • RISC-V (RV32 & RV64)

变更日志

许可证

根据您的要求,许可协议可以是以下之一:

任选其一。

贡献

除非您明确表示,否则根据Apache-2.0许可证定义的,任何有意提交以包含在作品中的贡献,都应按上述方式双许可,不附加任何额外的条款或条件。

依赖

~0–2.8MB
~44K SLoC