Использование malloc newlib в ARM Cortex-M3

Я создаю код для ARM Cortex-M3 (LCP17xx от NXP). До сих пор я использовал статическую память, и все работало хорошо. Я пытался добавить поддержку динамической памяти, но как только я вызываю malloc, система зависает.

Я компилирую с помощью gcc для голого металла и использую newlib. Версия: gcc-arm-none-eabi-4_6-2012q1

Чтобы добавить поддержку malloc, я реализовал простую функцию _sbrk и модифицировал свой скрипт компоновщика, чтобы освободить место для кучи (я читал много разных руководств по этой части, но ни одно из них не описывает проблему, с которой я столкнулся далее).

С помощью некоторых светодиодов я могу быть уверен, что код работает до момента, когда он вызывает malloc, затем он не продолжается. Он даже не достигает моей функции _sbrk. Кроме того, он застрянет в вызове sizeof, если позже я включу вызов malloc в код.

Итак, что я могу делать неправильно, что при вызове malloc код зависает, так и не достигнув _sbrk или не вернувшись?

Посмотрев некоторое время на карту памяти, сгенерированную, когда вызов malloc включен, а когда нет, я подозреваю, что это связано со структурами, которые используются malloc.

Это часть скрипта ld, определяющая оперативную память:

.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.

А теперь сравнение разных карт. Без использования malloc в коде:

 *(COMMON)
            0x1000000c                _ebss = .
            0x10000010                . = ALIGN (0x8)
 *fill*     0x1000000c        0x4 00
            0x10000010                _end = .
            0x10000010                . = ALIGN (0x4)
            0x10000010                _end_bss = .
            0x10000100                . = ALIGN (0x100)
            0x10000100                _start_heap = .

Карта памяти с использованием malloc в коде:

*(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 = .

person Marga Manterola    schedule 06.05.2012    source источник
comment
Вы уверены, что правильно вызываете код запуска, который инициализирует структуры кучи?   -  person Nathan Wiebe    schedule 06.05.2012
comment
Я явно делаю что-то не так, проблема в том, что я не знаю, ЧТО я делаю неправильно. Я добавил дополнительную информацию о картах памяти, надеюсь, это поможет найти ошибку.   -  person Marga Manterola    schedule 06.05.2012
comment
Очень похожая проблема возникает при использовании sprintf. Так что дело не только в malloc. Это связано со всем материалом newlib. Вероятно, нужно изменить сценарий связывания, хотя я не могу понять, что именно.   -  person Marga Manterola    schedule 07.05.2012
comment
Если это укладывается в ваш бюджет, рассмотрите возможность приобретения лицензии на компилятор ARM. Keil и IAR оба делают отличные компиляторы ARM, которые позаботятся о подавляющем большинстве ваших проблем с набором инструментов, и оба имеют достойную поддержку.   -  person Ben Gartner    schedule 08.10.2012


Ответы (1)


Итак, после примерно 10 часов, потраченных на отладку, я, наконец, заставил это работать. Проблема была в скрипте компоновщика. Однако это было не в разделе bss, который я разместил, а в разделе текста и данных. Вот работающий скрипт.

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 = .);

Мне также пришлось добавить некоторую инициализацию переменных в мой код инициализации:

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++;
    }

Эти две страницы оказались очень полезными, хотя мне все равно потребовалось много времени, чтобы понять, что происходит: http://fun-tech.se/stm32/linker/index.php и http://e2e.ti.com/support/microcontrollers/stellaris_arm_cortex-m3_microcontroller/f/473/t/44452.aspx?pi23648=1

Я надеюсь, что это может быть полезно для кого-то еще, столкнувшегося с теми же проблемами, что и я.

person Marga Manterola    schedule 06.05.2012
comment
Также необходимо обнулить раздел .bss: for(uint32_t* p = &_start_bss; p < &_ebss; ++p) *p = 0; - person Mike DeSimone; 10.07.2012