今天和20年前的记忆对齐

0x0*_*x00 13 c x86 assembly gcc

在着名的论文"Smashing the Stack for Fun and Profit"中,它的作者采用了C函数

void function(int a, int b, int c) {
  char buffer1[5];
  char buffer2[10];
}
Run Code Online (Sandbox Code Playgroud)

并生成相应的汇编代码输出

pushl %ebp
movl %esp,%ebp
subl $20,%esp
Run Code Online (Sandbox Code Playgroud)

作者解释说,由于计算机以字大小的倍数对存储器进行寻址,因此编译器在堆栈上保留了20个字节(缓冲区1为8个字节,缓冲区2为12个字节).

我试图重新创建这个例子并获得以下内容

pushl   %ebp
movl    %esp, %ebp
subl    $16, %esp
Run Code Online (Sandbox Code Playgroud)

不同的结果!我尝试了缓冲区1和缓冲区2的各种大小组合,似乎现代的gcc不再将缓冲区大小填充到字大小的倍数.相反,它遵守-mpreferred-stack-boundary选项.

作为一个例子 - 使用纸张的算术规则,对于buffer1 [5]和buffer2 [13],我会在堆栈上保留8 + 16 = 24个字节.但实际上我有32个字节.

这篇论文很古老,自那以后发生了很多事情.我想知道,究竟是什么推动了这种行为的改变?这是向64位机器的转变吗?或者是其他东西?

编辑

代码使用gcc版本4.8.2(Ubuntu 4.8.2-19ubuntu1)在x86_64机器上编译,如下所示:

$ gcc -S -o example1.s example1.c -fno-stack-protector -m32

Sha*_*our 7

更改的是SSE,它需要16字节对齐,这在旧的gcc文档中涵盖-mpreferred-stack-boundary = num,其中说明(强调我的):

在Pentium和PentiumPro上,double和long double值应与8字节边界对齐(请参阅-malign-double)或遭受严重的运行时性能损失.在Pentium III上,流式SIMD扩展(SSE)数据类型__m128如果不是16字节对齐则会受到类似的惩罚.

这也是Smashing The Modern Stack For Fun和Profit的论文,它涵盖了另一个现代化的变化,打破了Smashing the Stack for Fun and Profit.