Information Security 13 min read

Position Independent Code (PIC), Position Independent Executable (PIE), and Address Space Layout Randomization (ASLR) in Linux

The article explains how Position Independent Code (PIC) enables code to run at any address using GOT and PLT tricks, how Position Independent Executables (PIE) extend this to whole binaries, and how Linux’s Address Space Layout Randomization (ASLR) leverages PIE to fully randomize process memory, making exploitation significantly harder.

OPPO Kernel Craftsman
OPPO Kernel Craftsman
OPPO Kernel Craftsman
Position Independent Code (PIC), Position Independent Executable (PIE), and Address Space Layout Randomization (ASLR) in Linux

This article explains the principles and interrelations of Position Independent Code (PIC), Position Independent Executable (PIE), and Address Space Layout Randomization (ASLR) on Linux systems, focusing on X86 and X64 platforms. It begins with an overview of the process virtual address space: the kernel space is identical for all processes, while the user space consists of segments such as the text (.text), data (.data), BSS (.bss), heap, stack, and memory‑mapped regions. Without address space randomization, the loader places these segments at fixed virtual addresses. To illustrate, a simple test program is compiled statically with

gcc -o addr_test addr_test.c -static

. Running the program repeatedly shows that all segment addresses remain constant. Using

objdump

reveals that accesses to global variables and functions employ absolute addresses, which is characteristic of position‑dependent code. Position‑dependent code cannot be relocated; if the load address changes, the hard‑coded addresses become invalid, preventing normal execution. This limitation hinders features like shared libraries, whose load address varies between processes. Consequently, Position Independent Code (PIC) is introduced so that code can execute correctly regardless of its load address. PIC is generated with the compiler flag

-fPIC

. Its implementation relies on two key ideas: (1) the linker knows the fixed offset between the code and data sections, allowing any instruction to compute the data section’s address relative to the instruction pointer; (2) on X86, where a direct way to read the instruction pointer is unavailable, a call‑pop trick obtains the current address: - When

call STUB

executes, the return address (next instruction) is pushed onto the stack. - The label

STUB

contains

pop ebx

, which loads that address into EBX, giving the caller the value of the instruction pointer. With the instruction pointer known, the Global Offset Table (GOT) – a table in the data section – enables position‑independent data accesses. A instruction loads the GOT’s base address (computed from the instruction pointer and a known offset), then adds the entry’s offset to obtain the absolute address of a variable. The dynamic loader resolves relocations of type

R_386_GLOB_DAT

in the

.rel.dyn

section, writing the actual symbol addresses into the corresponding GOT entries. For function calls, PIC uses the Procedure Linkage Table (PLT) together with the GOT to implement lazy binding. On the first call to a function, the PLT entry jumps to a resolver routine in PLT[0]; the resolver looks up the real function address, updates the GOT entry, and then transfers control to the function. Subsequent calls jump directly via the PLT‑GOT indirection without invoking the resolver. Position Independent Executable (PIE), produced with

-fPIE

, extends PIC to the executable itself, allowing its text, data, and BSS segments to be relocated. When Address Space Layout Randomization (ASLR) is enabled, the kernel can randomize the layout of these segments, making exploitation techniques such as return‑to‑libc or return‑to‑shellcode considerably harder. ASLR, introduced in Linux 2.6.12, randomizes the virtual address space of processes. It is configured via

/proc/sys/kernel/randomize_va_space

or

sysctl -w kernel.randomize_va_space=level

: - Level 0: address space layout is disabled (fixed addresses). - Level 1: randomizes the stack, memory‑mapped regions, and the heap. - Level 2: adds randomization of the heap (brk‑based allocations) in addition to level 1. Without PIE, levels 1 and 2 cannot randomize the executable’s text, data, and BSS segments because those contain position‑dependent code. Enabling PIE allows those segments to be relocated, so with ASLR=2 the entire layout—text, data, BSS, heap, stack, and mmap—is fully randomized. The article concludes with experimental evidence showing that, when PIE is combined with ASLR=2, all segment addresses vary across runs, whereas with PIE disabled only the stack, heap, and mmap regions change. References: 1. https://zhuanlan.zhihu.com/p/73885473 2. https://www.cnblogs.com/baiduboy/p/7000198.html 3. http://www.360doc.com/content/14/1020/21/19947352_418512226.shtml

LinuxSecuritymemory layoutASLRPICPIE
OPPO Kernel Craftsman
Written by

OPPO Kernel Craftsman

Sharing Linux kernel-related cutting-edge technology, technical articles, technical news, and curated tutorials

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.