Foundations — Compilation stages — preprocessing, compilation, assembly, linking
Why this page exists
The parent note throws around words like translation unit, symbol table, relocation, object file, register, and ABI as if you already own them. Here we build each one from zero, in the order they stack on top of each other. Nothing below assumes you have seen assembly, compilers, or even what a "file" truly is. Each concept is introduced before any later concept uses it.
The four stages, named once and for all
Before any symbol, fix the four stages the parent note describes, so every later "Stage N" reference below is unambiguous. Each stage is a tool that reads one file and writes a more machine-like one:
Keep this list handy: whenever a note below says "Stage 3" it means assembly, and "Stage 4" means linking.
Symbol 0 — What is a "file", "source code", and a "file extension"?

hello.c, hello.i, hello.s, hello.o, hello, each with a small italic subtitle (text, pure C, assembly, bytes, runs!). Four labelled arrows connect them left to right: "preprocess", "compile", "assemble", "link". A caption underneath reads "Same program the whole way — only its 'clothes' change."
Why the topic needs it: the whole chapter is literally "what does each rewrite do, and what extension marks its output". If you do not see the file as the same program in different clothes, the pipeline looks like four unrelated tools.
Symbol 1 — A "token"
Why the topic needs it: the compiler's very first internal step (lexing in the parent note) is "turn text into tokens, dropping whitespace and comments". Also the preprocessor works by replacing tokens with other tokens (that is exactly what #define PI 3.14 does — swap the token PI for the tokens 3.14).
Symbol 2 — A "directive" and the # sign
The picture: imagine your program text with some lines highlighted. The highlighted (#) lines are editing instructions; everything else is the actual program. Stage 1 (preprocessing) obeys the highlighted lines, then deletes them, leaving only program.
Why the topic needs it: the parent's entire Stage 1 (preprocessing) is "handle every # line and remove it". Recognising # as the preprocessor's marker — even when indented or split across lines with \ — is the key to the whole first stage. See Preprocessor macros and include guards for how these directives are used in practice.
Symbol 3 — A "translation unit" (and what .i holds)
The picture: your small .c file swells up because #include <stdio.h> pastes hundreds of lines of stdio.h into it. That swollen result — one big self-contained slab of C — is one translation unit, and that is exactly what a .i file contains.

hello.c (small) and a butter-yellow stdio.h (big). Two coral arrows labelled "pasted" point right into one large mint box titled "translation unit (.i)". A dashed line inside the big box separates an upper region labelled "hundreds of lines from stdio.h" from a lower region labelled "your few lines of hello.c". Caption: "No more '#' lines remain — this slab is what the compiler reads."
Why the topic needs it: it explains the parent's mistake-buster "header files are not compiled separately". Headers get pasted into a translation unit; only the unit (the .i) is compiled. One unit → one object file.
Symbol 4 — The "compiler" and the "assembler" as two distinct tools
Before we look at .s bytes, name the two tools that produce them, because the parent note treats them separately (Stage 2 and Stage 3).
The picture: the compiler is the author who writes the recipe in chef-shorthand (assembly); the assembler is the typesetter who converts that shorthand, symbol by symbol, into the exact byte codes the machine reads. gcc -S stops after the author; gcc -c runs the author and the typesetter.
Why the topic needs it: the parent enumerates Stage 2 (compile → .s) and Stage 3 (assemble → .o) as distinct. Confusing them makes it impossible to explain why .s is readable text but .o is raw bytes.
Symbol 5 — A "register", eax, and what .s holds
The picture: if normal memory (RAM) is a huge warehouse, a register is a coin held in the CPU's hand — instant to use, but you only have a few hands. The .s file is a step-by-step script that shuffles coins between hands and the warehouse.
Why the topic needs it: the parent's assembly example mov eax, 42 means "put the number 42 into the register named eax". The comment "return value goes in eax" is a convention — which brings us to the next symbol.
Symbol 6 — "ABI", "calling convention", and "stack slot"
The picture: two people who never met can still shake hands because both learned the same custom. The ABI is that custom for machine code: "the return value always rides home in eax", so any caller knows where to look.
Why the topic needs it: it explains why the compiler emitted eax and not some other slot, and it is the invisible glue that lets the linker join code compiled at different times. Deeper detail lives in ABI and calling conventions.
Symbol 7 — A "symbol"
The picture: every object file carries a two-column list — one column "names I provide", one column "names I still need". That list is the symbol table.

hello.o, split into two columns. Under the heading "I PROVIDE" sits a lavender chip labelled main. Under "I NEED" sit two coral chips labelled printf and helper?. On the right, a butter-yellow box titled libc contains a lavender chip printf. A "resolved" arrow connects the needed printf to the printf inside libc. Red text notes: "helper found nowhere -> 'undefined reference'."
Why the topic needs it: the linker's whole job is matching every "I need X" to some "I define X". The famous undefined reference to 'foo' means: some file needed foo, no file defined it. See Object files and symbol tables.
Symbol 8 — An "address" and a "placeholder / relocation"
The picture: a form with Call ______ where the blank will hold printf's real address once the linker discovers it.
Why the topic needs it: this is why a .o file "cannot be run" (parent's Stage 3, assembly). It still contains blanks. Only after the linker fills every blank (relocation) do you get a runnable executable.
Symbol 9 — The "linker", "object file" vs "executable", "static" vs "dynamic"
Why the topic needs it: these are the two flavours of Stage 4 (linking). Knowing what the linker fills in makes the static/dynamic split obvious — it is just "copy now" versus "look up later".
Symbol 10 — What "incremental build" hints at
How these foundations feed the topic
Read the map below top to bottom: an arrow X --> Y means "you need X before Y makes sense". Start at Source text and file extension (top-left) — it feeds Tokens, which feed Directives and the Translation unit. The translation unit plus the ABI (built from the register and stack slot ideas) feed the Compilation stage (Stage 2), whose output the Assembly stage (Stage 3) turns into an object file carrying Symbols. Symbols gather into a Symbol table; that plus Addresses and their Relocation placeholders feed the Linking stage (Stage 4). Linking yields the object-file-vs-executable distinction and the static/dynamic choice. Every path eventually converges on the single node Preprocess compile assemble link pipeline — the parent topic itself, i.e. the full four-stage flow.
Equipment checklist
How to use this: cover the right side of each line with your hand. Read the prompt, say your answer out loud, then uncover to check. If you miss any item, re-read that section before moving to the main topic — you are not ready until you clear all fourteen.
Recite the four pipeline stages in order, each with its tool
Explain what a "file" is and what it means for a program to "live in" one
hello.cRecite the four output extensions in pipeline order
.c → .i → .s → .o → executableState what a token is AND what is not a token
Give two real-world subtleties of a # directive
# is allowed; a \ at line end continues the directive onto the next lineSay what a .i file contains and how it differs from .c
# lines/comments removed — bigger and cleaner than the .cState how the compiler and the assembler differ
Say what a .s file contains and how it differs from .o
.o is the same meaning as raw bytesDefine a register and name the x86 return register
eax carries the return valueDefine a "stack slot" and when it is used
State in one line what an ABI decides
Distinguish a defined vs undefined symbol
Explain what a relocation placeholder is and who fills it
Say in one line what the linker does and when it runs
Explain why a .o cannot run
Contrast static vs dynamic linking in one line
Connections
- Parent (Hinglish)
- Build Systems & Toolchain
- Preprocessor macros and include guards
- Object files and symbol tables
- ABI and calling conventions
- Static vs Dynamic Linking
- Make and incremental builds