12 min read Foundations Lesson 03 of 12
Course 02 · Part 1 — The Foundations of Software

From Source Code to Running Program

What actually happens behind the scenes when you press "Run"? How do a few lines of plain text in an editor turn into a living, interactive application?

The Thrilling Journey Behind Pressing "Run"

In the previous lesson, we saw that a CPU cannot directly execute high-level programming language code. That leads to a fundamental question: When I write code in my editor and click Run, what actually takes place under the hood?

Consider a simple program written in C:

#include <stdio.h>

int main() {
    printf("Hello World\n");
    return 0;
}

Before the phrase "Hello World" ever paints across your terminal or display, your computing system orchestrates a sophisticated sequence of transformations.

The Complete Transformation Pipeline

From high-level source text to hardware execution on silicon, the entire pipeline can be summarized as follows:

Source Code (.c)
→
Preprocessor (.i)
→
Compiler (.s)
→
Assembler (.o)
→
Linker (Binary)
→
OS & CPU (Run)

1. Source Code — Human Intent

Everything begins with Source Code. This is human-readable text authored by programmers describing logical instructions:

int a = 10;
int b = 20;
int result = a + b;

Any developer easily understands what is happening here. But physical CPU hardware has no concept of variable names, semicolons, or data types like int. The code must be progressively translated.

2. What Does a Compiler Actually Do?

A Compiler is a sophisticated software program that deeply analyzes high-level source code and translates it into a lower-level format tailored to a target computer architecture.

Core Responsibilities of a Modern Compiler:

  • Lexical & Syntax Analysis: Verifies that code strictly adheres to grammatical rules and language grammar.
  • Type Checking: Validates operations and identifies type inconsistencies across expressions.
  • Optimization: Eliminates dead code, rearranges operations, and restructures loops for maximum speed and minimal memory footprint.
  • Instruction Generation: Maps programming constructs to instructions for the target processor architecture.

3. The First Stage — Preprocessing

In compiled languages such as C and C++, the compiler invokes a Preprocessor before performing lexical or syntax analysis.

Notice the first line of our example: #include <stdio.h>. Any line beginning with a hash symbol (#) is called a Preprocessor Directive.

#include <stdio.h>

The preprocessor opens the header file stdio.h, copies all function prototypes and definitions contained inside it, and pastes them directly in place of the #include line.

#define & Macros

When constants or macros are defined (such as #define PI 3.1416), the preprocessor replaces every occurrence of the token with its actual replacement value, while stripping out all comments.

4. Compilation to Assembly Language

Once preprocessing is finished, the compiler analyzes the parsed source statements. When encountering int result = a + b;, the compiler must evaluate:

  • Which CPU registers are optimal to hold variables a and b?
  • Which exact architecture instruction produces the fastest addition with the least overhead?

The compiler translates high-level C code into human-readable Assembly Language:

; x86-64 Assembly representation
movl -4(%rbp), %eax    ; Load variable 'a' into register eax
addl -8(%rbp), %eax    ; Add variable 'b' to eax
movl %eax, -12(%rbp)   ; Store sum in 'result' memory location

Assembly language is intimately tied to CPU hardware. The assembly instructions for an Intel/AMD chip (x86-64), an Apple Silicon / ARM processor, or a RISC-V microcontroller are completely distinct.

5. The Assembler and Object Files

While assembly mnemonics are readable by humans, they remain textual strings. Silicon circuits cannot parse text characters.

The Assembler bridges this gap. It translates each assembly mnemonic directly into binary opcodes and formats the result into an Object File (typically main.o on Unix or main.obj on Windows).

What Lives Inside an Object File?

Machine Code (Text Segment) Pure binary processor instructions for compiled functions.
Data (Data Segment) Global variables and static constants like the literal string "Hello World".
Symbol Table Names and relative memory offsets of functions and variables declared in this file.
Relocation Information A list of unresolved addresses that must be adjusted during final linking.

Crucial Note: An object file cannot run on its own! Our code called printf(), yet the actual machine code implementation of printf() does not exist inside main.o.

6. The Linker — Stitching the Pieces Together

Real-world software is never authored inside a single isolated file. Production projects contain dozens or hundreds of compiled modules, plus runtime libraries provided by the operating system (such as the standard C library, libc).

The Linker collects multiple separate object files and external libraries, resolves all cross-file symbol references, adjusts relative memory offsets, and stitches them into a single, cohesive Executable File.

main.o
math.o
libc (printf code)
LINKER Relocates addresses and resolves external symbols
myprogram.exe Self-contained executable binary

Static Linking

The linker copies the actual machine code of every library function directly into the executable binary. The output file is larger, but it runs independently without relying on external system files.

Dynamic Linking

Library code is omitted from the executable file; only reference stubs remain. When launched, the operating system dynamically loads shared libraries (.dll on Windows, .so on Linux) into memory and binds them on the fly.

7. Executable File Formats

The executable generated by the linker conforms strictly to the standard binary container format defined by the host operating system:

Windows — PE Format

Portable Executable format (typically having a .exe file extension).

Linux — ELF Format

Executable and Linkable Format (no specific file extension required).

macOS — Mach-O Format

Mach Object architecture format and application bundle layout.

Having an executable saved to your hard drive or SSD does not make it run by itself. On disk, an executable is simply passive data—dormant bytes waiting to be brought to life.

8. How the Operating System Launches a Program

When you double-click an executable icon or run ./myprogram in a terminal, the operating system's OS Loader springs into action.

Program vs. Process

Program

A passive, dormant entity resting on permanent storage media (a file on disk).

Process

An active, living program instance loaded into memory with allocated CPU time, registers, and OS resources.

If you launch three separate windows of your browser or calculator, only one executable file exists on your disk, but three independent Processes are executing in RAM.

9. Process Memory Layout (Virtual Memory)

When the OS creates a new process, it allocates a dedicated Virtual Memory space isolated from other applications. This virtual address space is organized into four core segments:

High Memory Address
Stack Function call frames, local variables & return addresses (grows downward ↓)
↕ Free Memory Space ↕
Heap Dynamic memory allocations (malloc / new) (grows upward ↑)
Data / BSS Segment Global and static variables initialized or uninitialized
Text / Code Segment Executable binary machine code instructions (Read-Only)
Low Memory Address (0x0000...)

Modern operating systems do not load the entire binary file into physical RAM all at once. Through virtual memory paging, code pages are loaded into physical RAM on-demand as instruction execution demands them.

10. How the CPU Begins Execution

Once process memory structures are prepared, the OS initializes the CPU's Program Counter (PC) register with the virtual address of the program's entry point (such as the initial instruction of main()).

At this point, the processor initiates its tireless rhythmic loop: Fetch → Decode → Execute. From this millisecond onward, your software is actively alive on hardware silicon.

11. What About Python, Java, and JavaScript?

Not all programming languages compile directly into standalone OS-native binaries like C does. Modern language ecosystems leverage powerful alternative execution strategies:

Python (CPython Flow)

The source file script.py is compiled on the fly into intermediate Bytecode (often cached in .pyc files). The Python Virtual Machine (PVM) then interprets and evaluates these bytecode instructions line by line.

Java (JVM Flow)

The javac compiler transforms Java code into platform-independent .class bytecode. The Java Virtual Machine (JVM) executes this bytecode across any OS, realizing the famous promise: "Write Once, Run Anywhere."

JIT (Just-In-Time) Compilation — V8 & Modern JavaScript

Modern JavaScript engines (like Google Chrome's V8) profile running scripts dynamically. Frequently executed routines ("hot code") are compiled directly into optimized native machine instructions right while the application runs, providing interpreted scripts with near-native execution speed.

12. Real-World Walkthrough: Pressing 25 + 75 on a Calculator

You open your operating system's calculator application, type 25 + 75, and hit =. Within a single millisecond, the entire computing stack cooperates to produce your answer:

1. Hardware: Physical keystrokes generate electrical signals; the USB controller fires a hardware interrupt.
2. OS Kernel: The kernel interrupt handler processes the scan code and routes keyboard input events to the calculator process.
3. App Logic: The calculator event loop parses input tokens and registers 25 and 75 as numeric operands.
4. CPU ALU: The processor's Arithmetic Logic Unit executes the binary add opcode, yielding the calculation 100.
5. Display: The window manager and GPU render the glyph '100' as colored pixels onto the monitor frame buffer.

13. Summary: The 3 Pillars of Software Execution

1. Human Code

High-level syntax, algorithms, data structures, and developer logic.

2. Execution Environment

Compilers, runtime engines, OS kernels, virtual memory managers, and shared libraries.

3. Physical Hardware

CPU cores, registers, ALU, RAM buses, and display controllers.

🧠 Key Takeaways

  1. Source code does not run directly on CPUs: It requires progressive, multi-stage translation.
  2. Compiled languages undergo four key stages: Preprocessing → Compilation → Assembly → Linking.
  3. The Linker makes programs whole: It connects fragmented object files with operating system libraries into a standalone executable.
  4. A Program is code on disk; a Process is that code running in memory: The OS supervises all active processes.
  5. Process memory is partitioned into four zones: Code (Text), Data, Heap (dynamic memory), and Stack (local call frames).
  6. Python, Java, and JS rely on intermediate runtimes: Using Bytecode and JIT compilers to balance portability with near-native performance.
  7. Modern execution is a layered pipeline: Where hardware, operating system, and language runtime collaborate seamlessly.
🚀 Next Lesson

Lesson 04 — What Is an Operating System and Why Do We Need It?

We have seen that the operating system is the ultimate orchestrator behind running software. In the next part, we unravel the operating system's inner mechanisms—how it governs the CPU, arbitrates RAM, and manages peripheral hardware.

"Windows, macOS, or Linux is far more than a visual interface—it is the supreme traffic controller of every computing resource in your machine."
Start Lesson 04