1

Consider this C program:

#include <stdio.h>
#include <time.h>

int main(){
    printf("Hellow world\n");
    return 0;
}

when compiled with gcc program.c this produces an ELF file with the following segments:

❯ readelf a.out -l

Elf file type is DYN (Shared object file)
Entry point 0x1050
There are 11 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000000040 0x0000000000000040
                 0x0000000000000268 0x0000000000000268  R      0x8
  INTERP         0x00000000000002a8 0x00000000000002a8 0x00000000000002a8
                 0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000590 0x0000000000000590  R      0x1000
  LOAD           0x0000000000001000 0x0000000000001000 0x0000000000001000
                 0x00000000000001f5 0x00000000000001f5  R E    0x1000
  LOAD           0x0000000000002000 0x0000000000002000 0x0000000000002000
                 0x0000000000000120 0x0000000000000120  R      0x1000
  LOAD           0x0000000000002de8 0x0000000000003de8 0x0000000000003de8
                 0x0000000000000250 0x0000000000000258  RW     0x1000
  DYNAMIC        0x0000000000002df8 0x0000000000003df8 0x0000000000003df8
                 0x00000000000001e0 0x00000000000001e0  RW     0x8
  NOTE           0x00000000000002c4 0x00000000000002c4 0x00000000000002c4
                 0x0000000000000044 0x0000000000000044  R      0x4
  GNU_EH_FRAME   0x0000000000002010 0x0000000000002010 0x0000000000002010
                 0x0000000000000034 0x0000000000000034  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  GNU_RELRO      0x0000000000002de8 0x0000000000003de8 0x0000000000003de8
                 0x0000000000000218 0x0000000000000218  R      0x1

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

and when loaded into memory these are the memory maps:

5610ff2b4000-5610ff2b5000 r--p 00000000 08:12 11031860                   /home/a.out
5610ff2b5000-5610ff2b6000 r-xp 00001000 08:12 11031860                   /home/a.out
5610ff2b6000-5610ff2b7000 r--p 00002000 08:12 11031860                   /home/a.out
5610ff2b7000-5610ff2b8000 r--p 00002000 08:12 11031860                   /home/a.out
5610ff2b8000-5610ff2b9000 rw-p 00003000 08:12 11031860                   /home/a.out
5610ff899000-5610ff8ba000 rw-p 00000000 00:00 0                          [heap]
7fdae2e26000-7fdae2e4b000 r--p 00000000 08:12 8397529                    /usr/lib/libc-2.31.so
7fdae2e4b000-7fdae2f97000 r-xp 00025000 08:12 8397529                    /usr/lib/libc-2.31.so
7fdae2f97000-7fdae2fe2000 r--p 00171000 08:12 8397529                    /usr/lib/libc-2.31.so
7fdae2fe2000-7fdae2fe5000 r--p 001bb000 08:12 8397529                    /usr/lib/libc-2.31.so
7fdae2fe5000-7fdae2fe8000 rw-p 001be000 08:12 8397529                    /usr/lib/libc-2.31.so
7fdae2fe8000-7fdae2fee000 rw-p 00000000 00:00 0
7fdae3024000-7fdae3026000 r--p 00000000 08:12 8397499                    /usr/lib/ld-2.31.so
7fdae3026000-7fdae3046000 r-xp 00002000 08:12 8397499                    /usr/lib/ld-2.31.so
7fdae3046000-7fdae304e000 r--p 00022000 08:12 8397499                    /usr/lib/ld-2.31.so
7fdae304f000-7fdae3050000 r--p 0002a000 08:12 8397499                    /usr/lib/ld-2.31.so
7fdae3050000-7fdae3051000 rw-p 0002b000 08:12 8397499                    /usr/lib/ld-2.31.so
7fdae3051000-7fdae3052000 rw-p 00000000 00:00 0
7ffd579e4000-7ffd57a06000 rw-p 00000000 00:00 0                          [stack]
7ffd57a65000-7ffd57a68000 r--p 00000000 00:00 0                          [vvar]
7ffd57a68000-7ffd57a69000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0                  [vsyscall]

Checking the linker script with gcc program.c -Wl,--verbose we see that the linker is mapping the text segment to 0x0000000000000000 (as we can see in the ELF segments):

...
SECTIONS
{
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0)); . = SEGMENT_START("text-segment", 0) + SIZEOF_HEADERS;
  .interp         : { *(.interp) }
...

but if the first LOAD section is the text segment, why it has no executable permissions? It does not have either in the segments:

  LOAD           0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000590 0x0000000000000590  R      0x1000

nor in the maps:

5610ff2b4000-5610ff2b5000 r--p 00000000 08:12 11031860                   /home/a.out

does this mean that the text segment is splitter into two memory maps? If so, why that happens? Where in the linker script is that logic?

Dargor
  • 623
  • 1
  • 4
  • 12

0 Answers0