试图了解 printf() 的 gcc 程序集输出

Trying to understand the gcc assembly output for printf()

我正在努力学习如何理解汇编代码,所以我一直在研究一些愚蠢程序的 GCC 汇编输出。其中之一就是 int i = 0;,我现在或多或少完全理解了它的代码(最大的困难是理解散布的 GAS 指令)。反正我往前加了printf("%d\n", i);看能不能看懂,突然代码就乱多了

    .file   "helloworld.c"
    .text
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "%d\n"
    .section    .text.startup,"ax",@progbits
    .p2align 4
    .globl  main
    .type   main, @function
main:
    subq    , %rsp
    xorl    %edx, %edx
    leaq    .LC0(%rip), %rsi
    xorl    %eax, %eax
    movl    , %edi
    call    __printf_chk@PLT
    xorl    %eax, %eax
    addq    , %rsp
    ret
    .size   main, .-main
    .ident  "GCC: (Gentoo 10.2.0-r3 p4) 10.2.0"
    .section    .note.GNU-stack,"",@progbits

我正在用 gcc -S -O3 -fno-asynchronous-unwind-tables 编译它以删除 .cfi 指令,但是 -O2 生成相同的代码,所以 -O3 是多余的。我对汇编的理解非常有限,但在我看来编译器在这里做了很多不必要的事情。 rsp 为什么减去然后加 8?为什么要执行这么多xor?只有一个变量。 movl , %edi 在做什么?我想也许编译器在尝试优化时做了一些愚蠢的事情,但正如我所说,它没有优化超过 -O2,而且它甚至在 -O1 时执行所有这些操作。老实说,我根本不理解未优化的代码,所以我认为它效率低下。

唯一想到的是对 printf 的调用使用了这些寄存器,否则它们未被使用并且没有任何作用。事实真的如此吗?如果是,怎么可能知道?

提前致谢。我正在阅读一本关于编译器设计的书,并且我已经阅读了大部分 GCC 手册(我阅读了关于优化的整章)并且我已经阅读了一些介绍性的 x86_64 asm material,如果有人可以向我指出一些其他资源(除了 Intel x86 手册)以了解更多信息,我也将不胜感激。

对于您正在使用的编译器,它看起来像 printf(...) 映射到 __printf_chk(1, ...)

要理解代码,you need to understand the parameter passing conventions for the platform (part of the ABI)。一旦你知道在 %rdi、%rsi、%rdx、%rcx 中最多传递了 4 个参数,你就可以理解其中的大部分内容了:

subq    , %rsp             ; allocate 8 bytes of stack
xorl    %edx, %edx           ; i = 0 ; put it in the 3rd parameter for __printf_chk
leaq    .LC0(%rip), %rsi     ; 2nd parameter for __printf_chk.  The: "%d\n"
xorl    %eax, %eax           ; 0 variadic fp params
movl    , %edi             ; 1st parameter for __printf_chk
call    __printf_chk@PLT     ; call the runtime loader wrapper for __printf_chk
xorl    %eax, %eax           ; return 0 from main
addq    , %rsp             ; deallocate 8 bytes of stack.
ret

Nate 在评论中指出 ABI 中的第 3.5.7 节解释了 %eax = 0(无浮点可变参数。)