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

ELF可执行文件PT_LOAD段与/proc/pid/maps内存映射差异疑问

提问:ELF加载时PT_LOAD段与/proc/maps显示段数差异的原因

我正在研究Linux内核中ELF二进制文件的加载机制,对PT_LOAD段的内存加载方式存在疑惑。我有一个名为test.elf的ELF可执行文件,通过readelf -l查看其程序头,显示包含4个PT_LOAD段:

root@ubuntu-s-4vcpu-8gb-amd-sgp1-01:~/C# readelf -l test.elf

Elf file type is DYN (Position-Independent Executable file)
Entry point 0x10c0
There are 13 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040
                 0x00000000000002d8 0x00000000000002d8  R      0x8
  INTERP         0x0000000000000318 0x0000000000000318 0x0000000000000318
                 0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000718 0x0000000000000718  R      0x1000
  LOAD           0x0000000000001000 0x0000000000001000 0x0000000000001000
                 0x0000000000000259 0x0000000000000259  R E    0x1000
  LOAD           0x0000000000002000 0x0000000000002000 0x0000000000002000
                 0x0000000000000124 0x0000000000000124  R      0x1000
  LOAD           0x0000000000002da0 0x0000000000003da0 0x0000000000003da0
                 0x0000000000000274 0x0000000000000278  RW     0x1000
  DYNAMIC        0x0000000000002db0 0x0000000000003db0 0x0000000000003db0
                 0x00000000000001f0 0x00000000000001f0  RW     0x8
  NOTE           0x0000000000000338 0x0000000000000338 0x0000000000000030  R      0x8

我原本预期该程序加载到内存后对应4个段,但查看/proc/209183/maps时发现,运行中的进程从该可执行文件加载了5个段:

root@ubuntu-s-4vcpu-8gb-amd-sgp1-01:~/C# cat /proc/209183/maps
55acd5d31000-55acd5d32000 r--p 00000000 fc:01 517731                     /root/C/test.elf
55acd5d32000-55acd5d33000 r-xp 00001000 fc:01 517731                     /root/C/test.elf
55acd5d33000-55acd5d34000 r--p 00002000 fc:01 517731                     /root/C/test.elf
55acd5d34000-55acd5d35000 r--p 00002000 fc:01 517731                     /root/C/test.elf
55acd5d35000-55acd5d36000 rw-p 00003000 fc:01 517731                     /root/C/test.elf
55acd6e88000-55acd6ea9000 rw-p 00000000 00:00 0                          [heap]
7f85da4fe000-7f85da501000 rw-p 00000000 00:00 0
7f85da501000-7f85da529000 r--p 00000000 fc:01 31232                      /usr/lib/x86_64-linux-gnu/libc.so.6
7f85da529000-7f85da6be000 r-xp 00028000 fc:01 31232                      /usr/lib/x86_64-linux-gnu/libc.so.6
7f85da6be000-7f85da716000 r--p 001bd000 fc:01 31232                      /usr/lib/x86_64-linux-gnu/libc.so.6

我还注意到其中第三个和第四个段具有相同的偏移量和权限。我已查阅相关资料并检查了段中包含的节,但未发现特殊之处:

Section to Segment mapping:
  Segment Sections...
   00
   01     .interp
   02     .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
   03     .init .plt .plt.got .plt.sec .text .fini
   04     .rodata .eh_frame_hdr .eh_frame
   05     .init_array .fini_array .dynamic .got .data .bss
   06     .dynamic
   07     .note.gnu.property
   08     .note.gnu.build-id .note.ABI-tag
   09     .note.gnu.property
   10     .eh_frame_hdr
   11
   12     .init_array .fini_array .dynamic .got

希望有人能解释这一差异的原因!我原本预期仅存在4个段。


回答

核心原因:内存页权限约束与GOT安全拆分

1. 内存页是权限控制的最小单位

Linux内核不允许在同一个内存页(通常4KB)中设置不同的权限。当PT_LOAD段的映射范围跨越内存页,且不同页的权限需求冲突时,内核会将单个文件页拆分为多个独立的内存映射。

看你的第四个PT_LOAD段(RW权限):

  • 它的文件起始偏移0x2da0属于文件的0x2000-0x3000页(该页归第三个只读PT_LOAD段所有)
  • 对应的内存起始地址0x3da0落在0x3000-0x4000内存页中
  • 内核为了保证内存页权限统一,会将0x3000-0x3da0区间映射为只读(继承原文件页的R权限),而0x3da0-0x4000及后续页映射为可写(遵循RW段的权限),这就额外生成了一个只读映射段。

2. 只读GOT的安全机制

现代GNU链接器为了防御攻击,会将全局偏移表(GOT)拆分为只读静态绑定区和可写动态绑定区:

  • 只读区存储无需运行时修改的静态绑定地址,属于.got节的一部分
  • 可写区存储需要动态链接时更新的地址

你的第四个PT_LOAD段包含了.got节,其中的只读区会被内核单独映射为只读内存页,与可写区分开,这也是导致内存段数增加的原因之一。

对重复偏移段的解释

你看到的两个偏移0x2000的只读段,本质是同一个文件页(0x2000-0x3000)的不同内存映射:

  • 第一个对应第三个PT_LOAD段的有效内容区间(0x2000-0x2124)
  • 第二个对应该文件页的剩余空洞部分,映射到内存的0x3000-0x4000区间,权限保持只读

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 06:04:50