可重定位目标
重定位是将EFL文件中的未定义符号关联到有效值的处理过程。在main.o中,这意味着对printf和puts的未定义的引用必须替换为该进程的虚拟地址空间中适当的机器代码所在的地址。在目标中用到的相关符号之处,都必须替换。
对用户空间程序符号的替换,内核并不涉及其中,因为所有的替换操作都是由外部工具完成的。对内核模块来说,情况有所不同,因为内核所收到的模块裸数据,与其存储在二级制文件中的形式完全相同,内核本身需要负责重定位操作。
在每个目标文件中,都有一个专门的表,包含了重定位项,标识了需要进行重定位之处。每个表项都包含下列信息:
1)一个偏移量,指定了修改的项的位置
2)对符号的引用(符号表的索引),提供了需要插入到重定位位置的数据
重定位步骤
1)重定位节和符号定义。
链接器将所有相同类型的节合并为同一类型的新的聚合节。例如来自输入模块的.data节全部合并成一个节,这个节成为输出可执行目标文件的.data节。然后链接器将运行时存储器地址赋给新的聚合节,赋给输入模块定义的每个节,以及赋给输入模块定义的每个符号。当这一步完成时,程序中的每个指令和全局变量都有唯一的运行时存储器地址了。
2)重定位节中的符号引用。
在这一步中,链接器修改代码节和数据节中对每个符号的引用,使得他们指向正确的运行时地址。为了执行这一步,链接器依赖于称之为重定位条目的可重定位目标模块中的数据结构。
重定向条目
当汇编器生成一个目标模块时,它并不知道数据和代码最终将存放在存储器中的什么位置。它也不知道这个模块引用的任何外部定义的函数和全局变量。所以,无论何时汇编器遇到对最终位置未指定目标引用,它就会生成一个重定位条目,告诉链接器在将目标文件合并可执行文件时如何修改这个引用。代码重定位条目放在.rel.text中。已经初始化数据的重定位条目放在.rel.data中。
数据结构
由于技术原因,有两种类型的重定位信息,由两种稍有不同的数据结构表示。第一种类型称之为普通重定位。SHT_REL类型的节中的重定位项由以下数据结构定义:
/*Relocation table entry without addend (in section of type SHT_REL). */
typedef struct
{
Elf32_Addr r_offset; /* Address */指定需要重定位的项的位置
Elf32_Word r_info; /* Relocation type andsymbol index */提供了符号表中的一个位置,同时还包括重定位类型的有个信息。这是通过将值划分为两部分来达到的。
r_info == int symbol:24,type:8;
} Elf32_Rel;
另一种类型,称之为需要添加常数的重定位项,只出现在SHT_RELA类型的节中。数据结构如下: