前言:
当前小伙伴们对“链接器名词解释预处理和多目标程序链接的过程”大概比较关切,你们都想要学习一些“链接器名词解释预处理和多目标程序链接的过程”的相关内容。那么小编在网上网罗了一些有关“链接器名词解释预处理和多目标程序链接的过程””的相关知识,希望各位老铁们能喜欢,我们快快来了解一下吧!一:使用链接带来的好处
1:模块化
一个程序可以分成很多源程序文件
可构建公共函数库,如数学库,标准C库等
2:效率高(代码重用,开发效率高)
时间上,可分开编译只需重新编译被修改的源程序文件,然后重新链接
空间上,无需包含共享库所有代码源文件中无需包含共享库函数的源码,只要直接调用即可(如,只要直接调用printf()函数,无需包含其源码)可执行文件和运行时的内存中只需包含所调用函数的代码而不需要包含整个共享库
二:链接的本质
话不多说先上图,我们先从一个简单的例子开始分析,大家都知道每个模块有自己的代码、数据(初始化全局变量、未初始化全局变量,静态变量、局部变量)
其中局部变量temp分配在栈中,不会在过程外被引用,因此不是符号定义。具体这些变量在链接的时候如何处理,我们在后面的内容中再逐一分析,这里先简单说明一下。
下面我们用一条命令将上面的源文件转换成可执行文件:
使用GCC编译器编译并链接生成可执行程序P:
– $ gcc -O2 -g -o p main.c swap.c
$ ./p
-O2:2级优化
-g:生成调试信息
-o:目标文件名
虽然上面只有一条语句,但是其中还是包含了,预处理,编译,汇编,链接这些动作的。大致的流程如下图所示:
很明显最终生成的可执行文件P,是由两个可重定位目标文件(main.o,swap.o)经过链接生成的,下面我们就分析一下,这个链接过程的本质是什么。
前面我们讲过,每个模块有自己的代码、数据(初始化全局变量、未初始化全局变量,静态变量、局部变量),当然main.o,swap.o其中也包含这些数据,这些数据都有自己保存的位置,代码在text节中,初始化全局变量、静态变量在data节中,未初始化的全局变量挡在bss节中。
正是由于每个模块都有上述的text,data,bss节,链接的时候就需要将相同节的数据合并在一起,链接的本质就是合并相同的节。如下图所示:
三:链接的步骤
前面我们已经讲过,链接的本质就是合并相同的节,那么具体怎么合并我们进一步分析。链接过程是将多个可重定位的目标文件生成一个可执行的目标文件,从名字上来看,可重定位已经暴露了链接的步骤了,不错,链接的时候我们需要将我们的符号表进行重定位,具体怎么操作,我们一起来看一下。
Step 1. 符号解析(Symbol resolution)
- 程序中有定义和引用的符号 (包括变量和函数等)
•void swap() {…} /* 定义符号swap */
•swap(); /* 引用符号swap */
•int *xp = &x; /* 定义符号 xp, 引用符号 x */
– 编译器将定义的符号存放在一个符号表( symbol table)中.符号表是一个结构数组
每个表项包含符号名、长度和位置等信息
- 链接器将每个符号的引用都与一个确定的符号定义建立关联
• Step 2. 重定位
- 将多个代码段与数据段分别合并为一个单独的代码段和数据段
- 计算每个定义的符号在虚拟地址空间中的绝对地址
- 将可执行文件中符号引用处的地址修改为重定位后的地址信息
上面步骤细化开来可以分为下面几个步骤,其中第一步是符号解析,2,3,4为符号重定位
1:确定符号引用关系(符号解析)
2:合并相关.o文件
3:确定每个符号的地址
4:在指令中填入新地址
简单画了一个例子,供大家理解