ARM MDK Build Pipeline: Compilation Stages and Output File Formats

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.

Tags: ARM MDK Embedded Toolchain ELF Format Scatter Loading Firmware Build

Posted on Sun, 20 Sep 2026 16:26:07 +0000 by fernyburn