代码之家  ›  专栏  ›  技术社区  ›  Zhou

如何使用当前IP和BP寄存器计算出完整的调用堆栈?

  •  4
  • Zhou  · 技术社区  · 7 年前

    我正在用GCC 5.4在Ubuntu LTS 16.04.1 X86\u 64上做一个简单的实验。

    这个实验是为了得到一个正在运行的C程序的完整调用堆栈。

    我所做的是:

    • 使用ptrace的ptrace_ATTACH&PTRACE\u GETREGS暂停正在运行的C程序并获取其当前IP和BP。
    • 使用PTRACE\u PEEKDATA获取[BP]和[BP+4](或64位目标的+8)处的数据,这样我就可以得到调用函数的BP和返回地址。

    因为BPs是一个链,所以我应该能够获得一系列返回地址。之后,通过使用列表文件或dwarf数据分析地址序列,我最终应该能够了解完整的调用堆栈。类似于“main-->funcA-->函数-->函数…'。

    我的问题是,如果调用堆栈完全在测试程序的代码中,那么这种方法就可以正常工作。我的意思是,每个函数都是我写的。但是,如果测试程序在CRT或系统API中停止,例如“scanf”或“sleep”,则BP链将不再工作。

    我检查了分解,发现CRT或系统API函数不像我的函数那样通过“push ebp”和“mov ebp,esp”建立堆栈框架。难怪上述方法不起作用。但我无法解释为什么GDB在这种情况下仍能正常工作?!所以,关于Linux C程序的调用堆栈,我肯定有很多事情不知道。

    你能理解我的错误/误解吗?或者你可以简单地建议一些文章/链接让我阅读吗?非常感谢你。

    1 回复  |  直到 7 年前
        1
  •  3
  •   Employed Russian    7 年前

    因为BPs是一条链

    他们是 。以前是在上使用帧指针链 i386 ,但几年来,GCC默认为 -fomit-frame-pointer 在优化编译中,即使在 i386 。在…上 x86_64 这个 -fno-omit-frame-pointer 从不 优化代码中的默认值。

    如果调用堆栈完全在我的测试程序代码中,那么这很好。

    这将 只有 如果编译时没有进行优化,则可以工作(如果 而且 使用 -fno忽略帧指针 )。

    我无法解释为什么GDB在这种情况下仍能正常工作

    GDB(和 libunwind )使用 DWARF 展开信息,您可以使用它进行检查 readelf -wf a.out