Skip to main content
Associate II
September 21, 2026
Solved

Confusion about the Purpose of ._user_heap_stack

  • September 21, 2026
  • 4 replies
  • 25 views

 

I’ve been discovering the different views available in STM32Cube IDE and among them is the Build Analyzer view.

In this view I found a section in the RAM called ._user_heap_stack.

I searched for it in the linker script and found that it corresponds to the following section :

 

/* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */

._user_heap_stack :

{

. = ALIGN(8);

PROVIDE ( end = . );

PROVIDE ( _end = . );

. = . + _Min_Heap_Size;

. = . + _Min_Stack_Size;

. = ALIGN(8);

} >RAM

With 

_Min_Heap_Size = 0x200; /* required amount of heap */

_Min_Stack_Size = 0x400; /* required amount of stack */

In Figure 1, one can see that the start address of the ._user_heap_stack is 0x2000 002C. This means that the last address of the stack should be 0x2000 002C + 200 + 400 = 0x2000 062C, with the 8 byte alignment that would make it 0x2000 0630. 

Figure 1 : Screenshot of the Build Analyzer View

To make sure my calculations are correct, I added a variable in the linker script (highlighted in yellow):

/* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */

._user_heap_stack :

{

. = ALIGN(8);

PROVIDE ( end = . );

PROVIDE ( _end = . );

. = . + _Min_Heap_Size;

. = . + _Min_Stack_Size;

. = ALIGN(8);

PROVIDE ( _euserheapstack = .);

} >RAM

I accessed its value via the debugger console and it did give me this address

p/x & _euserheapstack

$2 = 0x2000 0630

So our calculations are correct!

Now what I expect is that at the start of the program (main), the stack pointer would point to this address, i.e. 0x2000 0630.

However, I found that it points to 0x2000 A000 as shown in Figure 2 which is a screenshot from the debugger console.

Figure 2: Screenshot from Debugger Console with Information about Main’s Stack Frame

I called other functions from main and I found that indeed the stack grows downwards from 0x2000A000. I opened the Memory Monitor view to verify that it is true, which you can see from Figure 3.

Figure 3 : Screenshot from Memory Monitor View Inspecting the End of the RAM Section

If we refer back to the build analyser view in Figure 1, we find that this corresponds to the last address of our RAM (start address is 0x2000 0000, size is 40KB = 0xA000, so end address is 0x2000 A000).

I took another look at the linker script and the startup code and found the lines that correspond to the initialization of the stack pointer:

  • In linker file:

/* Highest address of the user mode stack */

_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */

  • In startup code:

Reset_Handler:

ldr sp, =_estack /* Atollic update: set stack pointer */

 

I am confused why the stack pointer is assigned to start at 0x2000 A000 which is the end of the RAM and not at 0x2000 0630 which is the end of the ._user_heap_stack section. Does anyone know the what’s purpose of this section if the stack is not stored there? If it is called ._user_heap_stack one expects that the stack grows downward in that section and not way past it to at the end of the RAM section….

For reference, I am using an STM32F303 Discovery board and STM32CubeIDE Version 2.1.1 

Best answer by TDK

why is the stack pointer set to the highest address of the RAM at startup and not the highest address of the ._user_heap_stack? 

In order to maximize the space available to the stack and the heap, the stack is set to the end of the RAM section.

Setting it to the end of the ._user_heap_stack section is possible but is strictly worse than setting it to the end of the RAM section as the two would collide sooner.

I mean when one reads the linker script, he assumes that his stack would be found in this ._user_heap_stack but that’s not the case, the stack starts at an address that is higher (which is the highest address of RAM).

I can see how it would be confusing, but the reason it is done that way is because it is more performant. The linker also defines _estack explicitly. It is not hidden.

/* Highest address of the user mode stack */
_estack = ORIGIN(DTCMRAM) + LENGTH(DTCMRAM);

The user_heap_stack section is only there to check if there is enough space, as the comments note.

4 replies

TDK
September 21, 2026

The stack pointer is explicitly set during startup to _estart (“ldr sp, =_estack”), which is at the end of the RAM region. That’s why it points where it does. It points at the end because the stack pointer grows downward. The heap grows upwards.

The _Min_Heap_Size and _Min_Stack_Size inclusions you show are done in order to ensure the RAM has enough memory for the minimum sizes specified. If there isn’t enough memory, you’ll get a linker error.

Things are done this way to maximize the amount of memory available to the heap/stack before they collide.

 

The reason for it is right there in the linker comments:

/* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */

"If you feel a post has answered your question, please click ""Accept as Solution""."
melek_gAuthor
Associate II
September 21, 2026

Yes, the stack pointer grows downward, but shouldn’t it grow downward starting from the highest address of the ._user_heap_stack section? This is what I am confused about, why is the stack pointer set to the highest address of the RAM at startup and not the highest address of the ._user_heap_stack? 

I mean when one reads the linker script, he assumes that his stack would be found in this ._user_heap_stack but that’s not the case, the stack starts at an address that is higher (which is the highest address of RAM).

It’s like the code is contradicting itself, you find a section called “_user_heap_stack” that is only 1.5 KB big and you assume that’s the stack/heap space your application is relying on, then you find the stack pointer is set to the highest memory of the RAM section, meaning your application’s stack/heap is relying on the whole RAM space that is left from .bss and .data

I hope this made my confusion clearer.

TDK
TDKBest answer
September 21, 2026

why is the stack pointer set to the highest address of the RAM at startup and not the highest address of the ._user_heap_stack? 

In order to maximize the space available to the stack and the heap, the stack is set to the end of the RAM section.

Setting it to the end of the ._user_heap_stack section is possible but is strictly worse than setting it to the end of the RAM section as the two would collide sooner.

I mean when one reads the linker script, he assumes that his stack would be found in this ._user_heap_stack but that’s not the case, the stack starts at an address that is higher (which is the highest address of RAM).

I can see how it would be confusing, but the reason it is done that way is because it is more performant. The linker also defines _estack explicitly. It is not hidden.

/* Highest address of the user mode stack */
_estack = ORIGIN(DTCMRAM) + LENGTH(DTCMRAM);

The user_heap_stack section is only there to check if there is enough space, as the comments note.

"If you feel a post has answered your question, please click ""Accept as Solution""."