【发布时间】:2020-11-14 15:16:21
【问题描述】:
我正在努力学习如何理解汇编代码,所以我一直在研究 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 $8, %rsp
xorl %edx, %edx
leaq .LC0(%rip), %rsi
xorl %eax, %eax
movl $1, %edi
call __printf_chk@PLT
xorl %eax, %eax
addq $8, %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?为什么它执行这么多xors?只有一个变量。 movl $1, %edi 在做什么?我想也许编译器在尝试优化时做了一些愚蠢的事情,但正如我所说,它没有优化超出-O2,即使在-O1 上它也执行所有这些操作。老实说,我根本不了解未优化的代码,所以我认为它效率低下。
唯一想到的是对printf 的调用使用了这些寄存器,否则它们没有被使用并且没有任何用处。真的是这样吗?如果有,怎么可能知道?
提前致谢。我现在正在阅读一本关于编译器设计的书,并且我已经阅读了大部分 GCC 手册(我阅读了关于优化的整章)并且我已经阅读了一些介绍性的 x86_64 asm 材料,如果有人可以指出我其他的学习更多资源(除了 Intel x86 手册)我也将不胜感激。
【问题讨论】:
-
这个程序集似乎与您应该编译的代码不匹配。如果您调用
printf(1, "%d\n"),这就是我希望看到的,并且从godbolt 判断,这实际上非常接近您调用printf(1, "%d\n")时得到的程序集。 -
@Aplet123 这是代码:
#include <stdio.h> main() { int i = 0; printf("%d\n", i); } -
查看stackoverflow.com/tags/x86/info 以获得许多有用的链接。 ABI 可能特别受关注。