Language Processing System in Compiler Design: Preprocessor to Loader with a Worked Trace
Follow one macro-containing C program through every language processor. The trace shows object-file relocation, a linker patch of +4, and the final value 7.
KnowledgeGate Team
Exam prep & CS education

A compiler is often shorthand for everything between source and execution. That makes the other processors easy to confuse. Each processor has a distinct input, output, and responsibility in the two-file C example, whose linker patches a +4 displacement before the program returns 7. Compiler Design: Six Phases Traced in One Example develops the compiler's internal analysis and synthesis phases; the surrounding toolchain begins with preprocessing and continues through object-file relocation, linking, loading, and execution.
Language processing system: the complete source-to-execution pipeline
A language processing system comprises cooperating programs that transform source text into an executable image and place it in memory. The canonical teaching flow is:
source program -> preprocessor -> modified source -> compiler -> target assembly -> assembler -> relocatable object code -> linker plus library/object files -> executable -> loader -> running memory image
Real toolchains may combine steps, but the responsibilities remain distinct. For the broader exam-preparation map, use GATE CS Exam Preparation.
Program | Input | Output | Job |
|---|---|---|---|
Preprocessor | Source text | Expanded source | Expands directives |
Compiler | Expanded source | Target assembly or object-level code | Analyses meaning and generates code |
Assembler | Assembly | Relocatable object | Encodes instructions and records relocations |
Linker | Object files and libraries | Executable | Resolves symbols and patches references |
Loader | Executable | Memory image and entry point | Maps and starts the executable |

Preprocessor: expand directives before translation
The example has two translation units. main.c contains a macro and an external declaration:
#define SCALE 4
extern int add(int, int);
int main(void) {
return add(SCALE, 3);
}add.c supplies the definition:
int add(int a, int b) {
return a + b;
}For the relevant part of main.c, preprocessing produces:
extern int add(int, int);
int main(void) {
return add(4, 3);
}Substitution changes token text from SCALE to 4. It does not calculate 4 + 3, resolve add, or allocate an address. A preprocessor also expands include directives and selects conditional-compilation branches.
Compiler: analyse meaning and generate target code
The compiler is one pipeline component. Lexical analysis identifies tokens and lexemes, while syntax analysis builds structure through parsing. Semantic analysis checks declarations and types. Intermediate representation, optional optimisation, and target-code generation follow.
In this teaching ISA, every instruction is 4 bytes. Registers are R0 and R1, arguments use R0 then R1, and a returned integer remains in R0. Expanded main becomes:
main:
MOV R0, 4
MOV R1, 3
CALL add
RETThe other unit becomes add: ADD R0, R0, R1; RET. These toy instructions are not bytes from a named processor.
The compiler type-checks add(4, 3) and generates symbolic CALL add. It cannot assign a final address while the files remain separate translation units.
Assembler: create object code, symbols, and relocation records
The assembler creates a 16-byte .text section for main:
Offset | Instruction |
|---|---|
|
|
|
|
|
|
|
|
Its symbols are main: defined at .text+0x0000 and add: undefined external. One relocation says to patch the PC-relative call operand at .text+0x0008 for add, measured from the next instruction.
For add, the 8-byte .text section is 0x0000 ADD R0,R0,R1, then 0x0004 RET. Its symbol table defines add at .text+0x0000; no relocation is needed here.
main.o remains valid with unresolved add. A relocatable object is not an executable. The assembler translated mnemonics and recorded linker work; it loaded nothing into memory.
Linker and loader worked example: patch +4, then run
The linker places the 16-byte main.o section at 0x00400000 through 0x0040000F, then the 8-byte add.o section at 0x00400010 through 0x00400017. Therefore:
main = 0x00400000add = 0x00400010
CALL begins at 0x00400008. Its 4-byte size makes the next address 0x00400008 + 0x4 = 0x0040000C. Relocation uses a displacement from that next PC:
target - next PC = 0x00400010 - 0x0040000C = 0x00000004 = +4
The linker patches +4. Answers 0x00400010 and +8 are wrong because this relocation stores neither an absolute address nor a displacement from the start of CALL.
The loader maps the executable at the stated virtual addresses and sets PC = 0x00400000. Execution now proceeds step by step:
MOV R0, 4makesR0=4.MOV R1, 3makesR1=3.CALL +4transfers control from the next PC,0x0040000C, to0x00400010.ADD R0, R0, R1computes4 + 3, soR0=7.RETreturns throughmain, whose final returned integer is7.

Language processor errors and traps that mix the stages up
An error belongs to the first stage unable to continue.
Example | Responsible stage | Reason |
|---|---|---|
| Preprocessor | Requested source text cannot be included |
| Compiler semantic analysis | Identifier is undeclared |
| Assembler |
|
| Linker | External symbol remains undefined |
Executable segment cannot be mapped | Loader | The executable exists but cannot be placed into the process image |
Remember four distinctions. The compiler is one component, not the pipeline. Object code may contain unresolved symbols. The linker creates an executable, not source analysis. The loader maps that executable, not C grammar.
Static linking resolves these references before loading. A dynamic loader may perform runtime symbol work, but the fully linked model keeps the displacement calculation unambiguous.
Language processing system questions: four reasoning patterns
Use four miniature checks:
Reorder
loader, assembler, preprocessor, linker, compiler. The answer ispreprocessor -> compiler -> assembler -> linker -> loader.Which program produces a relocation record, and which consumes it? The assembler produces it; the linker consumes it.
What displacement is patched into the worked
CALL? It is+4because0x00400010 - 0x0040000C = 0x4.Where does an undefined external
addbecome an error? At the linker, if no supplied object or library defines it.
Contrast question: main.o contains a symbolic call to add; is that an error? No. It carries an undefined symbol and relocation, becoming erroneous only if the linker finds no definition in supplied objects or libraries.
The short version: track the artefact, then choose the program
Track the artefact. Directives mean preprocessor; grammar, types, and code generation mean compiler; mnemonics to relocatable object mean assembler; cross-object symbols and relocations mean linker; executable into memory and entry point mean loader.
Keep SCALE=4, second argument 3, add=0x00400010, relocation +4, and returned value 7 fixed. For the broader Compiler Design sequence, GATE Guidance by Sanchit Sir is the next step. If you needed only this distinction, stop after reproducing the artefact table and +4 calculation unaided.
Keep learning

Intermediate Code Generation in Compiler Design: TAC, Backpatching and DAGs
Connect expressions, short-circuit control flow and local optimisation through a single worked translation, from source code to resolved TAC and a reusable DAG.

Ambiguous Grammars and Inherent Ambiguity: Worked CFG Examples for GATE
Learn what two derivations really prove, resolve expression ambiguity with precedence, and trace the classic inherently ambiguous language through aabbcc.

Simplification of CFG: Remove Epsilon, Unit and Useless Productions Step by Step
Simplify one context-free grammar from nine nonterminals to six. See each intermediate grammar, complete unit closures, symbol checks and final derivations.

Phases of Compiler Explained: Worked Example from Tokens to Target Code
Follow one four-line program through lexical, syntax and semantic analysis, then see its intermediate code optimized to a final stored value of 24.0.