What is the -fPIE option for position-independent

2019-01-04 19:29发布

How will it change the code, e.g. function calls?

标签: gcc linker
2条回答
一夜七次
2楼-- · 2019-01-04 19:48

PIE is to support address space layout randomization (ASLR) in executable files.

Before the PIE mode was created, the program's executable could not be placed at a random address in memory, only position independent code (PIC) dynamic libraries could be relocated to a random offset. It works very much like what PIC does for dynamic libraries, the difference is that a Procedure Linkage Table (PLT) is not created, instead PC-relative relocation is used.

After enabling PIE support in gcc/linkers, the body of program is compiled and linked as position-independent code. A dynamic linker does full relocation processing on the program module, just like dynamic libraries. Any usage of global data is converted to access via the Global Offsets Table (GOT) and GOT relocations are added.

PIE is well described in this OpenBSD PIE presentation.

Changes to functions are shown in this slide (PIE vs PIC).

x86 pic vs pie

Local global variables and functions are optimized in pie

External global variables and functions are same as pic

and in this slide (PIE vs old-style linking)

x86 pie vs no-flags (fixed)

Local global variables and functions are similar to fixed

External global variables and functions are same as pic

Note, that PIE may be incompatible with -static

查看更多
Anthone
3楼-- · 2019-01-04 19:56

Minimal runnable example: GDB the executable twice

For those that want to see some action:

printf '
#include <stdio.h>
int main() {
    puts("hello world");
}
' > main.c
gcc -std=c99 -pie -fpie -ggdb3 -o pie main.c
gcc -std=c99 -no-pie -fno-pie -ggdb3 -o nopie main.c
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
gdb -batch -nh -ex 'set disable-randomization off' \
  -ex 'start' -ex 'info line' \
  -ex 'start' -ex 'info line' \
  ./pie
gdb -batch -nh -ex 'set disable-randomization off' \
  -ex 'start' -ex 'info line' \
  -ex 'start' -ex 'info line' \
  ./nopie

For the one with -pie, we see that the address of main changes between runs:

Temporary breakpoint 1 at 0x7a9: file memory_layout.c, line 31.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Temporary breakpoint 1, main (argc=1, argv=0x7ffe57d75318) at memory_layout.c:31
31      int main(int argc, char **argv) {
Line 31 of "memory_layout.c" starts at address 0x55db0066b79a <main> and ends at 0x55db0066b7a9 <main+15>.
Temporary breakpoint 2 at 0x55db0066b7a9: file memory_layout.c, line 31.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Temporary breakpoint 2, main (argc=1, argv=0x7ffd03b67d68) at memory_layout.c:31
31      int main(int argc, char **argv) {
Line 31 of "memory_layout.c" starts at address 0x563910ccd79a <main> and ends at 0x563910ccd7a9 <main+15>.

so in this example, the address for the first run was 0x55db0066b79a and for the second 0x563910ccd79a.

But for the one with -no-pie, the address of main remains the same 0x400627 for both runs:

Temporary breakpoint 1 at 0x400636: file ./memory_layout.c, line 28.

Temporary breakpoint 1, main (argc=1, argv=0x7ffd5f69c8b8) at ./memory_layout.c:28
warning: Source file is more recent than executable.
28      int bss = 0;
Line 28 of "./memory_layout.c" starts at address 0x400627 <main> and ends at 0x400636 <main+15>.
Temporary breakpoint 2 at 0x400636: file ./memory_layout.c, line 28.

Temporary breakpoint 2, main (argc=1, argv=0x7ffdd9f74bd8) at ./memory_layout.c:28
28      int bss = 0;
Line 28 of "./memory_layout.c" starts at address 0x400627 <main> and ends at 0x400636 <main+15>.

echo 2 | sudo tee /proc/sys/kernel/randomize_va_space ensures that ASLR is on (the default in Ubuntu 17.10): How can I temporarily disable ASLR (Address space layout randomization)? | Ask Ubuntu.

set disable-randomization off is needed otherwise GDB, as the name suggests, turns off ASLR for the process by default to give fixed addresses across runs to improve the debugging experience: Difference between gdb addresses and "real" addresses? | Stack Overflow

readelf fun

Furthermore, we can also observe that:

readelf -s ./nopie | grep main

gives the actual runtime load address:

69: 0000000000400627   370 FUNC    GLOBAL DEFAULT   13 main

while:

readelf -s ./pie | grep main

gives just an offset:

70: 000000000000079a   401 FUNC    GLOBAL DEFAULT   14 main

By turning ASLR off (with either randomize_va_space or set disable-randomization off), GDB always gives main the address: 0x5555555547a9, so we deduce that the -pie address is composed from:

0x555555554000 + random offset + symbol offset (79a)

TODO where is 0x555555554000 hard coded in the Linux kernel / glibc loader / wherever? How is the address of the text section of a PIE executable determined in Linux?

Tested in Ubuntu 18.04.

Related question: How can I tell, with something like objdump, if an object file has been built with -fPIC?

查看更多
登录 后发表回答