Linking & trust

Linking & Loading

Composing object files into something runnable, resolving what the compiler could not know, and handing the result to an operating system loader.

What a Linker Does
▶ lab

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.

Q · What happens between my `.o` files and an executable I can actually run?
Object Files
▶ lab

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.

Q · What is inside a `.o` file, and why is `.bss` free?
Symbols and References
▶ lab

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.

Q · What do the letters in `nm` output mean, and why does `undefined reference` appear for a function I definitely wrote?
Relocations
▶ lab

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.

Q · How does a `call` with four zero bytes in it turn into a working call, and what are the GOT and the PLT for?
Static Linking
▶ lab

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.

Q · What do I actually get by linking statically, and what am I giving up?
Dynamic Linking
▶ lab

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`.

Q · What actually happens at load time when my program uses a shared library, and why do I get `GLIBC_2.34 not found`?
Shared Libraries
▶ lab

`.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.

Q · What is in a `.so`, what does the soname mean, and why is `-fvisibility=hidden` recommended?
Symbol Resolution Order
▶ lab

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.

Q · When two loaded libraries define the same symbol, which one wins — and who decides?
The Loader
▶ lab

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.

Q · What actually runs between `exec` and the first line of `main`?