For the complete documentation index, see llms.txt. This page is also available as Markdown.

From C++ Source Code to Execution

What Happens at Every Stage

From C++ Source Code to Execution: What Happens at Every Stage

One of the best ways to understand C++, operating systems, reverse engineering, and exploit development is to understand what actually happens between writing a .cpp file and the CPU executing your program.

Consider the following program:

#include <iostream>

#define PI 3.14

int square(int x) {
    return x * x;
}

int main() {
    int a = 5;
    std::cout << square(a) << std::endl;
}

Although it looks simple, this program passes through multiple stages before it runs.


The Complete Pipeline

Each stage has a completely different responsibility.


Stage 1 - Preprocessing

Command:

What is the Preprocessor?

The preprocessor is not the compiler.

Instead, it performs simple text processing before compilation begins.

It handles directives such as:

For example:

becomes

Notice that the preprocessor simply replaces text.

It does not understand C++.


Header Expansion

Suppose your program contains:

The preprocessor literally opens the iostream header and copies its contents into your file.

That header itself includes other headers.

Those headers include more headers.

Eventually your tiny program may expand into tens of thousands of lines.

Conceptually:


Comment Removal

Comments are removed completely.

The compiler never sees comments.


Conditional Compilation

The preprocessor can also include or exclude parts of your program.

If DEBUG is not defined, this code simply disappears.


Output

The result is:

This is still C++ source code, but with:

  • Headers expanded

  • Macros replaced

  • Comments removed

  • Conditional compilation completed


Important

The preprocessor does not understand:

  • Variables

  • Functions

  • Classes

  • Templates

  • Objects

It is essentially a sophisticated text replacement engine.


Stage 2 - Compilation

Command:

Now the actual compiler begins.

This is where the compiler understands the C++ language.

Internally, several important steps occur.


Parsing

Suppose you write:

The compiler no longer sees plain text.

Instead, it constructs an Abstract Syntax Tree (AST).

Conceptually:

This structured representation makes it easier for the compiler to analyze your program.


Semantic Analysis

The compiler verifies whether your program is valid.

Examples:

Valid:

Invalid:

Invalid:

from an int function.

This stage checks:

  • Types

  • Function signatures

  • Scope

  • Variable declarations

  • Language rules


Optimization

When optimization is enabled , the compiler attempts to improve performance.

Example:

can become:

Small functions may also be inlined.

Instead of:

the compiler may directly generate:

without making a function call.


Code Generation

Finally, the compiler converts your C++ program into assembly language.

Example:

might become:

The exact instructions depend on your CPU architecture.


Output

The compiler produces:

This is human-readable assembly language.


Stage 3 - Assembly

Command:

Now the assembler takes over.

The assembler converts assembly instructions into machine code.

For example:

becomes binary instructions similar to:

The CPU only understands binary machine instructions.

It does not understand:

  • C++

  • Variables

  • Functions

  • Classes

  • Assembly mnemonics


The Object File

The assembler creates:

An object file contains much more than machine code.

Typical sections include:

.text

Machine instructions.


.data

Initialized global variables.

Example:


.bss

Uninitialized global variables.

Example:


.rodata

Read-only data.

Example:

String literals usually live here.


Symbols

Suppose you call:

but the implementation exists in another file.

The object file records:

I need a function named square.

It does not know where that function lives.

That responsibility belongs to the linker.


Stage 4 - Linking

Command:

The linker combines everything together.

Imagine two files:

main.cpp

math.cpp

Each source file is compiled independently.

The linker connects them.

Conceptually:

Static vs Dynamic Linking


Libraries

Your program also uses:

Where is it defined?

Not inside your source code.

It lives inside the C++ Standard Library.

The linker connects your executable with that library.

Without linking you would see errors such as:


Relocation

During compilation, the compiler does not know where functions will be placed.

For example:

The linker eventually decides that:

and updates every function call accordingly.

This process is called relocation.


Output

The linker produces:

A complete executable.


Stage 5 - Program Execution

Command:

Now the operating system takes control.


The Loader

The operating system loads your executable into memory.

Different sections are mapped into different memory regions.


Runtime Initialization

Before main() executes, the C++ runtime performs initialization.

This includes:

  • Global variables

  • Static variables

  • Global constructors

  • Exception handling

  • Heap initialization

  • Thread-local storage

  • Standard library initialization

Only after this does your program reach:


CPU Execution

The CPU repeatedly performs the following cycle:

For example:

The CPU executes one instruction after another.

At this point, there is no C++ anymore.

Only machine instructions remain.


Putting Everything Together

Human-readable C++ source code.

Preprocessed source code.

Headers expanded.

Macros replaced.

Comments removed.

Assembly generated by the compiler.

Machine code with:

  • Symbols

  • Relocations

  • Metadata

  • Debug information (optional)

Fully linked executable.

Creates a process.

Maps memory.

Loads required libraries.

Initializes the runtime.

Execution begins.


Why This Matters

Understanding each stage is essential for systems programming and cybersecurity.

Stage
Why It Matters

Preprocessor

Macro expansion, conditional compilation, source-level obfuscation

Compiler

Optimizations, debugging behavior, code generation

Assembler

Relationship between assembly and machine code

Object Files

Symbols, sections, relocations used during reverse engineering

Linker

Static vs dynamic linking, imports, exports, relocation

Loader

Process creation, virtual memory, ASLR, DEP/NX

Runtime

Stack initialization, heap setup, global constructors, transition into main()

Last updated