我怎样才能用当前的 IP 和 BP 寄存器找出完整的调用堆栈?

How can I figure out the full call stack with the current IP and BP registers?

我正在使用 GCC 5.4 在 Ubuntu LTS 16.04.1 X86_64 上做一个简单的实验。

实验是获取运行 C程序的完整调用堆栈。

我所做的是:

因为BP是一条链,所以我应该可以得到一个return个地址序列。之后,通过使用列表文件或 dwarf 数据分析地址序列,我最终应该能够计算出完整的调用堆栈。类似于 'main --> funcA --> funcB --> funcC ...'.

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

我检查了 disassambly 并注意到 CRT 或系统 API 函数不像我的函数那样通过 'push ebp' 和 'mov ebp,esp' 建立堆栈框架。难怪为什么上述方法不起作用。但是我无法解释为什么 GDB 在这种情况下仍然可以正常工作?! Linux C程序的调用栈肯定有很多我不知道的地方

你能算出我的 mistake/misunderstanding 吗?或者你能简单地推荐一些 articles/links 给我阅读吗?非常感谢。

Because the BPs are a chain

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

this works fine if the call stack is totally inside my test programme's code.

这将 如果你在没有优化的情况下编译(或者如果你 使用 -fno-omit-frame-pointer 进行优化)。

I cannot explain why GDB can still work properly in such case

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

检查