7.7 重定位
当完成了符号解析, 也就意味着代码中的每个符号引用和符号定义联系起来了, 那么对于那些在一个文件中定义而在另外一个文件中引用的符号, 我们就可以确切地知道其大小. 接下来是重定位 :
7.7.1 重定位条目
重定位条目有两种, 分别放在.rel.text和.rel.data中 :
1. 前者用来标记那些在代码中被引用但却是在外部定义的函数或者全局变量, 这里要注意的是, 是专指在代码中被引用, 也就是说这个段其实是专门用来修改.text段的, 只要这个段中出现一些目前位置不确定的符号, 就会被标记...
可以看到7到14行属于.text段, 所以在.rel.text中需要添加四处 : 分别是bufp1, buf, bufp1, bufp1...
2. 后者用来标记那些已经初始化(已经分配了地址)但是初始化值是在外部定义的符号, 比如一个全局变量它的初始化值是另一个全局变量的地址,或者是外部函数的地址(一般也只有这两种情况, 因为在函数外初始化的变量只能是常数或者地址) ,那么显然该值还要被重定位, 类似这样的值就会被放入.rel.data段, 到时候根据这个到.data中修改相应的已初始化的值...
如果仍然使用上面这张图来看的话, 可以看到第3行的bufp0就是这样的符号, 所以bufp0将出现的.rel.data中...
下面展示的是ELF中重定位条目的格式 :
typedef struct { int offset; int symbol:24, type:8; }Elf32_Rel;
offset是需要修改的引用的节偏移, 也就是我们所要修改的位置相遇于节起点(.data或者.text)的偏移量, symbol是被修改的引用应该指向的符号(然后在我实际的考察过程中好像这个不是这么简单的映射关系, 也就是说不知道如何使用这个值求这个符号) , 最后有一个type告诉链接该使用哪种重定位类型. 总共11中, 这里只介绍基本的两种 :
1. R_386_PC32 : 重定位一个使用32位PC相对地址的引用.
2. R_386_32 : 重定位一个使用32为绝对地址的引用.
伪代码如图所示 :
先来看第一种, 也就是相对寻址(书上这一部分我只能说讲的像一坨翔) :
首先要明确 : 我们将要更新的这个值 + 运行时PC的值(此时PC中的值应该指向下一条指令的位置) == 运行时所引用符号的地址. 依次分析第三行的refptr其实就是在文件中我们所要修改的位置(这个位置的值就是我们所要修改的), 然后refaddr是运行时这个引用的位置, 因为refaddr其实是在指令中的某个位置, 而我们必须要得到的是下一条指令的位置(也就是当前PC的位置), 那么在有不同的指令大小和编码方式的机器上, 由这条指令中引用了符号的某个位置求得下一条指令开头的位置确实是不方便的, 所以一般来讲编译器会将这个偏移量放在等待重定位的位置refptr中(所以*refptr就是这个偏移量), 所以第八行可以这么理解, 左边那个其实是跳转时刻PC与所引用符号实际地址的距离(也就是我们上面提到的要更新的这个值) = 运行时所引用的符号的地址(ADDR(r.symbol)) - 运行时引用的位置(refaddr) + 运行时引用的位置与下一条指令的位置(也就是右边的那个 *refptr )...
然后是第二种, 绝对寻址 : 这个很简单, 那个*refptr里面一般来说是0...
下面这几节都不太了解, 也没什么话语权, 就直接抄书了...
7.8 可执行目标文件
7.9 加载可执行目标文件
7.10 动态链接共享库
7.11从应用程序中加载和链接共享库
7.12 与位置无关的代码(PIC)
7.13 处理目标文件的工具