我试图理解 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]。)现在,我不知道如何获取 64 位代码中的等效地址。
Thanks.
我认为区别在于 amd64 中更鼓励省略帧指针。第 16 页的脚注abi https://refspecs.linuxbase.org/elf/x86-64-abi-0.99.pdf#17 says
可以通过使用来避免传统使用 %rbp 作为堆栈帧的帧指针
%rsp(堆栈指针)来索引堆栈帧。该技术在序言和尾声中保存了两条指令,并提供了一个额外的通用寄存器 (%rbp)。
我不知道GDB是做什么的。我假设当编译时-g
,对象具有神奇的调试信息,允许 GDB 重建它需要的内容。我认为我没有在没有调试信息的情况下在 64 位机器上尝试过 GDB。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)