Newlib 在 ARM 嵌入式系统中首次调用时无法分配堆

Newlib fails to allocate heap on first call in ARM embedded system

我正在使用 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 个字节。对于 halbmalloc 的每个后续调用似乎 return 一个超出我的堆栈范围的地址。第一次调用看起来像是错误,还是应该忽略它?如果是,malloc 编辑的地址 return 怎么了?

谢谢, 德文

似乎某些变量可能未正确初始化。我更新了我的链接描述文件如下:

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 博客 post。

我在使用 GNU Tools ARM Embedded arm-none-eabi 4.9 2015q1 和与 CodeSourcery 的 arm-none-eabi 4.5.2 配合良好的 _sbrk 时遇到了问题。我想我会尝试更新的版本。

然后我去尝试了 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
}