有时你需要知道的最重要的信息是什么,你当前的程序状态是如何到达那里的。有一个 backtrace
命令,它给你提供了程序当前的函数调用链。这篇文章将向你展示如何在 x86_64 上实现堆栈展开以生成这样的回溯。
系列索引
这些链接将会随着其他帖子的发布而上线。
用下面的程序作为例子:
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
| ... |
++
+16| Arg 2 |
++
EBP+--> |Saved EBP|
++
ESP+--> | Var 2 |
+
via: <https://blog.tartanllama.xyz/c++/2017/06/24/writing-a-linux-debugger-unwinding/>
作者:[Simon Brand](https://twitter.com/TartanLlama) 译者:[geekpi](https://github.com/geekpi) 校对:[wxy](https://github.com/wxy)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
发表回复