Dynamic linking is the mechanism that lets multiple programs share the same library code at runtime. Instead of embedding a copy of printf in every binary, the linker leaves a placeholder that gets resolved when the program runs.
Why Shared Libraries?
Code Sharing
One copy of libc in memory serves every running program. Hundreds of processes can share the same physical pages of library code.
Smaller Binaries
Executables only contain their own code. A dynamically-linked hello world is often 10-100x smaller than its statically-linked counterpart.
Easy Updates
Fixing a bug in a shared library instantly benefits every program that uses it, without recompilation.
PLT and GOT — The Indirection Mechanism
Two data structures work together to make dynamic linking possible:
PLT (Procedure Linkage Table)
A table of small code stubs, one per external function. When your code calls printf, it actually calls printf@plt, which is a trampoline that reads the GOT to find the real address.
GOT (Global Offset Table)
A writable table of function addresses. Initially, each entry points back into the PLT. After resolution, it holds the real address of the function in the shared library.
Lazy Binding in Action
PLT/GOT Lazy Binding
The 5-Step Resolution Process
Your code calls the PLT stub instead of the real function.
The PLT stub does an indirect jump through the GOT. On first call, the GOT entry points back to the next PLT instruction.
The PLT pushes a relocation index and jumps to PLT[0], which calls the dynamic linker's resolver.
The dynamic linker looks up printf in libc, finds its address, and overwrites the GOT entry.
Now the GOT entry holds the real address. The PLT stub jumps straight to libc's printf, bypassing the resolver.
.dynamic — Library Dependencies
The .dynamic section is an array of tag-value pairs that tell the dynamic linker everything it needs. Key entries include:
| Tag | Purpose |
|---|---|
| DT_NEEDED | Shared library dependency (e.g., libc.so.6) |
| DT_STRTAB | Address of the dynamic string table |
| DT_SYMTAB | Address of the dynamic symbol table |
| DT_PLTGOT | Address of the GOT |
| DT_JMPREL | Address of PLT relocation entries |
.rela.plt and .rela.dyn — Relocation Entries
Relocation entries tell the dynamic linker which GOT slots to patch and how. .rela.plt handles function calls through the PLT (R_X86_64_JUMP_SLOT), while .rela.dyn handles data references like global variable addresses (R_X86_64_GLOB_DAT).
After lazy resolution, what does the GOT entry contain?