Build Pipeline Architecture
ARM MDK (Microcontroller Development Kit) orchestrates a multi-stage build process transforming source code into machine-executable binaries. The toolchain comprises armcc (C/C++ compiler), armasm (assembler), armlink (linker), and fromelf (format converter). Each stage generates distinct intermediate artifacts with specific roles in the final image generation.
Compilation Stage
Source files (.c, .cpp, .s) undergo syntax analysis and code generation. The compiler produces relocatable object files (.o) containing machine instructions, data sections, and symbolic relocation information. These objects remain position-independent, with memory addresses unresolved pending final linkage.
armcc -c --cpu=Cortex-M4 -O1 -g driver_config.c -o driver_config.o
armasm --cpu=Cortex-M4 startup_sequence.s -o startup_sequence.o
Object files adhere to the ELF (Executable and Linkable Format) specification but marked as ET_REL (relocatable). They contain section headers describing code (.text), initialized data (.data), and debugging metadata, though physical memory addresses remain unassigned.
Linkage and Image Generation
The linker aggregates object files and library archives (.lib), resolving symbol references and assigning absolute addresses based on memory region definitions. Output formats include:
- .axf (ARM Executable File): ELF-based executable containing debug symbols, section tables, and program headers. Debuggers utilize this format for source-level debugging.
- .lib (Library Archive): Static library created via
armar, encapsulating object collections for distribusion without source disclosure.
armlink driver_config.o startup_sequence.o system_utils.lib \
--scatter=memory_layout.sct --output=firmware_image.axf
Post-Link Conversion
Raw binary generation requires format conversion since microcontrolers lack operating system loaders for ELF files. The fromelf utility extracts pure machine code with optional address metadata:
# Generate Intel HEX format (includes address records)
fromelf --i32 --output=firmware.hex firmware_image.axf
# Generate raw binary (memory image only)
fromelf --bin --output=firmware.bin firmware_image.axf
Intel HEX Structure: Records begin with colon (:) followed by byte count, 16-bit address, record type, data payload, and checksum. Extended linear address records (type 04) prefix subsequent data records with upper 16-bits of the 32-bit address space.
:020000040800F2 # Extended address 0x0800
:10000000000400204501000829030008BF02000881 # 16 bytes at 0x08000000
:00000001FF # End-of-file
Memory Domain Classification
Embedded applications partition memory into distinct regions tracked during compilation:
| Region | Description | Storage Location |
|---|---|---|
| Code | Executable machine instructions | FLASH/ROM |
| RO-data | Constants and string literals | FLASH/ROM |
| RW-data | Initialized global/static variables | FLASH (copy) → RAM (runtime) |
| ZI-data | Zero-initialized variables (BSS) | RAM only |
During startup, the C runtime executes __scatterload routines copying RW-data from FLASH to RAM and zeroing ZI-data sections before invoking main().
Project File Taxonomy
Build Artifacts
- .o (Object): Intermediate ELF relocatables containing unlinked machine code.
- .axf (Executable): Final linked image with debug metadata. Consumed by debug probes (J-Link, ULINK).
- .bin (Binary): Raw memory dump suitable for bootloader updates or production programming.
- .hex (Hexadecimal): ASCII-encoded format with address information for ISP programming.
Analysis and Map Files
- .map: Linker-generated memory layout report detailing symbol addresses, section sizes, and cross-reference tables. Critical for analyzing RAM/FLASH utilization.
- .htm: Static call graph documentation revealing maximum stack depth and function call hierarchies. Example output indicates:
Maximum Stack Usage: 584 bytes Call Chain: main → InitializePeripherals → ConfigureClockTree → ErrorHandler - .crf (Cross-Reference): Browsable information database enabling IDE navigation (Go to Definition) by mapping symbols to source locations.
Dependency Tracking
- .d (Dependency): Per-source-file listings of header inclusions, enabling incremental builds.
- .dep: Project-wide dependency manifest tracking inter-file relationships.
Configuration Files
- .uvprojx: Primary project structure definition (XML format) specifying source files, toolchain settings, and target device parameters.
- .uvoptx: User-specific workspace preferences including debugger configurations, breakpoint persistence, and open document states.
- .uvguix: IDE window layout and GUI geometry preferences.
ELF Format Internals
File Header Analysis
Object files (ET_REL) lack program headers, while executable images (ET_EXEC) contain memory loading instructions:
# Object file header (driver_config.o)
File Type: ET_REL (Relocatable)
Program Headers: 0
Section Headers: 24
# Executable header (firmware_image.axf)
File Type: ET_EXEC (Executable)
Entry Point: 0x08000101
Program Headers: 3
Section Headers: 18
Section vs. Segment Mapping
Sections (linker view): Logical groupings of code/data (.text, .data, .bss). In object files, addreses are relative (0x00000000).
Segments (loader view): Contiguous memory regions specified in program headers. The PT_LOAD type indicates segments requiring physical memory allocation:
Program Header:
Type: PT_LOAD
Virtual Address: 0x08000000
Physical Address: 0x08000000
File Size: 0x00004200 (16896 bytes)
Memory Size: 0x00004200
Flags: R-X (Read-Execute)
Disassembly Comparison
Pre-linking object code contains placeholder addresses for external symbols:
; Object file (unlinked)
ConfigureHardware:
PUSH {r4-r7,lr}
LDR r0, =0x40021000 ; GPIO base (placeholder)
BL InitializeClock ; 0x00000000 (unresolved)
BL ConfigurePins ; 0x00000000 (unresolved)
Post-linking resolves absolute addresses and updates branch instructions:
; Linked executable
ConfigureHardware:
0x08000372: PUSH {r4-r7,lr}
0x08000374: LDR r0, [pc, #32] ; 0x40021000
0x08000376: BL 0x08000210 ; InitializeClock
0x0800037A: BL 0x08000458 ; ConfigurePins
Scatter Loading Mechanism
The linker script (.sct file) defines memory region layouts:
LR_IROM1 0x08000000 0x00010000 { ; Load region
ER_IROM1 0x08000000 0x00010000 { ; Execute region
*.o (RESET, +First)
*(InRoot$$Sections)
.text (+RO)
}
RW_IRAM1 0x20000000 0x00005000 { ; RAM region
.data (+RW)
.bss (+ZI)
}
}
The __scatterload runtime routine processes initialization tables embedded in the ELF file, copying .data sections from FLASH to RAM and zeroing .bss sections prior to application entry.
Toolchain Command Reference
Compiler Options:
--cpu: Target processor architecture (e.g., Cortex-M4)-O1/-O2/-O3: Optimization levels-g: Debug symbol generation-D: Preprocessor macro definitions
Linker Options:
--scatter: Scatter loading description file--entry: Program entry point symbol--map: Generate memory map file
Format Conversion:
--bin: Raw binary output--i32: Intel HEX 32-bit format--vhx: Verilog Hex format
Security Considerations
AXF files contain extensive metadata facilitating code reconstruction through disassembly. Binary (.bin) files offer minimal protection; determined reverse engineers can reconstruct assembly logic from FLASH dumps. Distribution of library files (.lib) obscures source implementation while preserving API accessibility, though symbol names remain visible unless stripped.