This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

GNU GCC Stack and Heap Size Issues

The end of memory bss is @ 0x20002DAC (11692B). The start of the heap is @ 0x20002DB0 with a size of 2560B. The heap ends at 0x200037B0. The stack works back from the end 0x20004000 to 0x20003800 (14336B).

So there should be enough room and I should be able to allocate 2560B....?

BUT,

I have written two routines to check the stack and heap.

The stack checks memory for 0xDEADBEEF. This gives: Stack=1596B

That iterates through increasing block sizes (16B) until the heap fails. This gives: Heap=576B

So what am I missing?

And whats this object doing in the heap?
0x20002db0 0xa00 build/gcc_startup_nrf51.o

Is it overflow, and If so why does the compiler not complain and how can I get it to be more verbose about problems like this?

LINKER SCRIPT (NORDIC GCC) - Relevant code of the script:

    .syntax unified
    .arch armv6-m

    .section .stack
    .align 3
    .equ    Stack_Size, 2048
    .globl    __StackTop
    .globl    __StackLimit
__StackLimit:
    .space    Stack_Size
    .size __StackLimit, . - __StackLimit
__StackTop:
    .size __StackTop, . - __StackTop

    .section .heap
    .align 3
    .equ    Heap_Size, 2560
    .globl    __HeapBase
    .globl    __HeapLimit
__HeapBase:
    .if    Heap_Size
    .space    Heap_Size
    .endif
    .size __HeapBase, . - __HeapBase
__HeapLimit:
    .size __HeapLimit, . - __HeapLimit

MAP FILE - Relevant output of the map file:

.bss           0x20001da8        0x0 ../../tools/gccarm/arm-none-eabi/lib/armv6-m\libnosys.a(_exit.o)
 *(COMMON)
 COMMON         0x20001da8     0x1000 build/fifo.o
                0x20001da8                uFifoTxMem
                0x200025a8                uFifoRxMem
 COMMON         0x20002da8        0x4 ../../tools/gccarm/arm-none-eabi/lib/armv6-m\libc.a(lib_a-reent.o)
                0x20002da8                errno
                0x20002dac                . = ALIGN (0x4)
                0x20002dac                __bss_end__ = .

.heap           0x20002db0      0xa00
                0x20002db0                __end__ = .
                0x20002db0                end = __end__
 *(.heap*)
 .heap          0x20002db0      0xa00 build/gcc_startup_nrf51.o
                0x20002db0                __HeapBase
                0x200037b0                __HeapLimit = .

.stack_dummy    0x20002db0      0x800
 *(.stack*)
 .stack         0x20002db0      0x800 build/gcc_startup_nrf51.o
                0x20004000                __StackTop = (ORIGIN (RAM) + 0x3ff8)
                0x20003800                __StackLimit = (__StackTop - SIZEOF (.stack_dummy))
                0x20004000                PROVIDE (__stack, __StackTop)
                0x00000001                ASSERT ((__StackLimit >= __HeapLimit), region RAM overflowed with stack)
OUTPUT(build/main_xxab.out elf32-littlearm)
  • Post the code you're using to determine that the stack and heap only have 1596 and 576 bytes respectively. You clearly have a whole load more than that so unless something else is using heap on startup, like a linked-in library etc, you have close to 0xa00 bytes available. Close to as the heap itself will have some overhead book-keeping the allocations.

    That object isn't 'in the heap', those global symbols are defined in gcc_startup_nrf51.s in the .heap section and are then used in startup to initialize the heap. Those 0xA00 bytes are your heap.

  • Code is below. Its simply, just trys to allocate increasing blocks:

    uint32_t get_stack_status(void)
    {
       uint32_t stack = 0;
       uint32_t offset = 0;
       uint32_t *address = (uint32_t *) &__StackLimit;
       
       for (; offset<((uint32_t)&__StackTop-(uint32_t)&__StackLimit); offset=offset+4)
       {
          if (*(address + offset) != 0xDEADBEEF )
          {
             break;
          }
       }
       
       stack = ((uint32_t)&__StackTop - (uint32_t)&__StackLimit) - offset;
       return stack;
    }
    
    uint32_t get_heap_usage(void)
    {  
       uint16_t mul  = 0;
       uint16_t size = 0;
       uint8_t *mem  = 0;
       
       for (mul=1; mul<200; mul++)
       {
          size = mul*16;
          mem = (uint8_t*)malloc(size);
          
          if (!mem)
          {
             break;
          }
          
          free(mem);
       }
       
       return mul;
    } 
    

    I also use a bootloader where the stack pointers are reset to that of the main image and jumps to the startup of main image.

       /* Set the vector table offset to main image. */
       LDR   R0, =0x20000000
       LDR   R1, =0x8000
       STRH  R1, [R0]
       
       /* Set the stack pointer and jump to main. */
       LDR   R2, [R1]
       MSR   MSP, R2
       LDR   R3, [R1, #0x4]
       BX    R3
    

    I dont use the heap in the bootcode but is there anything that needs to be done.

  • You're changing mul inside the loop so at the first iteration it's 1, then 16, so you try to malloc 16, then the for() adds 1 to get 17, multiply by 16 to get 272 which you malloc, now you're already larger than 200 so the loop finishes (if it didn't it would become 273, x16 = 4368 and then the malloc would fail).

    I doubt that's what you really want. I think you just want to malloc( mul * 16 ) and not change it in the loop.

    That's the first thing I see for a start

  • Sorry that was a typo which Ive corrected above. I dont have access to the same computer at the moment. I put some debug in when I performed the test yesturday to know it was iteratively testing the block sizes and passing the size to malloc. It failed at 576B max. I know theres a problem but not sure of how the heap is initialised.

  • I see - it's better to wait until you are at the machine so you can copy the actual code you have else it just masks other issues.

    heap is initialised in crt0 somewhere. That gets linked in by gcc automatically, you don't have to specify it. You should be able to single-step the assembly of malloc to see if it's doing what you expect and if the memory region looks like it was correctly set up or not.

Related