正确调用 printf 的堆栈对齐?

Posted

技术标签:

【中文标题】正确调用 printf 的堆栈对齐?【英文标题】:Correct stack alignment for call to printf? 【发布时间】:2021-03-03 13:55:00 【问题描述】:

我已经看到堆栈指针/esp 在调用printf 之前由4 递减并在调用printf 之后由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 递减并在调用printf 之后由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

从我读到的内容来看,esp 应该减少8,然后在从 libc 调用任何函数之前重新调整/增加16

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

【问题讨论】:

第二个例子实际上是错误的,堆栈没有在 16 字节边界上对齐。并不是网上所有的例子都是正确的。 【参考方案1】:

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

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

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

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

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

【讨论】:

sub esp,8 示例确实 not 看起来像编译器输出。一方面,它是 NASM 语法,所以这已经是一些非标准的编译器了。 C 源代码几乎不可能使用static char msg[] = "print me!"; 等等来在读写.data 部分(而不是.rodata)中获取这些字符串,或者玩具编译器可能已经够糟糕了,总是这样做.此外,如果您不打算将堆栈重新对齐 16,则 sub esp 是不必要的。这个 main 不会返回 0 (xor eax,eax),所以我猜 C89 可能是? 当我们为业余项目编译器找到足够多的借口来生成此代码时,它与那些并不真正知道自己在做什么的人的手写 asm 处于同一水平。所以我们不妨直接说“谁写了这段代码”,因为不管他们是直接写还是写了一个不符合 ABI 的编译器都没有关系。 (公平地说,只有 Linux 版本的 i386 Sys V ABI 需要 16 字节堆栈对齐;其他操作系统将其保留为 4 以用于 32 位模式。)

以上是关于正确调用 printf 的堆栈对齐?的主要内容,如果未能解决你的问题,请参考以下文章

设置堆栈后在 C 中调用 printf 时出现分段错误

如何将堆栈对齐到 SRAM 的末尾?

LD_PRELOAD 不适用于 printf

使用printf来打印列

解释 printf 中的评估顺序 [重复]

如何阻止 ANSI 颜色代码弄乱 printf 对齐?