我试图清楚地了解谁(调用者或被调用者)负责堆栈对齐. 64位汇编的情况相当清楚,它来自调用者.
参考System V AMD64 ABI,第3.2.2节“堆栈帧”:
The end of the input argument area shall be aligned on a 16 (32, if
__m256 is passed on stack) byte boundary.
换句话说,应该可以安全地假设,对于被调用函数的每个入口点:
16 | (%rsp 8)
保持(额外八个是因为调用隐含地在栈上推送返回地址).
它在32位世界中的表现(假设是cdecl)?我注意到gcc将对齐放在被调用函数中,并使用以下构造:
and esp, -16
这似乎表明,这是被召唤者的责任.
为了更清楚,请考虑遵循NASM代码:
global main
extern printf
extern scanf
section .rodata
s_fmt db "%d %d", 0
s_res db `%d with remainder %d
`, 0
section .text
main:
start 0, 0
sub esp, 8
mov DWORD [ebp-4], 0 ; dividend
mov DWORD [ebp-8], 0 ; divisor
lea eax, [ebp-8]
push eax
lea eax, [ebp-4]
push eax
push s_fmt
call scanf
add esp, 12
mov eax, [ebp-4]
cdq
idiv DWORD [ebp-8]
push edx
push eax
push s_res
call printf
xor eax, eax
leave
ret
是否需要在调用scanf之前对齐堆栈?如果是这样,那么在将这两个参数推送到scanf之前,这需要将%esp减少4个字节:
4 bytes (return address)
4 bytes (%ebp of previous stack frame)
8 bytes (for two variables)
12 bytes (three arguments for scanf)
= 28