How to Use Linker Script in Embedded C: Syntax and Example
In embedded C, a
linker script controls how the program's sections are placed in memory. You write a .ld file defining memory regions and section placement, then pass it to the linker during build to customize your program's memory layout.Syntax
A linker script typically defines memory regions and section placement. The basic syntax includes:
MEMORYblock: Defines physical memory areas with start address and size.SECTIONSblock: Maps program sections like.text,.data, and.bssto memory regions.- Symbols: You can define symbols to mark addresses or sizes.
ld
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 40K
}
SECTIONS
{
.text :
{
*(.text*)
*(.rodata*)
} > FLASH
.data :
{
*(.data*)
} > RAM
.bss :
{
*(.bss*)
} > RAM
}Example
This example shows a simple embedded C program with a linker script that places code in FLASH and variables in RAM. It demonstrates how to define memory and sections, then build and run the program.
c
#include <stdint.h> // Variable in RAM int counter = 0; // Function in FLASH void increment_counter(void) { counter++; } int main(void) { increment_counter(); return counter; // Should return 1 } /* Linker script (simple.ld): MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256K RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 40K } SECTIONS { .text : { *(.text*) *(.rodata*) } > FLASH .data : { *(.data*) } > RAM .bss : { *(.bss*) } > RAM } */
Output
Program returns 1
Common Pitfalls
Common mistakes when using linker scripts include:
- Not matching memory sizes with actual hardware, causing linker errors or runtime faults.
- Forgetting to place initialized data (
.data) in RAM and code (.text) in FLASH. - Missing or incorrect section names causing sections to be discarded or misplaced.
- Not aligning sections properly, which can cause crashes on some processors.
Always verify your memory map matches your device datasheet and test the program on hardware.
ld
/* Wrong: placing .data in FLASH (read-only) */ SECTIONS { .data : { *(.data*) } > FLASH } /* Correct: place .data in RAM */ SECTIONS { .data : { *(.data*) } > RAM }
Quick Reference
Key points to remember when writing linker scripts:
- MEMORY: Define all physical memory regions with correct addresses and sizes.
- SECTIONS: Map program sections to these memory regions.
- Symbols: Use symbols to mark important addresses if needed.
- Alignment: Align sections properly for your CPU architecture.
- Build: Pass the linker script to the linker with
-T yourscript.ld.
Key Takeaways
A linker script controls how program sections are placed in memory for embedded systems.
Define MEMORY regions and map SECTIONS like .text, .data, and .bss to them.
Always match memory sizes and addresses to your hardware specifications.
Place executable code in FLASH and writable data in RAM to avoid runtime errors.
Pass the linker script to the linker using the -T option during build.