Analyzing Binary Dependencies and Symbols: What nm and objdump Actually Tell You
Understanding how programs find and connect to their dependencies at runtime is one of those skills that separates someone who can write code from someone who can debug production systems. When your program crashes with “symbol not found” or mysteriously behaves differently in production than it did on your laptop, you need to understand what’s happening at the binary level.
This guide will walk you through the tools and techniques that professional systems engineers use to debug these issues. We’ll start with the theory, then get our hands dirty with real code.
Why Your Binary Crashes at Runtime (Not Compile Time)
Dynamic linking defers symbol resolution to runtime. Your code compiles perfectly because the linker only checks that symbols exist in the shared libraries you link against. But at runtime, ld.so has to actually find and bind those symbols. That’s where things fall apart.
I once deployed a service that worked everywhere except one production cluster. Turns out we had two libraries both providing weak definitions of
operator new. Load order differed between environments - staging loaded libA first, production loaded libB first. Runtime picked different implementations. Five hours of debugging later,nm -Cshowed both libraries exportingW operator new(weak binding). The fix? Make our override strong (remove__attribute__((weak))).


