首页
社区
课程
招聘
[原创]编译和链接、装载过程
发表于: 2025-12-6 15:27 586

[原创]编译和链接、装载过程

2025-12-6 15:27
586

平常开发中,很少会关注到编译和链接过程,因为通常的开发环境都是流行的集成开发环境(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;
}
预编译过程: 主要就是处理哪些源代码中以“`#`”开始的预编译指令,比如 “`#include`”
 
编译过程,就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后生产相应的汇编代码文件,这个过程往往是我们所说的整个程序构建的核心部分,也是最复杂的部分之一
 
现在版本的`GCC`将预编译和编译两个步骤合并成了一个步骤
预编译过程: 主要就是处理哪些源代码中以“`#`”开始的预编译指令,比如 “`#include`”
 
编译过程,就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后生产相应的汇编代码文件,这个过程往往是我们所说的整个程序构建的核心部分,也是最复杂的部分之一
 
现在版本的`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一些繁琐的结构](https://secure2.wostatic.cn/static/hdM59e4b8GhPMtGM59qYjv/image.png?auth_key=1765005263-wcVXxTFy2fXeFHrrLf85sF-0-da78e9e3f5e2548eb46400435a1d9c25)
 
### 文件头
 
  ![](https://secure2.wostatic.cn/static/jr9cCepJuiSdfceDZVmMd7/image.png)
 
  - `ELF`文件头结构成员含义
 
    ![](https://secure2.wostatic.cn/static/X77a98b1fYcCR5x9AGvBH/image.png)
 
### 段表(Section Header Table)
 
  段表描述了`ELF`的各个段的信息,比如每个段的段名、段的长度、在文件中的偏移、读写权限以及段的其他属性
 
  `ELF`文件的段结构就是由段表决定的,编译器、链接器和装载器都是依赖段表来定位和访问各个段的属性的
 
  ![](https://secure2.wostatic.cn/static/5gNi6j73HP3mSBvfgyZGNX/image.png)
 
只有分析`ELF文件头`,就可以得到`段表`和`段表字符串表`的位置,从而解析整个`ELF`文件
![省去了ELF一些繁琐的结构](https://secure2.wostatic.cn/static/hdM59e4b8GhPMtGM59qYjv/image.png?auth_key=1765005263-wcVXxTFy2fXeFHrrLf85sF-0-da78e9e3f5e2548eb46400435a1d9c25)
 
### 文件头

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

收藏
免费 25
支持
分享
最新回复 (9)
雪    币: 530
活跃值: (2000)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
666
2025-12-6 16:23
0
雪    币: 204
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
666
2025-12-6 21:59
0
雪    币: 223
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
感谢分享,很有参考价值!
2025-12-8 03:08
0
雪    币: 440
活跃值: (812)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
感谢分享
2025-12-8 08:15
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
666
2025-12-8 09:47
0
雪    币: 8965
活跃值: (3679)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
学习一下,好像编译器都一样
2025-12-8 10:06
0
雪    币: 26
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
感谢分享,很有参考价值!
2025-12-8 10:11
0
雪    币: 13
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
666
2025-12-8 17:25
0
雪    币: 7
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
10
111
2025-12-9 17:53
0
游客
登录 | 注册 方可回帖
返回