“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-symbolsto reuse symbol addresses- Exporting stable symbols
__weakrewriting for patches- Shared RAM segments
- Interface consistency constraints
- Equivalent implementation in Keil using
--symdefs
I. ROM Packaging with gcc --just-symbols
Gather ROM code into a dedicated section
Tag the reusable functions with__attribute__((section(".rom")))so the object files collect them neatly.Pin the section in the linker script
Inrom.ld, addKEEP(*(.rom*))and place it squarely inside the ROM region. That keeps app code from drifting into the reserved space.Build ROM once and export the ELF
Compile only the ROM sources (proj=ROM) and producerom/rom.elf. This ELF is the authoritative list of addresses.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.Call ROM functions directly
At runtime the APP jumps straight to the ROM addresses it imported, keeping ownership lines clean.
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 theweakattribute and the linker instantly prefers it.Principle: strong beats weak every single time.
__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 definesrom_share_data_tin.xramand exposes a pointerg_sharethat will later reference APP memory.APP registers shared memory and callbacks
The APP allocatesg_share_mem, callsrom_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_sharefor explicit calls.
{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:
__weakallows 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
- Build ROM with Keil → output
rom.axf - Export symbols:
fromelf --symdefs rom.axf --output rom.symdefs - Build APP with
--symdefs=rom.symdefsso it can reuse ROM symbols without linking ROM code.
This is equivalent to GCC--just-symbols.