找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2070

积分

0

好友

287

主题
发表于 4 天前 | 查看: 15| 回复: 0

.rel.dyn节详解

.rel.dyn 节是 ELF (Executable and Linkable Format) 文件格式中的一个关键组成部分,专门服务于动态链接时的数据重定位。简单来说,它负责在程序运行时,修正那些无法在编译或静态链接时确定地址的全局数据引用。

1. 基本概念

.rel.dyn 节是动态链接重定位表的一部分,其内部条目记录了程序加载进内存后,哪些位置的指令或数据需要被动态链接器修改。与 .rel.plt 节(主要负责函数调用重定位)不同,.rel.dyn 聚焦于处理除函数调用以外的数据重定位问题,例如对全局变量、静态变量等数据符号的地址引用。

2. 作用和功能

.rel.dyn 节承担着几个核心职责:

  1. 数据重定位:为全局变量、静态变量等数据符号提供运行时地址修正。
  2. 动态链接支持:在程序启动时,动态链接器(如 ld-linux.so)会读取此节的信息,完成对符号引用的实际地址绑定。
  3. GOT表更新:许多重定位条目指向 .got (Global Offset Table,全局偏移表) 中的特定位置,动态链接器会根据这些信息计算出正确的地址并填入GOT表项中,后续代码通过访问GOT来间接获取数据地址。

3. 与.rel.plt的区别

理解两者的区别有助于明确它们的应用场景:

  • .rel.dyn:处理数据引用的重定位,主要针对全局数据变量、静态变量的地址。
  • .rel.plt:处理函数调用的重定位,主要针对外部函数(如库函数)的地址。

4. 数据结构

.rel.dyn 节在文件中的实质是一个结构体数组。其元素类型根据架构和重定位类型不同,分为 Elf32_RelElf64_RelElf32_RelaElf64_Rela。最常见的两种结构定义如下:

// REL类型(不含加数)
typedef struct {
    Elf32_Addr r_offset; // 需要重定位的位置(通常是GOT表中的地址)
    Elf32_Word r_info;   // 低8位表示重定位类型,高24位表示符号表索引
} Elf32_Rel;

// RELA类型(包含加数)
typedef struct {
    Elf64_Addr r_offset;      // 需要重定位的位置
    Elf64_Xword r_info;       // 重定位类型和符号表索引的信息
    Elf64_Sxword r_addend;    // 加数(用于RELA类型)
} Elf64_Rela;

每个字段的含义如下:

  • r_offset:指定内存中哪个位置的内容需要被修改。对于可执行文件或共享库,这通常是 .got 表中某个条目的虚拟地址。
  • r_info:一个复合字段,其低位字节定义了重定位的计算方式(如相对寻址、绝对寻址),高位部分则指向动态符号表 (.dynsym) 中的一个条目,标识被引用的符号。
  • r_addend(仅 RELA 类型存在):一个常量加数,用于参与最终地址的计算。REL 类型则认为加数为0。

5. 常见重定位类型

在不同的处理器架构上,.rel.dyn 节中会出现不同的重定位类型。以下是两种常见架构的示例:

  • x86_64架构
    • R_X86_64_GLOB_DAT:用于重定位指向全局数据符号的指针
    • R_X86_64_RELATIVE:用于重定位与加载地址相关的相对地址。
  • ARM架构
    • R_ARM_GLOB_DAT:ARM架构下的全局数据重定位。
    • R_ARM_RELATIVE:ARM架构下的相对重定位。

6. 工作原理

动态链接器在加载程序时,会遵循以下流程处理 .rel.dyn 节:

  1. 解析 ELF 文件头,找到程序头表,进而定位到 .dynamic 节。
  2. .dynamic 节中读取关键信息,包括 .rel.dyn 节的地址和大小。
  3. 遍历 .rel.dyn 节中的每一个重定位条目 (Elfxx_Rel/Rela)。
  4. 对于每个条目:
    • 根据 r_info 找到对应的符号。
    • 结合符号的实际加载地址、重定位类型 (r_info 低位) 和可能的加数 (r_addend) 计算出最终的目标地址。
    • 将这个地址写入由 r_offset 指定的内存位置(通常是 .got 表的某个槽位)。
  5. 完成所有重定位后,程序中所有通过 GOT 访问的外部数据便拥有了正确的地址。

7. 实际应用示例

考虑一个简单的跨模块数据引用场景:

module_a.c:

extern int global_var; // 声明一个外部全局变量
int *ptr_to_global = &global_var; // 一个指向该外部变量的指针

module_b.c:

int global_var = 42; // 实际定义该全局变量

在编译 module_a.c 时,编译器无法知道 global_var 最终会被放在内存的哪个地址,因此它只能为 ptr_to_global 生成一个占位符,并在 .rel.dyn 节中创建一个重定位条目,指示动态链接器:“请将 global_var 的实际地址填到 ptr_to_global 对应的 GOT 槽位里”。程序加载时,动态链接器就会完成这个“填空”工作。

8. 查看.rel.dyn节的方法

我们可以使用 readelf 工具来探查一个 ELF 文件的重定位信息:

readelf -r <可执行文件或共享库>  # 查看所有重定位节(.rel.dyn, .rel.plt)的详细信息
readelf -S <可执行文件或共享库>  # 查看节头表,确认.rel.dyn节的偏移和大小

.rel.dyn 节是现代操作系统动态链接机制的基石之一,它使得程序模块间的数据共享和地址无关性成为可能。理解它的工作原理,对于深入掌握链接、加载及底层系统编程至关重要。如果你对更多底层技术细节感兴趣,欢迎到 云栈社区 交流探讨。




上一篇:2FA绕过实战:从Web3登录到银行权限提升的四个漏洞场景解析
下一篇:InnoDB锁机制解析:记录锁、间隙锁、临键锁如何影响并发
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-10 18:23 , Processed in 0.222145 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表