链接就是把代码和数据组合成一个单一的文件。三种情况下进行链接,编译时,加载时,运行时。
为什么要使用链接器?
分离编译,分解成更小的更易于管理的模块。修改某个模块时,只需要重新编译该文件,并重新编译其他文件,而不需要重新编译所有的文件。
静态链接: 将可重定位文件组合成一个完整的可执行目标文件。
链接过程: 源文件通过cpp, ccl, as 生成可重定位目标文件,可重定位目标文件在经过链接器形成可执行文件。
链接器主要完成的过程:
符号解析: 将符号引用与定义联系起来
重定位: 编译器和汇编器生成从地址0开始的代码和数据节。链接器通过把每个符号定义与存储器位置连接起来,修改所有对这些符号进行的引用。
目标文件有三种,可执行目标文件,可重定位目标文件,共享目标文件
符号表中有三种不同的符号,由该模块定义的全局符号,引用其他模块的全局符号,只被模块m定义和引用的本地符号(这里与本地程序变量是不同的,局部变量在运行时在栈中被管理,链接器不感兴趣。
为什么C++和Java中能支持重载?
编译器将每个唯一的方法和参数列表组合编码成一个队链接器来说唯一的名字。
链接器如何解析多重定义的全局符号?
unix链接器使用下面的规则来处理多重定义的符号,强符号是指全局函数和已经初始化的全局变量,弱符号只未初始化的全局变量
1. 不能定义多个强符号
2. 如果有一个强符号和多个若符号,则选择强符号
3. 多个弱符号,则随意挑选一个
静态库以一种存档的特殊文件格式存放在磁盘中。存档文件是一组连接起来的可重定位目标文件的集合。
缺点:占用较大空间,升级之后的库与程序需要进行重新连接
优点:运行速度快
链接器如何使用静态库来解析引用?
在符号解析阶段,链接器从左到后的按照它们在编译器驱动程序命令行上出现的顺序来扫描可重定位文件和存档文件。链接器维持一个可重定位目标文件的集合E,一个未解析的符号集合U,一个前面输入文件中已经定义的符号集合D。
1. 对于每个输入文件,先判断是目标文件还是存档文件,如果是目标文件,则将目标文件添加到E中并且修改U和D以反映目标文件中的定义以及引用
2. 如果是存档文件,则尝试匹配U中未解析的符号和由存档文件定义的符号。如果某个成员m定义了U中某个符号,则把该成员加入到E中,修改U和D以反映m中的定义和引用。对存档文件中的成员重复执行步骤2,直到U和D不再变化。
3. 如果链接器完成对命令行上输入文件的扫描后,U非空,则输出错误并终止,否则合并所有的可重定位文件以构建可执行文件。
加载, 将程序拷贝到内存并执行的过程。
动态库,共享库是一个目标模块,可以加载到任意的存储器地址,并和一个在存储器中的程序连接起来。