一、什么是链接?
gcc编译器读取源程序文件(.c文件)并把它翻译成一个可执行目标文件,翻译过程中就包括了链接。
在现代系统中,链接是由叫做链接器的程序自动执行的。
链接是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可被加载到内存并执行。也就是将所有的文件汇总为一个文件。
二、静态链接
·静态连接器以一组可重定位目标文件和命令行参数作为输入,生成一个完全链接的,可以加载和运行的可执行目标文件作为输出。
·可重定位目标文件由各种不同的代码和数据节组成,每一节都是一个连续的字节序列
·为了构造可执行文件,链接器必须完成两个主要任务
1、符号解析:目标文件定义和引用符号,每个符号对应于一个函数、一个全局变量或者一个静态变量。其目的是将每个符号引用正好和一个符号定义关联起来
2、重定位:编译器和汇编器生成从地址 0 开始的代码和数据节,然偶修改所有对这些符号的引用,使他们指向这个内存位置。
这里我们可以通俗的理解一下,所谓链接,就是在多个程序同时编译的情况下,我们需要将文件中的变量函数整合到一个文件中,分配内存,方便使用。
现代x86-64Linux和Unix系统的目标文件使用可执行可链接格式(ELF)
三、可重定位目标文件
我们来看一下一个典型的ELF可重定位目标文件的格式
ELF头 | 以一个16个字节的序列开始,这个序列描述了生成该文件的系统的字的大小和字节顺序。ELF头剩下的部分包含帮助链接器语法分析和解释目标文件的信息。其中包括ELF头的大小,目标文件的类型,及其类型,节头部表的文件偏移,节头部表中条目的大小和数量。 |
.text节 | 已编译程序的机器代码(比如一些函数) |
.rodata | 只读数据,比如printf语句中的格式串和开关语句跳转表 |
.data | 已初始化的全局变量和静态C变量(局部C变量在运行时被保存在栈中,不出现在data和bss中) |
.bss | 未初始化的全局和静态C变量,以及所有被初始化为0的全局或静态变量 |
.symtab | 一个符号表,它存放在程序中定义和应用的函数和全局变量的信息(不包含局部变量条目) |
.rel.text | .text节的重定位信息,用于重新修改代码段的指令中的地址信息 |
.rel.data | .data节的重定位信息,用于对被模块使用或定义的全局变量进行重定位的信息 |
.debug | 一个调试符号表(gcc -g) |
.line | 原始C源程序中的行号和.text节中机器指令之间的映射,通过-g调用 |
.strtab | 一个字符串表。其中内容包括.symtab和.debug节中的符号表,以及头节部中的节名字。 |
节头表 | 每个节的节名,偏移和大小 |
接下来,我们用一段简单的代码演示一下
int sum(int *a, int n);
int array[2] = {1, 2};
int main()
{
int val = sum(array, 2);
return val;
}
①ELF头
先只链接,生成可重定位目标文件
调出ELF头的内容
②节头部表
根据节头部表信息看可重定位文件。根据每个节的偏移量和大小画出整个可重定位文件。
eg: .text:000040~000040+00001f
③.text .data .bss
每个节放什么变量详细去看前面的表格,这里以一个程序举例
·.bss节中不放入初始值,只要说明将来执行是占用几个字节即可,因此,.bss实际上不占用磁盘空间,提高了磁盘空间利用率
·有时.common和.bss会交替出现,这里分辨一下
.common 未初始化的全局变量
.bss 未初始化的静态变量,以及初始化为0的全局或静态变量