连接
连接的定义
连接是将各类代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载(或被拷贝)到存储并执行
连接能够执行于编译时,也就是在源代码被翻译成机器代码时;也能够执行于加载时,也就是在程序被加载器加载到存储器并执行时javascript
静态连接
javascript Unix的静态连接器ld,以一组可重位目标文件和命令行参数做为输入,生成一个彻底连接的能够加载和运行的可执行目标文件做为输出
输入的可重定位目标文件由各类不一样的代码和数据节组成
指令在一个节中,初始化的全局变量在另外一个节中,而未初始化的变量又在另一个节中。javascriptjava
为了构造可执行文件,连接器必须完成两个主要任务:数据结构
- 符号解析(symbol resolution)。目标文件定义和引用符号。符号解析的目的是将每一个符号引用恰好和一个符号定义联系起来
- 重定位(relocation)。编译器和汇编器生成从地址0开始的代码和数据节。
目标文件
目标文件有三种形式:函数
- 可重定位目标文件。包含二进制代码和数据,其形式能够在编译时与其余可重定位目标文件合并起来,建立一个可执行目标文件
- 可执行目标文件。包含二进制代码和数据,其形式能够被直接拷贝到存储器并执行
- 共享目标文件。一种特殊类型的可重定位目标文件,能够在加载或者运行地被动态地加载到存储器并连接
一个典型的ELF可重定位目标文件的格式:工具
- text:已编译程序的机器代码
- rodata:只读数据,好比printf语句中的格式串和开关语句的跳转表
- data:已初始化的全局C变量
- bss:未初始化的全局C变量
- symtab:一个符号表,它存放在程序中定义和引用的函数和全局变量的信息
- rel.text:一个.text节中位置的列表,当连接器把这个目标文件和其余文件结合时,须要修改这些位置
- rel.data:被模块引用或定义的任何全局变量的重定位信息
- debug:一个调试符号表,其条目是程序中定义的局部变量和类型定义,程序中定义和引用的全局变量,以及原始的C源文件
- line:原始C源程序中的行号和.text节中机器指令之间的映射。
- strtab:一个字符串表,其内容包括:.symtab和.debug节中的符号表,以及节头部中的节名字。字符串表就是以null结尾的字符串序列
符号和符号表
每一个可重定位目标模块m都有一个符号表,它包含m所定义和引用的符号的信息。在连接器的上下文中,有三种不一样的符号:编码
- 由m定义并能被其余模块引用的全局符号。全局连接器符号对应于非静态的C函数以及被定义为不带C static属性的全局变量。
- 由其余模块定义并被模块m引用的全局符号。这些符号称为外部符号(external),对应于定义在其余模块中的C函数和变量。
- 只被模块m定义和引用的本地符号。有的本地连接器符号对应于带static属性的C函数和全局变量。
符号解析
连接器解析符号引用的方法是将每一个引用与它输入的可重定位目标文件的符号表中的一个肯定的符号定义联系起来操作系统
连接如何解析多重定义的全局符号
在编译时,编译器向汇编器输出每一个全局符号,或者是强或者弱的符号,而汇编器会把这个信息隐含地编码在可重定位目标文件的符号表里。函数和已初始化的全局变量是强符号,未初始化的全局变量是弱符号命令行
根据强弱符号的定义,Unix连接器使用下面的规则来处理多重定义的符号(其它系统应该也适用的,I think,并且还要看编译器):
- 规则1:不容许有多个强符号
- 规则2:若是有一个强符号和多个弱符号,那么选择强符号
- 规则3:若是有多个弱符号,那么从这些弱符号中任意选择一个
重定位
-------
重定位节和符号定义:
连接器将全部相同类型的节合并为同一类型的新的聚合节,将运行时存储器地址赋给新的聚合节,赋给输入模块定义的每一个节,以及赋给输入模块定义的每一个符号
此时,程序中的每一个指令和全局变量都有惟一的运行时存储器地址了
重定位节中的符号引用:
连接器修改代码节和数据节中对每一个符号的引用,使得它们指向正确的运行时地址
连接器依赖于称为重定位条目的可重定位目标模块中的数据结构翻译
重定位条目
不管什么时候汇编器遇到对最终位置位置的目标引用,它就会生成一个重定位条目,告诉连接器在将目标文件合并成可执行文件时如何修改这个引用
代码的重定位条目放在.rel.text中
已初始化的数据的重定位条目放在.rel.data中
ELF定义了11种不一样的重定位类型debug
动态链接共享库
静态库的缺点:
- 静态库在更新时,使用该库的程序须要与更新的库进行从新连接
- 因为使用静态库的程序在连接时都会拷贝静态库里被应用程序引用的目标模块,像printf和scanf这样的函数的代码在运行时都会被复制到每一个运行进程的文本段中,这形成了冗余,浪费了稀缺的存储器资源
共享库:
- 共享库是一个目标模块,在运行时,能够加载到任意的存储器地址,并和一个在存储器中的程序连接起来。这个过程称为动态连接,是由一个叫作动态连接器的程序来执行的
- 共享库也称为共享目标,在Unix系统中一般用.so后缀来表示。微软的操做系统大量地利用了共享库,它们称为DLL(动态连接库)
- 共享库是以两种不一样的方式来“共享”的(在Windows中分别称为“隐式连接”和“显示连接”)
处理目标文件的工具
- AR:建立静态库,插入、删除、列出和提取成员
- STRINGS:列出一个目标文件中全部可打印的字符串
- STRIP:从目标文件中删除符号的信息
- NM:列出一个目标文件的符号表中定义的符号
- SIZE:目标文件中节的名字和大小
- READELF:显示一个目标文件的完整结构,包括ELF头中的编码的全部信息。包含SIZE和NM的功能
- OBJDUMP:全部二进制工具之母,可以显示一个目标文件中全部的信息。它最大的做用是反汇编.text节中的二进制指令
- LDD:列出一个可执行文件在运行时所须要的共享库