Type a branch, a subject or a topic — “round robin”, “paging”, “civil”.

The Language Processing System

Preprocessor, compiler, assembler, linker, loader — five tools, and the file each one hands to the next.

Skip to the animation

Typing one compile command runs five separate programs in sequence — preprocessor, compiler, assembler, linker and loader — each consuming a specific file the previous one produced.

Five tools, five files

gcc hello.c looks like invoking a compiler. It is invoking a driver that runs a pipeline. Learning it as the files rather than the tools is what makes error messages readable.

#ToolInOut
1Preprocessorhello.chello.i — expanded source
2Compilerhello.ihello.s — assembly text
3Assemblerhello.shello.o — object file
4Linkerhello.o + librariesa.out — executable
5Loadera.outa running process

What each one actually does

Preprocessor
Pure text substitution — it does not understand C at all. #include pastes in a whole file, #define substitutes macros, #ifdef keeps or deletes branches. This is why a macro can produce an error inside a header you never opened: the compiler only ever sees the result.
Compiler
The entire rest of this subject lives here: lexical analysis, parsing, semantic analysis, intermediate code, optimisation, code generation. Its product is assembly source — readable mnemonics, not binary.
Assembler
Assembly maps almost one-to-one onto machine instructions, so this is a lookup rather than a translation. It emits an object file: real machine code, plus a relocation table naming every symbol it could not resolve.
Linker
Collects every object file and library, builds one table of which symbol is defined where, and patches each unresolved reference with a real address.
Loader
Belongs to the operating system. Allocates an address space, copies code and data in, sets up stack and heap, resolves dynamic libraries and jumps to the entry point.

Why an object file has holes in it

When the assembler reaches call printf, it has never seen printf — it is in a library compiled long ago. It cannot invent the address, so it emits the instruction with the operand left blank and records an entry in the relocation table saying "whoever links this, put the address of printf here".

This is the whole explanation of undefined reference to 'printf'. Compilation succeeded; the linker simply found nothing defining that name. It is never a syntax problem, and adding a header will not fix it — a header declares, a library defines.

Static versus dynamic linking

Static (.a, .lib)Dynamic (.so, .dll)
Library codecopied into the executablereferenced by name
Executable sizelargersmaller
Resolvedat link timeat load time, every run
Library security fixrequires a rebuildapplies system-wide immediately
Runs on a machine without the libraryyesno — startup fails
Memory with many processesone copy eachone copy shared

"Cannot open shared object file" and "missing DLL" are the loader failing at step 5, on a program that built and linked perfectly. The distinction above is the entire explanation.

Reading errors by which file they mention

  • Something expanded strangely, or an error inside a system header → preprocessor, look at your macros.
  • Syntax error, type error, undeclared variable → compiler.
  • undefined reference / unresolved external symbollinker, you are missing a library or a definition.
  • multiple definitionlinker, the same symbol defined in two object files.
  • Builds fine, fails to start with a missing library → loader, dynamic linking.

Watch it work

loading visualisation…

Check yourself

question 1 / 4

One question at a time. Pick an answer to see why it is right or wrong, then move on — there is no score to keep and nothing is saved.

undefined reference to 'sqrt' — which tool produced this, and what does it mean?
What does the assembler actually emit?
Which of the five tools runs every time you launch the program?
Static versus dynamic linking: which statement is right?

0 / 4

4 still unanswered — the dots above jump straight to them.