【发布时间】:2012-01-27 08:41:43
【问题描述】:
我试图理解 GCC (4.4.3) 为在 Ubuntu Linux 下运行的 x86_64 机器生成的可执行代码。特别是,我不明白代码如何跟踪堆栈帧。在过去,在 32 位代码中,我习惯于在几乎每个函数中看到这个“序言”:
push %ebp
movl %esp, %ebp
然后,在函数结束时,会出现一个“尾声”
sub $xx, %esp # Where xx is a number based on GCC's accounting.
pop %ebp
ret
或者干脆
leave
ret
完成同样的事情:
- 将堆栈指针设置为当前帧的顶部,就在 退货地址
- 恢复旧的帧指针值。
在 64 位代码中,正如我通过 objdump 反汇编看到的那样,许多函数不遵循这个约定——它们不推送 %rbp 然后将 %rsp 保存到 %rbp,像 GDB 这样的调试器如何构建一个回溯?
我的真正目标是尝试找出一个合理的地址,当执行到达程序中任意函数的开头时,将其视为用户堆栈的顶部(最高地址),堆栈指针可能在该处下移了。例如,对于“top”,argv 的原始地址是理想的——但我无法从 main 调用的任意函数访问它。起初我以为可以使用旧的回溯方法:追逐保存的帧指针值,直到保存的值为 0——然后,之后的下一个可以算作最高实用值。 (这与获取 argv 的地址不同,但它会做——例如,找出 _start 或任何 _start 调用的堆栈指针值[例如,__libc_start_main]。)现在,我不知道如何获取 64 位代码中的等效地址。
谢谢。
【问题讨论】:
-
确实。不仅仅是
-fomit-frame-pointer。 -
你试过-fno-omit-frame-pointer吗?你能用那个标志编译这个其他代码吗?
-
libunwind的源代码可能有用。 -
感谢所有这三个 cmets。我认为这里的问题是我的库实际上是 GCC libgomp 的修改版本,所以我使用 Gnu 构建系统构建它,并尽可能避免更改默认值。我相信 GCC 默认使用 -O2 编译,我很确定它包括 -fomit-frame-pointer。发布后,但在看到 Firoze 的评论之前,我确实查看了 glibc 的 debug/backtrace.c 的代码,这就是我去寻找 __libc_stack_end 的原因,这就是我找到一个有点合理和通用的解决方案的方法。
-
sub $xx, %esp是序幕的一部分。它在堆栈上保留空间。结语确实add $xx, %esp将堆栈指针返回到指向需要弹出的东西。 (或者在简单的情况下leaveincludesmov %ebp, %esp,所以你可以在不先调整 ESP 的情况下使用它。)
标签: c stack x86-64 calling-convention backtrace