printf在x86-64架构上是否需要额外的栈空间?

3
虽然我知道使用编译器内部函数和printf_chk,并将数据放入.rodata段是最好的选择,但我正在努力深入了解汇编语言,并对简洁的代码感兴趣。关于printf,我有些疑问。我知道在哪里放置参数,也知道如何使用%al来处理可变参数,但它似乎需要额外的堆栈空间,我无法解释这一点。
以下是这个短程序:
        .text
        .globl  main
main:
        movsd   value(%rip), %xmm0    # value to print
        movl    $format, %edi         # format string
        movl    $1, %eax              # one floating-point arg
        call    printf
        movl    $0, %eax              # return 0 from main
        ret
        .align 8
value:  .double 74.321 
format: .asciz "%g\n"

出现了段错误。

但是,当我为帧添加额外的堆栈空间时,它可以正常工作:

        .text
        .globl  main
main:
        subq    $8, %rsp              # ADD SOME STACK SPACE TO FRAME (WHY?)
        movsd   value(%rip), %xmm0    # value to print
        movl    $format, %edi         # format string
        movl    $1, %eax              # one floating-point arg
        call    printf
        movl    $0, %eax              # return 0 from main
        addq    $8, %rsp              # REMOVE ADDED STACK SPACE
        ret
        .align 8
value:  .double 74.321 
format: .asciz "%g\n"

可能是对齐问题吗?(当valueformat.rodata部分时,我遇到了同样的问题。)


https://dev59.com/oWoy5IYBdhLWcg3wN7i8 - Ciro Santilli OurBigBook.com
1个回答

4

已接受!通过将值从8更改为16并获得segfault,然后更改为24并且未获得segfault进行验证。有趣的是,只有当我在向量寄存器中使用printf时才会发生这种情况。我没有看到第一个示例与第二个示例之间的任何真正区别,在不调整rsp的情况下,第一个示例可以正常工作,而第二个示例需要进行调整。第二个版本有什么特别之处,使得堆栈失去对齐,而第一个版本则没有? - Ray Toal
啊,我现在记起来了:栈在main()函数开始时是16字节对齐的。调用指令将8字节的返回地址推入栈中,这会导致它失去对齐并需要subq一些奇数倍的8字节来重新对齐。为什么一个未对齐的栈只有在使用矢量寄存器(一个寄存器!而不是栈!)时才会导致seg fault,这对我来说并不完全清楚。可能是对varargs工作原理的理解不足。 - mattst88
在x86-64 System V ABI(在Linux上,从使用.rodata看起来像这样),在call之前,rsp是16字节对齐的,因此在像main这样的普通函数开始时,rsp-8按16对齐,准备好进行另一个call。当al>0时,可变参数函数通过使用16B对齐存储将xmm0..7转储到堆栈中利用了这个保证。(因为__m128参数可以传递给可变参数函数,并且gcc不会针对从未寻找超过8字节的FP寄存器参数的可变参数函数进行代码生成优化。) - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接