5.5.17Embedded Systems & Real-Time Software

Linker scripts — memory regions, sections (.text, .data, .bss)

2,153 words10 min readdifficulty · medium

WHY does a linker script even exist?

A microcontroller has at least two very different memories:

  • Flash (ROM) — non-volatile, keeps contents with power off, but you can't cheaply write to it at runtime. → good for code and constants.
  • RAM — volatile, loses contents at power-off, fast read/write. → good for variables that change.

The core tension it resolves:

  • Initialized variables (int x = 5;) need their value stored in Flash (survives power-off) but must run from RAM (so they can change). So they have two addresses: a load address (Flash) and a run address (RAM). This is the famous LMA vs VMA distinction.

WHAT are the standard sections?

Section Holds Lives at runtime in Initial value stored in Flash?
.text machine code + const data Flash yes (it is the data)
.rodata read-only constants Flash yes
.data initialized globals/statics (int x=5) RAM yes (copied at boot)
.bss zero-initialized globals (int y;) RAM no (just zeroed at boot)
.stack/.heap runtime stack & dynamic memory RAM no

HOW the boot sequence uses the script

When the chip resets, no C code has run yet — RAM is garbage. The Reset_Handler (assembly/C startup) must:

  1. Copy .data from its Flash load address (LMA) to its RAM run address (VMA).
  2. Zero .bss in RAM.
  3. Set up the stack pointer.
  4. Call main().

The linker script exports the symbols this code needs:

_sidata = LOADADDR(.data);   /* where .data init values sit in Flash */
_sdata  = ADDR(.data);       /* where .data should run, in RAM       */
_edata  = _sdata + SIZEOF(.data);
_sbss   = ADDR(.bss);
_ebss   = _sbss + SIZEOF(.bss);
// startup.c — the WHY of each line:
for (uint32_t *p=&_sidata, *q=&_sdata; q < &_edata; ) *q++ = *p++; // copy .data Flash→RAM
for (uint32_t *q=&_sbss; q < &_ebss; )                  *q++ = 0;  // zero .bss
Figure — Linker scripts — memory regions, sections (.text, .data, .bss)

A minimal, complete linker script (derived piece by piece)



Common mistakes (steel-manned)


Recall Feynman: explain to a 12-year-old

Imagine moving into a new house (the chip). You have two storage areas: an attic (Flash) that never loses your stuff even when the power's off, and a desk (RAM) that's super easy to use but gets wiped clean every night. Your instructions (code) and a list of starting values for your toys live permanently in the attic. Every morning when you wake up (reset), a little helper (startup code) carries the starting values down from the attic to the desk so you can play with them, and clears off any empty desk spots to zero. The linker script is the floor plan that tells everyone exactly which shelf each thing goes on.


Flashcards

What does a linker script's MEMORY block define?
The physical memory regions — ORIGIN (start address), LENGTH (size), and attributes (rwx) for things like FLASH and RAM.
Where does .text live and run?
Both LMA and VMA in Flash — code executes in place from non-volatile memory.
Why does .data have two different addresses (LMA and VMA)?
Initial values must be stored in non-volatile Flash (LMA), but the variables must be writable at runtime in RAM (VMA); startup code copies Flash→RAM.
What is .bss and why does it save Flash space?
Zero-initialized globals; only its size is recorded, not the zeros — startup code zeroes that RAM region in a loop.
What does AT> FLASH do in a SECTIONS entry?
Sets the section's load address (LMA) to Flash while > RAM sets the run address (VMA).
What two jobs must the Reset_Handler do before calling main()?
Copy .data from Flash to RAM, and zero out the .bss region in RAM.
Why use KEEP() around the interrupt vector table?
Nothing references it by symbol, so linker garbage collection would discard it; KEEP forces it to be retained.
What does LOADADDR(.data) return vs ADDR(.data)?
LOADADDR = LMA (Flash address of init values); ADDR = VMA (RAM run address).
If you forget to copy .data in startup, what happens?
Initialized globals contain power-on garbage instead of their declared values.
What is the meaning of the rwx attributes in RAM (rwx)?
read, write, execute permissions the linker may assign to that region.

Connections

  • Startup code & Reset_Handler
  • Vector table & interrupt handling
  • Compilation pipeline — object files & relocation
  • Volatile keyword & memory-mapped IO
  • Stack vs Heap in embedded systems
  • Flash vs RAM tradeoffs
  • ELF file format & sections

Concept Map

defines

includes

includes

places

places

places

runs from

LMA in Flash, VMA in RAM

zeroed in

exports

used by

copies .data, zeros .bss

then calls

Linker Script .ld

Memory Regions

Flash ROM non-volatile

RAM volatile

.text code + const

.data init vars

.bss zero vars

Exported Symbols

Reset_Handler startup

main

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Dekho, microcontroller mein do tarah ki memory hoti hai: Flash (power off hone par bhi data rehta hai, par runtime pe likhna mushkil) aur RAM (fast, likh-padh sakte ho, par power jaate hi sab udd jaata hai). Compiler tumhara code aur data toh banaata hai, par usse pata nahi chip ke andar kaun si cheez kahan rakhni hai. Yeh kaam linker script karta hai — yeh ek naksha (map) hai jo batata hai ki FLASH yahan hai, RAM wahan hai, aur kaun sa section kahan jayega.

Sections ko samjho: .text = tumhara code, Flash mein rehta aur wahin se chalta hai. .rodata = constants, Flash mein. .data = woh global variables jinki initial value di hai (jaise int x = 5;). Yahan twist hai — value Flash mein store hoti hai (taaki power off pe na ude), par variable RAM mein chalna chahiye (taaki change ho sake). Isi liye uske do address hote hain: LMA (Flash wala load address) aur VMA (RAM wala run address). .bss = woh variables jo zero se start hote hain (jaise int y;) — inke liye Flash mein zeros store karna waste hai, toh sirf size note karke boot pe RAM mein zeros bhar dete hain.

Ab boot ke time jab chip reset hota hai, RAM mein kuda-kachra hota hai. Toh startup code (Reset_Handler) sabse pehle .data ki values Flash se RAM mein copy karta hai, phir .bss ko zero karta hai, stack pointer set karta hai, aur tab main() call karta hai. Linker script in symbols (_sdata, _edata, _sbss, _sidata) ko export karta hai jinhe yeh copy/zero loop use karta hai.

Important baat: agar startup code mein .data copy karna bhool gaye, toh tumhare initialized globals mein garbage aayega — yeh classic bug hai. Aur .bss Flash binary mein jagah nahi leta, sirf RAM mein. Yeh sab samajh lo toh embedded mein "memory overflow" aur "variable garbage" wale errors khud solve kar paoge.

Go deeper — visual, from zero

Test yourself — Embedded Systems & Real-Time Software

Connections