我想知道如何使用GCC来将我的C源文件转换成机器码的助记符版本,以便我可以看到我的代码被编译成了什么。在Java中可以这样做,但我没有找到使用GCC的方法。
我正在尝试用汇编语言重写一个C方法,查看GCC是如何实现的会很有帮助。
我想知道如何使用GCC来将我的C源文件转换成机器码的助记符版本,以便我可以看到我的代码被编译成了什么。在Java中可以这样做,但我没有找到使用GCC的方法。
我正在尝试用汇编语言重写一个C方法,查看GCC是如何实现的会很有帮助。
-O3
1),可以使用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 MASM 类似的 .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
,跳入主调指令中间。请记住,在调用编码中的rel32
位移仅用作占位符,直到链接器填充实际偏移量(在这种情况下,到PLT存根,除非您静态链接libc)。
otool -tV
命令。 - transang-fverbose-asm
标志,它会在生成的汇编代码中添加额外的注释信息,使其更易读。添加的注释包括:编译器版本和命令行选项的信息,与汇编指令相关联的源代码行,以 FILENAME:LINENUMBER:CONTENT OF LINE 的形式呈现,以及关于哪些高级表达式对应于各种汇编指令操作数的提示。objdump -drwCS -Mintel
中的所有开关,那么我该如何在 objdump
中使用类似于 verbose
的东西呢?这样我就可以在汇编代码中添加注释,就像 gcc 中的 -fverbose-asm
一样了。 - Herdsman-fverbose-asm
添加的额外信息是以注释形式出现在输出的汇编代码中,而不是指令,不会将任何额外内容放入 .o
文件中。 这些信息在汇编时全部被丢弃了。 请查看编译器生成的汇编输出,而不是反汇编输出,例如在 https://godbolt.org/ 上,你可以通过鼠标悬停和相应源代码/汇编代码行的颜色高亮匹配它们。 如何从GCC / clang汇编输出中删除“噪音”? - Peter Cordesgcc -O2 -S foo.c
会把生成的汇编代码保存到文件foo.s中。gcc -march=native -O3 -save-temps
。您仍然可以使用 -c
停止在创建目标文件而不是尝试链接,或者其他操作。 - Peter Cordes-save-temps
来查看某些源代码编译为汇编的方式,而是使用-masm=intel -S -o- | less
,反汇编.o
或可执行文件,或将其放在https://godbolt.org/上。 - Peter Cordes在基于x86系统上使用-S
开关对GCC进行编译,会默认生成AT&T语法的转储文件。可以使用-masm=att
开关指定生成AT&T语法的转储文件,如下:
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̲̳̳.o
和ASM文件的并行输出时,即通过-Wa,-ahls -o yourfile.o yourfile.cpp>yourfile.asm
,此方法同样适用。 - underscore_d-M
选项,它与--disassembler-options
相同,但更短,例如objdump -d -M intel a.out | less -N
。 - Ericgodbolt 是一个非常有用的工具,它们只列出了 C++ 编译器,但是你可以使用 -x c
标志来让它将代码视为 C 代码进行处理。然后,它会生成你的代码的汇编清单,并且你可以使用 Colourise
选项生成彩色条,以视觉上指示哪些源代码映射到生成的汇编代码。例如下面的代码:
#include <stdio.h>
void func()
{
printf( "hello world\n" ) ;
}
使用以下命令行:
-x c -std=c99 -O3
同时使用 Colourise
会生成以下内容:
-masm=intel
,但其他过滤器呢? - Z boson-x c
的 C++ 编译器。 - phuclvgcc -S -fverbose-asm -O source.c
命令,然后查看生成的汇编文件source.s
吗?source.s
文件中(你可以用-o
assembler-filename来覆盖默认文件名);-fverbose-asm
选项要求编译器生成一些汇编注释来“解释”生成的汇编代码。而-O
选项是让编译器进行轻微的优化(使用-O2
或-O3
能够进行更多优化)。gcc
正在做什么,可以尝试使用-fdump-tree-all
选项,但需谨慎:你将得到数百个转储文件。source.s
中,因为许多人期望在控制台上输出打印。 - RubenLaguna-S -o-
输出到标准输出。如果要使用NASM/YASM语法,-masm=intel
是有帮助的(但是它使用的是qword ptr [mem]
,而不仅仅是qword
,所以更像Intel/MASM而不是NASM/YASM)。http://gcc.godbolt.org/ 在整理输出时做得很好:可选地剥离仅注释行、未使用的标签和汇编指令。 - Peter Cordes-Og
比-O1
更好。它的意思是“为调试优化”,可以生成汇编代码,而不包含太多复杂/难以跟踪的优化内容,并且会按照源代码的意思执行操作。这个选项自gcc4.8之后就已经可用了,但clang 3.7仍然没有这个选项。我不知道他们是否决定放弃它。 - Peter Cordes-Og
。此外,请参见如何从GCC / clang汇编输出中删除“噪音”?,了解有关编写编译为有趣汇编代码的测试函数以及查看GCC / clang选项的更多信息,以直接查看它们的汇编输出,而不是反汇编。我不知道为什么这个问题的答案大多是关于反汇编而不是GCC汇编输出。 - Peter Cordes(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
as --help- Hartmut Schorrig
对于RISC-V反汇编,这些标志很好用:
riscv64-unknown-elf-objdump -d -S -l --visualize-jumps --disassembler-color=color --inlines
-d
:反汇编,最基本的标志
-S
:混合源代码。注意:编译时必须使用-g
标志
-l
:行号
--visualize-jumps
:花哨的箭头,没有太多用处,但为什么不试一下呢。有时会变得太杂乱,实际上使阅读源代码更加困难。引用自Peter Cordes
的评论:--visualize-jumps=color
也是一个选项,可以为不同的箭头使用不同的颜色
--disassembler-color=color
:给反汇编添加一些颜色
--inlines
:打印内联函数
可能有用:
-M numeric
:使用数字寄存器名称而不是ABI名称,如果您正在进行CPU开发并且不知道ABI名称,这将非常有用
-M no-aliases
:不要使用伪指令,如li
和call
示例:main.o
:
#include <stdio.h>
#include <stdint.h>
static inline void example_inline(const char* str) {
for (int i = 0; str[i] != 0; i++)
putchar(str[i]);
}
int main() {
printf("Hello world");
example_inline("Hello! I am inlined");
return 0;
}
如果您想混合使用源代码,我建议使用-O0
。如果使用-O2
,则混合使用源代码会变得非常混乱。
命令:
riscv64-unknown-elf-gcc main.c -c -O0 -g
riscv64-unknown-elf-objdump -d -S -l --disassembler-color=color --inlines main.o
反汇编:
main.o: file format elf64-littleriscv
Disassembly of section .text:
0000000000000000 <example_inline>:
example_inline():
/Users/cyao/test/main.c:4
#include <stdio.h>
#include <stdint.h>
static inline void example_inline(const char* str) {
0: 7179 addi sp,sp,-48
2: f406 sd ra,40(sp)
4: f022 sd s0,32(sp)
6: 1800 addi s0,sp,48
8: fca43c23 sd a0,-40(s0)
000000000000000c <.LBB2>:
/Users/cyao/test/main.c:5
for (int i = 0; str[i] != 0; i++)
c: fe042623 sw zero,-20(s0)
10: a01d j 36 <.L2>
0000000000000012 <.L3>:
/Users/cyao/test/main.c:6 (discriminator 3)
putchar(str[i]);
12: fec42783 lw a5,-20(s0)
16: fd843703 ld a4,-40(s0)
1a: 97ba add a5,a5,a4
1c: 0007c783 lbu a5,0(a5)
20: 2781 sext.w a5,a5
22: 853e mv a0,a5
24: 00000097 auipc ra,0x0
28: 000080e7 jalr ra # 24 <.L3+0x12>
/Users/cyao/test/main.c:5 (discriminator 3)
for (int i = 0; str[i] != 0; i++)
2c: fec42783 lw a5,-20(s0)
30: 2785 addiw a5,a5,1
32: fef42623 sw a5,-20(s0)
0000000000000036 <.L2>:
/Users/cyao/test/main.c:5 (discriminator 1)
36: fec42783 lw a5,-20(s0)
3a: fd843703 ld a4,-40(s0)
3e: 97ba add a5,a5,a4
40: 0007c783 lbu a5,0(a5)
44: f7f9 bnez a5,12 <.L3>
0000000000000046 <.LBE2>:
/Users/cyao/test/main.c:7
}
46: 0001 nop
48: 0001 nop
4a: 70a2 ld ra,40(sp)
4c: 7402 ld s0,32(sp)
4e: 6145 addi sp,sp,48
50: 8082 ret
0000000000000052 <main>:
main():
/Users/cyao/test/main.c:9
int main() {
52: 1141 addi sp,sp,-16
54: e406 sd ra,8(sp)
56: e022 sd s0,0(sp)
58: 0800 addi s0,sp,16
/Users/cyao/test/main.c:10
printf("Hello world");
5a: 000007b7 lui a5,0x0
5e: 00078513 mv a0,a5
62: 00000097 auipc ra,0x0
66: 000080e7 jalr ra # 62 <main+0x10>
/Users/cyao/test/main.c:11
example_inline("Hello! I am inlined");
6a: 000007b7 lui a5,0x0
6e: 00078513 mv a0,a5
72: 00000097 auipc ra,0x0
76: 000080e7 jalr ra # 72 <main+0x20>
/Users/cyao/test/main.c:13
return 0;
7a: 4781 li a5,0
/Users/cyao/test/main.c:14
}
7c: 853e mv a0,a5
7e: 60a2 ld ra,8(sp)
80: 6402 ld s0,0(sp)
82: 0141 addi sp,sp,16
84: 8082 ret
PS. 拆解后的代码中有颜色标记。