如何获得调用堆栈回溯? (深嵌,无图书馆支持)

我希望我的异常处理程序和调试功能能够打印调用堆栈回溯,基本上就像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 %08xn", *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不会丢失)和事实上它甚至可以追溯到主引导初始化代码。 所以大概问题不在于链接器脚本或编译器选项。 (另外,从反汇编中确认,在这个ARM7测试中也没有使用帧指针)。

代码是用-fomit-frame-pointer编译的,但是我的平台(裸机ARM Cortex M3)定义了一个ABI,它不会使用帧指针。 (该系统的以前版本使用ARM7上的旧APCS ABI,带有强制堆栈帧和帧指针,以及像这里一样的回溯,其工作完美)。

整个系统用-fexception编译,确保_Unwind使用的必要元数据包含在ELF文件中。 (_Unwind是为我认为的异常处理而设计的)。

所以,我的问题是: 在使用GCC的嵌入式系统中是否有一种“标准”,可接受的方式来获得可靠的回溯?

如果有必要,我不介意链接器脚本和crt0代码,但不想让工具链本身有任何机会。

谢谢!


为此,您需要-funwind-tables-fasynchronous-unwind-tables在某些目标中,为了使_Unwind_Backtrace正常工作,这是必需的!


gcc确实会返回优化。 在func1()和func2()中它不会调用func2()/ func3() - 而是跳转到func2()/ func3(),所以func3()可以立即返回到main()。

在你的情况下,func1()和func2()不需要设置堆栈帧,但是如果他们会这样做(例如对于局部变量),如果函数调用是最后一条指令,gcc仍然可以进行优化 - 然后它会清理在跳转到func3()之前调用堆栈。

查看生成的汇编代码以查看它。


编辑/更新:

为了验证这是什么原因,在函数调用之后做一些无法由编译器重新排序的内容(例如使用返回值)。 或者试试用-O0编译。


由于ARM平台不使用帧指针,因此您不会完全知道堆栈帧的大小,不能简单地将堆栈超出R14中的单个返回值。

在调查没有调试符号的崩溃时,我们只需转储整个堆栈并查找指令范围中每个项目的最近符号。 它确实产生了一些误报,但对于调查崩溃仍然非常有用。

如果您正在运行纯ELF可执行文件,则可以将调试符号从发行版可执行文件中分离出来。 然后,gdb可以帮助您从标准的unix核心转储中找出正在发生的事情

链接地址: http://www.djcxy.com/p/86059.html

上一篇: How to get a call stack backtrace? (deeply embedded, no library support)

下一篇: Catching segfaults in C