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