When you press Run, nothing mystical happens.
A program is a plan. The computer is a machine that repeatedly turns plans into tiny electrical decisions. Between your editor and those voltages sits a clean chain of translations - and once you see the chain, “how code runs” stops feeling like a black box.
This is that chain: from source text to bare metal.
You do not need to design a CPU to benefit from this map. You need enough of the ladder - source → binary → process → fetch/decode/execute → voltage - that performance, crashes, and security stop sounding like superstition.
Start with the real question
People ask “How does code run?” and usually mean one of three things:
- How does my language turn into something the CPU understands?
- How does the OS load and schedule that program?
- How does a CPU instruction become physics?
The honest answer needs all three. Software is layers of meaning. Hardware is layers of energy. Execution is where those layers meet.
The big picture

A useful map:
| Stage | What changes | Who does it |
|---|---|---|
| Source code | Human-readable text | You |
| Compile / assemble | Text → object code | Compiler, assembler |
| Link | Objects → executable image | Linker |
| Load | File → process memory | OS loader |
| Execute | Instructions → state changes | CPU |
| Signal | Bits → voltages | Transistors on the die |
Miss a row and the story gets superstition. Keep the rows and you can debug with intent.
Source code is not what the machine runs
Your program in C, Go, Rust, TypeScript, or Python is a description. The CPU does not understand descriptions. It understands instruction encodings - patterns of bits that select operations hardwired into the silicon.
So the first job of the toolchain is translation.
Compiled languages
A compiler front-end parses and checks your code. The back-end lowers it toward machine operations (often through intermediate representations). You get object files: machine code plus metadata (symbols, relocations).
The linker resolves references (printf, your own functions, library calls)
and produces an executable or shared library with a defined layout.
Interpreted and JIT languages
Python, JavaScript, and friends still end up at machine work - just later and more dynamically. Interpreters execute bytecode in a loop. JITs watch hot paths and emit machine code at runtime. The destination is the same neighborhood: instructions the CPU can fetch.
Assembly is the last human dialect
Assembly language is almost one-to-one with machine instructions, named for
humans (mov, add, jmp). Assemblers turn those names into opcodes and
operands. If you can read a little assembly for your architecture, you can see
what “the compiler decided.”
# Peek at what a tiny C function becomes on Linux/x86-64
cat > /tmp/add.c <<'EOF'
int add(int a, int b) { return a + b; }
EOF
gcc -O1 -S -o - /tmp/add.c | head -40
You do not need to love assembly. You need to know it exists as a readable checkpoint between “my code” and “the chip.”
What an executable actually contains
An executable is not “the program” in the poetic sense. It is a structured file:
- headers describing architecture and entry point
- sections/segments for code (
.text) and data (.data,.rodata,.bss) - symbols and dynamic linking info when libraries are involved
On Linux, that format is commonly ELF. When you run the file, the kernel does not “run the file on disk.” It maps relevant parts into a process address space, sets up stacks, then jumps to the entry point.
That jump is the formal beginning of “running.”
file /bin/ls
readelf -h /bin/ls | head
Those two commands turn “a mysterious binary” into “a structured image the loader knows how to map.”
Memory is where the running program lives
A running process has a private (virtual) view of memory:
- code - instructions
- heap - dynamic allocations
- stack - call frames, locals, return addresses
- mappings for libraries, files, and kernel-assisted regions
Virtual memory is a kindness and a lie: your program thinks it owns a clean address space. The MMU and OS page tables translate those addresses into physical RAM (or fault and recover when pages are missing).
Every load and store is eventually a request that reaches memory hierarchy: registers → caches → DRAM - with latency climbing as you leave the core.
A segfault is usually not “random.” It is the CPU refusing an address your process was not allowed to touch - often a null pointer, a use-after-free, or a corrupted return address. The language runtime may dress it up; the metal story is still a bad access.
The CPU’s eternal loop: fetch, decode, execute
At the heart of bare-metal execution is a boring miracle:
- Fetch the next instruction from memory (via caches when you are lucky)
- Decode the bit pattern into an operation and operands
- Execute it - add numbers, compare, branch, load, store, trap to the OS
- Update architectural state (registers, flags, program counter)
- Repeat
Modern CPUs pipeline these stages, speculate, reorder, and execute out-of-order for speed. The programming model still looks sequential. The microarchitecture is a factory optimizing throughput while preserving the illusion.
Registers are the CPU’s fastest storage. The instruction pointer (program counter) is simply “which instruction is next?” Branches rewrite that pointer. Function calls push return addresses. Interrupts temporarily steal the pipeline for urgent work.
That is software, reduced to state machines ticking on a clock.
Instructions are contracts with silicon
An instruction is a promise: “if these bits arrive, the hardware will do this.”
Examples of the contract family:
- arithmetic and logic
- data movement (register ↔ memory)
- control flow (jump, call, return)
- system-level ops (privilege, interrupts, virtual memory assists)
ISAs (x86-64, Arm, RISC-V) define the contract. Microarchitecture implements it. Two chips can speak the same ISA and still feel different because of cache sizes, pipeline width, and branch predictors - not because the language changed.
From bits to voltage

Here is the part most tutorials skip - and the part that makes “bare metal” literal.
Inside the chip, information is not abstract. It is electrical levels.
Digital logic approximates two worlds:
- Logic 0 - a voltage near ground
- Logic 1 - a voltage near the supply rail (often called Vdd)
CMOS circuits (the dominant style in modern CPUs) use complementary transistors as switches. A gate input voltage turns paths on or off, connecting an output node either toward ground or toward Vdd. That is how a bit is held and how it flips.
Clocks synchronize those flips so billions of gates agree on “this moment’s answer.” Power delivery networks keep those rails stable while current surges with activity. Heat is the tax for all that switching.
So when your program increments a counter, somewhere on the die a cloud of transistors rearranged voltages into a new pattern that means the next integer - under the ISA’s encoding rules.
That is as close as software gets to physics without becoming an EE lecture.
The operating system is still in the room
Bare metal does not mean “no OS” for most programs you write. Even on Linux, a normal user process:
- runs in user mode with restricted privilege
- asks the kernel for I/O, allocation help, and new processes via system calls
- shares the CPU through the scheduler
A system call is a controlled transition: user code traps into kernel code,
kernel work happens at higher privilege, then control returns. Disk reads,
network sends, and mmap are not language features. They are OS services
implemented with more instructions - just privileged ones.
Containers and VMs add more layers. They do not erase the chain. They insert checkpoints in it.
A concrete walk-through
Imagine a tiny C function:
int add(int a, int b) {
return a + b;
}
Under the hood, a plausible story is:
- Compiler emits machine code that loads
aandbinto registers, executes an add, and returns the result in a calling-convention register. - Linker places that code into an executable image.
- At runtime, the loader maps the image;
callreachesadd. - CPU fetches the add instruction encoding.
- Decode steers ALU hardware.
- Transistor networks compute sum bits as voltage patterns.
- Result voltages are latched into a register as the new architectural state.
retfetches the return address and continues the caller.
Your mental model can stay high-level for product work. When performance, correctness, or security gets weird, drop down the ladder one rung at a time.
Why this mental model makes you better
Understanding the path from code to voltage pays rent:
- Performance - caches, branches, syscalls, and allocations stop being folklore
- Debugging - segfaults, races, and “impossible” states map to memory and concurrency realities
- Security - privilege boundaries and memory safety become physical constraints with software seams
- Systems design - you stop expecting magic from layers that are only translations
You do not need to design a CPU. You need to respect that every abstraction leaks into energy and time.
A practical ladder for the next weird bug
When something feels impossible, climb down deliberately:
- Language / runtime - null, exception, GC pause, event-loop stall?
- Process / OS - permissions, open files, scheduler, syscall errors?
- Binary / ABI - wrong arch, missing library, bad linking?
- Memory - ownership, aliasing, cache-line sharing, page fault storms?
- Hardware realities - NUMA, thermal throttling, disk latency, NIC drops?
Most product bugs die at steps 1–2. The ones that survive teach you why the lower rungs exist.
What “running code” really means
A program runs when:
- its meaning has been translated into instructions
- those instructions live in a process the OS can schedule
- a CPU repeatedly fetches and executes them
- the execution is implemented by switching voltages in silicon
Everything else - frameworks, runtimes, containers, clouds - is staging and policy around that core loop.
Source code is literature. Machine code is choreography. Voltage is the dance floor.
Once you can hold all three in one picture, “How does a program run?” becomes a question you can answer - and a map you can use.






