了解堆栈对齐实施 [英] Understanding stack alignment enforcement
问题描述
考虑以下C代码:
#include <stdint.h>
void func(void) {
uint32_t var = 0;
return;
}
GCC 4.7.2为上述代码生成的未经优化的(即 -O0
选项)汇编代码为:
The unoptimized (i.e.: -O0
option) assembly code generated by GCC 4.7.2 for the code above is:
func:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movl $0, -4(%ebp)
nop
leave
ret
根据 System V ABI 的堆栈对齐要求,必须在每个 call
指令之前将堆栈对齐16个字节(如果未使用选项 -mpreferred-stack-boundary
进行更改,则堆栈边界默认为16个字节.因此,在函数调用之前, ESP
modulo 16的结果必须为零.
According to the stack alignment requirements of the System V ABI, the stack must be aligned by 16 bytes before every call
instruction (the stack boundary is 16 bytes by default when not changed with the option -mpreferred-stack-boundary
). Therefore, the result of ESP
modulo 16 has to be zero prior to a function call.
考虑到这些堆栈对齐要求,在执行 leave
指令之前,我假设以下堆栈的状态表示是正确的:
Bearing in mind these stack alignment requirements, I assume the following stack's status representation just before executing the leave
instruction to be right:
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
牢记这种堆栈表示法,有两点使我感到困惑:
With this representation of the stack in mind, there are two points that puzzle me:
var
is obviously not aligned on the stack to 16 bytes. This issue seems to contradict what I have read in this answer to this question (the emphasis is of my own):
-mpreferred-stack-boundary = n
,编译器在其中尝试将堆栈上的项目保持对齐到2 ^n
.>>
-mpreferred-stack-boundary=n
where the compiler tries to keep items on the stack aligned to 2^n
.
在我的情况下,未提供 -mpreferred-stack-boundary
,因此根据
In my case -mpreferred-stack-boundary
wasn't provided, so it is set by default to 4 (i.e.: 2^4=16 bytes boundary) according to this section of GCC's documentation (I got indeed the same results with -mpreferred-stack-boundary=4
).
在堆栈上分配16个字节的目的(即 subl $ 16,%esp
指令)而不是仅分配8个字节:分配16个字节后,堆栈均未对齐16个字节,也不保留任何存储空间.通过仅分配8个字节,堆栈将按16个字节对齐,并且不会浪费额外的8个字节.
The purpose of allocating 16 bytes on the stack (i.e.: the subl $16, %esp
instruction) instead of allocating just 8 bytes: after allocating 16 bytes neither the stack is aligned by 16 bytes nor any memory space is spared. By allocating just 8 bytes instead, the stack gets aligned by 16-bytes and no additional 8 bytes are wasted.
推荐答案
查看 -O0
生成的机器代码通常是徒劳的.编译器将以最简单的方式发出任何有效的作品.这通常会导致奇异的伪像.
Looking at -O0
-generated machine code is usually a futile exercise. The compiler will emit whatever works, in the simplest possible way. This often leads to bizarre artifacts.
堆栈对齐仅指堆栈框架的对齐.它与堆栈上对象的对齐没有直接关系.GCC将分配具有所需对齐方式的堆栈对象.如果GCC知道堆栈帧已经提供了足够的对齐方式,那么这会更简单,但如果没有,GCC将使用帧指针并执行显式对齐.
Stack alignment only refers to alignment of the stack frame. It is not directly related to the alignment of objects on the stack. GCC will allocate on-stack objects with the required alignment. This is simpler if GCC knows that the stack frame already provides sufficient alignment, but if not, GCC will use a frame pointer and perform explicit alignment.
这篇关于了解堆栈对齐实施的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!