【问题标题】:Why ELF program headers have two LOAD entries, while the program layout three sections为什么 ELF 程序头有两个 LOAD 条目,而程序布局三个部分
【发布时间】:2015-01-23 16:27:24
【问题描述】:

我编写了一个启用了 PIE/PIC 的“hello world”程序。我观察到程序头有 2 个 LOAD 条目:

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
... ...
  LOAD           0x000000 0x00000000 0x00000000 0x00870 0x00870 R E 0x1000
  LOAD           0x000eb0 0x00001eb0 0x00001eb0 0x0015c 0x00164 RW  0x1000

所以在我的理解中,ELF 二进制文件将被加载到 2 个页面中。第一页包含从偏移量 0 到文件大小 0x870 的二进制文件,它是读取和执行。由于它们是 0x1000 对齐的,因此第二个条目将加载到包含二进制文件的第二页中(从偏移量 0xeb0 到 0xeb0+0x15c)。此页面具有读写权限。

当我“pmap”正在运行的进程(或 cat /proc/pid/maps)时,它显示正在运行的程序有 3 个页面:

b7554000      4K rw---   [ anon ]
b7555000   1700K r-x-- libc-2.19.so
b76fe000      4K ----- libc-2.19.so
b76ff000      8K r---- libc-2.19.so
b7701000      4K rw--- libc-2.19.so
b7702000     12K rw---   [ anon ]
b771b000     16K rw---   [ anon ]
b771f000      4K r-x--   [ anon ]
b7720000    128K r-x-- ld-2.19.so
b7740000      4K r---- ld-2.19.so
b7741000      4K rw--- ld-2.19.so
b7742000      4K r-x-- main
b7743000      4K r---- main
b7744000      4K rw--- main
bfe68000    132K rw---   [ stack ]
 total     2032K

那么 ELF 二进制文件是如何加载的,程序头如何指示 LOAD 指令?

部分章节标题如:

  [13] .text             PROGBITS        00000480 000480 0002c2 00  AX  0   0 16
  [14] .fini             PROGBITS        00000744 000744 000014 00  AX  0   0  4
  [15] .rodata           PROGBITS        00000758 000758 000060 00   A  0   0  4
  [16] .eh_frame         PROGBITS        000007b8 0007b8 000094 00   A  0   0  4
  [17] .eh_frame_hdr     PROGBITS        0000084c 00084c 000024 00   A  0   0  4
  [18] .jcr              PROGBITS        00001eb0 000eb0 000004 00  WA  0   0  4
  [19] .fini_array       FINI_ARRAY      00001eb4 000eb4 000004 00  WA  0   0  4
  [20] .init_array       INIT_ARRAY      00001eb8 000eb8 000004 00  WA  0   0  4
  [21] .dynamic          DYNAMIC         00001ebc 000ebc 000108 08  WA  5   0  4
  [22] .got              PROGBITS        00001fc4 000fc4 00001c 00  WA  0   0  4
  [23] .got.plt          PROGBITS        00001fe0 000fe0 000020 00  WA  0   0  4
  [24] .data             PROGBITS        00002000 001000 00000c 00  WA  0   0  4
  [25] .tm_clone_table   PROGBITS        0000200c 00100c 000000 00  WA  0   0  4
  [26] .bss              NOBITS          0000200c 00100c 000008 00  WA  0   0  4

【问题讨论】:

  • 正在运行的进程可能调用了mmap(例如,用于加载一些共享库,如libc.so)。尝试strace你的程序
  • 是的,我试过 strace。它确实对几块内存进行了 mmap 和 mprotect。

标签: linux loading loader elf


【解决方案1】:

我观察到程序头有 2 个 LOAD 条目:

您省略了一个重要的程序头:GNU_RELRO,它告诉加载器在映射LOAD 段之后,它应该将其中的mprotect 部分设置为只读。

当在现有映射上调用具有不同权限的mprotect 时,内核必须将该映射拆分为多个映射,这就是额外条目的来源。

在我的测试二进制文件中:

LOAD           0x000000 0x00000000 0x00000000 0x0075c 0x0075c R E 0x1000
LOAD           0x000ef4 0x00001ef4 0x00001ef4 0x0012c 0x00130 RW  0x1000
...
GNU_RELRO      0x000ef4 0x00001ef4 0x00001ef4 0x0010c 0x0010c R   0x1

内核实际上在加载程序启动之前就映射了LOAD 段,此时的映射如下所示:

56555000-56556000 r-xp 00000000 fc:02 801500 /tmp/a.out
56556000-56558000 rw-p 00000000 fc:02 801500 /tmp/a.out
f7fdb000-f7fdc000 r-xp 00000000 00:00 0      [vdso]
...

mprotect之后的地图是这样的:

56555000-56556000 r-xp 00000000 fc:02 801500 /tmp/a.out
56556000-56557000 r--p 00000000 fc:02 801500 /tmp/a.out
56557000-56558000 rw-p 00001000 fc:02 801500 /tmp/a.out
...

【讨论】:

  • 哇,看起来不错!在我的测试中,就像GNU_RELRO 0x000eb0 0x00001eb0 0x00001eb0 0x00150 0x00150 **RW** 0x4 一样(奇怪的是,在我的情况下,标志是 RW)。 GNU_RELRO 是指“重定位后的 RO”吗?
  • 顺便说一句,如果我们查看第二个 LOAD 指令,FileSiz 和 MemSiz 并不相同。这仅仅是因为未初始化的数据部分(.bss)吗?
猜你喜欢
  • 2011-05-06
  • 1970-01-01
  • 2015-08-30
  • 1970-01-01
  • 2013-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多