链接器和加载器       

##链接器和加载器

###链接器
链接各个模块至同一个进程空间。

整合各个模块的段,确定段在地址空间的基地址。

根据重定位表,重定位符号。

主要任务:符号解析、重定位。

####目标文件 可重定位目标文件

可执行目标文件

共享目标文件

#####符号表 记录目标文件里的符号名和对应符号在目标文件所属段中的地址

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 如何知道引用的符号是共享模块还是其他目标文件?