正确调用 printf 的堆栈对齐?

Correct stack alignment for call to printf?

我见过调用printf之前堆栈指针/esp递减4并在调用[=14=之后重新调整12的例子]:

section .text
global  main
extern printf

main:
    sub   esp, 4
    push  msg          
    push  format_str
    call  printf
    add   esp, 12
    ret

section .data:
    msg db "print me!", 0
    format_str db "%s", 0

而且我已经看到堆栈指针/esp 在调用 printf 之前递减 8 并在调用 [=14] 之后由 16 重新调整的示例=]:

section .text
global  main
extern printf

main:
    sub   esp, 8
    push  msg        
    push  format_str
    call  printf
    add   esp, 16
    ret

section .data:
    msg db "print me!", 0
    format_str db "%s", 0

据我所知,在从 libc 调用任何函数之前,esp 应该递减 8,然后 re-adjusted/incremented 递减 16

这些例子的差异让我很困惑,哪个堆栈对齐例子是正确的,为什么? incrementing/decrementing 的这个过程可以解释一下吗?

I have seen examples where the stack pointer/esp is decremented by 4 before calling printf and re-adjusted by 12 after calling printf:

根据 another question 中的评论,堆栈在可以使用 SSE 指令的系统(例如库、操作系统)上应以 16 字节对齐。

假设调用函数(main)时堆栈指针正确对齐,call指令从esp中减去4个字节,所以subpush 指令必须从 esp 中减去 12、28、40 ... 字节以保持堆栈指针正确对齐。

sub esp, 8

显然,在这种情况下,编译器没有被告知要关心 16 字节堆栈对齐。

显然,在这种情况下,编译器分配的堆栈多于所需。

我刚刚告诉编译器为堆栈生成 8 字节和 16 字节对齐;所有其他编译器选项(当然还有源代码)都是相同的。

不同之处在于,在8字节对齐的情况下,编译器生成sub esp, 4,在16字节对齐的情况下,sub esp, 20

很明显,这是编译器优化的问题:

如果sub esp,20将堆栈对齐到16字节,sub esp, 4也会对齐到16字节。

并且使用“对齐到 8 字节”选项表明绝对可以执行 sub esp, 4 而不是 sub esp, 20

这表明某些编译器为某些未知目的保留了比所需更多的堆栈。