我试图理解 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 这样的调试器如何构建回溯?
我的真正目标是尝试找出一个合理的地址,当执行到达程序中任意函数的开头时,将其视为用户堆栈的顶部(最高地址),堆栈指针可能已向下移动。例如,对于“顶部”,argv 的原始地址是理想的——但我无法从 main 调用的任意函数访问它。起初我以为可以使用旧的回溯方法:追逐保存的帧指针值,直到保存的值为 0——然后,之后的下一个可以算作最高实用值。(这与获取 argv 的地址不同,但它可以——比如说,找出 _start 或任何 _start 调用的堆栈指针值[例如,__libc_start_main]。)现在,我不知道
谢谢。