使用GCC生成可读的程序集?


256

我想知道如何在我的C源文件上使用GCC来转储机器码的助记符版本,以便可以看到我的代码被编译成什么。您可以使用Java来做到这一点,但我无法通过GCC找到一种方法。

我试图在汇编中重新编写C方法,看看GCC如何做到这一点会有很大帮助。


25
请注意,“字节码”通常表示VM使用的代码,例如JVM或.NET的CLR。GCC的输出更好地称为“机器代码”,“机器语言”或“汇编语言”
Javier

2
我添加了一个使用Godbolt的答案,因为它是用于快速试验不同选项如何影响代码生成的非常强大的工具。
Shafik Yaghmour 2014年



有关使asm输出易于阅读的更多提示,另请参见:如何从GCC / clang程序集输出中消除“噪音”?
彼得·科德斯

Answers:


335

如果使用调试符号进行编译,则可以objdump用来产生更具可读性的反汇编。

>objdump --help
[...]
-S, --source             Intermix source code with disassembly
-l, --line-numbers       Include line numbers and filenames in output

objdump -drwC -Mintel 很好:

  • -r在重定位上显示符号名称(因此您将putscall下面的说明中看到)
  • -R 显示动态链接重定位/符号名称(在共享库上有用)
  • -C 分解C ++符号名称
  • -w 为“宽”模式:它不会换行机器码字节
  • -Mintel:使用类似于GAS / binutils MASM的.intel_syntax noprefix语法,而不是AT&T
  • -S:交叉插入源代码行和反汇编。

你可以alias disas="objdump -drwCS -Mintel"在你的东西~/.bashrc


例:

> gcc -g -c test.c
> objdump -d -M intel -S test.o

test.o:     file format elf32-i386


Disassembly of section .text:

00000000 <main>:
#include <stdio.h>

int main(void)
{
   0:   55                      push   ebp
   1:   89 e5                   mov    ebp,esp
   3:   83 e4 f0                and    esp,0xfffffff0
   6:   83 ec 10                sub    esp,0x10
    puts("test");
   9:   c7 04 24 00 00 00 00    mov    DWORD PTR [esp],0x0
  10:   e8 fc ff ff ff          call   11 <main+0x11>

    return 0;
  15:   b8 00 00 00 00          mov    eax,0x0
}
  1a:   c9                      leave  
  1b:   c3                      ret

3
是否可以仅使用Intel指令进行切换?
詹姆斯,

3
所有这些都是Intel指令,因为它们在Intel处理器:D上运行。
TOTO

12
@toto我认为他的意思是Intel语法而不是AT&T语法
Amok,2009年

7
通过使用切换序列,可以放弃中间目标文件-Wa,-adhln -g to gcc。这假设组装者是气体,并且并非总是如此。
马克·巴特勒

8
@詹姆斯是的,供应-Mintel
2015年

106

如果给GCC标记-fverbose-asm,它将

在生成的汇编代码中添加额外的注释信息,以使其更具可读性。

[...]添加的评论包括:

  • 有关编译器版本和命令行选项的信息,
  • 与汇编指令相关的源代码行,格式为FILENAME:LINENUMBER:CONTENT OF LINE,
  • 提示哪些高级表达式对应于各种汇编指令操作数。

但是然后,我将丢失所有用于objdump- 的开关objdump -drwCS -Mintel,那么如何使用verbosewith objdump呢?这样我就可以像-fverbose-asm在gcc中一样在asm代码中添加注释了?
牧民

1
@牧民:你不能。多余的内容-fverbose-asm以输出的asm语法中的注释形式出现,而不是会在.o文件中添加任何多余内容的指令。在组装时将其全部丢弃。查看编译器的汇编输出而不是反汇编,例如在godbolt.org上,您可以通过鼠标悬停和相应的源/ asm行的颜色突出显示轻松地将其与源行进行匹配。 如何从GCC / c装配件输出中消除“噪音”?
彼得·科德斯

75

使用-S(注意:大写S)开关切换到GCC,它将把汇编代码发送到扩展名为.s的文件中。例如,以下命令:

gcc -O2 -S foo.c

将生成的汇编代码保留在文件foo.s中。

直接从http://www.delorie.com/djgpp/v2faq/faq8_20.html翻录(但删除了错误的-c


35
您不应混合使用-c和-S,而只能使用其中之一。在这种情况下,一个可能会覆盖另一个,这可能取决于它们的使用顺序。
亚当·罗森菲尔德2009年

4
@AdamRosenfield关于“不应该将-c和-S混合使用”的参考吗?如果是真的,我们应该提醒作者并进行编辑。
托尼

5
@Tony:gcc.gnu.org/onlinedocs/gcc/Overall-Options.html#Overall-Options “你可以使用... 一个选项-c,-S,或-E说,如果GCC是停止。 ”
Nate Eldredge

1
如果需要所有中间输出,请使用gcc -march=native -O3 -save-temps。您仍然可以-c在不尝试链接或任何其他操作的情况下停止创建目标文件。
彼得·科德斯

2
-save-temps有趣的是,它一次转储完全由代码生成的代码,而另一种调用编译器的选项-S意味着编译两次,并且可能使用不同的选项。但是 -save-temps将所有内容转储到当前目录中,这有点混乱。看起来它更适合作为GCC的调试选项,而不是用于检查代码的工具。
16StéphaneGourichon

50

-S默认情况下,在基于x86的系统上使用切换到GCC会产生AT&T语法的转储,可以使用该-masm=att开关指定它,如下所示:

gcc -S -masm=att code.c

而如果您想使用Intel语法生成转储,则可以使用该-masm=intel开关,如下所示:

gcc -S -masm=intel code.c

(两者都将转储code.c成不同的语法,code.s分别生成到文件中)

为了使用objdump产生类似的效果,您需要使用--disassembler-options= intel/ att开关(一个示例)(带有代码转储以说明语法上的差异):

 $ objdump -d --disassembler-options=att code.c
 080483c4 <main>:
 80483c4:   8d 4c 24 04             lea    0x4(%esp),%ecx
 80483c8:   83 e4 f0                and    $0xfffffff0,%esp
 80483cb:   ff 71 fc                pushl  -0x4(%ecx)
 80483ce:   55                      push   %ebp
 80483cf:   89 e5                   mov    %esp,%ebp
 80483d1:   51                      push   %ecx
 80483d2:   83 ec 04                sub    $0x4,%esp
 80483d5:   c7 04 24 b0 84 04 08    movl   $0x80484b0,(%esp)
 80483dc:   e8 13 ff ff ff          call   80482f4 <puts@plt>
 80483e1:   b8 00 00 00 00          mov    $0x0,%eax
 80483e6:   83 c4 04                add    $0x4,%esp 
 80483e9:   59                      pop    %ecx
 80483ea:   5d                      pop    %ebp
 80483eb:   8d 61 fc                lea    -0x4(%ecx),%esp
 80483ee:   c3                      ret
 80483ef:   90                      nop

$ objdump -d --disassembler-options=intel code.c
 080483c4 <main>:
 80483c4:   8d 4c 24 04             lea    ecx,[esp+0x4]
 80483c8:   83 e4 f0                and    esp,0xfffffff0
 80483cb:   ff 71 fc                push   DWORD PTR [ecx-0x4]
 80483ce:   55                      push   ebp
 80483cf:   89 e5                   mov    ebp,esp
 80483d1:   51                      push   ecx
 80483d2:   83 ec 04                sub    esp,0x4
 80483d5:   c7 04 24 b0 84 04 08    mov    DWORD PTR [esp],0x80484b0
 80483dc:   e8 13 ff ff ff          call   80482f4 <puts@plt>
 80483e1:   b8 00 00 00 00          mov    eax,0x0
 80483e6:   83 c4 04                add    esp,0x4
 80483e9:   59                      pop    ecx
 80483ea:   5d                      pop    ebp
 80483eb:   8d 61 fc                lea    esp,[ecx-0x4]
 80483ee:   c3                      ret    
 80483ef:   90                      nop

…… gcc -S -masm=intel test.c对我而言并不完全有效,我得到了Intel和AT&T语法的一些杂种,例如:mov %rax, QWORD PTR -24[%rbp],而不是:movq -24(%rbp), %rax
L̲̳o̲̳̳n̲̳̳g̲̳p̲̳o̲̳̳k̲̳̳e̲̳̳

1
不错的提示。应该注意的是,当同时执行.o和ASM文件的并行输出时,也可以使用该方法,即通过-Wa,-ahls -o yourfile.o yourfile.cpp>yourfile.asm
underscore_d

可以使用-M选项,它与选项相同,--disassembler-options但要短得多,例如objdump -d -M intel a.out | less -N
Eric Wang

34

godbolt是一个非常有用的工具,它们只列出了C ++编译器,但是您可以使用-x cflag来将其视为C。它将随后为您的代码并排生成汇编列表,您可以使用该Colourise选项生成彩色条以可视方式指示哪些源代码映射到生成的程序集。例如下面的代码:

#include <stdio.h>

void func()
{
  printf( "hello world\n" ) ;
}

使用以下命令行:

-x c -std=c99 -O3

Colourise生成以下内容:

在此处输入图片说明


很高兴知道Godbolt过滤器的工作原理:.LC0,.text,//和Intel。英特尔很容易,-masm=intel但是其余的呢?
Z玻色子

我想这是在这里解释stackoverflow.com/a/38552509/2542702
Z玻色子

godbolt确实支持C(以及大量其他语言,如Rust,D,Pascal等)。只是C编译器更少,因此,将C ++编译器与-x c
phuclv '19

23

您是否尝试过gcc -S -fverbose-asm -O source.c查看生成的source.s汇编文件?

生成的汇编代码进入source.s(您可以使用-o assembler-filename覆盖它);该-fverbose-asm选项要求编译器发出一些汇编注释,以“解释”生成的汇编代码。该-O选项要求编译器优化一点(它可以使用-O2或优化更多-O3)。

如果您想了解gcc正在执行的操作,请尝试传递-fdump-tree-all但要小心:您将获得数百个转储文件。

顺便说一句,GCC是可扩展的,可通过插件MELT(用于扩展GCC的高级领域特定语言;我于2017年放弃)


也许会提到输出将是in source.s,因为很多人希望在控制台上打印输出。
RubenLaguna

1
@ecerulm:-S -o-转储到标准输出。 -masm=intel如果要使用NASM / YASM语法,则很有帮助。(但它使用qword ptr [mem],而不是qword,因此更像是Intel / MASM而不是NASM / YASM)。 gcc.godbolt.org很好地整理了转储:可选地剥离仅注释行,未使用的标签和汇编程序指令。
彼得·科德斯

2
忘了提及:如果您正在寻找“类似于源代码,但在每个源代码行之后都没有存储/重新加载的噪音”,那么-Og它甚至比更好-O1。这意味着“针对调试进行优化”,并且使asm没有太多棘手的/难以遵循的优化,而该优化无法完成消息来源所说的一切。它从gcc4.8开始可用,但是clang 3.7仍然没有。IDK是否决定反对还是采取其他措施。
彼得·科德斯

19

您可以像objdump这样使用gdb。

此摘录摘自http://sources.redhat.com/gdb/current/onlinedocs/gdb_9.html#SEC64


这是一个显示Intel x86的混合源代码+程序集的示例:

  (gdb)disas / m main
函数main的汇编代码转储:
5 {
0x08048330:推送%ebp
0x08048331:mov%esp,%ebp
0x08048333:子$ 0x8,%esp
0x08048336:和$ 0xfffffff0,%esp
0x08048339:子$ 0x10,%esp

6 printf(“ Hello。\ n”);
0x0804833c:movl $ 0x8048440,(%esp)
0x08048343:致电0x8048284 

7返回0;
8}
0x08048348:mov $ 0x0,%eax
0x0804834d:离开
0x0804834e:重新

汇编器转储结束。

1
存档的

要将GDB的反汇编程序切换为Intel语法,请使用set disassembly-flavor intel命令。
罗斯兰

13

使用-S(注意:大写S)开关切换到GCC,它将把汇编代码发送到扩展名为.s的文件中。例如,以下命令:

gcc -O2 -S -c foo.c


4

我还没有尝试过gcc,但是在使用g ++的情况下。下面的命令对我有用。-g用于调试构建,-Wa,-adhln传递给汇编器以列出源代码

g ++ -g -Wa,-adhln src.cpp


它也适用于gcc!-Wa,...用于汇编器部分的命令行选项(在C / ++编译后在gcc / g ++中执行)。它作为内部调用(在Windows中为as.exe)。参见> as --help作为命令行以查看更多帮助
Hartmut Schorrig

0

在gcc或g ++上使用-Wa,-adhln作为选项来生成标准输出的列表输出。

-Wa,...用于汇编器部分的命令行选项(在C / ++编译后在gcc / g ++中执行)。它作为内部调用(在Windows中 as.exe)。看到

>为-帮助

作为命令行以查看gcc内的汇编工具的更多帮助

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.