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

我试图在汇编中重写一个C方法,看看GCC是如何做的,这将是一个很大的帮助。


当前回答

如果你使用调试符号进行编译(在GCC命令行中添加-g,即使你也在使用-O31), 你可以使用objdump -S来生成一个与C源交叉的更可读的反汇编。

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

objdump -drwC -Mintel是不错的:

-r显示重定位时的符号名称(所以你会在下面的调用指令中看到puts) -R显示动态链接重定位/符号名(在共享库中很有用) -C要求c++的符号名称 -w是"wide"模式:它不会行换行机器代码字节 -Mintel:使用GAS/binutils MASM-like .intel_syntax noprefix语法而不是AT&T -S:将源线与拆卸交叉。

你可以在你的~/.bashrc中加入一些类似alias disas="objdump -drwCS -Mintel"的东西。如果不是在x86上,或者如果你喜欢AT&T语法,请省略-Mintel。


例子:

> 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

注意,这里没有使用-r,所以调用rel32=-4没有标注puts符号名。看起来像一个中断的调用,跳到main中的调用指令中间。记住,调用编码中的rel32位移只是一个占位符,直到链接器填充一个真正的偏移量(在这种情况下,到PLT存根,除非静态链接libc)。


脚注1:交叉源代码可能会很混乱,对优化构建没有太大帮助;为此,可以考虑https://godbolt.org/或其他方法来可视化哪些指令对应哪些源行。在优化的代码中,并不总是有一个单独的源行来说明一条指令,但是调试信息将为每条asm指令选择一个源行。

其他回答

你试过gcc -S -fverbose-asm -O source.c然后查看生成的源代码吗?S汇编文件?

生成的汇编程序代码进入源代码。S(你可以用-o assembly -filename重写它);-fverbose-asm选项要求编译器发出一些汇编注释“解释”生成的汇编代码。-O选项要求编译器优化一点(它可以用-O2或-O3优化更多)。

如果您想了解gcc在做什么,请尝试传递-fdump-tree-但要小心:您将得到数百个转储文件。

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

你可以像objdump一样使用gdb。

本文节选自http://sources.redhat.com/gdb/current/onlinedocs/gdb_9.html#SEC64


下面是英特尔x86混合源代码+汇编的示例:

  (gdb) disas /m main
Dump of assembler code for function main:
5       {
0x08048330 :    push   %ebp
0x08048331 :    mov    %esp,%ebp
0x08048333 :    sub    $0x8,%esp
0x08048336 :    and    $0xfffffff0,%esp
0x08048339 :    sub    $0x10,%esp

6         printf ("Hello.\n");
0x0804833c :   movl   $0x8048440,(%esp)
0x08048343 :   call   0x8048284 

7         return 0;
8       }
0x08048348 :   mov    $0x0,%eax
0x0804834d :   leave
0x0804834e :   ret

End of assembler dump.

我还没有尝试gcc,但是对于g++,下面的命令对我来说是有效的。

-g用于调试构建 -Wa,-adhln被传递给汇编程序,用于与源代码一起列出

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

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

gcc -S -masm=att code.c

然而,如果你想用Intel语法生成一个转储,你可以使用-masm= Intel开关,如下所示:

gcc -S -masm=intel code.c

(两者都将code.c转储到各自的语法中,转储到文件代码中。分别)

为了用objdump产生类似的效果,你需要使用——disassemble -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

and

$ 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或g++上使用-Wa,-adhln作为选项来生成到标准输出的清单输出。

佤邦,……是用于汇编程序部分的命令行选项(在C/++编译后在gcc/g++中执行)。它在内部调用(Windows中的as.exe)。 看到

>——帮助

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