A linker is a program that combines one or more object files and library references into a single executable by resolving the symbols one object references against the symbols another defines. It runs after the compiler has translated each compilation unit into object code and before the operating system loader turns the executable into a running process.
Static vs Dynamic Linking
Two ways to satisfy library references
Static link — the needed library code is copied into the executable at link time. The resulting binary is self-contained and does not depend on any external libraries at runtime, at the cost of larger files and duplicated code across executables.
Dynamic link — the executable keeps only references to system libraries; those libraries are loaded and the references resolved at runtime by the dynamic linker. The binary is smaller and shares a single in-memory copy of each library across all processes that use it, but it depends on the system having the libraries present.
Inspect a dynamically linked binary’s runtime dependencies with ldd: it shows the shared objects the dynamic linker will load.
Lifecycle of a Compiled Program
Where the linker sits
source code --> compiler --> object file --> linker --> executable --> OS loader --> running process
gcc -c hello.c produces hello.o (a relocatable object file holding machine code whose library references are still unresolved);
gcc -o hello hello.o runs the linker, producing the hello executable (statically self-contained, or carrying references the loader will resolve dynamically);
invoking ./hello hands it to the OS loader, which maps it into a process image and starts execution.
Object files and executables are both stored in the ELF object file format.