5.5.17 · D5Embedded Systems & Real-Time Software
Question bank — Linker scripts — memory regions, sections (.text, .data, .bss)
Terms used below, defined once so nothing sneaks in undefined:
- LMA = Load Memory Address = where a section's bytes physically sit in the flashed image.
- VMA = Virtual Memory Address = where the section is expected to be when the code runs (its "run address").
.bin/ image = the raw bytes actually burned into Flash.- Reset_Handler = the very first code that runs on power-up, before
main()— see Startup code & Reset_Handler.
True or false — justify
.text has the same LMA and VMA.
True. Code executes in place from Flash, so its load address and run address are the same Flash address — there is nothing to copy.
.data has the same LMA and VMA.
False. Its initial values are stored in Flash (LMA) but the variables must be writable, so they run in RAM (VMA); the two addresses differ, which is exactly why
AT> FLASH exists..bss contributes bytes to the .bin file burned into Flash.
False. Only its size is recorded; the actual zeros are written into RAM by a startup loop, so the image carries no
.bss bytes..data contributes bytes to the .bin file.
True. Its initial values must survive power-off, so those bytes physically live in Flash (at the LMA) and are copied to RAM at boot.
Removing the copy loop from startup leaves initialized globals at whatever value RAM powered up with.
True. Without the Flash→RAM copy, RAM still holds garbage;
int x = 5; will not be 5 until something writes it.If you delete MEMORY { } sizes, the linker will silently produce a working image.
False. Sizes are how the linker detects overflow; without a bounded LENGTH it cannot warn you when a region overruns, so bugs surface at runtime instead.
A larger .bss makes your .bin file bigger.
False.
.bss only grows RAM usage and the recorded size field; the flashed image size is unaffected because no zeros are stored.KEEP() around the vector table is optional if you never enable garbage collection.
True but fragile. With
--gc-sections off it survives anyway, but relying on that is a trap — add KEEP so the build is correct regardless of flags.On a PC, forgetting to copy .data breaks initialized globals too.
False. The OS loader does that copy for you; the danger is unique to bare-metal where you are the loader.
Spot the error
.bss : { ... } > RAM AT> FLASH — what's wrong?
The
AT> FLASH is pointless: .bss has no initial values to load, so there is nothing for a load address to point at; it wastes intent and may confuse readers..data : { ... } > FLASH (no RAM) — what breaks?
The variables would run from Flash and be unwritable; any
x = 3; at runtime either faults or silently fails. .data must run in RAM (> RAM AT> FLASH).Startup zeroes .bss but the loop uses _sdata/_edata bounds — what's the bug?
It zeroes the
.data region instead of .bss, wiping your just-copied initial values and leaving .bss uninitialized. Use _sbss/_ebss._edata = _sdata; (forgot to add the size) — consequence?
The copy loop runs zero iterations because start equals end, so
.data is never copied; initialized globals stay garbage.Flash declared (rwx) and code tries to write a global in Flash at runtime — real problem?
The attribute is cosmetic here; the real issue is Flash physically can't be casually written at runtime, so the write is lost or faults regardless of the
w letter.Vector table placed after *(.text*) in .text — why might it fail?
The CPU reads the reset vector from a fixed low address; if the table isn't placed first (or wherever the core expects address 0 of the region), the CPU jumps to garbage on boot.
Why questions
Why does .data need two addresses at all?
Non-volatility (Flash, survives power-off) and writability (RAM) can't coexist in one address, so the value is stored in one place and run in another.
Why zero .bss in a loop instead of storing zeros in Flash?
Storing thousands of zeros wastes Flash; recording just the size plus a tiny loop costs a few bytes and a moment at boot.
Why must .bss zeroing and .data copying happen before main()?
Any C code that reads a global assumes it already holds its correct value; running
main() first would read garbage.Why is KEEP() needed for the vector table specifically?
Nothing references it by symbol name, so garbage collection thinks it's dead code and drops it — then the CPU boots into nothing.
Why does the linker script export symbols like _sdata and _ebss?
The startup C code has no way to know section addresses; the linker computes them and hands them over as symbols the loop can use as bounds.
Why is RAM usage measured as .data + .bss + stack + heap, but Flash usage as .text + .rodata + .data?
At runtime
.data and .bss both occupy RAM; in the image only .data's initial bytes (not .bss) sit in Flash alongside code and constants.Edge cases
A global int y; (never assigned) — which section, and is it in the .bin?
Zero-initialized, so it goes to
.bss and contributes no bytes to the image — just size and a boot-time zeroing.A global int x = 0; explicitly set to zero — .data or .bss?
.bss, because the value is zero; compilers put explicit-zero globals there to save Flash, same as uninitialized ones.A const int table[] = {...} — where does it live and run?
In
.rodata inside Flash for both LMA and VMA; it's read-only, so it never needs copying to RAM..data is empty (no initialized globals) — is the copy loop still safe?
Yes:
_sdata == _edata, so the loop makes zero iterations and does nothing harmful.RAM total requested exceeds LENGTH of the RAM region — when do you find out?
At link time — the linker errors on region overflow, which is why declaring correct sizes matters more than it looks.
Stack and heap grow toward each other in the same RAM region — what's the silent failure?
A stack/heap collision corrupts data with no linker warning, because both are runtime-only and their peak usage isn't known at link time.