静态链接和动态链接

静态链接:

第一步:空间与地址分配

扫描所有的输入目标文件,并且获得它们各个段的长度、属性和位置,并且将输入目标文件中的符号表中所有的符号定义和符号引用收集起来,统一放到一个全局符号表,

这一步中,链接器能够获得所有输入目标文件的段长度,并且将它们合并,计算出输出文件中各个段合并后的长度与位置,并建立映射关系。

第二步 符号解析与重定位

使用第一步收集到的信息,读取输入文件中段的数据、重定位信息,并且进行符号解析和重定位,调整代码中的地址等。


重定位:

编译器如何知道哪些指令需要被调整?

利用重定位表,描述如何修改相应段里的内容,通常是ELF文件中的一个段。例如代码段“.text”的重定位表为“.rel.text”,数据段".data"的重定位表为".rel.data"

每个要被重定位的地方叫一个重定位入口。

重定位表内容:每个重定位入口的以下三个属性——偏移offset,类型type,值value


符号解析:

重定位过程中,每个重定位的入口都是对一个符号的引用,当链接器要对某个符号的引用进行重定位时,它就要确定这个符号的目标地址。这时链接器会去查找由所有输入目标文件的符号表组成的全局符号表,找到相应的符号后进行重定位。


编译的时候,编译器把依赖全局符号的指令的地址设置为假地址,在链接时待链接器完成空间与地址分配之后修正为符号的真正的虚拟地址。




动态链接的过程:

OS读取可执行文件的头部,检查文件的合法性,然后从头部中的"ProgramHeader"中读取每个"Segment"的虚拟地址、文件地址和属性,并将它们映射到进程虚拟空间的相应位置。这时,可执行文件中对于外部符号的引用还处于无效地址状态,因此在映射完可执行文件后,OS会启动一个动态链接器。在Linux下,ld.so实际上就是一个共享对象,OS通过映射的方式将其加载到内存空间中。OS在加载完动态链接器后,将控制权交给动态链接器的入口地址,动态链接器获得控制权并完成一系列自身初始化工作,再根据当前的环境参数,对可执行文件进行动态链接工作。当所有动态链接工作完成以后,动态链接器将控制权交到可执行文件的入口地址,程序开始正式执行。


动态链接相关结构:

在动态链接的ELF文件中,有以下一些与动态链接相关的段。

1. ".interp"段 保存可执行文件需要的动态链接器的路径。

2. ".dynamic"段 保存动态链接器需要的基本信息,包括依赖于那些共享对象、动态链接符号表的位置、动态链接重定位表的位置、共享对象

初始化代码的地址等。

3. ".dynsym"段 动态符号表(Dynamic Symbol Table)表示动态链接模块之间的符号导入导出关系。

只保存与动态链接相关的符号,对于模块内部的符号则不保存。而".symtab"段中的保存了所有符号,包括".dynsym"

4. ".rel.dyn",".rel.plt" 动态链接重定位相关结构。(即使使用PIC技术,也只是代码段地址无关,其数据段中的GOT表以及可能存在的

绝对地址引用都需要进行重定位)

".rel.dyn"是对数据引用的修正,它修正的位置位于".got"以及数据段

".rel.plt"是对函数引用的修正,它修正的位置位于".got.plt"

5. 动态链接时进程堆栈初始化信息 在动态链接时,堆栈中会保存动态链接器所需要的一些辅助信息数组(结构数组,结构体struct Elf32_auxv_t定义在".elf.h"中)

位于堆栈中环境变量指针的后面

动态链接的步骤和实现

1.Bootstrap自举

动态链接器本身作为共享对象,必须有其特殊性。第一,动态链接器本身不可以依赖其他任何共享对象;其次,动态链接器本身所需要的全局和静态变量的重定位工作动态链接器本身作为共享对象,必须有其特殊性。第一,动态链接器本身不可以依赖其他任何共享对象;其次,动态链接器本身所需要的全局和静态变量的重定位工作由其自身完成。对于第二个条件,动态链接器启动时有一段非常精巧的代码完成这项艰巨的任务且同时不能用到全局和静态变量。这种具有一定限制条件的启动代码就被称为

Bootstrap(自举)

动态链接器入口地址就是自举代码的入口。自举代码首先找到自己的GOT,由此找到".dynamic"段,通过".dynamic"段中的信息,找到动态链接器本身的重定位表和符号表等,从而得到动态链接器本身的重定位入口,先讲它们全部重定位。从这一步开始,动态链接器带代码中才可以开始使用自己的全局变量和静态变量。

2.装载共享对象

完成基本自举后,动态链接器将可执行文件和链接器本身的符号表都合并到全局符号表(Global Symbol Table)中。然后链接器开始寻找可执行文件依赖的共享对象,并将它们的名字放入到一个装载集合中。然后链接器取出集合中的共享对象的名字,找到对应文件打开读取相应的ELF文件头和".dynamic"段,然后将其相应的代码段和数据段映射到进程空间中。如果这个文件又依赖于其他共享对象,则将它们加入集合。当一个新的共享对象装载进来时,它的符号表会被合并到全局符号表中。

全局符号介入问题

3.重定位和初始化

完成上面的步骤后,链接器开始重新遍历可执行文件和每个共享对象的重定位表,将它们的GOT/PLT中的每个需要重定位的位置进行修正。重定位完成后,如果共享对象有".init"段,链接器会执行".init"段中的代码,用以实现共享对象特有的初始化过程。当重定位与初始化完成,所有的准备工作就结束了,所需要的共享对象都装载并且链接完成,动态链接器工作结束,将控制权交给程序入口。


转载于:https://my.oschina.net/JaneL/blog/474893

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值