Newlib не может выделить кучу при первом вызове во встроенной системе ARM

Я использую gcc-arm-none-eabi 4.9 2014q4 для написания приложения для Cortex-M4. Когда приложение загружается, первый вызов _sbrk оказывается недействительным.

Я реализовал _sbrk следующим образом:

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
}

Компоновщик определяет пределы кучи следующим образом:

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

}

Код программы запускает быстрый тест стека и кучи:

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;

}

Получается следующий вывод:

_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)

Первое выделение предназначено для 1040965006 байт, это кажется неверным и не работает. После этого я предполагаю, что malloc продолжает выделять 626 байт. Каждый последующий вызов malloc для halb возвращает адрес, находящийся вне диапазона моего стека. Выглядит ли этот первый вызов как ошибка или его следует игнорировать, и если да, то что случилось с адресами, возвращаемыми malloc?

Спасибо, Деван


person devanl    schedule 04.03.2015    source источник
comment
Похоже, что при первом вызове sbrk() переданный аргумент либо не был инициализирован (и содержит случайно большое значение, либо преднамеренно передан как очень большое значение. Это то, что вы проверили, или Я неправильно понял.   -  person ryyker    schedule 04.03.2015
comment
Рюикер, я согласен с вашей оценкой, но у меня пока нет поддержки JTAG на этой платформе, поэтому подтвердить это непросто. Я надеялся, что это какой-то нюанс newlib. Я создал код инициализации, который проверяет перемещение данных и обнуление bss. Я предполагаю, что следующим шагом будет попытка подпрограммы проанализировать стек вызовов из _sbrk...   -  person devanl    schedule 05.03.2015


Ответы (2)


Похоже, что некоторые переменные могли быть неправильно инициализированы. Я обновил свой скрипт компоновщика следующим образом:

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

}

Спасибо за сообщение в блоге sushihangover.

person devanl    schedule 09.03.2015

У меня были проблемы с GNU Tools ARM Embedded arm-none-eabi 4.9 2015q1 и _sbrk, который хорошо работал с codeSourcery arm-none-eabi 4.5.2. Думал попробовать более новую версию.

Затем я пошел и попробовал это с 4.7 2013q1 и 4.8 2014q1, mallocs также дают отрицательное значение для начального запроса к sbrk.

Мне пришлось настроить мой _sbrk так, чтобы он игнорировал отрицательные значения, чтобы он правильно работал с обеими версиями компилятора. пожалуйста, поправьте меня, если я ошибаюсь, но я понимаю, что _sbrk не следует вызывать с отрицательными значениями, а только реентерабельная версия _sbrk_r (для ОС) должна работать с отрицательными значениями.

Вот моя реализация, если кому интересно

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
}
person Blacksheep    schedule 14.05.2015
comment
sbrk может быть присвоено отрицательное значение, чтобы уменьшить указатель кучи. Может быть, теперь ваш .text полностью инициализирован (см. предыдущий пост)? - person devanl; 14.05.2015
comment
Спасибо, Деван, я только что заметил параметр «free_memory_start» в скрипте компоновщика, которого нет в моем скрипте компоновщика. Я попробую это в понедельник и дам вам знать, если это имеет какое-либо значение, пока, похоже, все работает с изменениями, которые я внес в sbrk. - person Blacksheep; 15.05.2015
comment
Привет, Деван, похоже, это параметр free_memory_start в скрипте компоновщика, который ищет malloc. Спасибо еще раз за помощь. - person Blacksheep; 19.05.2015
comment
Опять же, я неправильно понял оператор (COMMON) в файле компоновщика. Мой файл компоновщика нуждался в *(.bss), когда у него был только *(.bss). Параметр free_memory_start необязателен. - person Blacksheep; 21.05.2015
comment
Мне пришлось учиться так же. Без правильного определения .data и .bss первые вызовы sbrk отбросят вас в сорняки, потому что первоначальный вызов будет иметь длину мусора. - person devanl; 22.05.2015