You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

简单ELF二进制段重叠、未对齐的原因及Linux处理机制问询

ELF加载器开发中的LOAD与DYNAMIC段疑问

我正在开发ELF加载器并研究ELF格式,使用Fedora 38中的Clang 16编译了一个无特殊编译选项的Hello World二进制文件(命令:clang hello.c -o hello),该文件可正常运行。通过readelf查看程序头时,发现与我对LOAD和DYNAMIC段的理解不符的情况:

程序头信息:

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
…
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x00000000000004f0 0x00000000000004f0  R      0x1000
  LOAD           0x0000000000001000 0x0000000000401000 0x0000000000401000
                 0x000000000000016d 0x000000000000016d  R E    0x1000
  LOAD           0x0000000000002000 0x0000000000402000 0x0000000000402000
                 0x00000000000000dc 0x00000000000000dc  R      0x1000
  LOAD           0x0000000000002df8 0x0000000000403df8 0x0000000000403df8
                 0x0000000000000214 0x0000000000000218  RW     0x1000
  DYNAMIC        0x0000000000002e08 0x0000000000403e08 0x0000000000403e08
                 0x00000000000001d0 0x00000000000001d0  RW     0x8

前三个LOAD段的虚拟地址对齐符合预期,但最后一个LOAD段与DYNAMIC段重叠且未对齐。该可执行文件在Linux中可正常加载,/proc/<pid>/maps显示的内存映射如下:

00400000-00401000 r--p 00000000 00:23 16905823                         /path/to/hello
00401000-00402000 r-xp 00001000 00:23 16905823                         /path/to/hello
00402000-00403000 r--p 00002000 00:23 16905823                         /path/to/hello
00403000-00404000 r--p 00002000 00:23 16905823                         /path/to/hello
00404000-00405000 rw-p 00003000 00:23 16905823                         /path/to/hello

我有以下疑问:

  1. 链接器为何生成未对齐的段?依赖操作系统修正似乎并非良策。
  2. 链接器为何创建重叠的段?同样,依赖操作系统修正并非良策。
  3. Linux是否有特定的规范、标准或算法来修正该问题?感觉存在未记载或我未找到的行为。

问题解答

1. 链接器为何生成未对齐的LOAD段?

这里的“未对齐”是针对页面大小(0x1000)而言,但ELF规范并没有要求LOAD段的虚拟地址必须是页面对齐的——它只要求LOAD段的**对齐值(Align字段)**符合规则,且满足VirtAddr % Align == Offset % Align的约束。

链接器这么做是为了缩减二进制文件体积:如果强行把最后一个LOAD段对齐到0x1000,需要在文件中填充大量无意义的空字节,浪费磁盘空间。你可以计算验证:最后一个LOAD段的0x403df8 % 0x1000 = 0xdf8,0x2df8 % 0x1000 = 0xdf8,完全符合ELF规范的对齐要求。

2. 链接器为何创建重叠的段?

这种“重叠”是正常设计,并非错误。DYNAMIC段本身就是最后一个LOAD段的一部分:它的虚拟地址落在最后一个LOAD段的地址范围(0x403df8到0x403df8 + 0x218 = 0x404010)内,且两者权限均为RW。

DYNAMIC是描述ELF元数据的程序头表项,不需要单独创建LOAD段——它的权限和所在的LOAD段完全一致,直接复用现有LOAD段的映射即可,这是合理的资源复用设计。

3. Linux内核的处理逻辑

Linux内核按页面粒度管理内存映射,这就是/proc/<pid>/maps中都是0x1000对齐区间的原因,核心处理逻辑如下:

  • 遍历所有LOAD段,将每个段的地址范围向上/向下对齐到页面边界。
  • 对跨页面的LOAD段,创建对应内存映射,并根据LOAD段的权限设置页面访问权限。
  • 对于LOAD段中MemSiz大于FileSiz的部分(比如最后一个LOAD段中4字节的差异),内核会将这部分映射为匿名内存并做零初始化,用来满足BSS段的需求(BSS段在文件中不占空间,加载时需要分配零填充的内存)。

这些逻辑完全符合ELF规范,并非未记载行为,内核源码fs/binfmt_elf.c中的load_elf_binary函数详细实现了ELF加载的全过程。


内容的提问来源于stack exchange,提问作者csnate

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 02:00:55