1. ELF文件与重定位基础概念
第一次逆向分析Linux程序时,看到那些跳转地址后面跟着"(plt)"或者"@got"的符号,我盯着反汇编代码发呆了半小时。后来才明白,这背后是ELF格式中重定位机制在起作用。重定位(Relocation)是链接器和加载器用来修正程序内存引用的关键技术,理解它能帮你从底层把握程序如何加载运行。
ELF(Executable and Linkable Format)是Unix/Linux系统下可执行文件、目标文件和共享库的标准格式。它比Windows的PE格式更模块化,通过节(Section)和段(Segment)的两级结构组织代码和数据。当编译器生成.o目标文件时,它并不知道最终代码会被加载到内存的哪个位置,于是会留下需要后期修正的"空白支票"——这就是重定位项的来源。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 重定位的核心原理与类型
2.1 静态链接与动态链接的重定位差异
用gcc编译时加上-v参数,你会看到链接器ld被调用时带着一堆复杂的参数。这背后是两种重定位处理方式:
- 静态链接重定位:发生在程序装载前,由ld链接器完成。比如将多个.o文件合并成可执行文件时,需要调整函数调用地址。
- 动态链接重定位:发生在运行时,由动态链接器(ld-linux.so)处理。例如调用printf时,需要定位glibc库中的实际函数地址。
我在开发一个嵌入式项目时曾犯过错误:静态链接了所有库导致可执行文件膨胀到8MB,后来改用动态链接缩减到200KB。这个选择直接影响重定位的发生时机和方式。
2.2 重定位项数据结构解析
用readelf -r查看目标文件时,你会看到类似这样的条目:
code复制Offset Info Type Sym.Value Sym. Name
00002008 00000105 R_X86_64_32 00000000 .rodata
这表示在输出文件的0x2008偏移处,有一个需要重定位的32位绝对地址引用,指向.rodata节。重定位类型R_X86_64_32决定了如何计算修正值。
在/usr/include/elf.h中,重定位项的结构体定义如下:
c复制typedef struct {
Elf64_Addr r_offset;
