首页 > 解决方案 > 正确调用 printf 的堆栈对齐?

问题描述

我已经看到了堆栈指针/在调用之前esp递减并4在调用之后printf重新调整的示例:12printf

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递减并8在调用之后printf重新调整的示例:16printf

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,然后重新调整/递增。16

这些示例中的差异让我感到困惑,哪个堆栈对齐示例是正确的,为什么?可以解释这个递增/递减过程以减少混乱吗?

标签: assemblyx86stackmemory-alignmentabi

解决方案


我已经看到堆栈指针/esp 在调用 printf 之前减少 4 并在调用 printf 之后重新调整 12 的示例:

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

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

子特别,8

显然,在这种情况下,编译器不会被告知要注意 16 字节的堆栈对齐。

显然,在这种情况下,编译器分配的堆栈比必要的多。

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

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

显然,这是编译器优化中的一个问题:

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

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

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


推荐阅读