Toolchains, Builds, and Project Structure
A firmware build is a controlled transformation from source code into a binary image that can run on a target. Reproducible builds make debugging, releases, and field support possible.
Learning Objectives
By the end of this lesson, you should be able to identify toolchain stages, explain common build artifacts, organize a firmware repository, and create build settings that are visible, repeatable, and reviewable.
Toolchain Stages
The compiler translates each source file. The linker places code and data into memory regions. The image tool extracts the format needed by the programmer or bootloader.
Important Artifacts
| Artifact | Purpose |
|---|---|
.o |
compiled object file |
.elf |
linked image with symbols and debug information |
.map |
memory placement and symbol map |
.hex |
Intel HEX programming file |
.bin |
raw binary image |
.lst |
optional mixed source and assembly listing |
Always archive the ELF and map file for releases. They are essential for crash analysis.
Project Structure
firmware/
app/
services/
drivers/
bsp/
startup/
include/
tests/
tools/
build/
Keep generated build output out of source directories. Keep vendor code isolated so updates are reviewable.
Compiler and Linker Flags
Typical debug flags include symbols and low optimization:
-g3 -Og
-Wall -Wextra
-ffunction-sections
-fdata-sections
Typical release builds use stronger optimization and still keep warnings enabled:
-O2
-Wall -Wextra -Werror
-ffunction-sections
-fdata-sections
Linker garbage collection removes unused sections:
-Wl,--gc-sections
-Wl,-Map=firmware.map
Worked Example: Build Identity
Embed version information in every image:
char fw_ver[] = "1.4.2";
char fw_sha[] = GIT_SHA;
char fw_type[] = BUILD_TYPE;
The build system can pass definitions such as -DGIT_SHA=\"d7a9ff7\". Field logs can then identify the exact code running on a device.
Reproducibility Checks
- Build from a clean checkout.
- Pin compiler version in documentation or container image.
- Record linker script and startup files in version control.
- Make warnings visible in CI.
- Generate and review code-size reports.
Common Mistakes
- Manually changing IDE settings that are not committed.
- Shipping only a BIN file and losing debug symbols.
- Mixing vendor generated code with hand-written application code.
- Ignoring warnings until release week.
- Not checking flash and RAM usage after feature additions.
Summary
A firmware toolchain compiles, links, maps, and packages code. A maintainable project makes build settings explicit, preserves debug artifacts, separates code layers, and produces identifiable images.
Further Reading
- GNU Arm Embedded Toolchain documentation.
- CMake, Ninja, and Make documentation for embedded builds.
- Memfault, "Firmware Build and Release Practices."