0

我有一个生成可重定位共享库的自定义工具链。这与一个同样定制的 ELF 加载器一起工作,它将这些加载到内存中,修复它们。我现在正试图说服 gcc 和 binutils 生成与此加载程序兼容的 ELF 文件。

不幸的是,binutils 似乎拒绝生成可重定位的共享对象。它会生成 PIC 共享对象,但我不想这样做,因为 GOT/PLT 的额外开销(此外,自定义 ELF 加载器不支持它)。它会生成可重定位的对象,但它们不是动态对象,因此没有 ELF 加载器希望能够加载它们的适当部分。

我不清楚为什么 GNU ld 拒绝允许我在同一命令行上指定 --relocatable 和 -shared 。任何人都可以启发我吗?有没有人知道让 ld 生成我正在寻找的目标文件的咒语?

4

1 回答 1

3

我看到没有人回答这个问题,所以我想我会尝试。

我将尝试通过一个实际示例来说明这是如何工作的。下面是一些 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 条目实现一些基本支持,即使您的主应用程序执行标准重定位也是如此。

然后,您的应用程序将付出搬迁的代价,而不是位置独立的代价。

于 2011-07-04T10:06:44.890 回答