Linking & Loading
Composing object files into something runnable, resolving what the compiler could not know, and handing the result to an operating system loader.
Object files and libraries in, one runnable image out. Four jobs: combine sections, resolve symbols, lay out an address space, and patch every reference that could not be resolved until the layout existed.
What is actually in a `.o`: sections holding code and data, a symbol table saying what is defined and what is needed, relocation records saying which bytes to patch, and debug metadata. `.bss` occupies no bytes in the file at all, and understanding why explains the whole format.
Defined, undefined, global, local, weak — five categories that decide every link outcome. And how to actually read `undefined reference to 'foo'`, which has four common causes and names none of them.
The compiler emits a zero and a note saying "this is an address, fix it later". The linker patches it once layout exists. Absolute versus PC-relative decides whether the code can be loaded anywhere — which is what position-independent code, the GOT and the PLT are all about.
Copy the library into the binary. One file to deploy, no runtime dependency, no version skew — paid for in binary size and in having to relink and redeploy for every library fix, including a security fix.
Leave the library out and bind to it at load time. One copy in memory serves every process, and a security fix ships as one file — paid for in load-time resolution, version skew, and `GLIBC_2.34 not found`.
`.so`, `.dll`, `.dylib` — one artifact, three platforms, three different policies. The soname is the compatibility promise, and exporting everything by default is the mistake that makes a library slow to load and impossible to change.
When several objects define the same name, the loader picks one, and the rule is positional rather than semantic. `LD_PRELOAD` weaponises that deliberately, which makes the search path and the scope order a real security surface.
From `exec` to the first instruction of `main`: the kernel maps the image, hands control to the dynamic loader, which maps libraries, applies relocations and runs initializers. `main` is not the first code to run, and a program can fail before it.