堆栈分配,填充和对齐
我一直在努力深入理解编译器如何生成机器代码,更具体地说,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字节对齐。 为何差异?
作为参考,我正在使用gcc 4.0.1运行10.5的Mac上进行编译,并且未启用优化。
这是一个gcc特性,由-mpreferred-stack-boundary=n
,编译器试图将堆栈上的项目对齐到2^n
。 如果将n
更改为2
,则只会在堆栈上分配8个字节。 n
的默认值是4
即它将尝试对齐到16个字节的边界。
为什么存在“默认”8个字节,然后是24 = 8 + 16个字节是因为堆栈已经包含8个字节的leave
和ret
,所以编译后的代码必须先将堆栈调整8个字节,使其与2 ^ 4 = 16。
SSEx系列指令需要打包的128位向量对齐到16个字节 - 否则会出现段错误,试图加载/存储它们。 也就是说,如果您想安全地传递16个字节的矢量以用于堆栈中的SSE,堆栈需要一直保持对齐至16位。默认情况下,GCC占用了该位置。
我发现这个网站,在页面底部有一些体面的解释,说明为什么堆栈可能更大。 将概念扩展到64位机器,它可能解释你所看到的。
链接地址: http://www.djcxy.com/p/14101.html