为什么ELF程序头有两个LOAD条目,而程序布局有三个部分

时间:2015-01-23 16:27:25

标签: linux loading loader elf

我写了一个启用了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的二进制文件,它是Read&执行。由于它们是0x1000对齐,第二个条目将加载到第二页,其中包含二进制文件(从偏移 0xeb0 0xeb0 + 0x15c )。这个页面有Read&写权限。

当我“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

1 个答案:

答案 0 :(得分:4)

  

我观察到程序头有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
...