linux – 32位x86程序集中堆栈对齐的责任
作者:互联网
我试图清楚地了解谁(调用者或被调用者)负责堆栈对齐. 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\n`, 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
解决方法:
GCC只在main中进行额外的堆栈对齐;这个功能很特别.如果你查看任何其他函数的code-gen,你将看不到它,除非你有本地的alignas(32)或其他东西.
GCC只是采用-m32的防御方法,不假设使用正确的16B对齐堆栈调用main.或者,这个特殊的处理方法是从-mpreferred-stack-boundary = 4只是一个好主意,而不是法律.
多年来,i386 System V ABI保证/要求ESP 4在进入功能时进行16B对齐. (即ESP必须在CALL指令之前进行16B对齐,因此堆栈上的args从16B边界开始.这与x86-64系统V相同.)
ABI还保证新的32位进程以ESP在16B边界上对齐开始(例如在_start,ELF入口点,其中ESP指向argc,而不是返回地址),并且glibc CRT代码保持该对齐.
就调用约定而言,EBP只是另一个调用保留寄存器.但是,带有-fno-omit-frame-pointer的编译器输出确实会在其他调用保留寄存器(如EBX)之前推送ebp,因此保存的EBP值会形成链表. (因为它也执行mov ebp,特别是在推送之后设置帧指针的一部分.)
也许gcc是防御性的,因为一个非常古老的Linux内核(从i386 ABI修订之前,当所需的对齐只有4B时)可能违反了这个假设,并且它只是在生命周期中运行一次的额外几个指令.进程(假设程序没有递归调用main).
与gcc不同,clang假设堆栈在进入main时正确对齐. (clang也是assumes that narrow args have been sign or zero-extended to 32 bits,即使当前的ABI版本没有指定那个行为(还).gcc和clang都发出了在调用者端执行的代码,但只有clang依赖于它在被调用者中.这发生在64位代码,但我没有检查32位.)
如果你很好奇,请查看http://gcc.godbolt.org/上的main和main之外的编译器输出.
我刚刚更新了x86标签wiki中的ABI链接. http://x86-64.org/仍然死了,似乎没有回来,所以我更新了System V链接以指向HJ Lu的github repo和his page with links中当前版本的PDF.
请注意,last version on SCO’s site不是当前版本,并且不包括16B堆栈对齐要求.
我认为一些BSD版本仍然不需要/维护16字节堆栈对齐.
标签:x86,linux,assembly,gcc,memory-alignment,x86 来源: https://codeday.me/bug/20190929/1830800.html