Reputation: 2718
Here is my linker script for a STM32L476:
/* Generate a link error if heap and stack don't fit into RAM */
__heap_size = 0x200;; /* required amount of heap */
__stack_size = 0x800;; /* required amount of stack */
/* Specify the memory areas */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 96K
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.default_exceptions :
{
. = ALIGN(8);
KEEP(*(.default_exceptions)) /* Startup code */
. = ALIGN(8);
} >FLASH
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(8);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(8);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data goes into FLASH */
.rodata :
{
. = ALIGN(8);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(8);
} >FLASH
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
.ARM : {
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} >FLASH
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} >FLASH
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
} >FLASH
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
} >FLASH
/* The startup code goes first into FLASH */
.exceptions :
{
. = ALIGN(8);
KEEP(*(.exceptions)) /* RAM vector table */
. = ALIGN(8);
} >RAM
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(8);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(8);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
} >RAM
/* User_heap_stack section, used to check that there is enough RAM left */
.heap :
{
. = ALIGN(4);
PROVIDE ( end = . );
_sheap = .;
. = . + __heap_size;
. = ALIGN(4);
_eheap = .;
} >RAM
.stack :
{
. = ALIGN(4);
_estack = .;
. = . + __stack_size;
. = ALIGN(4);
_sstack = .;
} >RAM
.ARM.attributes 0 : { *(.ARM.attributes) }
}
The corresponding map file is:
.stack 0x20002844 0x800 load address 0x0801cc68
0x20002844 . = ALIGN (0x4)
0x20002844 _estack = .
0x20003044 . = (. + __stack_size)
*fill* 0x20002844 0x800
0x20003044 . = ALIGN (0x4)
0x20003044 _sstack = .
I want to modify it in order that the stack is at the end of the RAM. I tried several ways (including the one discussed here but none is working. Even putting an hardcoded address returns an error ( the RAM goes up to 0x20018000 on this chip so it should fit ):
.stack :
{
. = 0x20001000;
_estack = .;
. = . + __stack_size;
. = ALIGN(4);
_sstack = .;
} >RAM
The error is:
20:01:46 **** Build of configuration Debug for project CardioNexion ****
make app=unit_test board=nucleo-l476rg V=1 all
c:/program files (x86)/atollic/truestudio for stm32 9.0.0/armtools/bin/../lib/gcc/arm-atollic-eabi/6.3.1/../../../../arm-atollic-eabi/bin/ld.exe: region `RAM' overflowed by 536789060 bytes
collect2.exe: error: ld returned 1 exit status
make: *** [link] Error 1
20:01:50 Build Finished (took 4s.3ms)
Any idea what may cause the issue and how do do this ? (placing the stack at end of ram).
Upvotes: 2
Views: 11949
Reputation: 67546
This "traditional" approach is not very good for the baremetal development.
Much better is to place the stack at the beginning of the RAM. There is no danger of silent variable overwrite, stack overflow will generate the exception - and its routine can take the appropriate action (for example switch the device into "safe" mode, restart, emergency stop the controlled machine etc etc.
Upvotes: 4
Reputation: 2769
If you want to put the stack on top of RAM you can use simple arithmetics in linker script like this (it is simplified):
MEMORY {
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 96K
}
SECTIONS {
__stacktop = ORIGIN(RAM) + LENGTH(RAM);
. = ORIGIN(FLASH);
.text : {
KEEP(*(.stack))
KEEP(*(.vectors))
KEEP(*(.text))
. = ALIGN(4);
KEEP(*(.rodata))
. = ALIGN(4);
} >FLASH
.data ALIGN(4) : {
__data_start = .;
*(.data)
. = ALIGN(4);
__data_end = .;
} >RAM AT >FLASH
.bss ALIGN(4) (NOLOAD) : {
__bss_start = .;
*(.bss)
. = ALIGN(4);
__bss_end = .;
} >RAM
. = ALIGN(4);
__heap_start = .;
}
It is important to have KEEP(*(.stack))
on begin of flash, then in code just put the __stacktop
to this section, like this:
one of startup files:
// top of stack
extern unsigned __stacktop;
// initial stack pointer is first address of program
__attribute__((section(".stack"), used)) unsigned *__stack_init = &__stacktop;
All unused RAM will be used from one side for HEAP and from opposite side for STACK.
Simple and complete example is here: https://github.com/cortexm/baremetal.
Upvotes: 6