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)
  • It is as above. So unless its allocating words Im not sure whats happening. Ive added a malloc of 800B at the beginning of main but that fails. As thats before any of my C code is running, I can assume that the problem is in the startup code. Is it allocating 32bit words?

    I also print out the heap limit and base. And the same with stack; all was as specified above in linker script. Ive removed the bootloader so basically the project is the same as the Nordic GCC setup with the above defintions for stack and heap side in the linker. Ive checked the makefile and theres nothing in there that seems to stand out.

    Stepping into malloc I follow the size through the various registers but its difficult to understand whats going on in the asm only. Ill keep looking at this.

    There must be some documentation as to where and how the table malloc uses is; does anyone know where this is for the Nordic GCC setup?

  • No it's not allocating words, it's allocating bytes (on a nice pointer boundary of 4 bytes, but still it's contiguous bytes from there). Stuff like this is really frustrating when it doesn't work.

    Basic startup goes like this. reset handler calls SystemInit and then calls _start. _start is defined in whichever crt is linked into your code and that takes all the globals the linker set and zeros memory, sets up the stack, sets up the heap etc. The source of gcc malloc is available but last time I looked at it it was horrible. I had this feeling that gcc doesn't just set up the heap as you'd expect and zero it but uses a dynamic page-on-demand (sbrk) to do the job. It's possible that actually does think you are running out of mem.

    I can't help much more - I stopped with gcc a while ago and moved to Crossworks and that gives me the source of crt0 and of malloc and its' all very simple.

  • Thanks RK. The really annoying thing is finding the correct source and as you say its all in assembler. I suppose I just need to track the addresses being used which Im trying too. I cant believe this is so flaky though; I just cant see what else Im supposed to do? Surely the actual process isnt that hard... Does no one at Nordic use the heap? Maybe they dont use GCC and allocate more than 560B. Ill check their test harnesses. Ultimately I just need to resolve this pronto.

  • The malloc source is all in C and you can download the source for the GNU ARM kit (whichever one you are using). But it's immense. Trying to work out which crt0 is being linked and which sbrk is being called and which of the 4 mallocs you have is just hard. I did compile blinky and put in a malloc line and look at the assembler (objdump) and it is using sbrk. I didn't try running it, I'm not really set up for gcc any more.

    you could try that too - just compile blinky with one malloc in and have it set the LED on success/fail, crank up the heap, see what happens. Then add a bigger malloc, rinse repeat.

    I also did wonder if malloc is particularly stupid and allocating ever-increasing sizes means nothing really gets freed, but 16 + 32 + .. + 576 is a lot bigger than 2560 so that's not it either.

    And no in general Nordic doesn't use heap.

Related