“Premature optimization is the root of all evil.” — Donald Knuth

Overview

Goal: Package low-level firmware as a ROM image plus symbol export. The application imports that symbol table during its own link stage, so it can call into ROM code without ever touching the original sources. ROM stays frozen and reusable; the APP continues to evolve.

Key techniques:

  • ld --just-symbols to reuse symbol addresses
  • Exporting stable symbols
  • __weak rewriting for patches
  • Shared RAM segments
  • Interface consistency constraints
  • Equivalent implementation in Keil using --symdefs

I. ROM Packaging with gcc --just-symbols

  1. Gather ROM code into a dedicated section
    Tag the reusable functions with __attribute__((section(".rom"))) so the object files collect them neatly.

  2. Pin the section in the linker script
    In rom.ld, add KEEP(*(.rom*)) and place it squarely inside the ROM region. That keeps app code from drifting into the reserved space.

  3. Build ROM once and export the ELF
    Compile only the ROM sources (proj=ROM) and produce rom/rom.elf. This ELF is the authoritative list of addresses.

  4. Let the APP import those symbols
    During APP linking, pass -Wl,--just-symbols=rom/rom.elf. The linker borrows the symbol table but doesn’t pull in the ROM code.

  5. Call ROM functions directly
    At runtime the APP jumps straight to the ROM addresses it imported, keeping ownership lines clean.

%%{init: {"theme": "neutral", "layout": "dagre", "look": "neo"}}%% flowchart TD subgraph Compile_ROM R1[ROM sources
rom/common, rom/os] --> R2[Compile: functions -> .rom/.xram] R2 --> R3[Link with rom.ld] R3 --> R4[Output rom.elf/rom.bin] end subgraph Compile_APP A1[APP sources
device, driver, project] --> A2[Compile] A2 --> A3[Link with sc9610.ld
--just-symbols=rom.elf] A3 --> A4[Output app.elf/app.bin] end R4 -.symbol table.-> A3 subgraph Memory MTP[MTP 0x0000~0x7FFF
APP code] ROM[ROM 0x8000~0xFFFF
ROM code] SRAM[SRAM 0x2000_0000~
Data/Stack] XRAM[XRAM 0x2000_1E00~
Shared area] end A4 --> MTP R4 --> ROM

II. __weak Rewrite Mechanism

  • ROM ships weak defaults
    For example, parity() lives in ROM as __weak. If nobody else speaks up, that implementation runs.

  • APP supplies strong overrides
    Define the same function without the weak attribute and the linker instantly prefers it.

  • Principle: strong beats weak every single time.

%%{init: {"theme": "neutral", "layout": "dagre", "look": "neo"}}%% graph TD subgraph No_Override R1["ROM weak function
__weak parity()"] --> L1((Linker)) L1 --> F1[Final uses ROM implementation] end subgraph With_Override R2["ROM weak function
__weak parity()"] --> L2((Linker)) A2["APP strong function
parity()"] --> L2 L2 --> F2[Final uses APP implementation] end

III. Shared Variables and External Functions

  • Shared structure and mapping
    ROM defines rom_share_data_t in .xram and exposes a pointer g_share that will later reference APP memory.

  • APP registers shared memory and callbacks
    The APP allocates g_share_mem, calls rom_share_attach, and fills in callback pointers.

  • ROM invokes APP functionality through:

    • Weak/strong overrides when the APP replaces default behavior.
    • Callback pointers stored inside g_share for explicit calls.
%%{init: {"theme": "neutral", "layout": "dagre", "look": "neo"}}%% flowchart TD subgraph APP_Memory G["g_share_mem: rom_share_data_t
{counter, callback}"] CB["app_callback()"] end subgraph XRAM P[g_share pointer] end subgraph ROM_Code INC["rom_share_inc()"] GET["rom_share_get()"] CALL["rom_share_call()"] end G -->|"rom_share_attach(&g_share_mem)"| P INC -->|counter++| G GET -->|read counter| G CALL -->|"callback()"| CB

IV. Advantages

  • Modular reuse: ROM can be built and tested once, then reused by multiple APPs.
  • Flexible patching: __weak allows APP to extend or fix features without touching the ROM image.
  • Smaller footprint: APP links against ROM symbols, avoiding duplicate code.
  • Two-way communication: Shared structures and callbacks enable ROM and APP to exchange state and call each other.

V. Keil Equivalent with --symdefs

  1. Build ROM with Keil → output rom.axf
  2. Export symbols:
    fromelf --symdefs rom.axf --output rom.symdefs
    
  3. Build APP with --symdefs=rom.symdefs so it can reuse ROM symbols without linking ROM code.
    This is equivalent to GCC --just-symbols.