2
votes

I am using gcc-arm-none-eabi 4.9 2014q4 to write a bare-metal application for the Cortex-M4. When the application loads the first call to _sbrk appears to be invalid.

I have implemented _sbrk as follows:

extern unsigned int _start_of_heap;
extern unsigned int _end_of_heap;
caddr_t heap = (caddr_t)&_start_of_heap;

#include "hardware/uart.h"

// low level bulk memory allocator - used by malloc
caddr_t _sbrk ( int increment ) {

    caddr_t prevHeap;
    caddr_t nextHeap;

    send_str("_sbrk(");
    send_dec(increment);
    send_str(")\n");

    prevHeap = heap;

    // Always return data aligned on a 8 byte boundary
    nextHeap = (caddr_t)(((unsigned int)(heap + increment) + 7) & ~7);

    // get current stack pointer
    register caddr_t stackPtr asm ("sp");

    send_str("\tstackPtr(");
    send_hex((uint32_t)stackPtr);
    send_str(")\n");
    send_str("\tprevHeap(");
    send_hex((uint32_t)prevHeap);
    send_str(")\n");

    // Check enough space and there is no collision with stack coming the other way
    // if stack is above start of heap
    if((nextHeap < stackPtr) && (nextHeap >= (caddr_t)&_start_of_heap) && (nextHeap < (caddr_t)&_end_of_heap)) {
        heap = nextHeap;
        return (caddr_t) prevHeap;
    }
    send_str("*\n");
    return NULL; // error - no more memory
}

Linker defines the heap limits as follows:

MEMORY
{
    SRAM_L (rwx) : ORIGIN = 0x00000000, LENGTH = 32K
    SRAM_U (rwx) : ORIGIN = 0x20000000, LENGTH = 32K
}

SECTIONS
{
    .vectors 0x00000000 :
    {
        *o(.vectors_)
    } >SRAM_L

    .text :
    {
        . = ALIGN (4);
        *(.text);
    } >SRAM_L

    . = . ;
    _datai = . ;
    .data :
    {
        . = ALIGN (4);
        _data = . ; *(.data .data.*); _edata = . ;
    } >SRAM_U AT >SRAM_L

    .data_init : 
    {
        _edatai = .;
    } >SRAM_L

    .bss :
    {
        . = ALIGN (4);
        _bss = . ; *(.bss) *(COMMON); _ebss = . ;
    } >SRAM_U

    . = ALIGN (4);
    . += 8;

    free_memory_start = .;
    _end_of_stack = .;
    end = .;
    _start_of_heap = .;
    . = 0x20007000;
    _start_of_stack = .;
    _end_of_heap = .;

}

The program code runs a quick stack and heap test:

extern unsigned int _start_of_heap;
extern unsigned int _end_of_heap;
extern caddr_t heap;

void foo(uint8_t i)
{
    unsigned long blah = 0;
    unsigned long * halb;
    halb = malloc(sizeof(unsigned long));
    iprintf("blah(%08x) halb(%08x) heap(%08x)\n", &blah, halb, heap);

    if(i)
        foo(i - 1);

    free(halb);

}

int main(int argc, char ** argv)
{
    init_uart((void*)UART2_IPS_BASE_ADDR, 115200);

    iprintf("Heap test (%08x - %08x)\n", &_start_of_heap, &_end_of_heap);
    foo(10);
    return 0;

}

The following output is produced:

_sbrk(1040965006)             <----- Note large size
        stackPtr(20006E18)
        prevHeap(2000089C)
*                             <----- '*' indicates out of range
_sbrk(626)
        stackPtr(20006E18)
        prevHeap(2000089C)
Heap test (2000089c - 20007000)
blah(20006fb8) halb(00000410) heap(20000b10)
blah(20006fa0) halb(00000420) heap(20000b10)
blah(20006f88) halb(00000430) heap(20000b10)
blah(20006f70) halb(00000440) heap(20000b10)
blah(20006f58) halb(00000450) heap(20000b10)
blah(20006f40) halb(00000460) heap(20000b10)
blah(20006f28) halb(00000470) heap(20000b10)
blah(20006f10) halb(00000480) heap(20000b10)
blah(20006ef8) halb(00000490) heap(20000b10)
blah(20006ee0) halb(000004a0) heap(20000b10)
blah(20006ec8) halb(000004b0) heap(20000b10)

The first allocation is for 1040965006 bytes, this seems incorrect and it fails. After this i assume malloc proceeds to allocate 626 bytes. Every subsequent call to malloc for halb appears to return an address that is outside of the range of my stack. Does this first call look like an error, or should it be ignored and if so, what's up with the addresses returned by malloc?

Thanks, Devan

2
It appears the first time sbrk() is called, the argument passed has either not been initialized (and contains a randomly large value, or intentionally passed as a very large value. Is this something you have checked, or have I got it wrong.ryyker
Ryyker, I agree with your assessment but, I do not have JTAG support on this platform yet so confirming this is not trivial. I was hoping this was some nuance of newlib. I have created a init code that verifies the data relocation and bss zero'ing. I guess next step is to try for a routine to parse the call stack from _sbrk...devanl

2 Answers

0
votes

It appears that some variables may have been improperly initialized. I have updated my linker script as follows:

SECTIONS
{
    . = ORIGIN(SRAM_L);

    .vectors :
    {
        KEEP(*o(.vectors_))
    } >SRAM_L

    .text :
    {
        . = ALIGN(4);
        _start_text = .;
        *(.text)
        *(.text*)
        *(.rodata)
        *(.rodata*)
        _end_text = .;
    } >SRAM_L

        .ARM.extab :
        {
                *(.ARM.extab* .gnu.linkonce.armextab.*)
        } > SRAM_L

        __exidx_start = .;
        .ARM.exidx :
        {
                *(.ARM.exidx* .gnu.linkonce.armexidx.*)
        } > SRAM_L
        __exidx_end = .;

    _end_text = .;

    . = . ;
    _datai = . ;
    .data :
    {
        . = ALIGN (4);
        _data = . ; 
        *(.data)
        *(.data*)
        . = ALIGN (4);
        _edata = . ;
    } >SRAM_U AT >SRAM_L

    .data_init : 
    {
        _edatai = .;
    } >SRAM_L

    .bss :
    {
        . = ALIGN (4);
        _bss = . ; 
        *(.bss) 
        *(.bss*) 
        *(COMMON)
        . = ALIGN(4);
        _ebss = . ;
    } >SRAM_U

    . = ALIGN (4);
    . += 8;

    free_memory_start = .;
    _end_of_stack = .;
    PROVIDE(end = .);
    _start_of_heap = .;
    . = 0x20007000;
    _start_of_stack = .;
    _end_of_heap = .;

}

Thanks to sushihangover blog post.

-1
votes

I have had problems with GNU Tools ARM Embedded arm-none-eabi 4.9 2015q1 and an _sbrk that worked well with CodeSourcery's arm-none-eabi 4.5.2. I thought I would try a newer version.

I then went and tried it with 4.7 2013q1 and 4.8 2014q1, the mallocs also give the negative value for the initial request to sbrk.

I had to adjust my _sbrk to ignore negative values in order for it to work properly with both versions of the compiler. please correct me if I'm wrong but I understand that _sbrk should not be called with negative values but only the re-entrant version _sbrk_r (For OSes) should work with negatives.

Here is my implementation, if anyone is interested

extern char __heap_start__; // defined by the linker script
extern char __heap_end__;   // defined by the linker script
unsigned char * HeapSize;

unsigned char * _sbrk ( int incr )
{
  unsigned char *prev_heap;

  if (HeapSize == 0)
    HeapSize = (unsigned char *)&__heap_start__; // initialize the Heap to the start

  prev_heap = HeapSize; // store the start of this block of memmory

  if(incr > 0)
  { // only allow increments to the heap allocation

    // check that we don't run out of memory
    // could try using the stack pointer to maximise memmory use
    if(((unsigned long)HeapSize + incr) >= (unsigned long)&__heap_end__)
      return (unsigned char *) -1; // out of memmory

    HeapSize += incr; // increase the heap
  }

  return prev_heap; // return the start of the next block of memory
}