代码编写

写个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

代码执行

运行可执行文件

1
./HelloWorld

代码反编译

反编译可执行文件

反编译可执行文件 HelloWorld.dump

1
objdump -d HelloWorld > HelloWorld.dump

由于得到的文件内容有点多,故直接将其写入到文件

查看结果,main函数部分反汇编结果如下

反编译目标文件

可执行文件反编译出的内容较多,故我们可以暂时先仅关注main函数的反汇编结果,可执行文件是由目标文件经过重定位装配而成,故仅关注目标文件的反汇编结果即可,

反编译目标文件 HelloWorld.o.dump

1
objdump -d HelloWorld.o 
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

<未完待续>