Lazy vs. Immediate Binding: Performance Tradeoffs in Dynamic Linking
Your service starts up in 50ms, passes health checks, and begins handling requests. Then the first database connection hangs for 300ms. You profile the code and find _dl_runtime_resolve() at the top of your flamegraph. Welcome to the hidden cost of lazy symbol binding.
The Two-Phase Reality of Function Calls
When your C program calls
printf(), you think it’s a direct jump. It’s not. That call goes through the PLT (Procedure Linkage Table), which reads an address from the GOT (Global Offset Table), then jumps to that address. On first call with lazy binding, the GOT entry points back to a PLT stub that triggers the dynamic linker’s resolver. This resolver searches through symbol tables, performs relocations, writes the real address to the GOT, then jumps to the actual function. Subsequent calls skip the resolver and jump directly.


