##链接器和加载器
###链接器
链接各个模块至同一个进程空间。
整合各个模块的段,确定段在地址空间的基地址。
根据重定位表,重定位符号。
主要任务:符号解析、重定位。
####目标文件 可重定位目标文件
可执行目标文件
共享目标文件
#####符号表 记录目标文件里的符号名和对应符号在目标文件所属段中的地址
1.定义在本目标文件并能被其他模块引用的全局符号(非静态全局变量、非静态函数)
2.引用外部目标文件的全局符号
3.定义引用在本目标文件的本地符号(静态函数、静态变量)
4.段名(与链接无关)
5.静态局部符号(与链接无关)
6.行号信息(与链接无关)
强符号:函数和初始化的全局变量
弱符号:未初始化的全局变量
unix链接器处理多重定义符号的规则:不允许有多个强符号;如果有强符号和弱符号,选择强符号;对于多个弱符号定义,任意选择一个。
强引用:
强符号:
####静态链接 把多个目标文件打包成静态库,链接静态库的时候,载入静态库里被引用的目标文件,而不用载入所有目标文件
链接目标文件、重定位符号,生成可执行文件。
符号重定位在链接时确定。
#####符号解析 对于引用的符号,绑定到唯一的定义。
对于引用了但尚未在本地定义的符号,找到相应的目标文件中该符号的定义。
方法:遍历所有输入目标文件,找到定义符号的目标文件。
#####重定位 合并输入模块,并为所有符号分配运行时地址(进程虚拟地址)
1.重定位节和符号定义:合并相同的节,并重定位节地址和已经定义的符号地址。
2.重定位节中的符号引用。该步骤依赖于重定位表。
######重定位表 如果一个段中的符号需要重定位,则会有一个相应的重定位段。例如.text段需要重定位,则产生一个.rel.text重定位段。相对应.data有.rel.data重定位段。
记录需要重定位的符号在相应段中的偏移,和重定位类型(PC相对地址、绝对地址等)。
######unix重定位算法 遍历所有.rel段中的符号,根据重定位类型,重定位符号地址。
####动态链接 链接、重定位过程推迟到运行加载时。较之静态链接,使用共享模块,可以节省内存,易于维护升级程序。
#####共享模块(dll,so) 模块内的符号引用采用相对地址。
#####地址无关代码 代码中引用的符号地址通过查表得到,而不是把符号地址写入代码。
模块间的符号引用,因为外部符号地址在装载时候才能确定,查询全局偏移表(GOT)得到外部符号地址,令指令代码与地址无关。
对于引用函数的地址无关,还引入了延迟绑定的技术,通过过程链接表,把函数地址的绑定推迟到第一次调用该函数的时候,第一次调用时,把函数地址填入GOT,第二次调用直接使用GOT里的地址。
该表存在于数据段,装载时,填入外部符号的地址。一般动态库的代码段只有一份,数据段由每个调用模块各自维护一份。
令指令代码地址无关,可以使指令代码保持一份,节省内存;如果代码不是地址无关,则每个进程要维护一份副本,装载时重定位。使用-fPIC(linux)命令编译文件成地址无关共享模块。
#####动态链接器 加载可执行文件后,然后加载动态链接器,运行动态链接器,初始化动态链接的相关工作,最后才运行可执行文件。
#####链接步骤 1启动动态链接器
2装载共享对象
3重定位和初始化(初始化执行.init段中的代码)
###加载器加载可执行文件
操作系统创建进程,加载可执行文件,然后运行。具体做了以下三件事:
1创建独立虚拟地址空间(创建虚拟地址与内存的映射数据结构)
2读取可执行文件程序头,创建虚拟地址与可执行文件的映射数据结构。(可执行或目标文件的段信息有指定对应的虚拟基地址。)
3将CPU指令寄存器设置成可执行文件入口,启动运行。
###reference 链接器和加载器
程序员的自我修养-链接、装载与库
深入理解计算机系统
###question 如何知道引用的符号是共享模块还是其他目标文件?