平常开发中,很少会关注到编译和链接过程,因为通常的开发环境都是流行的集成开发环境(IDE),比如pycharm, visual studio,这些ide通常讲编译和链接过程一步完成了,通常将这种编译和链接合并到一起的过程称为构建(Build)
但我们使用gcc编译这个程序的时候,事实上其中被隐藏了多个过程
被隐藏的包括这么几个过程

从最直观的角度来看,编译器就是将高级语言翻译成机器语言的一个工具
程序设计的模块化是人们一直在追求的目标,因为当一个系统十分复杂的时候,我们不得不将一个复杂的系统逐步分割成小的系统,以达到各个突破的目的。
一个复杂的软件也如此,人们把每个源代码模块独立地编译,然后按照需要将他们拼装起来,这个组装模块的过程就是链接(Linking)
比如说:
ELF文件介绍
在链接中,我们将函数和变量统称为符号(Symbol),函数名和变量名就是 符号名(Symbol Name)
链接过程中很关键的一部分就是符号的管理,每一个目标文件都会有一个相应的符号表(Symbol Table), 这个表里记录了目标文件中所用到的所有符号。每个定义的符号有一个对应的值,叫做符号值(Symbol Value),对于变量和函数来说,符号值就是他们的地址。

可执行文件只有转载到内存以后才能被CPU执行。
#include <stdio.h>
int main(){
printf("Hello Workd\n");
return 0;
}
#include <stdio.h>
int main(){
printf("Hello Workd\n");
return 0;
}
预编译过程: 主要就是处理哪些源代码中以“`
编译过程,就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后生产相应的汇编代码文件,这个过程往往是我们所说的整个程序构建的核心部分,也是最复杂的部分之一
现在版本的`GCC`将预编译和编译两个步骤合并成了一个步骤
预编译过程: 主要就是处理哪些源代码中以“`
编译过程,就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后生产相应的汇编代码文件,这个过程往往是我们所说的整个程序构建的核心部分,也是最复杂的部分之一
现在版本的`GCC`将预编译和编译两个步骤合并成了一个步骤
- 扫描
- 语法分析
- 语义分析
- 源代码优化
- 代码生成和目标代码优化
- 扫描
- 语法分析
- 语义分析
- 源代码优化
- 代码生成和目标代码优化
我们在程序模块`main.c`中使用了另一个模块`func.c`的`foo()`函数
我们在`main.c`模块中每一处调用`foo`的时候都必须要确切的知道`foo`这个函数的地址
但是由于每个模块都是单独编译的,在编译器编译`main.c`的时候,它并不知道`foo`函数的地址,所以它暂时性把这些调用`foo`的指令的目标地址搁置,
等到最后链接的时候由链接器去将这些指令的目标地址进行修正。
我们在程序模块`main.c`中使用了另一个模块`func.c`的`foo()`函数
我们在`main.c`模块中每一处调用`foo`的时候都必须要确切的知道`foo`这个函数的地址
但是由于每个模块都是单独编译的,在编译器编译`main.c`的时候,它并不知道`foo`函数的地址,所以它暂时性把这些调用`foo`的指令的目标地址搁置,
等到最后链接的时候由链接器去将这些指令的目标地址进行修正。
目标文件中包括
- 编译后的机器指令代码、数据
- 链接时所需要的符号表,调试信息,字符串等
一般目标文件将这些信息按不同的属性,以“节(`Section`)”的形式存储,有时候也叫做“段(`Segment`)”
节、段 ,这两个 一般情况下都表示一个一定长度的区域,基本上不加以区别,唯一的区别是再`ELF`的链接视图和转载视图的时候
目标文件中包括
- 编译后的机器指令代码、数据
- 链接时所需要的符号表,调试信息,字符串等
一般目标文件将这些信息按不同的属性,以“节(`Section`)”的形式存储,有时候也叫做“段(`Segment`)”
节、段 ,这两个 一般情况下都表示一个一定长度的区域,基本上不加以区别,唯一的区别是再`ELF`的链接视图和转载视图的时候
|
|
|
.code / .text |
程序源代码编译后的机器指令常被放在代码段 |
|
.data |
全局变量和局部静态变量数据经常放在数据段 |
|
.bss |
.bss段为 未初始化的全局变量和局部静态变量预留位置,它没有内容 ,不占用空间 |
|
.rodata |
Read Only Data, 存放只读数据,比如字符串常量,全局const变量 |
|
.comment |
存放的是编译器版本信息,比如字符串:“GCC:(GNU)4.2.0” |
|
.debug |
调试信息 |
|
.dynamic |
动态链接信息 |
|
.hash |
符号哈希表 |
|
.line |
调试时的行号表 |
|
.note |
额外的编译器信息 |
|
.strtab |
String Table 字符串表, 用于存储ELF文件中用到的各种字符串 |
|
.symtab |
Symbol Table 符号表 |
|
.shstrtab |
Section String Table段名表 |
|
.plt .got |
动态链接的跳转表和全局入口表 |
|
.init .fini |
程序初始化与终结代码段 |
|


- `ELF`文件头结构成员含义

段表描述了`ELF`的各个段的信息,比如每个段的段名、段的长度、在文件中的偏移、读写权限以及段的其他属性
`ELF`文件的段结构就是由段表决定的,编译器、链接器和装载器都是依赖段表来定位和访问各个段的属性的

只有分析`ELF文件头`,就可以得到`段表`和`段表字符串表`的位置,从而解析整个`ELF`文件

传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!