我希望我的异常处理程序和调试函数能够打印调用堆栈(call stack)的回溯信息,就像glibc库中的backtrace()函数一样。不幸的是,我的C库(Newlib)没有提供这样的功能。
我有类似以下的代码:
更新:我在旧的 ARM7 系统上尝试了这个回溯代码,但使用了相同(或至少尽可能等价)的编译器选项和链接脚本,它打印出了一个正确、完整的回溯(即 func1 和 func2 没有丢失),而且甚至能够回溯到初始化引导代码之前的 main。因此,问题不是链接器脚本或编译器选项造成的。(还从汇编代码中确认,在这个 ARM7 测试中没有使用帧指针)。
该代码是使用 -fomit-frame-pointer 编译的,但我的平台(bare metal ARM Cortex M3)定义了不使用帧指针的 ABI。(先前版本的系统在 ARM7 上使用了旧的 APCS ABI 强制使用堆栈帧和帧指针,并且像这里一样的回溯完全正常)。
整个系统都使用 -fexception 编译,这确保了 _Unwind 使用的必要元数据包含在 ELF 文件中。(_Unwind 是为异常处理而设计的吧)
所以,我的问题是: 有没有使用 GCC 在嵌入式系统中获取可靠回溯的“标准”、公认的方法?
如果必要,我不介意在链接器脚本和 crt0 代码中进行混淆,但不想对工具链本身进行任何更改。
谢谢!
我有类似以下的代码:
#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()。(这显然是个玩具示例,但我已经检查过汇编代码并确认这些函数都完整存在,没有被优化掉或内联)。更新:我在旧的 ARM7 系统上尝试了这个回溯代码,但使用了相同(或至少尽可能等价)的编译器选项和链接脚本,它打印出了一个正确、完整的回溯(即 func1 和 func2 没有丢失),而且甚至能够回溯到初始化引导代码之前的 main。因此,问题不是链接器脚本或编译器选项造成的。(还从汇编代码中确认,在这个 ARM7 测试中没有使用帧指针)。
该代码是使用 -fomit-frame-pointer 编译的,但我的平台(bare metal ARM Cortex M3)定义了不使用帧指针的 ABI。(先前版本的系统在 ARM7 上使用了旧的 APCS ABI 强制使用堆栈帧和帧指针,并且像这里一样的回溯完全正常)。
整个系统都使用 -fexception 编译,这确保了 _Unwind 使用的必要元数据包含在 ELF 文件中。(_Unwind 是为异常处理而设计的吧)
所以,我的问题是: 有没有使用 GCC 在嵌入式系统中获取可靠回溯的“标准”、公认的方法?
如果必要,我不介意在链接器脚本和 crt0 代码中进行混淆,但不想对工具链本身进行任何更改。
谢谢!