代码编写
写个HelloWorld
1 2 3 4 5 6
| #include <stdio.h> int main(int argc, char const *argv[]) { printf("hello world!\n"); return 0; }
|
代码编译
程序的编译过程如下图,
根据源文件生成预处理文件 HelloWorld.i
1
| gcc -E HelloWorld.c -o HelloWorld.i
|
根据预处理文件生成汇编文件 HelloWorld.s
1
| gcc -S HelloWorld.i -o HelloWorld.s
|
根据汇编文件生成目标文件 HelloWorld.o
1
| gcc -c HelloWorld.s -o HelloWorld.o
|
根据目标文件生成可执行文件
1
| gcc HelloWorld.o -o HelloWorld
|
代码执行
运行可执行文件
代码反编译
反编译可执行文件
反编译可执行文件 HelloWorld.dump
1
| objdump -d HelloWorld > HelloWorld.dump
|
由于得到的文件内容有点多,故直接将其写入到文件
查看结果,main函数部分反汇编结果如下
反编译目标文件
可执行文件反编译出的内容较多,故我们可以暂时先仅关注main函数的反汇编结果,可执行文件是由目标文件经过重定位装配而成,故仅关注目标文件的反汇编结果即可,
反编译目标文件 HelloWorld.o.dump
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| HelloWorld.o: 文件格式 elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: 48 83 ec 10 sub $0x10,%rsp 8: 89 7d fc mov %edi,-0x4(%rbp) b: 48 89 75 f0 mov %rsi,-0x10(%rbp) f: 48 8d 05 00 00 00 00 lea 0x0(%rip),%rax # 16 <main+0x16> 16: 48 89 c7 mov %rax,%rdi 19: e8 00 00 00 00 call 1e <main+0x1e> 1e: b8 00 00 00 00 mov $0x0,%eax 23: c9 leave 24: c3 ret
|
上述反汇编结果中,分成了四列,
第一列为各个指令的16进制起始地址,第二列为16进制的计算机指令,第三列为汇编指令,第四列为指令操作数,第五列为相关注释。
可以看出,x86_64为可变长指令集。
目标文件中,main函数起始地址从0开始,可执行文件中则不是,可以看出可执行文件是由目标文件经过链接后得到的。
main函数反汇编代码解析
由于对可执行文件的反汇编结果中,提取main代码段,结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| 0000000000001139 <main>: // 寄存rbp压栈 1139: 55 push %rbp // 将寄存器rsp的值传递到rbp,即rbp = rsp 113a: 48 89 e5 mov %rsp,%rbp // rsp - 0x10 113d: 48 83 ec 10 sub $0x10,%rsp // [rbp-0x04] = edi 1141: 89 7d fc mov %edi,-0x4(%rbp) // [rbp-0x10] = rsi 1144: 48 89 75 f0 mov %rsi,-0x10(%rbp) // rax = rip + 0xeb5 1148: 48 8d 05 b5 0e 00 00 lea 0xeb5(%rip),%rax # 2004 <_IO_stdin_used+0x4> // rdi = rax 114f: 48 89 c7 mov %rax,%rdi // 调用puts函数 1152: e8 d9 fe ff ff call 1030 <puts@plt> // eax = 0 1157: b8 00 00 00 00 mov $0x0,%eax // 清空栈 115c: c9 leave // 函数返回 115d: c3 ret
|
<未完待续>