GCC不是一个人在战斗,GCC背后其实有一堆战友。
目录
一、GCC的合作伙伴
二、GCC编译具体过程
2.1 简单编译
2.2 实际编译
①预处理
②编译为汇编代码
③汇编
④连接
三、分析ELF文件
3.1 ELF文件的段
3.2 反汇编ELF
以下介绍部分gcc二进制程序处理工具
工具 | 作用 |
---|---|
addr2line | 用来将程序地址转换成其所对应的程序源文件及所对应的代码行,也可以得到所对应的函数。该工具将帮助调试器在调试的过程中定位对应的源代码位置 |
as | 主要用于汇编 |
ld | 主要用于链接 |
ar | 主要用于创建静态库 |
ldd | 可以用于查看一个可执行程序依赖的共享库 |
objcopy | 将一种对象文件翻译成另一种格式,譬如将.bin 转换成.elf、或者将.elf 转换成.bin 等 |
objdump | 主要的作用是反汇编 |
readelf | 显示有关ELF文件的信息 |
size | 列出可执行文件每个部分的尺寸和总尺寸,代码段、数据段、总大小等 |
示例程序如下
#include
int main(void)
{
printf("Hello World!\n");
return 0;
}
gcc test.c -o test
实质上,上述编译过程是分为四个阶段进行的,即预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编(Assembly)和连接(Linking)。
执行命令gcc -E test.c -o test.i 或gcc -E test.c
,生成test.i文件
test.i部分代码:
执行命令gcc -S test.i -o test.s
,生成test.s文件
test.s部分代码:
执行命令gcc -c test.s -o test.o
,生成test.o文件
执行命令gcc test.o -o test
,生成test可执行文件
ELF文件格式如下图,位于ELF Header和Section Header Table 之间的都是段(Section)。一个典型的ELF文件包含下面几个段:
·text:已编译程序的指令代码段。
·rodata:ro 代表 read only,即只读数据(譬如常数 const)。
·data:已初始化的 C 程序全局变量和静态局部变量。
·bss:未初始化的 C 程序全局变量和静态局部变量。
·debug:调试符号表,调试器用此段的信息帮助调试。
可以使用readelf -S test
查看test可执行文件各个section的信息如下:
There are 29 section headers, starting at offset 0x1928:
节头:
[号] 名称 类型 地址 偏移量
大小 全体大小 旗标 链接 信息 对齐
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000000238 00000238
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.ABI-tag NOTE 0000000000000254 00000254
0000000000000020 0000000000000000 A 0 0 4
······
[ 6] .dynstr STRTAB 0000000000000360 00000360
0000000000000082 0000000000000000 A 0 0 1
[ 7] .gnu.version VERSYM 00000000000003e2 000003e2
000000000000000e 0000000000000002 A 5 0 2
[ 8] .gnu.version_r VERNEED 00000000000003f0 000003f0
0000000000000020 0000000000000000 A 6 1 8
[ 9] .rela.dyn RELA 0000000000000410 00000410
00000000000000c0 0000000000000018 A 5 0 8
······
[13] .plt.got PROGBITS 0000000000000520 00000520
0000000000000008 0000000000000008 AX 0 0 8
[14] .text PROGBITS 0000000000000530 00000530
00000000000001a2 0000000000000000 AX 0 0 16
[15] .fini PROGBITS 00000000000006d4 000006d4
0000000000000009 0000000000000000 AX 0 0 4
······
[22] .got PROGBITS 0000000000200fb8 00000fb8
0000000000000048 0000000000000008 WA 0 0 8
[23] .data PROGBITS 0000000000201000 00001000
0000000000000010 0000000000000000 WA 0 0 8
······
[26] .symtab SYMTAB 0000000000000000 00001040
00000000000005e8 0000000000000018 27 43 8
[27] .strtab STRTAB 0000000000000000 00001628
0000000000000202 0000000000000000 0 0 1
[28] .shstrtab STRTAB 0000000000000000 0000182a
00000000000000fe 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
由于ELF文件无法被当做普通文本文件打开,如果希望直接查看一个 ELF文件包含的指令和数据,需要使用反汇编的方法。
使用objdump -D test对其进行反汇编如下:
test: 文件格式 elf64-x86-64
Disassembly of section .interp:
0000000000000238 <.interp>:
238: 2f (bad)
239: 6c insb (%dx),%es:(%rdi)
23a: 69 62 36 34 2f 6c 64 imul $0x646c2f34,0x36(%rdx),%esp
241: 2d 6c 69 6e 75 sub $0x756e696c,%eax
246: 78 2d js 275 <_init-0x273>
248: 78 38 js 282 <_init-0x266>
24a: 36 2d 36 34 2e 73 ss sub $0x732e3436,%eax
250: 6f outsl %ds:(%rsi),(%dx)
251: 2e 32 00 xor %cs:(%rax),%al
······
Disassembly of section .comment:
0000000000000000 <.comment>:
0: 47 rex.RXB
1: 43 rex.XB
2: 43 3a 20 rex.XB cmp (%r8),%spl
5: 28 55 62 sub %dl,0x62(%rbp)
8: 75 6e jne 78 <_init-0x470>
a: 74 75 je 81 <_init-0x467>
c: 20 37 and %dh,(%rdi)
e: 2e 35 2e 30 2d 33 cs xor $0x332d302e,%eax
14: 75 62 jne 78 <_init-0x470>
16: 75 6e jne 86 <_init-0x462>
18: 74 75 je 8f <_init-0x459>
1a: 31 7e 31 xor %edi,0x31(%rsi)
1d: 38 2e cmp %ch,(%rsi)
1f: 30 34 29 xor %dh,(%rcx,%rbp,1)
22: 20 37 and %dh,(%rdi)
24: 2e cs
25: 35 .byte 0x35
26: 2e 30 00 xor %al,%cs:(%rax)
使用objdump -S
将其反汇编并且将其C语言源代码混合显示出来:
执行命令gcc -o test -g test.c
再执行objdump -S test
得到如下结果:
test: 文件格式 elf64-x86-64
Disassembly of section .init:
00000000000004e8 <_init>:
4e8: 48 83 ec 08 sub $0x8,%rsp
4ec: 48 8b 05 f5 0a 20 00 mov 0x200af5(%rip),%rax # 200fe8 <__gmon_start__>
4f3: 48 85 c0 test %rax,%rax
4f6: 74 02 je 4fa <_init+0x12>
4f8: ff d0 callq *%rax
4fa: 48 83 c4 08 add $0x8,%rsp
4fe: c3 retq
······
xchg %ax,%ax
Disassembly of section .text:
0000000000000530 <_start>:
530: 31 ed xor %ebp,%ebp
532: 49 89 d1 mov %rdx,%r9
535: 5e pop %rsi
536: 48 89 e2 mov %rsp,%rdx
539: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
53d: 50 push %rax
53e: 54 push %rsp
53f: 4c 8d 05 8a 01 00 00 lea 0x18a(%rip),%r8 # 6d0 <__libc_csu_fini>
546: 48 8d 0d 13 01 00 00 lea 0x113(%rip),%rcx # 660 <__libc_csu_init>
54d: 48 8d 3d e6 00 00 00 lea 0xe6(%rip),%rdi # 63a
······
00000000000006d0 <__libc_csu_fini>:
6d0: f3 c3 repz retq
Disassembly of section .fini:
00000000000006d4 <_fini>:
6d4: 48 83 ec 08 sub $0x8,%rsp
6d8: 48 83 c4 08 add $0x8,%rsp
6dc: c3 retq