Search code examples
gccposition-independent-code

Position independent executable and fixed entry point address


A position independent ELF executable can be loaded and run from arbitrary address within the virtual address space.

I tried to build below simple program:

pie.c

#include <stdio.h>

void main(void)
{
    printf("hello, pie!\n");
}

Build command:

gcc pie.c -o pie -pie

The ELF header of the executable is:

pie:

ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x530 <================= FIXED entry point
  Start of program headers:          64 (bytes into file)
  Start of section headers:          6440 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)
  Number of section headers:         29
  Section header string table index: 28

So the entry point address is fixed.

And it is the symbol _start there:

0000000000000530 T _start

So it means the _start must be placed at 0x530.

Isn't this contradictory to being position independent?

ADD 1 - 10:44 AM 8/16/2021

I tried to build the same program without the -pie flag:

gcc pie.c -o pie_not

The generated ELF header is:

pie_not:

ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x530  <============ Still the same value as with -pie flag
  Start of program headers:          64 (bytes into file)
  Start of section headers:          6440 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)
  Number of section headers:         29
  Section header string table index: 28

And I further compared the built results pie and pie_not. Their binaries are identical.

So how could the operating system tell which one should be treated as position independent code??

ADD 2 - 10:56 AM 8/16/2021

I guess I figured it out. It seems gcc use -pie by default. To avoid it, I have to add -no-pie flag explicitly.

gcc pie.c -o pie_not -no-pie

And the generated ELF header is like this:

pie_not:

ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file) <==== File type also changed!!!
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x400400 <===== Entry point address changed!
  Start of program headers:          64 (bytes into file)
  Start of section headers:          6376 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)
  Number of section headers:         29
  Section header string table index: 28

I believe the loader relies on the Type filed in ELF header to decide how to treat the binary.


Solution

  • For a position-independent executable, the entry point address in the ELF header isn't used as the absolute address of the entry point. Rather, it's used as an offset from the randomly chosen base address.

    So in your example, if the base address is randomly chosen as 0x567812340000, then execution will start at absolute address 0x567812340530.