Question
What are the stages a C program goes through from source code to executable?
Answer
Compile each .c to assembly, assemble that to a .o object file, then link all the objects plus the C runtime into one executable.
* Each source file travels its own compile → assemble path to a .o; the linker fuses all objects plus the C runtime into the final executable. *
The compilation pipeline:
| Stage | Tool | Input | Output |
|---|---|---|---|
| 1. Compilation | cc1/cpp | .c file |
.s (assembly) |
| 2. Assembly | as | .s file |
.o (relocatable object) |
| 3. Linking | ld/collect2 | .o files + libraries |
executable |
Key insight: Each .c file is compiled independently to its own .o file. The linker combines all .o files plus the C runtime library (CRT) into the final executable.
# View the full compilation process
$ gcc -v -O2 -Wall -g -o prog main.c swap.c
Tip: GCC is actually a "compiler driver" - it orchestrates calls to the preprocessor, compiler, assembler, and linker.
Go deeper:
GCC — the compiler driver (Wikipedia) — how one
gcccommand chains preprocessor, compiler, assembler and linker.Ian Lance Taylor — Linkers, part 1 — start of a linker author's classic 20-part deep dive into what happens after compilation.
Note saved — thanks!
Question
What is the role of the dynamic linker (ld-linux.so) in program execution?
Answer
It loads the executable and its shared libraries into memory, then resolves every symbol and relocation before main runs.
* What the dynamic linker builds: code and read-only data map low, writable data and heap above, shared libraries in the middle, and the stack at the top. *
The kernel actually launches the dynamic linker first (its path is recorded in the ELF header); the linker then maps in the program and its .so dependencies and patches everything up so the code can find the functions and data it calls.
The dynamic linker handles:
- Load the executable into memory
- Find required shared libraries (.so files)
- Load shared libraries into the process address space
- Resolve symbols - connect function calls to their implementations
- Perform relocations for position-independent code
Process memory layout after dynamic linking:
| Region | Contents |
|---|---|
| Kernel virtual memory | OS kernel space |
| User stack | Function call frames |
| Memory-mapped region | Shared libraries (.so) |
| Run-time heap | malloc allocations |
| Read/write segment | .data, .bss |
| Read-only segment | .init, .text, .rodata |
Tip: The dynamic linker itself is specified in the ELF header and loaded by the kernel before the program starts.
Go deeper:
ld.so(8) — dynamic linker/loader man page — the authoritative reference for how ld-linux.so finds, loads and binds shared objects.
LWN — How programs get run: ELF binaries — what the kernel does on
execve, and how it hands off to the dynamic linker.Dynamic linker (Wikipedia) — load-time linking across ELF, Windows and macOS.
Note saved — thanks!