RELRO
/ REL-roh /
Some parts of a running program that hold pointers — most importantly the global offset table — must be writable during startup so the dynamic linker can fill them in with resolved addresses. But once startup is done, those pointer tables should never change again. Leaving them writable hands an attacker a wonderful target: overwrite one GOT entry and you redirect a function call to code of your choosing. RELRO is the hardening feature that closes this window by making those relocation-related areas read-only after the dynamic linker has finished with them.
It comes in two strengths. Partial RELRO rearranges the binary so that the variables the linker must relocate are grouped together, and it marks the part of the GOT used for data (.got) read-only after relocation — but it leaves the .got.plt (the part used for lazy function binding) writable, because lazy binding needs to keep patching it on each function's first call. Full RELRO goes further: it turns off lazy binding entirely (resolving every symbol eagerly at startup, as if LD_BIND_NOW were set), so the whole GOT can then be marked read-only. You request this at link time, for example with -Wl,-z,relro for partial and adding -Wl,-z,now for full. After full RELRO the loader applies a memory-protection change (an mprotect-style call) to drop write permission on those pages.
It matters because the GOT-overwrite attack is a classic exploitation primitive, and full RELRO simply takes it off the table for that binary. The trade-off is honest and small: full RELRO makes startup a little slower (everything is resolved up front) and disables the lazy-binding optimization, in exchange for a meaningfully smaller attack surface. Most security-conscious distributions enable full RELRO by default. You can check a binary's status with tools like checksec, which report 'No RELRO', 'Partial RELRO', or 'Full RELRO'.
$ gcc -Wl,-z,relro -Wl,-z,now main.c -o app # full RELRO $ checksec --file=app RELRO: Full RELRO # GOT made read-only after startup # partial only: omit '-z now' -> .got.plt stays writable for lazy binding
Full RELRO (-z relro -z now) resolves all symbols at startup, then makes the entire GOT read-only so an attacker cannot overwrite a GOT entry to redirect a call.
Partial RELRO protects only .got and leaves .got.plt writable to allow lazy binding; full RELRO disables lazy binding to protect the whole GOT, at a small startup cost. RELRO hardens the GOT but is one layer among many — it is not a complete defense by itself.