Line-by-Line Analysis of the Linker File
In the previous article, we analyzed the startup file, and here we will supplement an important C function within the startup file.
/**
* @brief Setup the microcontroller system
* Initialize the FPU setting, vector table location and External memory
* configuration.
* @param None
* @retval None
*/
void SystemInit(void)
{
/* FPU settings ------------------------------------------------------------*/
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
#endif
#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */
/* Configure the Vector Table location -------------------------------------*/
#if defined(USER_VECT_TAB_ADDRESS)
SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#endif /* USER_VECT_TAB_ADDRESS */
}
<span>SystemInit()</span> is the “official” hardware-level initialization entry after powering on the STM32 chip, executed by the startup file before calling <span>main()</span>. The main functions of this function are as follows:
- • Set FPU, relocate the interrupt vector table, and initialize external SRAM/SDRAM (if enabled).
- •
<span>#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)</span>If the compilation options confirm the presence of an FPU and it is to be used, enable it. - •
<span>SystemInit_ExtMemCtl();</span>Call the external memory controller initialization function to configure FSMC/FMC timing, pin multiplexing, and map external SRAM/SDRAM to addresses such as 0x6000 0000/0xC000 0000. - •
<span>#if defined(USER_VECT_TAB_ADDRESS)</span>If the user wants to move the interrupt vector table to internal SRAM, reset the vector table offset register VTOR = new base address + offset, so that the CPU exception/interrupt entry points to the new vector table.
<span>SystemInit()</span> completes three tasks before entering <span>main()</span>:
- 1. Enable FPU (if enabled);
- 2. Initialize external SRAM/SDRAM (if enabled);
- 3. Move the interrupt vector table to the specified location (if enabled).
STM32Fxx_FLASH.ld
Top Comments
** Abstract : Linker script for STM32F407VETx series
** 512Kbytes FLASH and 192Kbytes RAM
**
** Set heap size, stack size and stack location according
** to application requirements.
**
** Set memory bank area and size if external memory is used.
This linker script is suitable for the STM32F407VETx series: 512 KB FLASH, 192 KB RAM and can be configured according to application requirements:
- • Heap size
- • Stack size and stack location If external memory is used, please set the corresponding memory area and its size.
Program Entry Point
/* Program entry point: The executable file generated by the linker uses Reset_Handler as the first instruction address */
ENTRY(Reset_Handler)
/* User mode main stack (MSP) top address = highest address of RAM area */
_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of RAM */
/* If the combined heap and stack exceed the actual RAM capacity, the linker will report an error */
_Min_Heap_Size = 0x200; /* Minimum required heap space of 0x200 bytes */
_Min_Stack_Size = 0x400; /* Minimum required stack space of 0x400 bytes */
/* Define the available memory areas and their attributes on the chip */
MEMORY
{
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 512K
}
RAM Area is Divided into Two Parts:
- • RAM: Starts at 0x2000 0000, total 128 KB, executable, readable, and writable.
- • CCMRAM: Starts at 0x1000 0000, total 64 KB (CPU zero-wait SRAM, not accessible by DMA), executable, readable, and writable.
Flash Area
- • FLASH: 0x800 0000, total 512K, readable, executable.
Sections
/* Define output sections */
SECTIONS
{
...
}
Specific Sections
1. isr_vector
.isr_vector : /* Section name */
{ /* Content description */
. = ALIGN(4); /* 4-byte alignment */
KEEP(*(.isr_vector))/* Merge all .isr_vector sections from .o files */
. = ALIGN(4); /* Align again */
} >FLASH /* Finally placed in FLASH area, address calculated by the linker incrementally */
- • Physical location: Starting from FLASH (0x0800 0000).
- • Function: Holds the interrupt vector table; the CPU reads the stack top from 0x0800 0000 and Reset_Handler from 0x0800 0004 upon power-up.
- • Key symbol: None (the vector table itself is defined by g_pfnVectors in the C startup file).
2. text
.text : { /* Actual code section */
. = ALIGN(4);
*(.text) /* All .text input sections */
*(.text*) /* Wildcard sections, e.g., .text.myfunc */
*(.glue_7) /* ARM↔Thumb glue code */
*(.glue_7t)
*(.eh_frame) /* C++ exception unwind table (GCC) */
KEEP(*(.init)) /* .init code for C runtime startup */
KEEP(*(.fini)) /* .fini code for program exit */
. = ALIGN(4);
_etext = .; /* Generate global symbol: end address of code section */
} >FLASH
- • Physical location: FLASH, immediately following the vector table.
- • Key symbol: _etext → used by startup code to determine the end position of data in FLASH when moving .data.
3. rodata
.rodata : {
. = ALIGN(4);
*(.rodata) /* const strings, global const arrays, etc. */
*(.rodata*)
. = ALIGN(4);
} >FLASH
- • Physical location: FLASH, placed after .text.
- • Function: Read-only constants; do not need to be moved at runtime, can be addressed directly.
4. ARM.extab
.ARM.extab () : {
. = ALIGN(4);
*(.ARM.extab* .gnu.linkonce.armextab.*)
. = ALIGN(4);
} >FLASH
- • Physical location: FLASH, read-only.
- • Function: ARM exception unwind auxiliary table (for exceptions/stack backtrace).
5. ARM
.ARM : {
. = ALIGN(4);
__exidx_start = .;
*(.ARM.exidx*) /* C++ exception index table */
__exidx_end = .;
. = ALIGN(4);
} >FLASH
- • Physical location: FLASH
- • Key symbols: __exidx_start / __exidx_end → used by C++/exception libraries to locate the table at runtime.
6. preinit_array
.preinit_array () : {
. = ALIGN(4);
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
} >FLASH
- • Physical location: FLASH
- • Function: Early C runtime hook array (rarely used).
- • Key symbols: __preinit_array_start / end → called by __libc_init_array.
7. init_array
.init_array () : {
. = ALIGN(4);
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*))) /* C++ constructor array with priority */
KEEP(*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
} >FLASH
- • Physical location: FLASH
- • Key symbols: __init_array_start / end → __libc_init_array executes global C++ constructors in order.
8. fini_array
.fini_array () : {
. = ALIGN(4);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP(*(SORT(.fini_array.*)))
KEEP(*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
. = ALIGN(4);
} >FLASH
- • Physical location: FLASH
- • Function: Destructor table called upon program exit (bare metal rarely returns to main, generally not used).
9. _sidata
_sidata = LOADADDR(.data);
- • Not a section, but a linker variable: Records the “LMA (Load Memory Address) of .data in FLASH” → used as the source address when the startup code moves .data.
10. data
.data : {
. = ALIGN(4);
_sdata = .; /* Starting address of .data in RAM (VMA) */
*(.data) *(.data*)
*(.RamFunc) *(.RamFunc*) /* Optional: place functions here when moved to RAM */
. = ALIGN(4);
_edata = .; /* Ending address of .data in RAM */
} >RAM AT> FLASH
- • VMA is in RAM (the actual read/write location at runtime).
- • LMA is in FLASH (AT> FLASH) → during startup, moved from _sidata to _sdata.._edata.
11. _siccmram
_siccmram = LOADADDR(.ccmram);
- • Similar to _sidata, records the LMA of .ccmram in FLASH for the startup code to use during movement.
12. ccmram
.ccmram : {
. = ALIGN(4);
_sccmram = .;
*(.ccmram) *(.ccmram*)
. = ALIGN(4);
_eccmram = .;
} >CCMRAM AT> FLASH
- • VMA is at 0x1000 0000 (CCMRAM).
- • LMA is in FLASH.
- • Important: The startup code does not move by default; if initial value variables are placed here, please add a memcpy segment in Reset_Handler.
13. bss
.bss : {
. = ALIGN(4);
_sbss = .; /* Starting address of .bss in RAM */
__bss_start__ = _sbss;
*(.bss) *(.bss*) *(COMMON)
. = ALIGN(4);
_ebss = .;
__bss_end__ = _ebss;
} >RAM
- • Runtime address in RAM, LMA does not exist (since all initial values are 0).
- • The startup code uses _sbss/_ebss to clear it.
14. _user_heap_stack
._user_heap_stack : {
. = ALIGN(8);
PROVIDE(end = .);
PROVIDE(_end = .); /* Traditional Unix symbol, malloc starting point */
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >RAM
- • Purely a placeholder section, allowing the linker to calculate: Remaining RAM = 128 KB – (.data + .bss + this section) If it cannot accommodate _Min_Heap_Size + _Min_Stack_Size, the linker will report an error.
- • Runtime library/bare metal malloc uses end/_end as the heap start.
15. DISCARD
/DISCARD/ : {
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
- • Discard certain unused sections from the standard library directly, reducing image size (actual discarding depends on –gc-sections and reference conditions).
Each logical section tells the linker: “These input sections (.text/.data/.bss…) → merge → place in which physical memory → generate which symbols → align how many bytes.” The startup code then uses these symbols to complete the movement, clearing, and setting stack boundaries, among other initialization tasks.