我想知道如何在我的C源文件上使用GCC来转储机器代码的助记符版本,这样我就可以看到我的代码被编译成什么。你可以用Java做到这一点,但我还没有找到一种方法与GCC。
我试图在汇编中重写一个C方法,看看GCC是如何做的,这将是一个很大的帮助。
我想知道如何在我的C源文件上使用GCC来转储机器代码的助记符版本,这样我就可以看到我的代码被编译成什么。你可以用Java做到这一点,但我还没有找到一种方法与GCC。
我试图在汇编中重写一个C方法,看看GCC是如何做的,这将是一个很大的帮助。
当前回答
你试过gcc -S -fverbose-asm -O source.c然后查看生成的源代码吗?S汇编文件?
生成的汇编程序代码进入源代码。S(你可以用-o assembly -filename重写它);-fverbose-asm选项要求编译器发出一些汇编注释“解释”生成的汇编代码。-O选项要求编译器优化一点(它可以用-O2或-O3优化更多)。
如果您想了解gcc在做什么,请尝试传递-fdump-tree-但要小心:您将得到数百个转储文件。
顺便说一句,GCC是可扩展的,可以通过插件或使用MELT(一种用于扩展GCC的高级领域特定语言;我在2017年放弃了它)
其他回答
使用-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
对于risc-v反汇编,这些标志很好:
riscv64-unknown-elf-objdump -d -S -l --visualize-jumps --disassembler-color=color --inlines
-d:拆解,最基本的标志
-S:混合源。注意:编译时必须使用-g标志
-l:行号
——可视化跳跃:花哨的箭头,不是很有用,但为什么不呢?有时候get太乱了,实际上会让阅读源代码变得更加困难。来自Peter Cordes的评论:——visualize- leaps =coloris一个选项,为不同的箭头使用不同的颜色
——disassembly -color=color:给反汇编一些颜色
——inlines:打印出内行
也许有用:
-M numeric:使用数字reg名称而不是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
Dissasembly:
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.在拆卸代码中有颜色
你可以像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,即使你也在使用-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指令选择一个源行。