【问题标题】:Call stack backtrace in C在 C 中调用堆栈回溯
【发布时间】:2017-08-22 10:51:50
【问题描述】:

我正在尝试在我的断言/异常处理程序中获取调用堆栈回溯。不能include "execinfo.h" 因此不能使用int backtrace(void **buffer, int size);。 另外,尝试使用__builtin_return_address(),但根据:http://codingrelic.geekhold.com/2009/05/pre-mortem-backtracing.html

...在某些架构上,包括我心爱的 MIPS,只有 __builtin_return_address(0) 有效。MIPS 没有帧指针,因此很难返回堆栈。第0帧可以直接使用返回地址寄存器。

如何重现完整的调用堆栈回溯?

【问题讨论】:

  • "不能包含 "execinfo.h""
  • 因为我的平台缺少它。 (错误1致命错误:execinfo.h: No such file or directory ...)
  • 好吧,那我那是因为缺少帧指针......看起来你的路上有一个巨大的障碍。
  • 你试过#include <execinfo.h>而不是#include "execinfo.h"吗?我不记得它在 gcc 上是否重要,但在某些编译器上确实如此。如果可行,您真的应该使用backtrace(),因为它非常酷。
  • 试过了,很酷但是缺少...

标签: c gcc mips


【解决方案1】:

我已成功使用here 描述的方法,从 MIPS32 上的堆栈中获取调用跟踪。

然后您可以打印出调用堆栈:

void *retaddrs[16];
int n, i;

n = get_call_stack_no_fp (retaddrs, 16);

printf ("CALL STACK: ");
for (i = 0; i < n; i++) {
    printf ("0x%08X ", (uintptr_t)retaddrs[i]);
}
printf ("\r\n");

...如果您有 ELF 文件,则使用 addr2line 将返回地址转换为函数名称:

addr2line -a -f -p -e xxxxxxx.elf addr addr ...

当使用这样的方法时,当然有很多陷阱,包括中断和异常处理程序或代码优化的结果。但是,有时它可能会有所帮助。

【讨论】:

    【解决方案2】:

    我已成功使用@Erki A 建议的方法并描述了here。 以下是该方法的简短摘要:

    问题:
    获取没有帧指针的调用堆栈。 解决方案主要思路: 从汇编代码中得出调试器从调试信息中理解的内容。 我们需要的信息: 1. 寄回地址存放于何处。 2. 堆栈指针递减多少。

    要重现整个堆栈跟踪,需要:

     1. Get the current $sp and $ra 
     2. Scan towards the beginning of the function and look for "addui 
        sp,sp,spofft" command (spofft<0) 
     3. Reprodece prev. $sp (sp- spofft) 
     4. Scan forward and look for "sw r31,raofft(sp)" 
     5. Prev. return address stored at [sp+ raofft] 
    

    上面我描述了一次迭代。当 $ra 为 0 时停止。 如何获得第一个 $ra?

     __builtin_return_address(0) 
    

    如何获得第一个$sp?

     register unsigned sp asm("29");
     asm("" : "=r" (sp));
    

    ***由于我的大部分文件都是用 micro-mips 优化编译的,所以我不得不处理 micro-mips-ISA。 当我尝试分析使用 microMips 优化编译的代码时出现了很多问题(请记住,每一步的目标是重现 prev.ra 和 prev.sp): 这让事情变得更复杂了:

     1. ra ($31) register contain unaligned return address.
        You may find more information at Linked questions.
        The unaligned ra helps you understand that you run over different 
        ISA(micro-mips-isa) 
     2. There are functions that do not move the sp. You can find more           
        information [here][3].
        (If a "leaf" function only modifies the temporary registers and returns 
        to a return statement in its caller's code, then there is no need for 
        $ra to be changed, and there is no need for a stack frame for that 
        function.)
     3. Functions that do not store the ra 
     4. MicroMips instructions can be both - 16bit and 32bit: run over the 
        commnds using unsinged short*.
     5. There are functions that perform "addiu sp, sp, spofft" more than once
     6. micro-mips-isa has couple variations for the same command
        for example: addiu,addiusp.
    

    我决定忽略部分问题,这就是为什么它适用于 95% 的情况。

    【讨论】:

      猜你喜欢
      • 2015-05-17
      • 1970-01-01
      • 1970-01-01
      • 2017-05-22
      • 1970-01-01
      • 2018-07-02
      • 2014-11-30
      • 2016-01-30
      • 2016-02-25
      相关资源
      最近更新 更多