我希望我的异常处理程序和调试函数能够打印调用堆栈回溯,基本上就像 glibc 中的 backtrace() 库函数一样。不幸的是,我的 C 库(Newlib)不提供这样的调用。
我有这样的事情:
#include <unwind.h> // GCC's internal unwinder, part of libgcc
_Unwind_Reason_Code trace_fcn(_Unwind_Context *ctx, void *d)
{
int *depth = (int*)d;
printf("\t#%d: program counter at %08x\n", *depth, _Unwind_GetIP(ctx));
(*depth)++;
return _URC_NO_REASON;
}
void print_backtrace_here()
{
int depth = 0;
_Unwind_Backtrace(&trace;_fcn, &depth;);
}
这基本上可以工作,但生成的痕迹并不总是完整的。例如,如果我这样做
int func3() { print_backtrace_here(); return 0; }
int func2() { return func3(); }
int func1() { return func2(); }
int main() { return func1(); }
回溯仅显示 func3() 和 main()。 (这显然是一个玩具示例,但我已经检查了反汇编并确认这些函数都完整地存在,并且没有优化或内联。)
Update:我在旧的 ARM7 系统上尝试了这个回溯代码,但使用相同(或至少尽可能等效)的编译器选项和链接器脚本,它打印了正确的、完整的回溯(即 func1 和 func2 没有丢失),而且确实如此甚至回溯过去的 main 进入启动初始化代码。因此,问题可能不在于链接器脚本或编译器选项。 (另外,从反汇编中确认,在此 ARM7 测试中也没有使用帧指针)。
该代码是使用 -fomit-frame-pointer 编译的,但我的平台(裸机 ARM Cortex M3)定义了一个不使用帧指针的 ABI。 (该系统的先前版本在 ARM7 上使用旧的 APCS ABI,具有强制堆栈帧和帧指针,以及像下面这样的回溯here http://csg.lbl.gov/pipermail/vxwexplo/2004-February/004397.html,效果非常好)。
整个系统使用 -fexception 进行编译,这确保 _Unwind 使用的必要元数据包含在 ELF 文件中。 (我认为 _Unwind 是为异常处理而设计的)。
所以,我的问题是:是否有一种“标准”、公认的方法可以使用 GCC 在嵌入式系统中获得可靠的回溯?
我不介意在必要时弄乱链接器脚本和 crt0 代码,但不想给工具链本身带来任何机会。
Thanks!