Skip to content

Concepts

general

GOT & PLT (dynamic linking)

How ELF programs call functions in shared libraries: the Procedure Linkage Table trampolines, the Global Offset Table holds resolved addresses, and lazy binding fills them in on first call.

When an ELF program calls a function from a shared library (printf, malloc, …), it doesn't know that function's address at link time — the library could be loaded anywhere (see PIE & ASLR). Two tables bridge the gap:

  • GOTGlobal Offset Table: a writable array of data slots that, at runtime, hold the resolved absolute addresses of external symbols.
  • PLTProcedure Linkage Table: a block of tiny code stubs (trampolines), one per imported function, that jump through the GOT.

What a call to an imported function looks like

The compiler emits a call to the PLT stub, not to the real function:

asm
call printf@plt       ; call the trampoline, not libc directly

The stub itself is essentially an indirect jump through the function's GOT slot:

asm
printf@plt:
    jmp QWORD PTR [rip + printf@GOTPCREL]   ; jump to whatever the GOT holds
    ; (first-call resolver path follows)

Lazy binding

By default the GOT slot for a function does not start with its real address. Instead it points back into the PLT resolver, so that on the first call the dynamic linker (ld.so) looks up the symbol, writes the real address into the GOT slot, and jumps to it. Every subsequent call reads the now-patched slot and goes straight to the function. This "resolve on first use" is lazy binding; LD_BIND_NOW=1 / full RELRO resolves everything up front instead.

Reverse-engineering & security notes

  • call something@plt tells you instantly that something is an imported library function — a fast way to find I/O, crypto, networking and anti-debug calls without analysing library internals.
  • The GOT is writable, which historically made it an exploitation target: overwrite a GOT entry (e.g. free's) and the next call jumps to attacker-controlled code — the GOT overwrite. The RELRO mitigation (-z relro -z now) maps the GOT read-only after startup to stop this; check with readelf -l / checksec.
  • A leaked GOT entry also leaks a libc address, defeating ASLR for the library — the start of many ret2libc chains.
  • The Windows analogue is the IAT (Import Address Table) reached via the import directory; the concept — a patched table of resolved imports — is the same.

See also: PIE & ASLR · CALL / RET · Memory protection (NX / W^X).