Assembly 是编译器的文本输出(来自clang -S),将包含标签而不是数字分支目标。反汇编只能为您提供任何进入符号表的符号名称。
clang 的 -S 输出使用 asm cmets,例如用于 SIMD 洗牌指令。对于 FP 位模式,它显示了表示的值:https://godbolt.org/z/efz1fq 显示了 float f = 1.25; 如何使用 clang 为 x86-64 编译(去除一堆“明显”且不太有趣的指令,如 .section .data)
f:
.long 0x3fa00000 # float 1.25
gcc -fverbose-asm 将操作数的 C 名称作为 cmets 添加到每条指令中,但 clang -fverbose-asm 并没有做太多事情。
.loc 是调试元数据,C/C++ 源代码行号。
.cfi_* 是回溯信息(调用帧信息),最终会出现在 .eh_frame 部分中。
对于性能调优/查看某些东西是如何编译的,我通常更喜欢编译器 asm 输出,而不是反汇编,而是过滤掉 CFI 和 .loc 指令等噪音 (How to remove "noise" from GCC/clang assembly output?)。查看分支目标上的标签,以便您知道何时可以从其他地方跳转到某个位置,这对于注意循环的顶部非常有用。 (像 Agner Fog 的 objconv 这样的一些反汇编程序可以反汇编成可以重新组装的源代码,并带有编译器使用的自动编号标签名称。)
如果您使用链接时优化,最好在跨文件内联后查看它以获得真实情况,因为它与通常的-S -O3 输出不同的代码生成。这可能意味着反汇编二进制文件,除非您的工具可以要求链接时优化器打印 asm 文本。
对于实际调试,像 GDB 这样的调试器让您可以选择使用 C 代码和行号进行源代码级调试,并使用调试信息将 C 名称与寄存器或内存位置相关联。通常你会使用它,但如果你想健全地检查逻辑以查看它是否按照你期望的方式编译,大多数调试器都可以轻松地在反汇编视图中单步执行指令。 (例如,有时你会注意到你使用了错误的变量名,因为一堆东西被优化掉了,而且 asm 比你预期的要简单得多。或者你的分支条件实际上是检查 or (|) 而不是短-电路逻辑||.)
在复杂代码中,反汇编视图中的单步执行是查看其正在做什么的好方法,至少可以找到主循环或特定输入所采用的执行路径。
-S 编译器生成的 asm 文本在调试时通常不可用。除非您要求,否则 Clang 根本不会生成它。 (GCC 总是生成一个.s 并在其上运行as,与cc1 C->asm 编译器分开,如果您询问可以保存)。但是没有元数据将运行程序中的代码地址与.s asm 行关联起来,所以在真正调试的时候并不好用。