gcc -mpreferred-stack-boundary=num


以C语言的hellow world为例
gcc正常编译后反汇编的main函数代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
lea    0x4(%esp),%ecx   ;保存esp+4的原始偏移量
and    $0xfffffff0,%esp    ;进行16字节对齐,如果之前不保存会使原始的esp无法恢复
pushl  -0x4(%ecx)          ;ecx在函数结束时需要恢复所以推入栈中
push   %ebp
mov    %esp,%ebp
push   %ecx
sub    $0x4,%esp
sub    $0xc,%esp
push   $0x80484c0
call   80482e0 <puts@plt>
add    $0x10,%esp
mov    $0x0,%eax
mov    -0x4(%ebp),%ecx     ;而结尾这里相当于函数序的逆操作,以确保可以正常结束
leave
lea    -0x4(%ecx),%esp
ret

会发现在函数序和函数跋多出几条奇怪的指令

如注释所言多出来指令作用便是对堆栈边界进行16字节对齐,并以16字节的倍数分配堆栈空间,这里可以使用-mpreferred-stack-boundary=num进行控制。如果使用-mpreferred-stack-boundary=2(2²=4byte对齐),因为堆栈边界始终至少为4字节对齐,便不会产生对齐的代码。

1
2
3
4
5
6
7
8
push   %ebp
mov    %esp,%ebp
push   $0x80484b0
call   80482e0 <puts@plt>
add    $0x4,%esp
mov    $0x0,%eax
leave
ret

根据gcc手册 -mpreferred-stack-boundary=num在未指定时num默认为4(2^4=16byte对齐)。

至于为什么默认是16bytes对齐,这和CPU相关,Intel在Pentium III推出了SSE指令集,SSE 加入新的 8 个16bytes寄存器(XMM0~XMM7)。最初的时候,这些寄存器智能用来做单精度浮点数计算,自从SSE2开始,这些寄存器可以被用来计算任何基本数据类型的数据了。往XMM0~XMM7里存放数据,是以16字节为单位,所以呢 内存变量首地址必须要对齐16字节,否则会引起CPU异常,导致指令执行失败。所以这就是gcc默认采用16bytes进行栈对齐的原因。

还有就是在SSE扩展被关闭时,-mpreferred-stack-boundary参数值虽然可以修改的。但是在修改后编译链接16bytes栈对齐的库时,会导致错误

看到了就随手记了一下,感觉有的地方还有点模糊,如果有错欢迎大佬指正。

最后放首歌 233333

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注