堆栈分配,填充和alignment

我一直试图深入了解编译器如何生成机器码,更具体地说,GCC如何处理堆栈。 为此,我一直在编写简单的C程序,将它们编译成程序集,并尽我所能了解结果。 这里有一个简单的程序和它生成的输出:

asmtest.c

 void main() { char buffer[5]; } 

asmtest.s

 pushl %ebp movl %esp, %ebp subl $24, %esp leave ret 

令我费解的是为什么24字节被分配给堆栈。 我知道,由于处理器如何处理内存,堆栈必须以4为增量进行分配,但如果是这种情况,我们应该只将堆栈指针移动8个字节,而不是24个。作为参考,缓冲区为17字节产生一个移动了40个字节的堆栈指针,并且根本没有任何缓冲区移动堆栈指针8.一个1到16字节的缓冲区移动了ESP 24个字节。

现在假定8个字节是一个必要的常量(它需要什么?),这意味着我们正在分配16个字节的块。 为什么编译器会这样调整呢? 我正在使用x86_64处理器,但是即使是64位字也只需要8字节的alignment方式。 为什么这个差距?

作为参考,我正在使用gcc 4.0.1运行10.5的Mac上进行编译,并且没有启用优化。

这是一个gcc特性,由-mpreferred-stack-boundary=n ,编译器试图将堆栈上的项目alignment到2^n 。 如果您将n更改为2 ,则只会在堆栈上分配8个字节。 n的默认值是4即它将尝试alignment到16字节的边界。

为什么“默认”8字节,然后是24 = 8 + 16字节是因为堆栈已经包含了8个字节的leaveret ,所以编译的代码必须先调整堆栈8个字节,使它alignment到2 ^ 4 = 16。

SSEx系列指令需要打包的128位向量alignment到16个字节,否则会出现段错误,试图加载/存储它们。 也就是说,如果您想安全地传递16个字节的vector用于堆栈中的SSE,堆栈需要一直保持alignment到16位。默认情况下,GCC占了这个位置。

我发现这个网站 ,在页面底部有一些体面的解释,为什么堆栈可能会更大。 把这个概念扩展到一个64位的机器,它可能解释你所看到的。

LWN有关于内存alignment的文章 ,您可能会感兴趣。

Mac OS X / Darwin x86 ABI需要16字节的堆栈alignment。 在其他x86平台上,例如Linux,Win32,FreeBSD,情况并非如此…

8字节在那里,因为第一条指令将%ebp的起始值压入堆栈(假定为64位)。