链接是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可被加载到内存并执行。本章主要讲链接的各个方面,不过本章的总结应该会比较少,毕竟链接有链接器自动执行,所以就看个大概吧。
- 为了构造可执行文件,链接器必须完成两个主要任务:符号解析;重定位。
- 目标文件有三种形式:
- 可重定位目标文件。包含二进制代码和数据,其形式可以在编译时与其他可重定位目标文件合并起来,创建一个可执行目标文件。
- 可执行目标文件。包含二进制代码和数据,其形式可以被直接复制到内存并执行。
- 共享目标文件。一种特殊类型的可重定位目标文件,可以在加载或者运行时被动态地加载进内存并链接。
- 每个可重定位目标模块m都有一个符号表,它包含m定义和引用地符号的信息。在链接器上下文中,有三种不同地符号:
- 由模块m定义并能被其他模块引用地全局符号。全局链接器符号对应于非静态地C函数和全局变量
- 由其他模块定义并被模块m引用地全局符号。这些符号称为外部符号,对应于在其他模块中定义地非静态C函数和全局变量。
- 只被模块m定义和引用地局部符号。它们对应于带static属性地C函数和全局变量。这些符号在模块m中任何位置都可见,但是不能被其他模块引用。
- 链接器解析符号引用地方法是将每个引用与它输入地可重定位目标文件的符号表中的一个确定的符号定义关联起来。但是对于全局符号的引用解析就非常棘手。
如果多个模块定义同名的全局符号,会怎样?
在编译时,编译器向汇编器输出每个全局符号,或者是强(strong)或者是弱(weak),而汇编器把这个信息隐含地编码在可重定位目标文件的符号表里。函数和已初始化的全局变量是强符号,未初始化的全局变量是弱符号。
根据强弱符号的定义,Linux链接器使用下面的规则来处理多重定义的符号名:
- 规则一:不允许由多个同名的强符号。
- 规则二:如果有一个强符号和多个弱符号同名,那么选择强符号。
- 规则三:如果有多个弱符号同名,那么从这些弱符号中任意选择一个。
- 一旦链接器完成了符号解析这一步,就把代码中的每个符号引用和正好一个符号定义关联起来。现在就可以开始重定位了。重定位由两步组成:
- 重定位节和符号定义。在这一步中,链接器将所有相同类型的节合并为统一类型的新的聚合节。然后链接器将运行时内存地址赋给新的聚合节,赋给输入模块定义的每个节,以及赋给输入模块定义的每个符号。当这一步完成时,程序中的每条指令和全局变量都有唯一的运行时内存地址了。
- 重定位节中的符号引用。这一步中,链接器修改代码节和数据节中对每个符号的引用,使得它们指向正确的运行时地址。
-
加载器将可执行目标文件中的代码和数据从磁盘复制到内存中,然后通过跳转到程序的第一条指令或者入口点来运行该程序。这个将程序复制到内存并运行的过程叫做加载。
-
共享库是一个目标模块,在运行和加载时,可以加载到任意的内存地址,并和一个在内存中的程序链接起来。这个过程称为动态链接,是由一个动态链接器的程序来执行的。目的是为了解决静态库的缺陷——函数代码会复制到每个运行进程的文本段中,造成内存资源的极大浪费。
只是总结了些概念性的东西,还有很多更详细的内容,感觉没有特别的了解必要,建议看书自己判断。