1.链接
链接 是将各种代码和数据片段收集并组合成为一个单一文件的过程
- 可执行于编译时,即在源代码翻译成机器代码
- 可执行于加载时,即程序被加载器加载到内存并执行
- 可执行于运行时,通过应用程序来执行
编译驱动程序 包括
- 预处理器:将程序从ASCII源文件翻译成 .i 文件
- 编译器:翻译成 .s ASCII汇编语言文件
- 汇编器:翻译成 .o 可重定位目标文件
- 链接器:翻译成可执行文件
链接器主要任务
- 符号解析
将函数,全局变量,静态变量(static)的定义和引用关联起来
- 重定位
汇编器生成从地址0开始的代码和数据(产生相对地址),链接器将每个符号定义和内存位置关联起来(产生绝对地址),重定位这些节,修改所有对这些符号的引用
- 重定位条目
- 重定位符号引用
这里的绝对地址是对于这个程序内部而言的绝对位置,并不是整个PC内的绝对位置
典型的ELF可重定位目标文件

每一个可重定位目标文件都有符号表,有三种不同的符号
- 全局符号: 由模块m定义并能被其他模块引用
- 对应非静态的C函数和全局变量
- 外部符号: 由其他模块定义并被模块m引用
- 对应其他模块中定义的非静态的C函数和全局变量
- 局部符号: 只被模块m定义和引用的局部符号
-
对应带static属性的C函数和全局变量,不能被其他模块引用
-
局部变量是在运行时保存在栈中,链接器不知道其存在,所以ELF文件的符号表中没有局部变量的条目
2.符号解析
全局符号分为强符号和弱符号
- 强符号为函数或者已经初始化的全局变量
- 弱符号是未初始化的全局变量
- 遇到同名符号,有强符号就将引用解析为强符号,否则随机选取一个弱符号解析
- 这样可能会引发错误
- 尽量避免使用全局变量
- 最好使用extern命令
- 全局变量一定要初始化
这个问题只有C中会出现,C++中有同名全局变量直接在链接时报错,C++中所有未定义的全局变量都默认初始化了
3.静态与动态链接
静态链接和动态链接
-
静态链接的过程就已经把要链接的内容已经链接到了生成的可执行文件中,就算你在去把静态库删除也不会影响可执行程序的执行
-
动态链接这个过程却没有把内容链接进去,而是在执行的过程中,再去找要链接的内容,生成的可执行文件中并没有要链接的内容,所以当你删除动态库时,可执行程序就不能运行。
-
优缺点
-
静态链接库执行速度比动态链接库快。(执行过程不需要找链接的内容)
-
动态链接库更节省内存与空间。(未写入要链接的内容,无需重复对同一个库引用)
-
静态链接库更新较为困难。(库函数修改需要重新编译链接)
静态库和静态链接
- 将常用文件(如标准I/O,数学函数等)存为.a后缀的存档文件,当链接时,会且仅会将库中被调用的函数连接到程序中
- 链接器从左到右扫描命令行出现的可重定位目标文件和存档文件
- 在扫描中,链接器维护了一个引用了但是没有解析的符号集合U
- 当输入文件尾.o文件时,添加函数或变量到U中
- 当输入文件为.a文件时,匹配U中符号和存档文件中定义的符号,并将没有匹配的丢弃掉
- 如果完成扫描后U非空,则报错
- 如果一个库出现在目标文件前,链接就会失败,所以一般将库放在最后
- 如果库a和b会互相调用则其中一个库要输入两遍
加载
加载器将可执行文件代码和数据复制到内存中,并将PC跳转到程序的第一条指令
共享库和动态链接
动态库中的代码和数据不会再链接时复制到可执行文件中,而是复制一些重定位和符号表信息,使得运行时能够解析共享库
动态链接器执行重定位完成链接
- 重定位libc.so的文本和数据至某个内存段
- 重定位libvector.so的文本和数据至另一个内存段
- 重定位prog21中所有对libc.so和libvector.so定义符号的引用
动态链接应用
- 分发软件
- 构建高性能Web服务器
位置无关代码
-
动态库每次调用在内存中出现的地址是不同的,如果固定一个地址的话会造成许多不连贯的内存,造成巨大的内存浪费,使用 位置无关代码 和 程序链接表 解决
-
位置无关代码在目标代码的数据段,包含被调用全局变量或函数的绝对内存地址
- 在数据段是因为目标代码的代码段一般不可修改
- 原理是数据段和代码段的相对位置是固定的
- 程序链接表是在调用全局函数时与 位置无关代码共同协作的