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

Mar*_*ark 5 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);
}
Run Code Online (Sandbox Code Playgroud)

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

拆解:

(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.
Run Code Online (Sandbox Code Playgroud)

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

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

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

EOF*_*EOF 7

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

  • 中断处理程序在内核堆栈上运行。*信号*处理程序将是异步“您的进程正在临时运行其他代码”事件的更好示例。 (2认同)
  • 更新:[内核代码不能使用 red-zone](/sf/ask/1805118591/#38043511) 特别是因为 x86 -64 中断处理不能遵守红区。堆栈上只能有一个不用于硬件中断的红色区域。因此,ABI 中的措辞告诉我们用户堆栈根本不用于中断。但在多任务操作系统上,我们已经知道出于安全原因确实如此,例如 32 位 Linux 甚至 Windows。仅限于大多数软件驱动的异步事件,例如信号或 GDB `print foo()` 函数 eval。 (2认同)