x86程序集中堆栈对齐的响应性

Grz*_*ski 8 linux x86 assembly gcc memory-alignment

我试图清楚地了解谁(调用者或被调用者)负责堆栈对齐.64位汇编的情况相当清楚,它是由调用者.

参考System V AMD64 ABI,第3.2.2节"堆栈帧":

输入参数区域的末尾应在16(32,如果在堆栈上传递__m256)字节边界上对齐.

换句话说,应该可以安全地假设,对于被调用函数的每个入口点:

16 | (%rsp + 8)

保持(额外八个是因为call隐含地在堆栈上推送返回地址).

它在32位世界中的表现(假设是cdecl)?我注意到,使用以下构造gcc将对齐放置在被调用函数:

and esp, -16
Run Code Online (Sandbox Code Playgroud)

这似乎表明,这是被召唤者的责任.

为了更清楚,请考虑以下代码:

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
Run Code Online (Sandbox Code Playgroud)

是否需要在调用之前对齐堆栈scanf?如果是这样,那么%esp在将这两个参数推送到之前,这将需要减少四个字节scanf:

4 bytes (return address)
4 bytes (%ebp of previous stack frame)
8 bytes (for two variables)
12 bytes (three arguments for scanf)
= 28
Run Code Online (Sandbox Code Playgroud)

Pet*_*des 10

gcc只是采取防御性方法main,而不是假设alignas(32)使用正确的16B对齐堆栈调用.

多年来,i386 System V ABI保证/要求ESP + 4在进入功能时进行16B对齐.(即ESP必须 CALL指令之前进行16B对齐,因此堆栈上的args从16B边界开始.这与x86-64系统V相同.)

ABI还保证新的32位进程以ESP在16B边界上对齐开始(例如-m32,在ELF入口点,ESP指向argc,而不是返回地址),并且glibc CRT代码保持该对齐.

就调用约定而言,EBP只是另一个调用保留寄存器.但是,编译器输出main确实-mpreferred-stack-boundary=4在其他调用保留寄存器(如EBX)之前完成,并且即使函数不需要使用EBP也这样做,因此保存的EBP值形成链表.


也许gcc是防御性的,因为一个非常古老的Linux内核(从i386 ABI修订之前,当所需的对齐只有4B时)可能违反了这个假设,并且它只是在生命周期中运行一次的额外几个指令.进程(假设程序没有_start递归调用).


与gcc不同,clang假设堆栈在进入main时正确对齐.(clang还假设窄args已经签名或零扩展到32位,即使当前的ABI版本没有指定该行为(尚未).gcc和clang都发出在调用者端执行的代码,但只是铿锵取决于它在被调用者.这发生在64位代码,但我没有检查32位.)

如果你很好奇,请查看http://gcc.godbolt.org/上的编译器输出,了解main以外的main和函数.


我刚刚更新了标签wiki中的ABI链接. http://x86-64.org/仍然死了,似乎没有回来,所以我更新了System V链接以指向HJ Lu的github repo中当前版本的PDF,以及带有链接的页面.

请注意,SCO站点上的最新版本不是当前版本,并且不包括16B堆栈对齐要求.

  • IDK为什么会被投票.更新以防只回答问题的前半部分>.< (3认同)