堆栈分配,填充和对齐

时间:2009-06-30 05:01:25

标签: c gcc assembly x86 stack

我一直在努力深入了解编译器如何生成机器代码,更具体地说是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 bytes产生一个移动40个字节的堆栈指针,并且根本没有缓冲区移动堆栈指针8. 1到16个字节之间的缓冲区移动ESP 24个字节。

现在假设8个字节是必要的常量(它需要什么?),这意味着我们分配16个字节的块。为什么编译器会以这种方式对齐?我正在使用x86_64处理器,但即使是64位字也只需要8字节对齐。为什么会出现这种差异?

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

6 个答案:

答案 0 :(得分:46)

这是由-mpreferred-stack-boundary=n控制的gcc功能,编译器会尝试将堆栈中的项目与2^n对齐。如果您将n更改为2,则它只会在堆栈上分配8个字节。 n的默认值为4,即它会尝试与16字节边界对齐。

为什么存在“默认”8个字节然后24 = 8 + 16个字节是因为堆栈已经包含leaveret的8个字节,因此编译后的代码必须首先调整堆栈8个字节使其与2 ^ 4 = 16对齐。

答案 1 :(得分:12)

SSEx系列指令要求打包128位向量以对齐16个字节 - 否则会出现尝试加载/存储它们的段错误。即如果你想安全地传递16字节向量以便在堆栈上使用SSE,那么堆栈需要始终保持与16的对齐。默认情况下,GCC会对此进行说明。

答案 2 :(得分:3)

我找到了this site,它在页面底部有一些关于堆栈可能更大的原因。将概念扩展到64位机器,它可以解释您所看到的内容。

答案 3 :(得分:2)

LWN have an article on memory alignment,你可能会感兴趣。

答案 4 :(得分:1)

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

答案 5 :(得分:-1)

存在8个字节,因为第一条指令在堆栈上推送%ebp的起始值(假设为64位)。