7

I wrote a "hello world" program with PIE/PIC enabled. I observed the program headers have 2 LOAD entries:

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

So in my understanding, the ELF binary will be loaded into 2 pages. The 1st page contains the binary from offset 0 to file size 0x870, and it is Read & Execute. Since they are 0x1000 aligned, the 2nd entry will be loaded in the 2nd page, which contains the binary(from offset 0xeb0 to 0xeb0+0x15c). This page has Read & Write privilege.

While I "pmap" the running process (or cat /proc/pid/maps), it show there are 3 pages for the running program:

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

So how the ELF binary is loaded, and how the program header indicates a LOAD directive?

Part of the sections headers like:

  [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
2
  • The running process may have called mmap (e.g. for loading some shared library like libc.so). Try to strace your program Commented Jan 23, 2015 at 16:40
  • Yes, I tried strace. It did mmap & mprotect several pieces of memory. Commented Jan 26, 2015 at 20:58

1 Answer 1

8

I observed the program headers have 2 LOAD entries:

You've omitted an important program header: GNU_RELRO, which tells the loader that after mapping the LOAD segments, it should mprotect part of them as read-only.

When mprotect with different permissions is called on existing mapping, the kernel has to split that mapping into multiple mappings, which is where the extra entry is coming from.

In my test binary:

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

The kernel actually maps the LOAD segments before the loader even starts, and the map at that point looks like this:

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]
...

After the mprotect, the map looks like this:

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
...
Sign up to request clarification or add additional context in comments.

2 Comments

Wow it looks right! In my test, it's like this GNU_RELRO 0x000eb0 0x00001eb0 0x00001eb0 0x00150 0x00150 **RW** 0x4 (It's weird the flag is RW in my case). Does GNU_RELRO mean "RO after relocation"?
BTW, if we look at the 2nd LOAD directive, the FileSiz and the MemSiz are not the same. Is that ONLY because of the uninitialized data section (.bss)?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.