x86_64调用约定和堆栈帧

Ami*_*ram 13 c stack x86-64 backtrace calling-convention

我试图理解GCC(4.4.3)为在Ubuntu Linux下运行的x86_64机器生成的可执行代码.特别是,我不明白代码如何跟踪堆栈帧.在过去,在32位代码中,我习惯于在几乎每个函数中看到这个"序幕":

push %ebp
movl %esp, %ebp
Run Code Online (Sandbox Code Playgroud)

然后,在功能结束时,也会出现"结局"

sub $xx, %esp   # Where xx is a number based on GCC's accounting.
pop %ebp
ret
Run Code Online (Sandbox Code Playgroud)

或者干脆

leave
ret
Run Code Online (Sandbox Code Playgroud)

这完成了同样的事情:

  • 将堆栈指针设置为当前帧的顶部,就在返回地址的正下方
  • 恢复旧的帧指针值.

在64位代码中,正如我通过objdump反汇编看到的那样,许多函数不遵循这个约定 - 它们不会推送%rbp然后将%rsp保存到%rbp,像GDB这样的调试器如何构建回溯?

我的真正目标是尝试找出一个合理的地址,当执行到达程序中的任意函数的开始时,可​​以将堆栈指针向下移动时作为用户堆栈的顶部(最高地址).例如,对于"top",argv的原始地址是理想的 - 但是我无法从主要调用的任意函数访问它.我一开始以为我可以使用旧的回溯方法:追踪保存的帧指针值,直到保存的值为0 - 然后,下一个可以算作最高实际值.(这与获取argv的地址不同,但它会 - 比如说,找出_start或任何_start调用的堆栈指针值[例如__libc_start_main].)现在,我不知道如何获得64位代码中的等效地址.

谢谢.

Adr*_*ala 5

我认为区别在于在amd64中更省略省略帧指针.abi第16页的脚注说

通过使用%rsp(堆栈指针)索引到堆栈帧,可以避免常规使用%rbp作为堆栈帧的帧指针.这种技术在序言和结语中保存了两条指令,并提供了一个额外的通用寄存器(%rbp).

我不知道GDB做了什么.我假设在编译时-g,对象具有魔术调试信息,允许GDB重建它所需的内容.我不认为我在没有调试信息的情况下在64位机器上尝试过GDB.