考虑 Linux 系统上的以下两个文件:
使用消息.cpp
#include <iostream>
extern const char* message;
void print_message();
int main() {
std::cout << message << '\n';
print_message();
}
libmessage.cpp
#include <iostream>
const char* message = "Meow!";
void print_message() {
std::cout << message << '\n';
}
我们可以将use_message.cpp编译为目标文件,将libmessage.cpp编译为共享库,并将它们链接在一起,如下所示:
$ g++ use_message.cpp -c -pie -o use_message.o
$ g++ libmessage.cpp -fPIC -shared -o libmessage.so
$ g++ use_message.o libmessage.so -o use_message
的定义message
最初位于libmessage.so中。执行时use_message
,动态链接器执行重定位:
- 使用字符串数据的加载地址更新libmessage.so
message
内部的定义 - 将libmessage.so
message
的定义复制到use_message的部分.bss
- 更新libmessage.so中的全局偏移表,使其指向use_message
message
内部的新版本
由 转储的相关搬迁readelf
是:
使用消息
Offset Info Type Sym. Value Sym. Name + Addend
000000004150 000c00000005 R_X86_64_COPY 0000000000004150 message + 0
这是我之前写的列表中的第 2 号搬迁。
libmessage.so
Offset Info Type Sym. Value Sym. Name + Addend
000000004040 000000000008 R_X86_64_RELATIVE 2000
000000003fd8 000b00000006 R_X86_64_GLOB_DAT 0000000000004040 message + 0
这些分别是重定位编号 1 和 3。
重定位编号 1 和 2 之间存在依赖性:对libmessage.so定义的更新message
必须在该值复制到use_message之前进行,否则use_message将不会指向正确的位置。
我的问题是:申请搬迁的顺序是如何规定的?ELF 文件中是否有编码指定这一点?或者在 ABI 中?或者动态链接器只是期望计算出重定位本身之间的依赖关系,并确保写入给定内存地址的任何重定位都在从同一位置读取的任何重定位之前运行?静态链接器是否仅输出重定位,以便可执行文件中的重定位始终可以在共享库重定位之后处理?