In a standard software interview, “linking” is something the IDE does automatically. In a System Architect interview, the Linker Script (.ld) is a primary topic. It is the bridge between your C/C++ code and the physical silicon. If the compiler creates the “bricks” (object files), the linker script is the “blueprint” that decides where those bricks sit in the memory map.
1. The Anatomy of a Linker Script
A linker script defines two critical things: MEMORY and SECTIONS.
-
MEMORY: This block describes the physical hardware. It tells the linker exactly where the Flash (ROM) and RAM start and how large they are.
-
SECTIONS: This block tells the linker which code/data segments (from Article 2) should go into which physical memory.
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (xrw): ORIGIN = 0x20000000, LENGTH = 128K
}
SECTIONS
{
.text : { *(.text) } > FLASH
.data : { *(.data) } > RAM AT > FLASH
.bss : { *(.bss) } > RAM
}
2. The LMA vs. VMA Distinction
This is the “Architect-level” question that catches many off guard: “What is the difference between Load Memory Address (LMA) and Virtual Memory Address (VMA)?”
-
LMA (Load Address): Where the data lives when the power is off (usually in Flash).
-
VMA (Virtual/Run Address): Where the data lives when the program is running (usually in RAM).
The Scenario: Initialized globals (.data) must be stored in Flash so they aren’t lost when the power is cut. But the CPU needs to write to them, so they must be moved to RAM at boot. The Linker Script handles this using the AT > FLASH syntax shown above.
3. Linker Symbols: Communication with C Code
How does your C code know where the RAM starts or where the .bss ends so it can zero it out? The Linker Script exports Symbols.
_bss_start = .;
.bss : { *(.bss) }
_bss_end = .;
In your C code, you can access these:
extern uint32_t _bss_start, _bss_end;
// Use these addresses to memset the BSS to zero at boot
4. Custom Sections: Mapping to Hardware
Sometimes you need a variable to live at a very specific hardware address (e.g., a mailbox for Inter-Processor Communication or a configuration block at the end of Flash).
-
Step 1 (C Code):
__attribute__((section(".config_data"))) uint32_t my_config = 0x1234; -
Step 2 (Linker Script): Create a section entry for
.config_dataand map it to a specific memory region.
5. Summary Table: Common Linker Terminology
| Term | Meaning |
ENTRY() |
Defines the first instruction to run (the Reset Vector). |
KEEP() |
Tells the linker NOT to delete a section even if it seems unused (essential for Vector Tables). |
. (Dot Operator) |
The “Location Counter”—the current address the linker is working on. |
| Alignment | Ensuring sections start on 4-byte or 8-byte boundaries (using ALIGN(4)). |
Architect’s Interview Tip
When discussing linker scripts, mention Memory Overlays or Tightly Coupled Memory (TCM). Explain how you use the linker script to place performance-critical code (like a fast FFT or an ISR) into internal SRAM (TCM) while keeping the rest of the OS in slower external Flash. This shows you are optimizing for the Memory Wall we discussed in the System Design series.
In the next article, we return to modern C++: RAII and Smart Pointers — Managing Resources without a Garbage Collector.
