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 个字节。对于 halb
对 malloc
的每个后续调用似乎 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
}
我正在使用 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 个字节。对于 halb
对 malloc
的每个后续调用似乎 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
}