GCC不是一个人在战斗,GCC背后有一堆战友。本文的目的是简单的介绍一下GCC编译工具集中各软件的作用。
目录
一、GCC编译工具集
1、GCC的组成:
2、GCC编译具体过程
(1)预处理
(2)编译
(3)汇编
(4)连接
二、分析ELF文件
1 ELF文件的段
2 反汇编ELF
GCC(GNU C Compiler)是编译工具。本文所要介绍的将 C/C++语言编写的程序 转换成为处理器能够执行的二进制代码的过程即由编译器完成。
Binutils: 一组二进制程序处理工具,包括:addr2line、ar、objcopy、objdump、as、ld、 ldd、readelf、 size 等。
工具 | 作用 |
addr2line | 用来将程序地址转换成其所对应的程序源文件及所对应的代码行,也可以得到所对应的函数。该工具将帮助调试器在调试的过程中定位对应的源代码位置 |
as | 主要用于汇编 |
ld | 主要用于链接 |
ar | 主要用于创建静态库 |
ldd | 可以用于查看一个可执行程序依赖的共享库 |
objcopy | 将一种对象文件翻译成另一种格式,譬如将.bin 转换成.elf、或者将.elf 转换成.bin 等 |
objdump | 主要的作用是反汇编 |
readelf | 显示有关ELF文件的信息 |
size | 列出可执行文件每个部分的尺寸和总尺寸,代码段、数据段、总大小等 |
C运行库:
C 语言标准主要由两部分组成:一部分描述 C 的语法,另一部分描述 C 标准库。 C 标准库定义了一组标准头文件,每个头文件中包含一些相关的函数、变量、类 型声明和宏定义,譬如常见的 printf 函数便是一个 C 标准库函数,其原型定义 在 stdio 头文件中。
C 语言标准仅仅定义了 C 标准库函数原型,并没有提供实现。因此,C 语言编译 器通常需要一个 C 运行时库(C Run Time Libray,CRT)的支持。C 运行时库又 常简称为 C 运行库。与 C 语言类似,C++也定义了自己的标准,同时提供相关支 持库,称为 C++运行时库。
先利用vim创建一个简单的hello.c文件
#include
int main(void)
{
printf("Hello World!\n");
return 0;
}
实质上,GCC编译过程是分为四个阶段进行的,即预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编(Assembly)和连接(Linking)。
编译阶段 |
编译命令 | 作用 |
预处理 | gcc -E test.c -o test.i | 编译器将源代码中包含头文件编译进来 |
编译 | gcc -S test.i -o test.s | 检查代码规范性并翻译成汇编语言 |
汇编 | gcc -c test.s -o test.o | 将.s文件转换为目标文件 |
链接 | gcc test.o -o test | 将目标文件转换为可执行文件 |
执行命令gcc -E
hello.c -o
hello.i
,生成hello.i文件
部分hello.i代码
执行命令gcc -S hello.i -o hello.s
,生成hello.s文件
部分hello.s代码:
执行命令gcc -c hello.s -o hello.o
,生成hello.o文件
执行命令gcc hello.o -o hello
,生成hello可执行文件
ELF文件格式如下图,位于ELF Header和Section Header Table 之间的都是段(Section)。一个典型的ELF文件包含下面几个段:
.text | 已编译程序的指令代码段。 |
.rodata | ro 代表 read only,即只读数据(譬如常数 const) |
.data | 已初始化的 C 程序全局变量和静态局部变量。 |
.bss | 未初始化的 C 程序全局变量和静态局部变量。 |
.debug | 调试符号表,调试器用此段的信息帮助调试。 |
可以使用readelf -S hello
查看hello可执行文件各个section的信息如下:
具体如下:
节头:
[号] 名称 类型 地址 偏移量
大小 全体大小 旗标 链接 信息 对齐
[ 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
[ 3] .note.gnu.build-i NOTE 0000000000000274 00000274
0000000000000024 0000000000000000 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000000298 00000298
000000000000001c 0000000000000000 A 5 0 8
[ 5] .dynsym DYNSYM 00000000000002b8 000002b8
00000000000000a8 0000000000000018 A 6 1 8
[ 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
[10] .rela.plt RELA 00000000000004d0 000004d0
0000000000000018 0000000000000018 AI 5 22 8
[11] .init PROGBITS 00000000000004e8 000004e8
0000000000000017 0000000000000000 AX 0 0 4
[12] .plt PROGBITS 0000000000000500 00000500
0000000000000020 0000000000000010 AX 0 0 16
[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
[16] .rodata PROGBITS 00000000000006e0 000006e0
0000000000000011 0000000000000000 A 0 0 4
[17] .eh_frame_hdr PROGBITS 00000000000006f4 000006f4
000000000000003c 0000000000000000 A 0 0 4
[18] .eh_frame PROGBITS 0000000000000730 00000730
0000000000000108 0000000000000000 A 0 0 8
[19] .init_array INIT_ARRAY 0000000000200db8 00000db8
0000000000000008 0000000000000008 WA 0 0 8
[20] .fini_array FINI_ARRAY 0000000000200dc0 00000dc0
0000000000000008 0000000000000008 WA 0 0 8
[21] .dynamic DYNAMIC 0000000000200dc8 00000dc8
00000000000001f0 0000000000000010 WA 6 0 8
[22] .got PROGBITS 0000000000200fb8 00000fb8
0000000000000048 0000000000000008 WA 0 0 8
[23] .data PROGBITS 0000000000201000 00001000
0000000000000010 0000000000000000 WA 0 0 8
[24] .bss NOBITS 0000000000201010 00001010
0000000000000008 0000000000000000 WA 0 0 1
[25] .comment PROGBITS 0000000000000000 00001010
0000000000000029 0000000000000001 MS 0 0 1
[26] .symtab SYMTAB 0000000000000000 00001040
00000000000005e8 0000000000000018 27 43 8
[27] .strtab STRTAB 0000000000000000 00001628
0000000000000203 0000000000000000 0 0 1
[28] .shstrtab STRTAB 0000000000000000 0000182b
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 hello对其进行反汇编如下:
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 .note.ABI-tag:
.....
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 hello -g hello.c
再执行objdump -S hello
得到如下:
hello: 文件格式 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
...
6a9: 41 ff 14 dc callq *(%r12,%rbx,8)
6ad: 48 83 c3 01 add $0x1,%rbx
6b1: 48 39 dd cmp %rbx,%rbp
6b4: 75 ea jne 6a0 <__libc_csu_init+0x40>
6b6: 48 83 c4 08 add $0x8,%rsp
6ba: 5b pop %rbx
6bb: 5d pop %rbp
6bc: 41 5c pop %r12
6be: 41 5d pop %r13
6c0: 41 5e pop %r14
6c2: 41 5f pop %r15
6c4: c3 retq
6c5: 90 nop
6c6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
6cd: 00 00 00
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
本篇到这里就结束啦~谢谢大家