你的位置:首页 > 操作系统

[操作系统]了解动态链接(六)—— 重定位表


柳条青青,南风熏熏,幻化奇峰瑶岛,一天的黄云白云,那边麦浪中间,有农妇笑语殷殷。问后园豌豆肥否,问杨梅可有鸟来偷;好几天不下雨了,玫瑰花还未曾红透;梅夫人今天进城去,且看她有新闻无有。—— 徐志摩·夏日田间即景

无论是可执行文件还是 so,只要它依赖于其他 so(.dynsym 动态符号表中有导入符号存在),那么在编译链接阶段,这些符号的地址未知,所以只能在动态链接阶段对其进行地址重定位。

注意:以 PIC 编译的 so,虽然称“地址无关代码”,但也需要重定位。因为对于 PIC 的 so 来说,只不过是把代码中的绝对地址提出来,放到了数据段的 GOT 表中。所以,虽然代码段不需重定位,但数据段的 GOT 表需要重定位。(以 PIC 编译 so,是为了复用 so 的代码段)

以 android liblog.so 为例。在代码中调用 memset 时,实际上会跳转到标号 memset_ptr 指向的内存。

 

而标号 memset_ptr 指向的内存就定义在 GOT 表中:

 

那么如何为 GOT 表做重定位呢?GOT 表中的每一项应该指向哪一个符号在内存中的地址呢?这些信息就由重定位表来描述。具体到 android 来说,重定位表保存在 .rel.dyn 和 .rel.plt 中。

 

从表中可以看出,位于 .rel.dyn 中的主要是 R_ARM_GLOB_DAT 类型的重定位项,而位于 .rel.plt 中的主要是 R_ARM_JUMP_SLOT 类型的重定位项。前者用于对数据引用做重定位,而后者用于对函数引用做重定位。除此之外,还看到了 R_ARM_RELATIVE 类型的重定位。

在 android linker 的源码中,调用了两次 soinfo_relocate 函数,分别为 .rel.dyn 和 .rel.plt 做重定位:

 

在 soinfo_relocate 函数的源码中可以看到,对于不同类型的重定位,计算符号地址的方式也有所不同。

 

实际上对于 R_ARM_GLOB_DAT、R_ARM_JUMP_SLOT 这两种重定位类型来说,只需将符号地址填入被修正的内存即可。而 R_ARM_RELATIVE 类型看起来特殊些,它的作用是进行基址重置 (Rebasing) 。

比如指针 p 指向静态变量 a,而静态变量 a 相对于 so 基址的偏移为 A。在编译时,so 的基址为 0,此时 p 的值为 A。而当 so 被装载到内存中时,p 的值就需要加上一个 so 在内存中的基址 base。R_ARM_RELATIVE 类型的重定位就是用来干这个的。

对于 android linker 的重定位细节,以及其他重定位类型,在后面写 android linker 源码分析笔记时再描述。

学习资料: 《程序员的自我修养——链接、装载和库》