Linker Script Reference
Free reference guide: Linker Script Reference
About Linker Script Reference
The Linker Script Reference is a searchable cheat sheet covering the GNU ld linker script language used in embedded firmware development. It is organized into six categories — MEMORY, SECTIONS, Symbols, ENTRY, ALIGN, and Debug — to address every stage of defining how a compiled binary is laid out in flash and RAM on microcontrollers such as STM32, nRF52, and other ARM Cortex-M devices.
Embedded systems engineers, firmware developers, and electronics students use this reference when writing or debugging .ld files for bare-metal and RTOS-based projects. The MEMORY section covers ORIGIN/LENGTH declarations, access attribute flags (r, w, x), and multi-bank configurations with multiple FLASH and SRAM regions. The SECTIONS category explains how to place .text (executable code), .data (initialized variables with LMA in flash and VMA in RAM), and .bss (zero-initialized variables) into the correct memory regions.
The Symbols section addresses the location counter (.), PROVIDE() for default symbol values, the classic _sdata/_edata/_sbss/_ebss startup symbols that startup code reads to copy and zero-initialize memory, LOADADDR() to retrieve the load address of a section, and SIZEOF() for size calculations. The Debug category covers practical tools like --print-memory-usage, arm-none-eabi-size, arm-none-eabi-nm for symbol table inspection, and generating .map files to verify the final memory layout.
Key Features
- MEMORY region declarations with ORIGIN/LENGTH, access flags (rx, rwx, !w), and multi-bank FLASH/SRAM configurations
- SECTIONS: .text placement with KEEP for ISR vectors, .data with VMA/LMA split (> RAM AT> FLASH), .bss zeroing pattern
- Location counter (.) for calculating section boundaries and defining _sdata/_edata/_sbss/_ebss startup symbols
- PROVIDE() for safe default symbol definitions referenceable from C startup code via extern declarations
- ENTRY(Reset_Handler), KEEP(*(.isr_vector)), and STARTUP() for controlling program entry and linking order
- ALIGN(n) for byte-boundary alignment, stack/heap region definitions, and MPU power-of-2 alignment requirements
- Debug tools: --print-memory-usage flag, arm-none-eabi-size for section sizes, arm-none-eabi-nm for symbol table
- .map file generation with -Map=output.map to inspect per-section sizes and per-input-file contributions
Frequently Asked Questions
What is a linker script and who uses it?
A linker script (.ld file) tells the GNU ld linker where to place each section of a compiled binary in the target device's memory map. Embedded firmware engineers write linker scripts when building bare-metal applications for microcontrollers, where the flash start address, RAM start address, stack size, and heap placement must be explicitly defined.
What is the difference between VMA and LMA?
VMA (Virtual Memory Address) is the address where a section lives at runtime. LMA (Load Memory Address) is the address where the section's data is stored in flash. For .data sections, the initial values are stored in flash (LMA) and copied to RAM (VMA) by startup code. The linker script expresses this with > RAM AT> FLASH, and LOADADDR(.data) retrieves the LMA for the copy loop.
Why do I need KEEP(*(.isr_vector)) in the linker script?
When the linker's garbage collection (--gc-sections) is enabled, it removes sections that appear unreferenced. The interrupt vector table (.isr_vector) is referenced only by the hardware, not by any C code, so the linker would discard it without KEEP. Always wrap critical sections like the ISR vector table and init code with KEEP() to prevent them from being stripped.
What does PROVIDE(symbol = value) do?
PROVIDE defines a symbol only if it is not already defined elsewhere. This is useful for setting default values for symbols like _stack_size or _heap_size that startup code expects. If the user defines these symbols in their C code or another object file, the PROVIDE definition is silently ignored.
How do I check how much flash and RAM my firmware uses?
Pass -Wl,--print-memory-usage to the GCC linker flags to see a summary after each build. You can also run arm-none-eabi-size firmware.elf to see text (flash code + rodata), data (initialized variables), and bss (zero-initialized variables) sizes. Generate a .map file with -Wl,-Map=output.map for a detailed breakdown by input object file.
Why must the stack/heap be aligned to 8 bytes?
The ARM AAPCS (Procedure Call Standard) requires the stack pointer to be 8-byte aligned at public function boundaries. If the stack is misaligned, certain operations involving 64-bit data types or NEON/FPU instructions can cause hard faults. The linker script uses . = ALIGN(8) before placing the stack region to guarantee this alignment.
What is the MPU alignment requirement?
The ARM Cortex-M MPU (Memory Protection Unit) requires each protection region to be naturally aligned to a power-of-2 boundary equal to its size. For example, a 256-byte privileged data region must start at an address that is a multiple of 256. The linker script enforces this with ALIGN(256) on the section definition.
How do I discard debug sections to reduce binary size?
Add a /DISCARD/ section to the linker script that lists patterns to remove, such as *(.ARM.exidx*), *(.comment), and *(.debug_*). This removes exception unwinding tables and debug info from the final binary, reducing flash usage for release builds.