The ELF PT_GNU_RELRO Segment: Making the GOT Read-Only After Relocation

2026-08-25

Your binary's Global Offset Table (GOT) is a table of pointers the dynamic linker patches at load time. It's writable — it has to be, because ld.so writes the resolved addresses into it. But once relocation finishes, most of it never needs to change again. Leaving it writable is a gift to attackers: overwrite one GOT entry and the next call to free() jumps to your shellcode.

RELRO (RELocation Read-Only) fixes this by re-mapping those pages as read-only after the linker is done writing them. The mechanism is a dedicated ELF program header: PT_GNU_RELRO. It's not a segment that gets loaded — it points into an existing PT_LOAD segment and says "after relocation, mprotect this range to PROT_READ."

Two flavors exist:

Concrete example. Compile a program with the classic __free_hook exploit pattern in mind. Without RELRO, you can spot the writable GOT with readelf -l ./a.out | grep GNU_RELRO (missing) and confirm with checksec --file=./a.out. With gcc -Wl,-z,now foo.c, run again — the RELRO line reads "Full RELRO." Try to mprotect-around it and you'll fault. Every distro package built after ~2016 ships with Full RELRO for setuid binaries and Partial for the rest.

Rule of thumb: Full RELRO adds one mprotect call and forces N symbol resolutions at startup, where N is your PLT size. For a program with 200 external calls, that's ~200µs of extra startup — invisible for a server, measurable for a CLI tool run in a tight loop. Partial RELRO is essentially free.

The subtle gotcha: RELRO only protects what the linker knows about. Runtime-allocated function-pointer tables (vtables in your own heap, JIT trampolines) get no protection — you need mprotect yourself, or a separate PROT_READ mapping.

Key Takeaway: PT_GNU_RELRO is the ELF header that tells the loader "flip these pages read-only after you're done writing to them," turning the GOT from an attacker's favorite hijack target into an immutable table — and Full RELRO extends that protection to .got.plt at the cost of eager symbol resolution.

All newsletters