Embedded and Cross-Platform Build Workflows
A linker script defines memory regions and section placement for toolchains that support this model.
What this module covers
Cross toolchains and target architecture Firmware build stages Linker scripts and memory maps
Debug/release and board-specific configurations Generated register headers and code generators
Build variants and product configuration Reproducible firmware images
Version metadata and firmware identification Flashing, packaging, and release artifacts
Hardware-independent CI and hardware-in-the-loop boundaries
17.1 Typical firmware pipeline
C/C++ + Device Headers + Generated Config
|
v
Cross-Compiler / Assembler
|
v
Object Files / Libraries
|
v
Linker Script + Startup Objects
|
v
ELF + Map File + Debug Symbols
|
v
HEX/BIN Conversion / Packaging
|
v
Integrity Checks / Signing
|
v
Release Artifact / Flash Process17.2 Linker script and memory map
A linker script defines memory regions and section placement for toolchains that support this model. It can place vectors, code, constants, initialized data, and zero-initialized data into specific memory regions.
Always verify:
Flash and RAM boundaries.
Bootloader/application offsets.
Stack/heap reservations.
Interrupt/startup conventions.
A successful link is not proof that the firmware will boot correctly on hardware.
Measuring sections and enforcing a RAM budget Without a cross toolchain on the build machine, this uses the host GCC — the same commands apply with arm-none-eabi- prefixes and a real linker script:
Terminal session (GCC 11.2, binutils 2.38, Ubuntu 22.04 on WSL2):
The map file attributes each section to the object that contributed it (0x400 = the 1024-byte frame buffer in .bss). A budget script like this turns “the image grew” into a failing CI step instead of a surprise on hardware.
$ cc -O2 -c fw.c -o fw.o && cc fw.o -Wl,-Map=fw.map -o fw.elf
$ size -A fw.elf | grep -E '^(section|\.text|\.rodata|\.data|\.bss) '
section size addr
.text 265 4160
.rodata 288 8192
.data 20 16384
.bss 1056 16416
$ grep -E '^ \.(rodata|bss|data)' fw.map | grep fw.o
.rodata 0x0000000000002020 0x100 fw.o
.data 0x0000000000004010 0x4 fw.o
.bss 0x0000000000004040 0x400 fw.o
$ objcopy -O ihex fw.elf fw.hex && head -3 fw.hex && wc -l < fw.hex
:100318002F6C696236342F6C642D6C696E75782D7C
:0C0328007838362D36342E736F2E3200DC
:10033800040000002000000005000000474E5500A2
137
$ ./budget.sh 2048
RAM used: 1076 bytes, budget: 2048 bytes
$ ./budget.sh 1024
RAM used: 1076 bytes, budget: 1024 bytes
[exit status 1]17.3 Variant management
Represent board, MCU, product, and feature variants in explicit configuration. Avoid scattered preprocessor definitions that create undocumented combinations. Record the selected variant in build metadata and, when appropriate, in firmware version information.
17.4 Build identification
A release may include a semantic version, Git commit ID, build profile, toolchain version, and reproducibility metadata. Be careful: embedding current time or machine-specific paths can make outputs non-reproducible. Define what metadata is essential and how it is normalized.
17.5 Hardware-independent CI
CI can build firmware, run unit tests, perform static analysis, inspect map files, and test host-side logic without physical hardware. Hardware-in-the-loop testing requires controlled device access, flashing safeguards, fixture health checks, and result traceability. Distinguish compile-time validation from target behavior validation.
Quiz
? Which section held the zero-initialised 1024-byte frame buffer?
- `.text` :: Code.
- `.rodata` :: Constant data — the lookup table.
- `.data` :: Initialised variables — `boot_count`.
+ `.bss` :: Zero-initialised data occupies RAM but not image bytes.
? Why avoid embedding the build time in firmware version metadata?
+ It makes otherwise identical builds differ, breaking reproducibility. :: Use the tag and commit ID instead.
- Firmware cannot store dates. :: It can.
- Time zones are unsupported. :: Not the reason.
- It is slower. :: Not the reason.
? What can hardware-independent CI verify? (select all that apply)
+ The firmware builds for each variant :: Compilation and linking.
+ Host-side unit tests pass :: Logic can be tested without hardware.
+ Memory budgets from map files :: As in the budget script.
- That the device boots correctly :: That needs hardware-in-the-loop testing.