Weird behavior of mallocr() function
Jeff Johnston
jjohnstn@redhat.com
Mon Jan 4 21:33:00 GMT 2010
On 03/01/10 05:52 AM, Denis Micolani wrote:
> Hi,
> I'm working on some embedded stuff, and for some reason I needed to
> get malloc() to work, newlib's release is the 1.18.0.
> I've wrote this simple function as stub (newlib is compiled to be reentrant):
>
> caddr_t _sbrk_r (struct _reent *r, int incr)
> {
> static unsigned char* _heap_current_end =&__heap_start;
>
> if (&__heap_start + incr> &__heap_end )
> {
> errno = ENOMEM;
> return (caddr_t) -1;
> }
> unsigned char* _old_heap_end = _heap_current_end;
> _heap_current_end += incr;
>
> return (caddr_t) _old_heap_end;
> }
>
> The constant addresses are taken from the linker script as above
> (these are correct and accessible):
>
> .heap 0x00200a9c 0x404 load address 0x000017b0
> 0x00200aa0 . = ALIGN (0x20)
> *fill* 0x00200a9c 0x4 00
> 0x00200aa0 __heap_start = .
> 0x00200ea0 . = (. + 0x400)
> *fill* 0x00200aa0 0x400 00
> 0x00200ea0 . = ALIGN (0x20)
> 0x00200ea0 __heap_end = .
>
>
> Anyway malloc() will ALWAYS return a NULL ptr (no matter how much I'm
> asking for, could even be 1 byte). This is the debugging output (sorry
> about the volume of it):
>
> (gdb) continue
> Continuing.
>
> Breakpoint 1, _sbrk_r (r=0x20000c, incr=32) at syscalls.c:16
> 16 if (&__heap_start + incr> &__heap_end )
> (gdb) step
> 21 unsigned char* _old_heap_end = _heap_current_end;
> (gdb) step
> 22 _heap_current_end += incr;
> (gdb) step
> 24 return (caddr_t) _old_heap_end;
> (gdb) step
> 25 }
> (gdb) step
> malloc_extend_top (reent_ptr=0x20000c, bytes=<value optimized out>)
> at ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c:2163
> 2163 ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c: No such file
> or directory.
> in ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c
> (gdb) step
> 2160 in ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c
> (gdb) step
> 2163 in ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c
> (gdb) step
> 2146 in ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c
> (gdb) step
> 2163 in ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c
> (gdb) step
> 2167 in ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c
> (gdb) step
> 2169 in ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c
> (gdb) step
> 2167 in ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c
> (gdb) step
> 2169 in ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c
> (gdb) step
> 2178 in ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c
> (gdb) step
> 2181 in ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c
> (gdb) step
> 2179 in ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c
> (gdb) step
> 2181 in ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c
> (gdb) step
> 2179 in ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c
> (gdb) step
> 2181 in ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c
> (gdb) step
> 2185 in ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c
> (gdb) step
> 2187 in ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c
> (gdb) step
> 2188 in ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c
> (gdb) step
> 2194 in ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c
> (gdb) step
> 2185 in ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c
> (gdb) step
> 2188 in ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c
> (gdb) step
> 2194 in ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c
> (gdb) step
> 2197 in ../../../../../newlib-1.18.0/newlib/libc/stdlib/mallocr.c
> (gdb) step
>
> Breakpoint 1, _sbrk_r (r=0x20000c, incr=1344) at syscalls.c:16
> 16 if (&__heap_start + incr> &__heap_end )
> (gdb) step
> 18 errno = ENOMEM;
>
>
> As you can see, there is a first request for memory, with incr =32,
> I've investigated and verified that my function (_sbrk_r()) returns
> 0x00200aa0 as address, that is the label __heap_start, and so I guess
> it's correct.
> Anyway mallocr() will make ANOTHER request to _sbrk_r, with incr=1344
> (those are bytes, what a huge number).
> I've checked the mallocr() source code and afaik this further call to
> the function _sbrk_r() has something to do with some disalignement,
> but I couldn't figure it out in its entirety.
>
> Could someone with some more experience help me about this?
For starters, your linker script is restricting the heap to be 0x400
bytes (1024). That is less that 1344 so your return code makes sense.
Malloc creates bins to reuse for small allocations so if you are really
this constrained for space, you won't be able to use the malloc logic in
newlib and will have to override it.
As well, your current code isn't going to work. You are adding incr to
heap_start and comparing this to heap_end (i.e. neither changes). I'm
pretty sure you meant to use heap_current_end in your if statement. As
well, if you are going to have a reentrant version, you might as well
set errno properly using the reentrant struct that is input. See
newlib/libc/include/sys/errno.h for the __errno_r macro.
Most sbrk implementations use the logic found in
libgloss/libnosys/sbrk.c which has the heap and stack going in opposite
directions and allocates a lump sum for the two in the linker script.
This allows peaks for either the stack or heap as long as the total is
within limits. You might want to check out this design before
implementing your own.
Regards,
-- Jeff J.
More information about the Newlib
mailing list