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

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

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

203779 次浏览

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

gcc -O2 -S foo.c

将生成的程序集代码保留在文件foo.s上。

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

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

gcc -O2 -S -c foo.c

如果你使用调试符号进行编译(在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显示了重定位时的符号名称(所以你会在下面的call指令中看到puts)
  • -R显示动态链接重定位/符号名称(在共享库中有用)
  • -C要求c++符号名称
  • -w是“宽”;模式:它不换行机器代码字节
  • -Mintel:使用GAS/binutils类似于asm的.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,所以call rel32=-4没有标注puts符号名。并且看起来像一个坏掉的call,跳到main中的调用指令中间。记住,调用编码中的rel32位移只是一个占位符,直到链接器填充一个真正的偏移量(在这种情况下,到PLT存根,除非你静态链接libc)。


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

如果你给海湾合作委员会一个标志-fverbose-asm,它会

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

[…新增的评论包括:

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

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

gcc -S -masm=att code.c

然而,如果你想用英特尔语法生成一个转储,你可以使用-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

你可以像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 -S -fverbose-asm -O source.c,然后查看生成的source.s汇编器文件?

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

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

顺便说一句,GCC可以通过插件融化(扩展GCC的高级域特定语言;我在2017年放弃了它)

godbolt是一个非常有用的工具,他们的列表只有c++编译器,但你可以使用-x c标志,以便让它把代码视为C。它会为你的代码并排生成一个程序集列表,你可以使用Colourise选项生成彩色条,以直观地表明哪些源代码映射到生成的程序集。例如下面的代码:

#include <stdio.h>


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

使用以下命令行:

-x c -std=c99 -O3

Colourise将生成以下内容:

enter image description here

我还没有尝试gcc,但在g++的情况下,下面的命令对我有用。

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

在gcc或g++上使用佤邦,-adhln作为选项来生成一个清单输出到stdout。

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

在——的帮助

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