Using newlib's malloc in an ARM Cortex-M3

arm, cortex-m, newlib

Solution

So, after some 10 hours spent debugging this, I have finally made it work. The problem was in the linker script. However, it was not in the bss section that I had posted, but in the text and data section. Here's the script that works.

OUTPUT_FORMAT("elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_startup)

MEMORY
{
    rom (rx)  : ORIGIN = 0x00000000, LENGTH = 512K
    ram (rwx) : ORIGIN = 0x10000000, LENGTH =  32K
}

/* Define the top our stack at the end of SRAM */
_end_stack = 0x10008000;

EXTERN(__interrupt_vector_table);

SECTIONS
{
    .text :
    {
        /* Insert the interrupt vector table first */
        __interrupt_vector_table = .;
        *(.interrupt_vector_table)
        /* Startup assembly */
        *(.startup)
        /* Rest of the code (C) */
        *(.text) *(.text.*) *(.glue_7) *(.glue_7t)
        *(.vfp11_veneer)
        *(.ARM.extab* .gnu.linkonce.armextab.*)
        *(.rodata) *(.rodata.*)
        . = ALIGN(8);
        _end_text = .;
        _start_datai = .;
    } >rom

    .data :
    {
        _start_data = .;
        *(vtable)
        *(.data) *(.data.*)
        . = ALIGN (8);
        _end_data = .;
    } >ram AT >rom

    .data_init : { _end_datai = .; } >rom

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

    .bss :
    {
        _start_bss = .;
        *(.bss)
        *(COMMON)
    } >ram 

    . = ALIGN(4);
    _end_bss = .;
    . = ALIGN(256);

    _start_heap = .;
    PROVIDE( __cs3_heap_start = _start_heap);

    /* Linker wants .eh_frame section defined because of gcc 4.4.X bug,
     * just discard it here. */
    /DISCARD/ : { *(.eh_*) }
}

_end = .;
PROVIDE(end = .);

I also had to add some variable initialization to my init code:

extern unsigned int _start_data;
extern unsigned int _end_data;
extern unsigned int _start_datai;
extern unsigned int _end_datai;

void init(void) {

    // (...) Other stuff

    // Initialize Global Variables
    uint32_t* data_begin  = (uint32_t*) &_start_data;
    uint32_t* data_end    = (uint32_t*) &_end_data;
    uint32_t* datai_begin = (uint32_t*) &_start_datai;
    uint32_t* datai_end   = (uint32_t*) &_end_datai;
    while(data_begin < data_end)
    {
        *data_begin = *datai_begin;
        data_begin++;
        datai_begin++;
    }

These two pages were quite helpful, although it still took me a lot to understand what was going on: http://fun-tech.se/stm32/linker/index.php and http://e2e.ti.com/support/microcontrollers/stellaris_arm_cortex-m3_microcontroller/f/473/t/44452.aspx?pi23648=1

I hope this might be useful to somebody else experiencing the same problems I was experiencing.

Problem

I'm creating code for an ARM Cortex-M3 (NXP's LCP17xx). I've been using static memory up to now and everything worked well. I tried to add dynamic memory support, but once I call malloc, the system gets stuck. I'm compiling with gcc for arm bare metal, and using newlib. Version: gcc-arm-none-eabi-4_6-2012q1 To add malloc support, I implemented a simple _sbrk function and modified my linker script to make some space for the heap (I've read many different tutorials about this part, but none cover the problem that I encountered next). With the help of some leds, I can be certain that the code runs up until the point that it calls `malloc`, then it doesn't go on. It doesn't even reach my `_sbrk` function. Also, it will get stuck in a call to `sizeof`, if I include a call to `malloc` later on in the code. So, what can I be doing wrong that when calling `malloc` the code gets stuck without ever reaching `_sbrk` or returning? After staring for quite a while to the memory map generated when the `malloc` call is included and when it's not, I suspect that it's related to the structures that are used by `malloc`. This is the part of the ld script that defines the ram memory: ``` .bss : { _start_bss = .; *(.bss) *(COMMON) _ebss = .; . = ALIGN (8); _end = .; } >sram . = ALIGN(4); _end_bss = .; . = ALIGN(256); _start_heap = .; PROVIDE( __cs3_heap_start = _start_heap) _end_stack = 0x10008000; ``` _end_stack is then set in the interrupt vector table. And now a comparison of the different maps. Without using malloc in the code: ``` *(COMMON) 0x1000000c _ebss = . 0x10000010 . = ALIGN (0x8) *fill* 0x1000000c 0x4 00 0x10000010 _end = . 0x10000010 . = ALIGN (0x4) 0x10000010 _end_bss = . 0x10000100 . = ALIGN (0x100) 0x10000100 _start_heap = . ``` Memory map using malloc in the code: ``` *(COMMON) COMMON 0x10000848 0x4 ...arm-none-eabi/lib/armv7-m/libc.a(lib_a-reent.o) 0x10000848 errno 0x1000084c _ebss = . 0x10000850 . = ALIGN (0x8) *fill* 0x1000084c 0x4 00 0x10000850 _end = . .bss.__malloc_max_total_mem 0x10000850 0x4 .bss.__malloc_max_total_mem 0x10000850 0x4 ...arm-none-eabi/lib/armv7-m/libc.a(lib_a-mallocr.o) 0x10000850 __malloc_max_total_mem (...) It goes on (...) 0x1000085c __malloc_current_mallinfo 0x10000884 . = ALIGN (0x4) 0x10000884 _end_bss = . 0x10000900 . = ALIGN (0x100) 0x10000900 _start_heap = . ```

Original source