使用未优化的 C (GCC) 固定大小缓冲区的 x86-32 和 x64 汇编堆栈分配的差异

Jam*_*McC 1 c assembly x86-64 buffer-overflow stack-memory

进行一些基本的反汇编,并注意到由于某种原因,缓冲区被给予了额外的缓冲区空间,尽管我在教程中看到的内容使用相同的代码,但仅给出了正确的(500)个字符长度。为什么是这样?

我的代码:

#include <stdio.h>
#include <string.h>

int main (int argc, char** argv){
    char buffer[500];
    strcpy(buffer, argv[1]);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

用GCC编译,反汇编代码为:

   0x0000000000001139 <+0>:     push   %rbp
   0x000000000000113a <+1>:     mov    %rsp,%rbp
   0x000000000000113d <+4>:     sub    $0x210,%rsp
   0x0000000000001144 <+11>:    mov    %edi,-0x204(%rbp)
   0x000000000000114a <+17>:    mov    %rsi,-0x210(%rbp)
   0x0000000000001151 <+24>:    mov    -0x210(%rbp),%rax
   0x0000000000001158 <+31>:    add    $0x8,%rax
   0x000000000000115c <+35>:    mov    (%rax),%rdx
   0x000000000000115f <+38>:    lea    -0x200(%rbp),%rax
   0x0000000000001166 <+45>:    mov    %rdx,%rsi
   0x0000000000001169 <+48>:    mov    %rax,%rdi
   0x000000000000116c <+51>:    call   0x1030 <strcpy@plt>
   0x0000000000001171 <+56>:    mov    $0x0,%eax
   0x0000000000001176 <+61>:    leave  
   0x0000000000001177 <+62>:    ret  
Run Code Online (Sandbox Code Playgroud)

然而,这个视频https://www.youtube.com/watch?v=1S0aBV-Waeo显然只分配了 500 字节

在此输入图像描述

为什么会出现这种情况,因为我在这里看到的唯一区别是一个是 32 位的,另一个(我的)是 x86-64 上的。

zwo*_*wol 5

500 不是 16 的倍数。

call每当指令即将发生时,x86-64 ABI(应用程序二进制接口)都要求堆栈指针为 16 的倍数。(由于call压入 8 字节返回地址,这意味着当控制到达被调用函数的第一条指令时,堆栈指针始终等于 8, mod 16。)对于所示的代码,编译器可以很方便地实现此目的通过增加指令中使用的值来满足要求sub,使其成为 16 的倍数。

x86-32 ABI 没有提出此要求,因此视频中使用的编译器没有理由增加堆栈帧的大小。

请注意,您似乎编译了未经优化的代码。我在-O2 得到这个:

   0x0000000000000000 <+0>:     sub    $0x208,%rsp
   0x0000000000000007 <+7>:     mov    0x8(%rsi),%rsi
   0x000000000000000b <+11>:    mov    %rsp,%rdi
   0x000000000000000e <+14>:    call   <strcpy@PLT>
   0x0000000000000013 <+19>:    xor    %eax,%eax
   0x0000000000000015 <+21>:    add    $0x208,%rsp
   0x000000000000001c <+28>:    ret
Run Code Online (Sandbox Code Playgroud)

堆栈调整仍然比数组的大小稍大,但没有你原来的那么大,并且不再是 16 的倍数;不同之处在于,启用优化后,帧指针被消除,因此不需要保存和恢复 %rbp,因此堆栈指针在指令处不是 16 的倍数sub

(顺便说一句,没有任何地方要求堆栈帧尽可能小。“实现质量”规定它应该尽可能小,但由于各种原因,编译器错过该目标是很常见的。在我的优化代码转储中,我看不出有任何理由说明为什么立即操作数sub不能add 0x1f8 (504)。