GCC 在生成调用栈时,为什么在局部变量后留了八个字节的空白呢?
关注者
21被浏览
4,332登录后你可以
不限量看优质回答私信答主深度交流精彩内容一键收藏
没有你说的这种情况。
修改调用栈的是call指令。你看到的8字节空白(64位系统)可能是Function Prologue:
function_foo:
push %rbp # 这一行要备份 %rbp,你看到的8字节空白可能就是这个值
movq %rsp,%rbp
# 好多代码
leave # leave指令会将%rbp从栈中恢复
修正:
我想了一下,你的问题应该不是那巧合的8字节,%rbp一般应该不会为空的。
而你在问题中所说的8字节也只是巧合,那只是表象。
深入查看,你会发现,GCC在函数局部变量栈的分配上,采用了16字节对齐。
首先,如果你使用下面的代码:
# file: foo.c
foo() {
int a=1,b=2,c=3,d=4,e=5;
}
编译查看生成的汇编代码:
gcc -S foo.c
你会看到生成的汇编代码是这样的(32位系统为例):
subl $32, %esp # 本来5个int类型变量只需要20字节,但采用16字节对齐,则会为32
movl $1, -20(%ebp)
movl $2, -16(%ebp)
movl $3, -12(%ebp)
movl $4, -8(%ebp)
movl $5, -4(%ebp)
先告诉你,使用gcc的-mpreferred-stack-boundary=n参数可以设置栈对齐字节长度为指定的2的n次方。如,你想使用4字节对齐,则使用下面的方式编译:
gcc -S foo.c -mpreferred-stack-boundary=2 # 4字节:2^2
则得到的汇编是这样的:
subl $20, %esp
其次,为什么GCC要采用16字节对齐呢?这问题的解释就比较冗长了。
可以直接参见GCC编译参数
-mpreferred-stack-boundary的文档。
简单来讲,CPU提供一个SSE功能(Streaming SIMD Extensions)。SSE 加入新的 8 个128Bit寄存器(XMM0~XMM7),正好每个寄存器大小为16字节。SSE功能提供给你肯定是让你用的,用了性能高,谁用谁知道。但要用的话,往XMM0~XMM7里存放数据,只能一次16字节一次16字节,所以呢,内存中数据当然要采用16字节对齐,为使用SSE作准备啦。
至于为何需要内存对齐则属于基础理论,超出此回答解释范围了。
如果你想了解GCC具体的内存对齐策略,可见这篇:
Ye, Joey - A proposal to align GCC stack