dynamic linking and the dynamic linker
When a program uses a shared library, the job of actually connecting the program's calls to the library's code is not finished when you compile — it is finished when the program starts running. Dynamic linking is that idea: the final wiring between a program and its shared libraries happens at load time (just before main runs) or even later, at run time, rather than being baked in by the regular linker at build time.
The piece of software that does this is the dynamic linker (also called the dynamic loader, ld.so on Linux). When you launch a program, the operating system notices it depends on shared libraries, and hands control to the dynamic linker first. The dynamic linker reads the list of libraries the program needs, searches the system for those .so/.dylib/.dll files, maps them into the process's memory, and patches each unresolved call in the program so it points at the right function inside the loaded library. Only then does your main run. Some symbols can even be resolved lazily — wired up the first time they are actually called rather than all up front.
Why it matters: dynamic linking is what makes shared libraries possible, and it is happening invisibly for almost every program you run. It also means a program's behavior can change without recompiling it, just by swapping the library it loads — powerful, but also a source of bugs and security concerns if the wrong library is found. The common failure is the dynamic linker not finding a needed library and aborting the launch with a 'cannot open shared object file' error.
See which shared libraries a program will load, and where they are found: $ ldd ./app libutil.so => ./libutil.so (0x00007f...) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 The dynamic linker resolves these at launch, before main runs.
ldd shows the dynamic linker's resolution of each shared library the program needs.
The dynamic linker is distinct from the regular (static) linker that runs at build time. The build-time linker records which libraries are needed; the dynamic linker actually finds and connects them when the program runs.