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

标签 c assembly x86-64 stack-memory 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.

我知道“将帧指针保存在堆栈上”是通过“push rbp”完成的,“将堆栈指针的值复制到基指针中”是通过“mov rbp, rsp”完成的,但是是什么让我令人困惑的是缺少用于“为局部变量节省一些内存”的“sub rsp $n_bytes”。我在很多展览中都看到了这一点(甚至在 stackoverflow 上的某些主题中)。

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

也许这些差异是由于我使用的是 64 位系统,但我的研究并没有让我找到任何可以解释我所面临的问题的东西。

最佳答案

x86-64 的 System V ABI 在 %rsp 下指定了一个 128 字节的 red zone。这 128 个字节属于函数,只要它不调用任何其他函数(它是一个叶函数)。

信号处理程序(和调试器调用的函数)需要遵守红色区域,因为它们实际上是非自愿函数调用。
test_function 的所有局部变量,即叶函数,适合红色区域,因此不需要调整 %rsp。 (此外,该函数没有明显的副作用,可以根据任何合理的优化设置进行优化)。

您可以使用 -mno-red-zone 进行编译,以阻止编译器使用堆栈指针下方的空间。内核代码必须这样做,因为硬件中断不会实现红区。

关于c - 为什么这个函数序言中没有 "sub rsp"指令,为什么函数参数存储在负 rbp 偏移处?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45860023/

相关文章:

linux - 如何链接C语言库?

.net - 从 32 位开发到 64 位开发,需要澄清 .net 框架和平台

objective-c - C代码中的内存泄漏

c++ - 如何通过 Windows API 获取所有(非禁用)用户 SID?

assembly - LLVM 如何避免为 `br` IR 指令生成冗余 native 代码?

assembly - DOSBox:debug.exe 读取文件 - 错误地处理命令

C:为什么我在一种情况下出现段错误,而在另一种情况下却没有?

c - OpenCV:缓冲 IplImage* 以在 pthreads 之间共享

计算数字中的位数

c - SHLD/SHRD 指令的 SIMD 版本