有时你须要知道的最重要的信息是什么,你当前的法度榜样状况是若何达到那边的。有一个 backtrace 敕令,它给你供给了法度榜样当前的函数调用链。这篇文┞仿将向你展示如安在 x86_64 上实现客栈展开以生成如许的回溯。
系列索引
这些链接将会跟着其他帖子的宣布而上线。
- 预备情况
- 断点
- 存放器和内存
- ELF 和 DWARF
- 源码和旌旗灯号
- 源码级慢慢履行
- 源码级断点
- 客栈展开
- 攫取变量
- 之后步调
用下面的法度榜样作为例子:
- void a() {
- //stopped here
- }
- void b() {
- a();
- }
- void c() {
- a();
- }
- int main() {
- b();
- c();
- }
如不雅调试器停在 //stopped here' 这行,那么竽暌剐两种办法可以达到:main->b->a或main->c->a`。如不雅我们用 LLDB 设置一个断点,持续履行并请求一个回溯,那么我们将获得以下内容:
- * frame #0: 0x00000000004004da a.out`a() + 4 at bt.cpp:3
- frame #1: 0x00000000004004e6 a.out`b() + 9 at bt.cpp:6
- frame #2: 0x00000000004004fe a.out`main + 9 at bt.cpp:14
- frame #3: 0x00007ffff7a2e830 libc.so.6`__libc_start_main + 240 at libc-start.c:291
- frame #4: 0x0000000000400409 a.out`_start + 41
这解释我们今朝在函数 a 中,a 大年夜函数 b 中跳转,b 大年夜 main 中跳转等等。最后两个帧是编译器若何引导 main 函数的。
如今的问题是我们如安在 x86_64 上实现。最稳健的办法是解析 ELF 文件的 .eh_frame 部分,并解决若何大年夜那边展开客栈,但这会很苦楚。你可以应用 libunwind 或类似的来做,但这很无聊。相反,我们假设编译器以某种方法设置了客栈,我们将手动遍历它。为了做到这一点,我们起首须要懂得客栈的构造。
- High
- | ... |
- +---------+
- | Arg 1 |
- +---------+
- | Arg 2 |
- +---------+
- | Return |
- +---------+
- |Saved EBP|
- +---------+
- | Var 1 |
- +---------+
- | Var 2 |
- +---------+
- | ... |
- Low
如你所见,最后一个客栈帧的帧指针存储在当前客栈帧的开端处,创建一个链接的指针列表。客栈根据这个链表解开。我们可以经由过程查找 DWARF 信息中的返回地址来找出列表中下一帧的函数。一些编译器将忽视跟踪 EBP 的帧基址,因为这可以表示为 ESP 的偏移量,并可以释放一个额外的存放器。即使启用了优化,传递 -fno-omit-frame-pointer 到 GCC 或 Clang 会强迫它遵守我们依附的商定。
推荐阅读
近日,人平易近日报评论快递业“虚胖的快递担保需瘦身”,让快递担保一向存在且愈演愈烈的“过度包装”,收受接收处理方法被提上日程受到广泛存眷。相干数据显示,2016年,我国快>>>详细阅读
本文标题:开发一个Linux调试器(八):堆栈展开
地址:http://www.17bianji.com/lsqh/37719.html
1/2 1