In this lesson, you will learn how to partition physical hardware memory by writing custom linker scripts to precisely define the boundaries of your Flash and RAM.
When working in no_std embedded environments, the compiler needs to know exactly where your code resides and where your data lives. By default, the linker doesn’t know the specific memory map of your chip. If you don't provide a script, the binary might attempt to write variables to read-only Flash or execute code from non-executable RAM.
Defining the Memory Layout
A linker script acts as a set of instructions for the ld linker. It maps the logical sections of your binary—like .text (code) and .data (initialized variables)—to physical memory addresses. You define these regions using the MEMORY command:
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
}
rx: The Flash region is marked as readable and executable, perfect for instructions.rwx: The RAM region is marked as readable, writable, and executable, housing your stack, heap, and mutable global data.
Mapping Sections
Once the regions are defined, you must map the sections of your Rust binary into them. You do this using the SECTIONS command. For instance, to ensure all executable code lands in FLASH, you use:
SECTIONS
{
.text : {
*(.text*)
} > FLASH
}
The asterisk * acts as a wildcard, pulling in code from your crate and any dependencies. If you were to accidentally place a large lookup table in FLASH that was intended to be mutable, your program would trigger a hard fault the moment it attempted to modify the data. Conversely, by segregating your stack and heap into RAM, you protect your code from stack-smashing attacks or memory corruption.
By mastering these scripts, you gain total control over the binary's footprint, ensuring that your application adheres strictly to the hardware’s architectural constraints.
Key Takeaway: Linker scripts bridge the gap between abstract Rust code and physical hardware by explicitly mapping memory regions and ensuring code and data are correctly placed for the underlying processor.