我已经拆解了一个程序.我在开始时看到一个AND带ESP和的指令0xFFFFFFF0.
这个面具是什么意思?这是对齐问题吗?
它是32位x86的ELF可执行文件.
gcc for i386 Linux默认为-mpreferred-stack-boundary=4(意味着2 4 = 16字节对齐).(其他非Linux系统也使用ELF可执行文件,但它们也具有相同的堆栈对齐默认值和SysV ABI.)
与clang不同,gcc不会假设堆栈在入口处对齐main,因此它使用AND指令来屏蔽堆栈指针的低位.这是在堆栈上保留足够的填充以到达下一个对齐边界的最便宜的方法.
堆栈对齐与您将看到调用另一个函数的函数保留堆栈上的某些空间的原因相同,它不会用于任何事情:
extern int bar(void);
int foo(int x) { return x+bar(); }
gcc 5.3 -O3
sub esp, 12 # align the stack for another call
call bar
add eax, DWORD PTR [esp+16] # add our arg (from the stack) to bar()'s return value (in eax)
add esp, 12
ret
Run Code Online (Sandbox Code Playgroud)
在Godbolt编译器资源管理器中查看此内容,您可以在其中尝试不同的编译器和选项.
-mincoming-stack-boundary=3(或更少)导致堆栈对齐样板被添加到每个函数(不仅仅是main). -mstackrealign并-mno-stackrealign有没有影响foo()或main()有或无小-mincoming-stack-boundary.基于gcc手册中的文档,我认为它将启用或禁用除main之外的函数或main的对齐函数.
在32位的x86的SysV ABI仅用于保障4个字节的栈对齐,但调用约定保证现在的16字节对齐%esp一个之前call指令.
第2.2.2节堆栈框架
...换句话说,
%esp + 4当控制转移到函数入口点时,value()总是16的倍数.(当32位ymm向量按值传递时为32)
所以gcc -mpreferred-stack-boundary=4不仅仅是一个好主意,而是法律(在像Linux这样的系统上,包含这种更强保证的更新ABI版本是官方标准).这使得除了main在使用对齐的SSE存储/加载到堆栈之前省略该对齐步骤之外的功能是安全的.这些指令(如movaps)将在未对齐的地址上出错.因此,正确性需要对齐,而不仅仅是性能.
AND没有实际需要,这些天指令32位的当前版本ABI 并保证一个freshly- execve版32位进程将开始%esp对齐16字节.(第2.3.1节"初始堆栈和寄存器状态",请参阅有关其%esp自身初始状态的项目符号,而不是堆栈内容.)
这意味着gcc在main的开头对齐堆栈的行为现在已经过时,假设调用的CRT启动代码main没有错位堆栈.
clang确实假设堆栈在main的开头对齐,就像64位gcc那样.
16B-alignment从一开始就是x86-64 SysV ABI的一部分,后来没有添加,因此它始终是一个安全的假设,并且没有旧的内核在进程启动时不提供.
在x86的标签wiki有链接到其他的ABI,以及更多.