我怎样才能用当前的 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程序的完整调用堆栈。
我所做的是:
- 使用ptrace的PTRACE_ATTACH & PTRACE_GETREGS挂起一个运行 C程序并获取其当前IP和BP
- 使用 PTRACE_PEEKDATA 在 [BP] 和 [BP+4] 处获取数据(或 64 位目标为 +8),这样我就可以获得调用函数的 BP 和 return地址。
因为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
.
检查
我正在使用 GCC 5.4 在 Ubuntu LTS 16.04.1 X86_64 上做一个简单的实验。
实验是获取运行 C程序的完整调用堆栈。
我所做的是:
- 使用ptrace的PTRACE_ATTACH & PTRACE_GETREGS挂起一个运行 C程序并获取其当前IP和BP
- 使用 PTRACE_PEEKDATA 在 [BP] 和 [BP+4] 处获取数据(或 64 位目标为 +8),这样我就可以获得调用函数的 BP 和 return地址。
因为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
.