眠りネ*_*ネロク 5 x86 assembly gcc abi memory-alignment
考虑以下 C 代码:
#include <stdint.h>
void func(void) {
uint32_t var = 0;
return;
}
Run Code Online (Sandbox Code Playgroud)
-O0
GCC 4.7.2 为上述代码生成的未优化(即:选项)汇编代码是:
func:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movl $0, -4(%ebp)
nop
leave
ret
Run Code Online (Sandbox Code Playgroud)
根据堆栈对准要求所述的系统V ABI,堆栈必须由16个字节每之前对准call
指令(该堆栈边界是默认的16个字节时不与该选项改变-mpreferred-stack-boundary
)。因此,在函数调用之前,ESP
模16的结果必须为零。
记住这些堆栈对齐要求,我假设在执行leave
指令之前以下堆栈的状态表示是正确的:
Size (bytes) Stack ESP mod 16 Description
-----------------------------------------------------------------------------------
| . . . |
------------------........0 at func call
4 | return address |
------------------.......12 at func entry
4 | saved EBP |
----> ------------------........8 EBP is pointing at this address
| 4 | var |
| ------------------........4
16 | | |
| 12 | |
| | |
----> ------------------........8 after allocating 16 bytes
Run Code Online (Sandbox Code Playgroud)
考虑到堆栈的这种表示,有两点让我感到困惑:
var
显然在堆栈上没有对齐到 16 个字节。这个问题似乎是矛盾的时候,我看过这个答案对这个问题(重点是我自己的):
-mpreferred-stack-boundary=n
编译器尝试将堆栈上的项目与2^对齐n
。
在我的情况下-mpreferred-stack-boundary
没有提供,因此根据GCC 文档的这一部分(我确实得到了与 相同的结果-mpreferred-stack-boundary=4
),它默认设置为 4(即:2^4=16 字节边界)。
在堆栈上分配 16 个字节(即:subl $16, %esp
指令)而不是仅分配 8 个字节的目的:在分配 16 个字节后,堆栈既没有按 16 个字节对齐,也没有保留任何内存空间。通过只分配 8 个字节,堆栈按 16 个字节对齐,并且不会浪费额外的 8 个字节。
查看-O0
生成的机器代码通常是徒劳的。编译器将以最简单的方式发出任何有效的结果。这通常会导致奇怪的伪影。
栈对齐仅指栈帧的对齐。它与堆栈上对象的对齐方式没有直接关系。GCC 将以所需的对齐方式分配堆栈上的对象。如果 GCC 知道堆栈帧已经提供了足够的对齐,那么这会更简单,但如果没有,GCC 将使用帧指针并执行显式对齐。
归档时间: |
|
查看次数: |
1333 次 |
最近记录: |