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

时间:2015-03-04 19:42:46

标签: c gcc newlib

我正在使用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的{​​{1}}的每次后续调用似乎都会返回超出我的堆栈范围的地址。这第一次调用看起来像是错误,还是应该被忽略?如果是,那么halb返回的地址是什么?

谢谢, 德万

2 个答案:

答案 0 :(得分:0)

似乎某些变量可能未正确初始化。我更新了我的链接器脚本,如下所示:

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博文。

答案 1 :(得分:-1)

我遇到了GNU工具ARM嵌入式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
}