Compilers and Toolchains
Introduction
You write firmware in C (or C++, Rust, etc.), but the MCU executes machine instructions. A toolchain translates, links, and loads your program onto the target. On embedded systems you almost always cross-compile: the compiler runs on your PC (host) and produces code for the MCU (target).
Understanding compile vs interpret, native vs cross-compile, and what a linker does saves hours when build errors mention "undefined reference" or wrong flash addresses.
Compiler vs interpreter
| Compiler | Interpreter | |
|---|---|---|
| Translation | Whole program (or translation unit) → machine code | Line by line at runtime |
| Output | Binary / object files | Immediate execution |
| Speed | Fast repeated execution | Slower; startup can be fast |
| On MCU | Standard (GCC, Clang, vendor tools) | Rare (MicroPython on some boards) |
Embedded firmware is almost always compiled for efficiency and predictable resource use.
Cross-compiler
A cross-compiler runs on one architecture and generates code for another:
1 2 3 | |
You cannot run the .elf file on your laptop — it is ARM machine code. You flash it via JTAG, SWD, UART bootloader, or USB DFU.
Typical toolchain components
| Tool | Role |
|---|---|
| Preprocessor | #include, #define |
| Compiler | C → assembly / object code |
| Assembler | Assembly → object code |
| Linker | Combines objects + libraries → single executable |
| Locator / script | Places sections in flash and RAM (linker script) |
| Flasher / debugger | OpenOCD, pyOCD, vendor IDE |
Build pipeline (simplified)
The linker script (memory.ld) maps .text (code) to flash, .data / .bss to RAM — mismatches cause link errors or boot failures.
Debug vs release builds
| Build | Optimisation | Debug symbols | Use |
|---|---|---|---|
| Debug | -O0 / low |
Yes | Breakpoints, single-step |
| Release | -Os / -O2 |
Often stripped | Shipping firmware |
Optimisation can reorder code — timing measurements and debugging differ between builds.
Higher-level virtual machines
Some systems run bytecode on a VM (Java JVM, .NET CLR). On embedded, bytecode interpreters appear in bootloaders or scripting layers but are uncommon for tight real-time loops. Paravirtualization and VMs are covered in Virtualization.
Relevant topics
- Introduction to computer systems
- Embedded systems overview
- GCC toolchain in depth — linking, symbols, and relocation in detail
- Memory systems
- GNU Arm Embedded Toolchain
- GCC documentation
Starting points
- Build a minimal project and list every command the IDE runs (verbose build log).
- Open the
.mapfile — find wheremainand your buffers landed in memory. - Change linker script RAM size deliberately wrong — observe link failure message.
- Compare
.elfsize vs.binsize — what was stripped?
Focus points
- Cross-compile is normal — host ≠ target.
- Linker errors are often missing objects or wrong library order.
- Linker script is part of architecture — flash/RAM layout matters.
- Optimisation changes timing — test release builds for real-time.
Key points
- Compilers translate full programs to machine code; interpreters execute step by step.
- Cross-compilers build firmware on a PC for an MCU target.
- Toolchain = compiler + assembler + linker + flash/debug tools.
- Linker scripts place code and data in the correct memory regions.