In this lesson, you will learn how to create linker-generated symbols that allow your C source code to "see" the exact start and end addresses of memory sections defined in your linker script.
Why Linker Symbols?
When writing low-level code—such as initializing a block of memory or clearing a section of RAM—the C compiler does not automatically know where those sections live in the physical address space. You need a way to pass the addresses defined in your linker script into your C code. Linker symbols act as bridges; they define a label that the linker resolves to a specific memory address during the final build phase.
Defining Symbols in the Linker Script
You define these symbols directly in your linker script using assignment syntax. By convention, we often use _s (start) and _e (end) prefixes.
SECTIONS {
.data : {
_sdata = .; /* Symbol gets the current location counter */
*(.data) /* Include all .data input sections */
_edata = .; /* Symbol gets the address right after .data */
} > RAM
}
Accessing Symbols in C
Because the linker calculates these symbols as addresses, we must declare them in C as extern variables. Crucially, since the symbol represents the address itself, you should not declare them as integers, but rather as placeholders. Using extern uint32_t and taking the address with the & operator is the standard approach to treat the symbol as an address pointer.
/* Declare the symbols as external */
extern uint32_t _sdata;
extern uint32_t _edata;
void init_data(void) {
uint32_t *src = &_sdata;
uint32_t *dst = &_edata;
// Now you can calculate the size or clear memory
size_t size = (size_t)dst - (size_t)src;
// Perform operations...
}
By placing these markers in your linker script, you gain the ability to perform precise memory management tasks, such as copying data from Flash to RAM or zero-initializing sections, without hard-coding addresses that might change if your memory layout is updated later.
Key Takeaway: Linker symbols bridge the gap between your memory layout defined in the linker script and your C source code, allowing your application to programmatically identify the boundaries of specific memory sections.