2

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).

Guillaume Petitjean
  • 2,408
  • 1
  • 21
  • 47

2 Answers2

5

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.

vlk
  • 2,581
  • 3
  • 31
  • 35
  • I am still wondering why this is so complicated with LD linker to do that ? I'm used to Keil and IAR and as far as I remember, it was obvious to place the stack exactly where I wanted. In particular why is specifying hard coded value for stack start not working ? – Guillaume Petitjean May 31 '18 at 07:17
4

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.

0___________
  • 60,014
  • 4
  • 34
  • 74
  • @GuillaumePetitjean just keep in mind that the fault handler will have **no stack** at this point, any stack operation in the handler would result in a reset, unless you set up separate application and system stacks beforehand, or as the very first thing in the fault handler. – followed Monica to Codidact May 31 '18 at 11:09
  • @P__J__ Just wondering: ARM architecture pushes registers when handling the hard fault handler because of context switch (at least, push ). If there is no ram left, the push operation will end up in a reset because of an hardfaulted hardfault. Should the hard fault handler be a na_k_ed function placing the stack at the beginning of the ram? – Catosh Jun 28 '19 at 10:25
  • If you put the stack at the start of RAM, the out-of-stack exception handler cannot even be started because that involves pushing the exception info onto the no-longer-existing stack. So, double hard fault = reset, no way around it. If that's what you want, fine, but keep in mind that you can't set a debugger trap on this happening. – Matthias Urlichs Nov 23 '20 at 11:10