为什么在这个函数序言中没有“sub rsp”指令,为什么函数参数存储在负rbp偏移量?

这就是我通过阅读一些内存分段文档所理解的:当一个函数被调用时,有一些指令(称为函数序言)将帧指针保存在堆栈上,将堆栈指针的值复制到基指针中并保存一些局部变量的内存。

这是我尝试使用GDB调试的一个简单代码:

void test_function(int a, int b, int c, int d) { int flag; char buffer[10]; flag = 31337; buffer[0] = 'A'; } int main() { test_function(1, 2, 3, 4); } 

调试此代码的目的是了解调用函数时堆栈中发生的情况:因此我必须在执行程序的各个步骤(在调用函数之前和执行期间)检查内存。 虽然我通过检查基指针设法看到返回地址和保存的帧指针之类的东西,但我真的无法理解在反汇编代码之后我要写的内容。

拆解:

 (gdb) disassemble main Dump of assembler code for function main: 0x0000000000400509 : push rbp 0x000000000040050a : mov rbp,rsp 0x000000000040050d : mov ecx,0x4 0x0000000000400512 : mov edx,0x3 0x0000000000400517 : mov esi,0x2 0x000000000040051c : mov edi,0x1 0x0000000000400521 : call 0x4004ec  0x0000000000400526 : pop rbp 0x0000000000400527 : ret End of assembler dump. (gdb) disassemble test_function Dump of assembler code for function test_function: 0x00000000004004ec : push rbp 0x00000000004004ed : mov rbp,rsp 0x00000000004004f0 : mov DWORD PTR [rbp-0x14],edi 0x00000000004004f3 : mov DWORD PTR [rbp-0x18],esi 0x00000000004004f6 : mov DWORD PTR [rbp-0x1c],edx 0x00000000004004f9 : mov DWORD PTR [rbp-0x20],ecx 0x00000000004004fc : mov DWORD PTR [rbp-0x4],0x7a69 0x0000000000400503 : mov BYTE PTR [rbp-0x10],0x41 0x0000000000400507 : pop rbp 0x0000000000400508 : ret End of assembler dump. 

我理解“将帧指针保存在堆栈上”是通过“push rbp”完成的,“将堆栈指针的值复制到基本指针”是由“mov rbp,rsp”完成的,但令我困惑的是缺少“sub rsp $ n_bytes”表示“为局部变量保存一些内存”。 我在很多展览中看到过(甚至在stackoverflow上的一些主题)。

我还读到,参数应该与基指针有一个正偏移(在它填充堆栈指针值之后),因为如果它们位于调用函数中并且堆栈朝向较低地址增长,那么当基本指针时它是完全有意义的。使用堆栈指针值更新编译器通过添加一些正数在堆栈中返回。 但是我的代码似乎将它们存储在负偏移中,就像局部变量一样。我也无法理解为什么它们被放入那些寄存器(在主要中)..它们不应该直接保存在rsp中“偏移” “?

也许这些差异是由于我使用的是64位系统这一事实,但我的研究并没有引导我去解释我所面临的任何问题。

System V ABI for x86-64指定低于%rsp的128字节的red zone 。 这些128个字节属于该函数,只要它不调用任何其他函数(它是leaf function )。 中断处理程序需要尊重红区,因为它们实际上是非自愿的函数调用。
test_function所有局部变量(叶子函数)都适合红色区域,因此不需要调整%rsp 。 (此外,该function没有明显的副作用,并将在任何合理的优化设置上进行优化)。

但是我的代码似乎将它们存储在负偏移中,就像局部变量一样

第一个x86_64参数在寄存器上传递,而不是在堆栈上传递。 因此,当rbp设置为rsp ,它们不在堆栈中,并且不能处于正偏移量。

他们只被推到:

  • 保存第二个函数调用的寄存器状态。

    在这种情况下,这不是必需的,因为它是叶函数 。

  • 使寄存器分配更容易。

    但是优化的分配器可以在没有内存溢出的情况下做得更好。

如果您有以下情况,情况会有所不同:

  • x86_64函数有很多参数。 那些不适合寄存器的东西会进入堆栈。
  • IA-32,其中每个参数都在堆栈上。

缺少“sub rsp $ n_bytes”表示“为局部变量保存一些内存”。

问题的叶子函数部分红区上缺少的sub rsp已经被问到: 为什么x86-64 GCC函数序言分配的堆栈少于局部变量?