为什么没有" sub rsp"这个函数序言中的指令以及为什么函数参数存储在负rbp偏移中?

时间:2015-02-24 10:53:26

标签: c assembly stack x86-64 red-zone

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

这是我尝试使用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 <+0>: push   rbp
   0x000000000040050a <+1>: mov    rbp,rsp
   0x000000000040050d <+4>: mov    ecx,0x4
   0x0000000000400512 <+9>: mov    edx,0x3
   0x0000000000400517 <+14>:    mov    esi,0x2
   0x000000000040051c <+19>:    mov    edi,0x1
   0x0000000000400521 <+24>:    call   0x4004ec <test_function>
   0x0000000000400526 <+29>:    pop    rbp
   0x0000000000400527 <+30>:    ret    
End of assembler dump.
(gdb) disassemble test_function 
Dump of assembler code for function test_function:
   0x00000000004004ec <+0>: push   rbp
   0x00000000004004ed <+1>: mov    rbp,rsp
   0x00000000004004f0 <+4>: mov    DWORD PTR [rbp-0x14],edi
   0x00000000004004f3 <+7>: mov    DWORD PTR [rbp-0x18],esi
   0x00000000004004f6 <+10>:    mov    DWORD PTR [rbp-0x1c],edx
   0x00000000004004f9 <+13>:    mov    DWORD PTR [rbp-0x20],ecx
   0x00000000004004fc <+16>:    mov    DWORD PTR [rbp-0x4],0x7a69
   0x0000000000400503 <+23>:    mov    BYTE PTR [rbp-0x10],0x41
   0x0000000000400507 <+27>:    pop    rbp
   0x0000000000400508 <+28>:    ret    
End of assembler dump.

我理解&#34;将帧指针保存在堆栈上#34;由&#34;完成push rbp&#34;,&#34;将堆栈指针的值复制到基指针&#34;由&#34; mov rbp,rsp&#34;完成但令我困惑的是缺少一个&#34; sub rsp $ n_bytes&#34; for&#34;为局部变量保存一些内存&#34;。我在很多展览中都看到了这一点(即使是在stackoverflow上的一些主题)。

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

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

2 个答案:

答案 0 :(得分:6)

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

答案 1 :(得分:2)

  

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

一样

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

他们只被推到:

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

    在这种情况下,这不是必需,因为它是leaf function

  • 使寄存器分配更容易。

    但是优化的分配器可以在没有memory spill的情况下做得更好。

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

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

缺少&#34; sub rsp $ n_bytes&#34; for&#34;为局部变量保存一些内存&#34;。

问题的叶子功能部分红色区域缺少sub rsp已经在Why does the x86-64 GCC function prologue allocate less stack than the local variables?

询问了