我看到没有人回答这个问题,所以我想我会尝试。
我将尝试通过一个实际示例来说明这是如何工作的。下面是一些 C 代码,它人为但有目的地混合了外部全局函数和数据——重定位的基础。
/* hello.c */
char* hello = "hello";
/* say.c */
#include "stdio.h"
extern char* hello;
void say(void){
printf(hello);
}
/* main.c */
extern void say(void);
void please_say(void){
say();
}
int main(void){
please_say();
return 0;
}
现在获取共享对象/库的常用方法是使用 -fPIC 编译每个 C 文件,并使用 -shared 链接该批次。在我们这样做之后,我们可以使用 readelf 检查重定位。像这样的东西:
gcc -fPIC -c *.c
gcc -shared -o libtemp.so *.o
readelf -r libtemp.so
搬迁数据如下:
偏移 0x34c 处的重定位节“.rel.dyn”包含 6 个条目:
偏移信息类型 Sym.Value Sym。姓名
000016dc 00000008 R_386_RELATIVE
000016e0 00000008 R_386_RELATIVE
000016ac 00000106 R_386_GLOB_DAT 00000000 __gmon_start__
000016b0 00000206 R_386_GLOB_DAT 00000000 _Jv_RegisterClasses
000016b4 00000d06 R_386_GLOB_DAT 000016e0 你好
000016b8 00000406 R_386_GLOB_DAT 00000000 __cxa_finalize
偏移 0x37c 处的重定位节“.rel.plt”包含 5 个条目:
偏移信息类型 Sym.Value Sym。姓名
000016c8 00000107 R_386_JUMP_SLOT 00000000 __gmon_start__
000016cc 00000507 R_386_JUMP_SLOT 000004fc please_say
000016d0 00000807 R_386_JUMP_SLOT 00000540 说
000016d4 00000307 R_386_JUMP_SLOT 00000000 printf
000016d8 00000407 R_386_JUMP_SLOT 00000000 __cxa_finalize
hello 的 R_386_GLOB_DAT 项目是 GOT 条目。同样,say、please_say 和 printf 的 R_386_JUMP_SLOT 项是 PLT 项。它们来自位置无关代码的使用,而不是我们制作了共享对象的事实。
在没有 -fPIC 的情况下执行相同的构建过程后,我们会得到不同的重定位。所以
gcc -c *.c
gcc -shared -o libtemp.so *.o
readelf -r libtemp.so
给我们
偏移 0x34c 处的重定位节“.rel.dyn”包含 9 个条目:
偏移信息类型 Sym.Value Sym。姓名
00001674 00000008 R_386_RELATIVE
00001678 00000008 R_386_RELATIVE
000004d3 00000802 R_386_PC32 000004f0 say
000004e0 00000502 R_386_PC32 000004cc please_say
000004f7 00000d01 R_386_32 00001678 hello
000004ff 00000302 R_386_PC32 00000000 printf
00001654 00000106 R_386_GLOB_DAT 00000000 __gmon_start__
00001658 00000206 R_386_GLOB_DAT 00000000 _Jv_RegisterClasses
0000165c 00000406 R_386_GLOB_DAT 00000000 __cxa_finalize
偏移量 0x394 处的重定位节“.rel.plt”包含 2 个条目:
偏移信息类型 Sym.Value Sym。姓名
0000166c 00000107 R_386_JUMP_SLOT 00000000 __gmon_start__
00001670 00000407 R_386_JUMP_SLOT 00000000 __cxa_finalize
现在共享对象对所有定义都有熟悉的重定位。hello 有绝对重定位,函数有 PC 相对重定位。
这是什么意思?好吧,GOT 和 PLT 表仍然存在。有两件重要的事情需要注意。首先是编译后的代码没有 GOT 或 PLT 条目。第二个是仍然需要 GOT 和 PLT 表。它们被用于初始化和清理(可能用于标准库)。由于您使用的是自定义 ELF 加载器,因此建议对 GOT 和 PLT 条目实现一些基本支持,即使您的主应用程序执行标准重定位也是如此。
然后,您的应用程序将付出搬迁的代价,而不是位置独立的代价。