在著名论文“Smashing the Stack for Fun and Profit”中,其作者采用了一个 C 函数
void function(int a, int b, int c) {
char buffer1[5];
char buffer2[10];
}
并生成相应的汇编代码输出
pushl %ebp
movl %esp,%ebp
subl $20,%esp
作者解释说,由于计算机以字大小的倍数对内存进行寻址,因此编译器在堆栈上保留了 20 个字节(8 个字节用于 buffer1,12 个字节用于 buffer2)。
我尝试重新创建这个示例并得到以下结果
pushl %ebp
movl %esp, %ebp
subl $16, %esp
不一样的结果!我尝试了 buffer1 和 buffer2 大小的各种组合,似乎现代 gcc 不再将缓冲区大小填充到字大小的倍数。相反,它遵守-mpreferred-stack-boundary
option.
作为一个例子——使用本文的算术规则,对于 buffer1[5] 和 buffer2[13],我会在堆栈上保留 8+16 = 24 字节。但实际上我得到了 32 个字节。
这篇论文已经很老了,从那以后发生了很多事情。我想知道,到底是什么促使了这种行为的改变?这是向 64 位机器发展的趋势吗?或者是其他东西?
Edit
该代码是在 x86_64 机器上使用 gcc 版本 4.8.2 (Ubuntu 4.8.2-19ubuntu1) 编译的,如下所示:
$ gcc -S -o example1.s example1.c -fno-stack-protector -m32