URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
Compare Revisions
- This comparison shows the changes necessary to convert path
/or1k/trunk/linux/uClibc/libc/stdlib/malloc
- from Rev 1325 to Rev 1765
- ↔ Reverse comparison
Rev 1325 → Rev 1765
/heap_free.c
0,0 → 1,89
/* |
* libc/stdlib/malloc/heap_free.c -- return memory to a heap |
* |
* Copyright (C) 2002 NEC Corporation |
* Copyright (C) 2002 Miles Bader <miles@gnu.org> |
* |
* This file is subject to the terms and conditions of the GNU Lesser |
* General Public License. See the file COPYING.LIB in the main |
* directory of this archive for more details. |
* |
* Written by Miles Bader <miles@gnu.org> |
*/ |
|
#include <stdlib.h> |
|
#include "heap.h" |
|
|
/* Return the block of memory at MEM, of size SIZE, to HEAP. */ |
struct heap_free_area * |
__heap_free (struct heap *heap, void *mem, size_t size) |
{ |
struct heap_free_area *fa, *prev_fa; |
void *end = (char *)mem + size; |
|
HEAP_DEBUG (heap, "before __heap_free"); |
|
/* Find the right position in the free-list entry to place the new block. |
This is the most speed critical loop in this malloc implementation: |
since we use a simple linked-list for the free-list, and we keep it in |
address-sorted order, it can become very expensive to insert something |
in the free-list when it becomes fragmented and long. [A better |
implemention would use a balanced tree or something for the free-list, |
though that bloats the code-size and complexity quite a bit.] */ |
for (prev_fa = 0, fa = heap->free_areas; fa; prev_fa = fa, fa = fa->next) |
if (unlikely (HEAP_FREE_AREA_END (fa) >= mem)) |
break; |
|
if (fa && HEAP_FREE_AREA_START (fa) <= end) |
/* The free-area FA is adjacent to the new block, merge them. */ |
{ |
size_t fa_size = fa->size + size; |
|
if (HEAP_FREE_AREA_START (fa) == end) |
/* FA is just after the new block, grow down to encompass it. */ |
{ |
/* See if FA can now be merged with its predecessor. */ |
if (prev_fa && mem == HEAP_FREE_AREA_END (prev_fa)) |
/* Yup; merge PREV_FA's info into FA. */ |
{ |
fa_size += prev_fa->size; |
__heap_link_free_area_after (heap, fa, prev_fa->prev); |
} |
} |
else |
/* FA is just before the new block, expand to encompass it. */ |
{ |
struct heap_free_area *next_fa = fa->next; |
|
/* See if FA can now be merged with its successor. */ |
if (next_fa && end == HEAP_FREE_AREA_START (next_fa)) |
/* Yup; merge FA's info into NEXT_FA. */ |
{ |
fa_size += next_fa->size; |
__heap_link_free_area_after (heap, next_fa, prev_fa); |
fa = next_fa; |
} |
else |
/* FA can't be merged; move the descriptor for it to the tail-end |
of the memory block. */ |
{ |
/* The new descriptor is at the end of the extended block, |
SIZE bytes later than the old descriptor. */ |
fa = (struct heap_free_area *)((char *)fa + size); |
/* Update links with the neighbors in the list. */ |
__heap_link_free_area (heap, fa, prev_fa, next_fa); |
} |
} |
|
fa->size = fa_size; |
} |
else |
/* Make the new block into a separate free-list entry. */ |
fa = __heap_add_free_area (heap, mem, size, prev_fa, fa); |
|
HEAP_DEBUG (heap, "after __heap_free"); |
|
return fa; |
} |
/free.c
0,0 → 1,262
/* |
* libc/stdlib/malloc/free.c -- free function |
* |
* Copyright (C) 2002,03 NEC Electronics Corporation |
* Copyright (C) 2002,03 Miles Bader <miles@gnu.org> |
* |
* This file is subject to the terms and conditions of the GNU Lesser |
* General Public License. See the file COPYING.LIB in the main |
* directory of this archive for more details. |
* |
* Written by Miles Bader <miles@gnu.org> |
*/ |
|
#include <stdlib.h> |
#include <unistd.h> |
#include <sys/mman.h> |
|
#include "malloc.h" |
#include "heap.h" |
|
|
static void |
free_to_heap (void *mem, struct heap *heap) |
{ |
size_t size; |
struct heap_free_area *fa; |
|
/* Check for special cases. */ |
if (unlikely (! mem)) |
return; |
|
/* Normal free. */ |
|
MALLOC_DEBUG (1, "free: 0x%lx (base = 0x%lx, total_size = %d)", |
(long)mem, (long)MALLOC_BASE (mem), MALLOC_SIZE (mem)); |
|
size = MALLOC_SIZE (mem); |
mem = MALLOC_BASE (mem); |
|
__heap_lock (heap); |
|
/* Put MEM back in the heap, and get the free-area it was placed in. */ |
fa = __heap_free (heap, mem, size); |
|
/* See if the free-area FA has grown big enough that it should be |
unmapped. */ |
if (HEAP_FREE_AREA_SIZE (fa) < MALLOC_UNMAP_THRESHOLD) |
/* Nope, nothing left to do, just release the lock. */ |
__heap_unlock (heap); |
else |
/* Yup, try to unmap FA. */ |
{ |
unsigned long start = (unsigned long)HEAP_FREE_AREA_START (fa); |
unsigned long end = (unsigned long)HEAP_FREE_AREA_END (fa); |
#ifndef MALLOC_USE_SBRK |
# ifdef __UCLIBC_UCLINUX_BROKEN_MUNMAP__ |
struct malloc_mmb *mmb, *prev_mmb; |
unsigned long mmb_start, mmb_end; |
# else /* !__UCLIBC_UCLINUX_BROKEN_MUNMAP__ */ |
unsigned long unmap_start, unmap_end; |
# endif /* __UCLIBC_UCLINUX_BROKEN_MUNMAP__ */ |
#endif /* !MALLOC_USE_SBRK */ |
|
#ifdef MALLOC_USE_SBRK |
/* Get the sbrk lock so that the two possible calls to sbrk below |
are guaranteed to be contiguous. */ |
__malloc_lock_sbrk (); |
/* When using sbrk, we only shrink the heap from the end. It would |
be possible to allow _both_ -- shrinking via sbrk when possible, |
and otherwise shrinking via munmap, but this results in holes in |
memory that prevent the brk from every growing back down; since |
we only ever grow the heap via sbrk, this tends to produce a |
continuously growing brk (though the actual memory is unmapped), |
which could eventually run out of address space. Note that |
`sbrk(0)' shouldn't normally do a system call, so this test is |
reasonably cheap. */ |
if ((void *)end != sbrk (0)) |
{ |
MALLOC_DEBUG (-1, "not unmapping: 0x%lx - 0x%lx (%ld bytes)", |
start, end, end - start); |
__malloc_unlock_sbrk (); |
__heap_unlock (heap); |
return; |
} |
#endif |
|
MALLOC_DEBUG (0, "unmapping: 0x%lx - 0x%lx (%ld bytes)", |
start, end, end - start); |
|
/* Remove FA from the heap. */ |
__heap_delete (heap, fa); |
|
if (__heap_is_empty (heap)) |
/* We want to avoid the heap from losing all memory, so reserve |
a bit. This test is only a heuristic -- the existance of |
another free area, even if it's smaller than |
MALLOC_MIN_SIZE, will cause us not to reserve anything. */ |
{ |
/* Put the reserved memory back in the heap; we asssume that |
MALLOC_UNMAP_THRESHOLD is greater than MALLOC_MIN_SIZE, so |
we use the latter unconditionally here. */ |
__heap_free (heap, (void *)start, MALLOC_MIN_SIZE); |
start += MALLOC_MIN_SIZE; |
} |
|
#ifdef MALLOC_USE_SBRK |
|
/* Release the heap lock; we're still holding the sbrk lock. */ |
__heap_unlock (heap); |
/* Lower the brk. */ |
sbrk (start - end); |
/* Release the sbrk lock too; now we hold no locks. */ |
__malloc_unlock_sbrk (); |
|
#else /* !MALLOC_USE_SBRK */ |
|
# ifdef __UCLIBC_UCLINUX_BROKEN_MUNMAP__ |
/* Using the uClinux broken munmap, we have to only munmap blocks |
exactly as we got them from mmap, so scan through our list of |
mmapped blocks, and return them in order. */ |
|
MALLOC_MMB_DEBUG (1, "walking mmb list for region 0x%x[%d]...", |
start, end - start); |
|
prev_mmb = 0; |
mmb = __malloc_mmapped_blocks; |
while (mmb |
&& ((mmb_end = (mmb_start = (unsigned long)mmb->mem) + mmb->size) |
<= end)) |
{ |
MALLOC_MMB_DEBUG (1, "considering mmb at 0x%x: 0x%x[%d]", |
(unsigned)mmb, mmb_start, mmb_end - mmb_start); |
|
if (mmb_start >= start |
/* If the space between START and MMB_START is non-zero, but |
too small to return to the heap, we can't unmap MMB. */ |
&& (start == mmb_start |
|| mmb_start - start > HEAP_MIN_FREE_AREA_SIZE)) |
{ |
struct malloc_mmb *next_mmb = mmb->next; |
|
if (mmb_end != end && mmb_end + HEAP_MIN_FREE_AREA_SIZE > end) |
/* There's too little space left at the end to deallocate |
this block, so give up. */ |
break; |
|
MALLOC_MMB_DEBUG (1, "unmapping mmb at 0x%x: 0x%x[%d]", |
(unsigned)mmb, mmb_start, mmb_end - mmb_start); |
|
if (mmb_start != start) |
/* We're going to unmap a part of the heap that begins after |
start, so put the intervening region back into the heap. */ |
{ |
MALLOC_MMB_DEBUG (0, "putting intervening region back into heap: 0x%x[%d]", |
start, mmb_start - start); |
__heap_free (heap, (void *)start, mmb_start - start); |
} |
|
MALLOC_MMB_DEBUG_INDENT (-1); |
|
/* Unlink MMB from the list. */ |
if (prev_mmb) |
prev_mmb->next = next_mmb; |
else |
__malloc_mmapped_blocks = next_mmb; |
|
/* Start searching again from the end of this block. */ |
start = mmb_end; |
|
/* We have to unlock the heap before we recurse to free the mmb |
descriptor, because we might be unmapping from the mmb |
heap. */ |
__heap_unlock (heap); |
|
/* Release the descriptor block we used. */ |
free_to_heap (mmb, &__malloc_mmb_heap); |
|
/* Do the actual munmap. */ |
munmap ((void *)mmb_start, mmb_end - mmb_start); |
|
__heap_lock (heap); |
|
# ifdef __UCLIBC_HAS_THREADS__ |
/* In a multi-threaded program, it's possible that PREV_MMB has |
been invalidated by another thread when we released the |
heap lock to do the munmap system call, so just start over |
from the beginning of the list. It sucks, but oh well; |
it's probably not worth the bother to do better. */ |
prev_mmb = 0; |
mmb = __malloc_mmapped_blocks; |
# else |
mmb = next_mmb; |
# endif |
} |
else |
{ |
prev_mmb = mmb; |
mmb = mmb->next; |
} |
|
MALLOC_MMB_DEBUG_INDENT (-1); |
} |
|
if (start != end) |
/* Hmm, well there's something we couldn't unmap, so put it back |
into the heap. */ |
{ |
MALLOC_MMB_DEBUG (0, "putting tail region back into heap: 0x%x[%d]", |
start, end - start); |
__heap_free (heap, (void *)start, end - start); |
} |
|
/* Finally release the lock for good. */ |
__heap_unlock (heap); |
|
MALLOC_MMB_DEBUG_INDENT (-1); |
|
# else /* !__UCLIBC_UCLINUX_BROKEN_MUNMAP__ */ |
|
/* MEM/LEN may not be page-aligned, so we have to page-align them, |
and return any left-over bits on the end to the heap. */ |
unmap_start = MALLOC_ROUND_UP_TO_PAGE_SIZE (start); |
unmap_end = MALLOC_ROUND_DOWN_TO_PAGE_SIZE (end); |
|
/* We have to be careful that any left-over bits are large enough to |
return. Note that we _don't check_ to make sure there's room to |
grow/shrink the start/end by another page, we just assume that |
the unmap threshold is high enough so that this is always safe |
(i.e., it should probably be at least 3 pages). */ |
if (unmap_start > start) |
{ |
if (unmap_start - start < HEAP_MIN_FREE_AREA_SIZE) |
unmap_start += MALLOC_PAGE_SIZE; |
__heap_free (heap, (void *)start, unmap_start - start); |
} |
if (end > unmap_end) |
{ |
if (end - unmap_end < HEAP_MIN_FREE_AREA_SIZE) |
unmap_end -= MALLOC_PAGE_SIZE; |
__heap_free (heap, (void *)unmap_end, end - unmap_end); |
} |
|
/* Release the heap lock before we do the system call. */ |
__heap_unlock (heap); |
|
if (unmap_end > unmap_start) |
/* Finally, actually unmap the memory. */ |
munmap ((void *)unmap_start, unmap_end - unmap_start); |
|
# endif /* __UCLIBC_UCLINUX_BROKEN_MUNMAP__ */ |
|
#endif /* MALLOC_USE_SBRK */ |
} |
|
MALLOC_DEBUG_INDENT (-1); |
} |
|
void |
free (void *mem) |
{ |
free_to_heap (mem, &__malloc_heap); |
} |
/realloc.c
0,0 → 1,91
/* |
* libc/stdlib/malloc/realloc.c -- realloc function |
* |
* Copyright (C) 2002 NEC Corporation |
* Copyright (C) 2002 Miles Bader <miles@gnu.org> |
* |
* This file is subject to the terms and conditions of the GNU Lesser |
* General Public License. See the file COPYING.LIB in the main |
* directory of this archive for more details. |
* |
* Written by Miles Bader <miles@gnu.org> |
*/ |
|
#include <stdlib.h> |
#include <string.h> |
#include <errno.h> |
|
#include "malloc.h" |
#include "heap.h" |
|
|
void * |
realloc (void *mem, size_t new_size) |
{ |
size_t size; |
char *base_mem; |
|
/* Check for special cases. */ |
if (! mem) |
return malloc (new_size); |
if (! new_size) |
{ |
free (mem); |
return malloc (new_size); |
} |
|
/* Normal realloc. */ |
|
base_mem = MALLOC_BASE (mem); |
size = MALLOC_SIZE (mem); |
|
/* Include extra space to record the size of the allocated block. |
Also make sure that we're dealing in a multiple of the heap |
allocation unit (SIZE is already guaranteed to be so).*/ |
new_size = HEAP_ADJUST_SIZE (new_size + MALLOC_HEADER_SIZE); |
|
MALLOC_DEBUG (1, "realloc: 0x%lx, %d (base = 0x%lx, total_size = %d)", |
(long)mem, new_size, (long)base_mem, size); |
|
if (new_size > size) |
/* Grow the block. */ |
{ |
size_t extra = new_size - size; |
|
__heap_lock (&__malloc_heap); |
extra = __heap_alloc_at (&__malloc_heap, base_mem + size, extra); |
__heap_unlock (&__malloc_heap); |
|
if (extra) |
/* Record the changed size. */ |
MALLOC_SET_SIZE (base_mem, size + extra); |
else |
/* Our attempts to extend MEM in place failed, just |
allocate-and-copy. */ |
{ |
void *new_mem = malloc (new_size - MALLOC_HEADER_SIZE); |
if (new_mem) |
{ |
memcpy (new_mem, mem, size - MALLOC_HEADER_SIZE); |
free (mem); |
} |
mem = new_mem; |
} |
} |
else if (new_size + MALLOC_REALLOC_MIN_FREE_SIZE <= size) |
/* Shrink the block. */ |
{ |
__heap_lock (&__malloc_heap); |
__heap_free (&__malloc_heap, base_mem + new_size, size - new_size); |
__heap_unlock (&__malloc_heap); |
MALLOC_SET_SIZE (base_mem, new_size); |
} |
|
if (mem) |
MALLOC_DEBUG (-1, "realloc: returning 0x%lx (base:0x%lx, total_size:%d)", |
(long)mem, (long)MALLOC_BASE(mem), (long)MALLOC_SIZE(mem)); |
else |
MALLOC_DEBUG (-1, "realloc: returning 0"); |
|
return mem; |
} |
/heap.h
0,0 → 1,254
/* |
* libc/stdlib/malloc/heap.h -- heap allocator used for malloc |
* |
* Copyright (C) 2002,03 NEC Electronics Corporation |
* Copyright (C) 2002,03 Miles Bader <miles@gnu.org> |
* |
* This file is subject to the terms and conditions of the GNU Lesser |
* General Public License. See the file COPYING.LIB in the main |
* directory of this archive for more details. |
* |
* Written by Miles Bader <miles@gnu.org> |
*/ |
|
#include <features.h> |
|
|
/* On multi-threaded systems, the heap includes a lock. */ |
#ifdef __UCLIBC_HAS_THREADS__ |
# include <pthread.h> |
# define HEAP_USE_LOCKING |
#endif |
|
|
/* The heap allocates in multiples of, and aligned to, HEAP_GRANULARITY. |
HEAP_GRANULARITY must be a power of 2. Malloc depends on this being the |
same as MALLOC_ALIGNMENT. */ |
#define HEAP_GRANULARITY_TYPE double |
#define HEAP_GRANULARITY (sizeof (HEAP_GRANULARITY_TYPE)) |
|
|
/* A heap is a collection of memory blocks, from which smaller blocks |
of memory can be allocated. */ |
struct heap |
{ |
/* A list of memory in the heap available for allocation. */ |
struct heap_free_area *free_areas; |
|
#ifdef HEAP_USE_LOCKING |
/* A lock that can be used by callers to control access to the heap. |
The heap code _does not_ use this lock, it's merely here for the |
convenience of users! */ |
pthread_mutex_t lock; |
#endif |
}; |
|
/* The HEAP_INIT macro can be used as a static initializer for a heap |
variable. The HEAP_INIT_WITH_FA variant is used to initialize a heap |
with an initial static free-area; its argument FA should be declared |
using HEAP_DECLARE_STATIC_FREE_AREA. */ |
#ifdef HEAP_USE_LOCKING |
# define HEAP_INIT { 0, PTHREAD_MUTEX_INITIALIZER } |
# define HEAP_INIT_WITH_FA(fa) { &fa._fa, PTHREAD_MUTEX_INITIALIZER } |
#else |
# define HEAP_INIT { 0 } |
# define HEAP_INIT_WITH_FA(fa) { &fa._fa } |
#endif |
|
/* A free-list area `header'. These are actually stored at the _ends_ of |
free areas (to make allocating from the beginning of the area simpler), |
so one might call it a `footer'. */ |
struct heap_free_area |
{ |
size_t size; |
struct heap_free_area *next, *prev; |
}; |
|
/* Return the address of the end of the frea area FA. */ |
#define HEAP_FREE_AREA_END(fa) ((void *)(fa + 1)) |
/* Return the address of the beginning of the frea area FA. FA is |
evaulated multiple times. */ |
#define HEAP_FREE_AREA_START(fa) ((void *)((char *)(fa + 1) - (fa)->size)) |
/* Return the size of the frea area FA. */ |
#define HEAP_FREE_AREA_SIZE(fa) ((fa)->size) |
|
/* This rather clumsy macro allows one to declare a static free-area for |
passing to HEAP_INIT_WITH_FA initializer macro. This is only use for |
which NAME is allowed. */ |
#define HEAP_DECLARE_STATIC_FREE_AREA(name, size) \ |
static struct \ |
{ \ |
HEAP_GRANULARITY_TYPE aligned_space; \ |
char space[HEAP_ADJUST_SIZE(size) \ |
- sizeof (struct heap_free_area) \ |
- HEAP_GRANULARITY]; \ |
struct heap_free_area _fa; \ |
} name = { (HEAP_GRANULARITY_TYPE)0, "", { HEAP_ADJUST_SIZE(size), 0, 0 } } |
|
|
/* Rounds SZ up to be a multiple of HEAP_GRANULARITY. */ |
#define HEAP_ADJUST_SIZE(sz) \ |
(((sz) + HEAP_GRANULARITY - 1) & ~(HEAP_GRANULARITY - 1)) |
|
|
/* The minimum allocatable size. */ |
#define HEAP_MIN_SIZE HEAP_ADJUST_SIZE (sizeof (struct heap_free_area)) |
|
/* The minimum size of a free area; if allocating memory from a free-area |
would make the free-area smaller than this, the allocation is simply |
given the whole free-area instead. It must include at least enough room |
to hold a struct heap_free_area, plus some extra to avoid excessive heap |
fragmentation (thus increasing speed). This is only a heuristic -- it's |
possible for smaller free-areas than this to exist (say, by realloc |
returning the tail-end of a previous allocation), but __heap_alloc will |
try to get rid of them when possible. */ |
#define HEAP_MIN_FREE_AREA_SIZE \ |
HEAP_ADJUST_SIZE (sizeof (struct heap_free_area) + 32) |
|
|
/* branch-prediction macros; they may already be defined by libc. */ |
#ifndef likely |
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96) |
#define likely(cond) __builtin_expect(!!(int)(cond), 1) |
#define unlikely(cond) __builtin_expect((int)(cond), 0) |
#else |
#define likely(cond) (cond) |
#define unlikely(cond) (cond) |
#endif |
#endif /* !likely */ |
|
|
/* Define HEAP_DEBUGGING to cause the heap routines to emit debugging info |
to stderr when the variable __heap_debug is set to true. */ |
#ifdef HEAP_DEBUGGING |
extern int __heap_debug; |
#define HEAP_DEBUG(heap, str) (__heap_debug ? __heap_dump (heap, str) : 0) |
#else |
#define HEAP_DEBUG(heap, str) (void)0 |
#endif |
|
/* Output a text representation of HEAP to stderr, labelling it with STR. */ |
extern void __heap_dump (struct heap *heap, const char *str); |
|
/* Do some consistency checks on HEAP. If they fail, output an error |
message to stderr, and exit. STR is printed with the failure message. */ |
extern void __heap_check (struct heap *heap, const char *str); |
|
|
#ifdef HEAP_USE_LOCKING |
# define __heap_lock(heap) __pthread_mutex_lock (&(heap)->lock) |
# define __heap_unlock(heap) __pthread_mutex_unlock (&(heap)->lock) |
#else /* !__UCLIBC_HAS_THREADS__ */ |
/* Without threads, mutex operations are a nop. */ |
# define __heap_lock(heap) (void)0 |
# define __heap_unlock(heap) (void)0 |
#endif /* HEAP_USE_LOCKING */ |
|
|
/* Delete the free-area FA from HEAP. */ |
static inline void |
__heap_delete (struct heap *heap, struct heap_free_area *fa) |
{ |
if (fa->next) |
fa->next->prev = fa->prev; |
if (fa->prev) |
fa->prev->next = fa->next; |
else |
heap->free_areas = fa->next; |
} |
|
|
/* Link the free-area FA between the existing free-area's PREV and NEXT in |
HEAP. PREV and NEXT may be 0; if PREV is 0, FA is installed as the |
first free-area. */ |
static inline void |
__heap_link_free_area (struct heap *heap, struct heap_free_area *fa, |
struct heap_free_area *prev, |
struct heap_free_area *next) |
{ |
fa->next = next; |
fa->prev = prev; |
|
if (prev) |
prev->next = fa; |
else |
heap->free_areas = fa; |
if (next) |
next->prev = fa; |
} |
|
/* Update the mutual links between the free-areas PREV and FA in HEAP. |
PREV may be 0, in which case FA is installed as the first free-area (but |
FA may not be 0). */ |
static inline void |
__heap_link_free_area_after (struct heap *heap, |
struct heap_free_area *fa, |
struct heap_free_area *prev) |
{ |
if (prev) |
prev->next = fa; |
else |
heap->free_areas = fa; |
fa->prev = prev; |
} |
|
/* Add a new free-area MEM, of length SIZE, in between the existing |
free-area's PREV and NEXT in HEAP, and return a pointer to its header. |
PREV and NEXT may be 0; if PREV is 0, MEM is installed as the first |
free-area. */ |
static inline struct heap_free_area * |
__heap_add_free_area (struct heap *heap, void *mem, size_t size, |
struct heap_free_area *prev, |
struct heap_free_area *next) |
{ |
struct heap_free_area *fa = (struct heap_free_area *) |
((char *)mem + size - sizeof (struct heap_free_area)); |
|
fa->size = size; |
|
__heap_link_free_area (heap, fa, prev, next); |
|
return fa; |
} |
|
|
/* Allocate SIZE bytes from the front of the free-area FA in HEAP, and |
return the amount actually allocated (which may be more than SIZE). */ |
static inline size_t |
__heap_free_area_alloc (struct heap *heap, |
struct heap_free_area *fa, size_t size) |
{ |
size_t fa_size = fa->size; |
|
if (fa_size < size + HEAP_MIN_FREE_AREA_SIZE) |
/* There's not enough room left over in FA after allocating the block, so |
just use the whole thing, removing it from the list of free areas. */ |
{ |
__heap_delete (heap, fa); |
/* Remember that we've alloced the whole area. */ |
size = fa_size; |
} |
else |
/* Reduce size of FA to account for this allocation. */ |
fa->size = fa_size - size; |
|
return size; |
} |
|
|
/* Allocate and return a block at least *SIZE bytes long from HEAP. |
*SIZE is adjusted to reflect the actual amount allocated (which may be |
greater than requested). */ |
extern void *__heap_alloc (struct heap *heap, size_t *size); |
|
/* Allocate SIZE bytes at address MEM in HEAP. Return the actual size |
allocated, or 0 if we failed. */ |
extern size_t __heap_alloc_at (struct heap *heap, void *mem, size_t size); |
|
/* Return the memory area MEM of size SIZE to HEAP. |
Returns the heap free area into which the memory was placed. */ |
extern struct heap_free_area *__heap_free (struct heap *heap, |
void *mem, size_t size); |
|
/* Return true if HEAP contains absolutely no memory. */ |
#define __heap_is_empty(heap) (! (heap)->free_areas) |
/heap_debug.c
0,0 → 1,142
/* |
* libc/stdlib/malloc/heap_debug.c -- optional heap debugging routines |
* |
* Copyright (C) 2002 NEC Corporation |
* Copyright (C) 2002 Miles Bader <miles@gnu.org> |
* |
* This file is subject to the terms and conditions of the GNU Lesser |
* General Public License. See the file COPYING.LIB in the main |
* directory of this archive for more details. |
* |
* Written by Miles Bader <miles@gnu.org> |
*/ |
|
#include <stdlib.h> |
#include <stdio.h> |
#include <stdarg.h> |
#include <string.h> |
|
#include "malloc.h" |
#include "heap.h" |
|
|
#ifdef HEAP_DEBUGGING |
int __heap_debug = 0; |
#endif |
|
|
static void |
__heap_dump_freelist (struct heap *heap) |
{ |
struct heap_free_area *fa; |
for (fa = heap->free_areas; fa; fa = fa->next) |
__malloc_debug_printf (0, |
"0x%lx: 0x%lx - 0x%lx (%d)\tP=0x%lx, N=0x%lx", |
(long)fa, |
(long)HEAP_FREE_AREA_START (fa), |
(long)HEAP_FREE_AREA_END (fa), |
fa->size, |
(long)fa->prev, |
(long)fa->next); |
} |
|
/* Output a text representation of HEAP to stderr, labelling it with STR. */ |
void |
__heap_dump (struct heap *heap, const char *str) |
{ |
static int recursed = 0; |
|
if (! recursed) |
{ |
__heap_check (heap, str); |
|
recursed = 1; |
|
__malloc_debug_printf (1, "%s: heap @0x%lx:", str, (long)heap); |
__heap_dump_freelist (heap); |
__malloc_debug_indent (-1); |
|
recursed = 0; |
} |
} |
|
|
/* Output an error message to stderr, and exit. STR is printed with the |
failure message. */ |
static void |
__heap_check_failure (struct heap *heap, struct heap_free_area *fa, |
const char *str, char *fmt, ...) |
{ |
va_list val; |
|
if (str) |
fprintf (stderr, "\nHEAP CHECK FAILURE %s: ", str); |
else |
fprintf (stderr, "\nHEAP CHECK FAILURE: "); |
|
va_start (val, fmt); |
vfprintf (stderr, fmt, val); |
va_end (val); |
|
putc ('\n', stderr); |
|
__malloc_debug_set_indent (0); |
__malloc_debug_printf (1, "heap dump:"); |
__heap_dump_freelist (heap); |
|
exit (22); |
} |
|
/* Do some consistency checks on HEAP. If they fail, output an error |
message to stderr, and exit. STR is printed with the failure message. */ |
void |
__heap_check (struct heap *heap, const char *str) |
{ |
typedef unsigned long ul_t; |
struct heap_free_area *fa, *prev; |
struct heap_free_area *first_fa = heap->free_areas; |
|
if (first_fa && first_fa->prev) |
__heap_check_failure (heap, first_fa, str, |
"first free-area has non-zero prev pointer:\n\ |
first free-area = 0x%lx\n\ |
(0x%lx)->prev = 0x%lx\n", |
(ul_t)first_fa, |
(ul_t)first_fa, (ul_t)first_fa->prev); |
|
for (prev = 0, fa = first_fa; fa; prev = fa, fa = fa->next) |
{ |
if (((ul_t)HEAP_FREE_AREA_END (fa) & (HEAP_GRANULARITY - 1)) |
|| (fa->size & (HEAP_GRANULARITY - 1))) |
__heap_check_failure (heap, fa, str, "alignment error:\n\ |
(0x%lx)->start = 0x%lx\n\ |
(0x%lx)->size = 0x%lx\n", |
(ul_t)fa, |
(ul_t)HEAP_FREE_AREA_START (fa), |
(ul_t)fa, fa->size); |
|
if (fa->prev != prev) |
__heap_check_failure (heap, fa, str, "prev pointer corrupted:\n\ |
(0x%lx)->next = 0x%lx\n\ |
(0x%lx)->prev = 0x%lx\n", |
(ul_t)prev, (ul_t)prev->next, |
(ul_t)fa, (ul_t)fa->prev); |
|
if (prev) |
{ |
ul_t start = (ul_t)HEAP_FREE_AREA_START (fa); |
ul_t prev_end = (ul_t)HEAP_FREE_AREA_END (prev); |
|
if (prev_end >= start) |
__heap_check_failure (heap, fa, str, |
"start %s with prev free-area end:\n\ |
(0x%lx)->prev = 0x%lx\n\ |
(0x%lx)->start = 0x%lx\n\ |
(0x%lx)->end = 0x%lx\n", |
(prev_end == start ? "unmerged" : "overlaps"), |
(ul_t)fa, (ul_t)prev, |
(ul_t)fa, start, |
(ul_t)prev, prev_end); |
} |
} |
} |
/heap_alloc.c
0,0 → 1,51
/* |
* libc/stdlib/malloc/heap_alloc.c -- allocate memory from a heap |
* |
* Copyright (C) 2002 NEC Corporation |
* Copyright (C) 2002 Miles Bader <miles@gnu.org> |
* |
* This file is subject to the terms and conditions of the GNU Lesser |
* General Public License. See the file COPYING.LIB in the main |
* directory of this archive for more details. |
* |
* Written by Miles Bader <miles@gnu.org> |
*/ |
|
#include <stdlib.h> |
|
#include "heap.h" |
|
|
/* Allocate and return a block at least *SIZE bytes long from HEAP. |
*SIZE is adjusted to reflect the actual amount allocated (which may be |
greater than requested). */ |
void * |
__heap_alloc (struct heap *heap, size_t *size) |
{ |
struct heap_free_area *fa; |
size_t _size = *size; |
void *mem = 0; |
|
_size = HEAP_ADJUST_SIZE (_size); |
|
if (_size < sizeof (struct heap_free_area)) |
/* Because we sometimes must use a freed block to hold a free-area node, |
we must make sure that every allocated block can hold one. */ |
_size = HEAP_ADJUST_SIZE (sizeof (struct heap_free_area)); |
|
HEAP_DEBUG (heap, "before __heap_alloc"); |
|
/* Look for a free area that can contain _SIZE bytes. */ |
for (fa = heap->free_areas; fa; fa = fa->next) |
if (fa->size >= _size) |
{ |
/* Found one! */ |
mem = HEAP_FREE_AREA_START (fa); |
*size = __heap_free_area_alloc (heap, fa, _size); |
break; |
} |
|
HEAP_DEBUG (heap, "after __heap_alloc"); |
|
return mem; |
} |
/malloc.c
0,0 → 1,211
/* |
* libc/stdlib/malloc/malloc.c -- malloc function |
* |
* Copyright (C) 2002,03 NEC Electronics Corporation |
* Copyright (C) 2002,03 Miles Bader <miles@gnu.org> |
* |
* This file is subject to the terms and conditions of the GNU Lesser |
* General Public License. See the file COPYING.LIB in the main |
* directory of this archive for more details. |
* |
* Written by Miles Bader <miles@gnu.org> |
*/ |
|
#include <stdlib.h> |
#include <unistd.h> |
#include <errno.h> |
#include <sys/mman.h> |
|
#include "malloc.h" |
#include "heap.h" |
|
|
/* The malloc heap. We provide a bit of initial static space so that |
programs can do a little mallocing without mmaping in more space. */ |
HEAP_DECLARE_STATIC_FREE_AREA (initial_fa, 256); |
struct heap __malloc_heap = HEAP_INIT_WITH_FA (initial_fa); |
|
#if defined(MALLOC_USE_LOCKING) && defined(MALLOC_USE_SBRK) |
/* A lock protecting our use of sbrk. */ |
malloc_mutex_t __malloc_sbrk_lock; |
#endif /* MALLOC_USE_LOCKING && MALLOC_USE_SBRK */ |
|
|
#ifdef __UCLIBC_UCLINUX_BROKEN_MUNMAP__ |
/* A list of all malloc_mmb structures describing blocsk that |
malloc has mmapped, ordered by the block address. */ |
struct malloc_mmb *__malloc_mmapped_blocks = 0; |
|
/* A heap used for allocating malloc_mmb structures. We could allocate |
them from the main heap, but that tends to cause heap fragmentation in |
annoying ways. */ |
HEAP_DECLARE_STATIC_FREE_AREA (initial_mmb_fa, 48); /* enough for 3 mmbs */ |
struct heap __malloc_mmb_heap = HEAP_INIT_WITH_FA (initial_mmb_fa); |
#endif /* __UCLIBC_UCLINUX_BROKEN_MUNMAP__ */ |
|
|
static void * |
malloc_from_heap (size_t size, struct heap *heap) |
{ |
void *mem; |
|
MALLOC_DEBUG (1, "malloc: %d bytes", size); |
|
/* Include extra space to record the size of the allocated block. */ |
size += MALLOC_HEADER_SIZE; |
|
__heap_lock (heap); |
|
/* First try to get memory that's already in our heap. */ |
mem = __heap_alloc (heap, &size); |
|
__heap_unlock (heap); |
|
if (unlikely (! mem)) |
/* We couldn't allocate from the heap, so grab some more |
from the system, add it to the heap, and try again. */ |
{ |
/* If we're trying to allocate a block bigger than the default |
MALLOC_HEAP_EXTEND_SIZE, make sure we get enough to hold it. */ |
void *block; |
size_t block_size |
= (size < MALLOC_HEAP_EXTEND_SIZE |
? MALLOC_HEAP_EXTEND_SIZE |
: MALLOC_ROUND_UP_TO_PAGE_SIZE (size)); |
|
/* Allocate the new heap block. */ |
#ifdef MALLOC_USE_SBRK |
|
__malloc_lock_sbrk (); |
|
/* Use sbrk we can, as it's faster than mmap, and guarantees |
contiguous allocation. */ |
block = sbrk (block_size); |
if (likely (block != (void *)-1)) |
{ |
/* Because sbrk can return results of arbitrary |
alignment, align the result to a MALLOC_ALIGNMENT boundary. */ |
long aligned_block = MALLOC_ROUND_UP ((long)block, MALLOC_ALIGNMENT); |
if (block != (void *)aligned_block) |
/* Have to adjust. We should only have to actually do this |
the first time (after which we will have aligned the brk |
correctly). */ |
{ |
/* Move the brk to reflect the alignment; our next allocation |
should start on exactly the right alignment. */ |
sbrk (aligned_block - (long)block); |
block = (void *)aligned_block; |
} |
} |
|
__malloc_unlock_sbrk (); |
|
#else /* !MALLOC_USE_SBRK */ |
|
/* Otherwise, use mmap. */ |
block = mmap (0, block_size, PROT_READ | PROT_WRITE, |
MAP_SHARED | MAP_ANONYMOUS, 0, 0); |
|
#endif /* MALLOC_USE_SBRK */ |
|
if (likely (block != (void *)-1)) |
{ |
#if !defined(MALLOC_USE_SBRK) && defined(__UCLIBC_UCLINUX_BROKEN_MUNMAP__) |
struct malloc_mmb *mmb, *prev_mmb, *new_mmb; |
#endif |
|
MALLOC_DEBUG (1, "adding system memroy to heap: 0x%lx - 0x%lx (%d bytes)", |
(long)block, (long)block + block_size, block_size); |
|
/* Get back the heap lock. */ |
__heap_lock (heap); |
|
/* Put BLOCK into the heap. */ |
__heap_free (heap, block, block_size); |
|
MALLOC_DEBUG_INDENT (-1); |
|
/* Try again to allocate. */ |
mem = __heap_alloc (heap, &size); |
|
__heap_unlock (heap); |
|
#if !defined(MALLOC_USE_SBRK) && defined(__UCLIBC_UCLINUX_BROKEN_MUNMAP__) |
/* Insert a record of BLOCK in sorted order into the |
__malloc_mmapped_blocks list. */ |
|
for (prev_mmb = 0, mmb = __malloc_mmapped_blocks; |
mmb; |
prev_mmb = mmb, mmb = mmb->next) |
if (block < mmb->mem) |
break; |
|
new_mmb = malloc_from_heap (sizeof *new_mmb, &__malloc_mmb_heap); |
new_mmb->next = mmb; |
new_mmb->mem = block; |
new_mmb->size = block_size; |
|
if (prev_mmb) |
prev_mmb->next = new_mmb; |
else |
__malloc_mmapped_blocks = new_mmb; |
|
MALLOC_MMB_DEBUG (0, "new mmb at 0x%x: 0x%x[%d]", |
(unsigned)new_mmb, |
(unsigned)new_mmb->mem, block_size); |
#endif /* !MALLOC_USE_SBRK && __UCLIBC_UCLINUX_BROKEN_MUNMAP__ */ |
} |
} |
|
if (likely (mem)) |
/* Record the size of the block and get the user address. */ |
{ |
mem = MALLOC_SETUP (mem, size); |
|
MALLOC_DEBUG (-1, "malloc: returning 0x%lx (base:0x%lx, total_size:%ld)", |
(long)mem, (long)MALLOC_BASE(mem), (long)MALLOC_SIZE(mem)); |
} |
else |
MALLOC_DEBUG (-1, "malloc: returning 0"); |
|
return mem; |
} |
|
void * |
malloc (size_t size) |
{ |
void *mem; |
#ifdef MALLOC_DEBUGGING |
static int debugging_initialized = 0; |
if (! debugging_initialized) |
{ |
debugging_initialized = 1; |
__malloc_debug_init (); |
} |
if (__malloc_check) |
__heap_check (&__malloc_heap, "malloc"); |
#endif |
|
#ifdef __MALLOC_GLIBC_COMPAT__ |
if (unlikely (size == 0)) |
size++; |
#else |
/* Some programs will call malloc (0). Lets be strict and return NULL */ |
if (unlikely (size == 0)) |
return 0; |
#endif |
|
/* Check if they are doing something dumb like malloc(-1) */ |
if (unlikely(((unsigned long)size > (unsigned long)(MALLOC_HEADER_SIZE*-2)))) |
goto oom; |
|
mem = malloc_from_heap (size, &__malloc_heap); |
if (unlikely (!mem)) |
{ |
oom: |
__set_errno (ENOMEM); |
return 0; |
} |
|
return mem; |
} |
/memalign.c
0,0 → 1,94
/* |
* libc/stdlib/malloc/memalign.c -- memalign (`aligned malloc') function |
* |
* Copyright (C) 2002 NEC Corporation |
* Copyright (C) 2002 Miles Bader <miles@gnu.org> |
* |
* This file is subject to the terms and conditions of the GNU Lesser |
* General Public License. See the file COPYING.LIB in the main |
* directory of this archive for more details. |
* |
* Written by Miles Bader <miles@gnu.org> |
*/ |
|
#include <stdlib.h> |
#include <unistd.h> |
#include <sys/mman.h> |
|
#include "malloc.h" |
#include "heap.h" |
|
|
#define MAX(x,y) ((x) > (y) ? (x) : (y)) |
|
/* |
______________________ TOTAL _________________________ |
/ \ |
+---------------+-------------------------+--------------+ |
| | | | |
+---------------+-------------------------+--------------+ |
\____ INIT ____/ \______ RETURNED _______/ \____ END ___/ |
*/ |
|
void * |
memalign (size_t alignment, size_t size) |
{ |
void *mem, *base; |
unsigned long tot_addr, tot_end_addr, addr, end_addr; |
struct heap *heap = &__malloc_heap; |
|
/* Make SIZE something we like. */ |
size = HEAP_ADJUST_SIZE (size); |
|
/* Use malloc to do the initial allocation, since it deals with getting |
system memory. We over-allocate enough to be sure that we'll get |
enough memory to hold a properly aligned block of size SIZE, |
_somewhere_ in the result. */ |
mem = malloc (size + 2 * alignment); |
if (! mem) |
/* Allocation failed, we can't do anything. */ |
return 0; |
if (alignment < MALLOC_ALIGNMENT) |
return mem; |
|
/* Remember the base-address, of the allocation, although we normally |
use the user-address for calculations, since that's where the |
alignment matters. */ |
base = MALLOC_BASE (mem); |
|
/* The bounds of the initial allocation. */ |
tot_addr = (unsigned long)mem; |
tot_end_addr = (unsigned long)base + MALLOC_SIZE (mem); |
|
/* Find a likely place inside MEM with the right alignment. */ |
addr = MALLOC_ROUND_UP (tot_addr, alignment); |
|
/* Unless TOT_ADDR was already aligned correctly, we need to return the |
initial part of MEM to the heap. */ |
if (addr != tot_addr) |
{ |
size_t init_size = addr - tot_addr; |
|
/* Ensure that memory returned to the heap is large enough. */ |
if (init_size < HEAP_MIN_SIZE) |
{ |
addr = MALLOC_ROUND_UP (tot_addr + HEAP_MIN_SIZE, alignment); |
init_size = addr - tot_addr; |
} |
|
__heap_free (heap, base, init_size); |
|
/* Remember that we've freed the initial part of MEM. */ |
base += init_size; |
} |
|
/* Return the end part of MEM to the heap, unless it's too small. */ |
end_addr = addr + size; |
if (end_addr + MALLOC_REALLOC_MIN_FREE_SIZE < tot_end_addr) |
__heap_free (heap, (void *)end_addr, tot_end_addr - end_addr); |
else |
/* We didn't free the end, so include it in the size. */ |
end_addr = tot_end_addr; |
|
return MALLOC_SETUP (base, end_addr - (unsigned long)base); |
} |
/heap_alloc_at.c
0,0 → 1,47
/* |
* libc/stdlib/malloc/heap_alloc_at.c -- allocate at a specific address |
* |
* Copyright (C) 2002 NEC Corporation |
* Copyright (C) 2002 Miles Bader <miles@gnu.org> |
* |
* This file is subject to the terms and conditions of the GNU Lesser |
* General Public License. See the file COPYING.LIB in the main |
* directory of this archive for more details. |
* |
* Written by Miles Bader <miles@gnu.org> |
*/ |
|
#include <stdlib.h> |
|
#include "heap.h" |
|
|
/* Allocate SIZE bytes at address MEM in HEAP. Return the actual size |
allocated, or 0 if we failed. */ |
size_t |
__heap_alloc_at (struct heap *heap, void *mem, size_t size) |
{ |
struct heap_free_area *fa; |
size_t alloced = 0; |
|
size = HEAP_ADJUST_SIZE (size); |
|
HEAP_DEBUG (heap, "before __heap_alloc_at"); |
|
/* Look for a free area that can contain SIZE bytes. */ |
for (fa = heap->free_areas; fa; fa = fa->next) |
{ |
void *fa_mem = HEAP_FREE_AREA_START (fa); |
if (fa_mem <= mem) |
{ |
if (fa_mem == mem && fa->size >= size) |
/* FA has the right addr, and is big enough! */ |
alloced = __heap_free_area_alloc (heap, fa, size); |
break; |
} |
} |
|
HEAP_DEBUG (heap, "after __heap_alloc_at"); |
|
return alloced; |
} |
/Makefile
0,0 → 1,58
# Makefile for uClibc |
# |
# Copyright (C) 2002,03 NEC Electronics Corporation |
# Copyright (C) 2002,03 Miles Bader <miles@gnu.org> |
# |
# This program is free software; you can redistribute it and/or modify it under |
# the terms of the GNU Library General Public License as published by the Free |
# Software Foundation; either version 2 of the License, or (at your option) any |
# later version. |
# |
# This program is distributed in the hope that it will be useful, but WITHOUT |
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
# FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more |
# details. |
# |
# You should have received a copy of the GNU Library General Public License |
# along with this program; if not, write to the Free Software Foundation, Inc., |
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
# |
# Derived in part from the Linux-8086 C library, the GNU C Library, and several |
# other sundry sources. Files within this library are copyright by their |
# respective copyright holders. |
|
TOPDIR=../../../ |
include $(TOPDIR)Rules.mak |
|
CSRC = malloc.c calloc.c free.c realloc.c memalign.c \ |
heap_alloc.c heap_alloc_at.c heap_free.c |
|
# Turn on malloc debugging if requested |
ifeq ($(UCLIBC_MALLOC_DEBUGGING),y) |
CSRC += malloc_debug.c heap_debug.c |
CFLAGS += -DMALLOC_DEBUGGING -DHEAP_DEBUGGING |
ifeq ($(UCLIBC_UCLINUX_BROKEN_MUNMAP),y) |
CFLAGS += -DMALLOC_MMB_DEBUGGING |
endif |
endif |
|
COBJS=$(patsubst %.c,%.o, $(CSRC)) |
OBJS=$(COBJS) |
|
all: $(OBJS) $(LIBC) |
|
$(LIBC): ar-target |
|
ar-target: $(OBJS) |
$(AR) $(ARFLAGS) $(LIBC) $(OBJS) |
|
malloc.o free.o realloc.o memalign.o: malloc.h |
$(COBJS): heap.h |
|
# Depend on uClinux_config.h to cache changes in __UCLIBC_MALLOC_DEBUGGING__ |
$(COBJS): %.o : %.c ../../../include/bits/uClibc_config.h |
$(CC) $(CFLAGS) -c $< -o $@ |
$(STRIPTOOL) -x -R .note -R .comment $*.o |
|
clean: |
$(RM) *.[oa] *~ core |
/malloc.h
0,0 → 1,214
/* |
* libc/stdlib/malloc/malloc.h -- small malloc implementation |
* |
* Copyright (C) 2002 NEC Corporation |
* Copyright (C) 2002 Miles Bader <miles@gnu.org> |
* |
* This file is subject to the terms and conditions of the GNU Lesser |
* General Public License. See the file COPYING.LIB in the main |
* directory of this archive for more details. |
* |
* Written by Miles Bader <miles@gnu.org> |
*/ |
|
/* The alignment we guarantee for malloc return values. */ |
#define MALLOC_ALIGNMENT (sizeof (double)) |
|
/* The system pagesize we assume; we really ought to get it with |
getpagesize, but gee, how annoying. */ |
#define MALLOC_PAGE_SIZE 4096 |
|
/* The minimum size of block we request from the the system to extend the |
heap for small allocations (we may request a bigger block if necessary to |
satisfy a particularly big request). */ |
#define MALLOC_HEAP_EXTEND_SIZE MALLOC_PAGE_SIZE |
|
/* When a heap free-area grows above this size, try to unmap it, releasing |
the memory back to the system. */ |
#define MALLOC_UNMAP_THRESHOLD (8*MALLOC_PAGE_SIZE) |
/* When unmapping a free-area, retain this many bytes if it's the only one, |
to avoid completely emptying the heap. This is only a heuristic -- the |
existance of another free area, even if it's smaller than |
MALLOC_MIN_SIZE, will cause us not to reserve anything. */ |
#define MALLOC_MIN_SIZE (2*MALLOC_PAGE_SIZE) |
|
/* When realloc shrinks an allocation, it only does so if more than this |
many bytes will be freed; it must at at least HEAP_MIN_SIZE. Larger |
values increase speed (by reducing heap fragmentation) at the expense of |
space. */ |
#define MALLOC_REALLOC_MIN_FREE_SIZE (HEAP_MIN_SIZE + 16) |
|
|
/* For systems with an MMU, use sbrk to map/unmap memory for the malloc |
heap, instead of mmap/munmap. This is a tradeoff -- sbrk is faster than |
mmap/munmap, and guarantees contiguous allocation, but is also less |
flexible, and causes the heap to only be shrinkable from the end. */ |
#ifdef __UCLIBC_HAS_MMU__ |
# define MALLOC_USE_SBRK |
#endif |
|
|
/* The current implementation of munmap in uClinux doesn't work correctly: |
it requires that ever call to munmap exactly match a corresponding call |
to mmap (that is, it doesn't allow you to unmap only part of a |
previously allocated block, or to unmap two contiguous blocks with a |
single call to munmap). This behavior is broken, and uClinux should be |
fixed; however, until it is, we add code to work around the problem in |
malloc. */ |
#ifdef __UCLIBC_UCLINUX_BROKEN_MUNMAP__ |
|
/* A structure recording a block of memory mmapped by malloc. */ |
struct malloc_mmb |
{ |
void *mem; /* the mmapped block */ |
size_t size; /* its size */ |
struct malloc_mmb *next; |
}; |
|
/* A list of all malloc_mmb structures describing blocsk that malloc has |
mmapped, ordered by the block address. */ |
extern struct malloc_mmb *__malloc_mmapped_blocks; |
|
/* A heap used for allocating malloc_mmb structures. We could allocate |
them from the main heap, but that tends to cause heap fragmentation in |
annoying ways. */ |
extern struct heap __malloc_mmb_heap; |
|
/* Define MALLOC_MMB_DEBUGGING to cause malloc to emit debugging info about |
about mmap block allocation/freeing by the `uclinux broken munmap' code |
to stderr, when the variable __malloc_mmb_debug is set to true. */ |
#ifdef MALLOC_MMB_DEBUGGING |
# include <stdio.h> |
extern int __malloc_mmb_debug; |
# define MALLOC_MMB_DEBUG(indent, fmt, args...) \ |
(__malloc_mmb_debug ? __malloc_debug_printf (indent, fmt , ##args) : 0) |
# define MALLOC_MMB_DEBUG_INDENT(indent) \ |
(__malloc_mmb_debug ? __malloc_debug_indent (indent) : 0) |
# ifndef MALLOC_DEBUGGING |
# define MALLOC_DEBUGGING |
# endif |
#else /* !MALLOC_MMB_DEBUGGING */ |
# define MALLOC_MMB_DEBUG(fmt, args...) (void)0 |
# define MALLOC_MMB_DEBUG_INDENT(indent) (void)0 |
#endif /* MALLOC_MMB_DEBUGGING */ |
|
#endif /* __UCLIBC_UCLINUX_BROKEN_MUNMAP__ */ |
|
|
/* The size of a malloc allocation is stored in a size_t word |
MALLOC_ALIGNMENT bytes prior to the start address of the allocation: |
|
+--------+---------+-------------------+ |
| SIZE |(unused) | allocation ... | |
+--------+---------+-------------------+ |
^ BASE ^ ADDR |
^ ADDR - MALLOC_ALIGN |
*/ |
|
/* The amount of extra space used by the malloc header. */ |
#define MALLOC_HEADER_SIZE MALLOC_ALIGNMENT |
|
/* Set up the malloc header, and return the user address of a malloc block. */ |
#define MALLOC_SETUP(base, size) \ |
(MALLOC_SET_SIZE (base, size), (void *)((char *)base + MALLOC_HEADER_SIZE)) |
/* Set the size of a malloc allocation, given the base address. */ |
#define MALLOC_SET_SIZE(base, size) (*(size_t *)(base) = (size)) |
|
/* Return base-address of a malloc allocation, given the user address. */ |
#define MALLOC_BASE(addr) ((void *)((char *)addr - MALLOC_HEADER_SIZE)) |
/* Return the size of a malloc allocation, given the user address. */ |
#define MALLOC_SIZE(addr) (*(size_t *)MALLOC_BASE(addr)) |
|
|
/* Locking for multithreaded apps. */ |
#ifdef __UCLIBC_HAS_THREADS__ |
|
# include <pthread.h> |
|
# define MALLOC_USE_LOCKING |
|
typedef pthread_mutex_t malloc_mutex_t; |
# define MALLOC_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER |
|
# ifdef MALLOC_USE_SBRK |
/* This lock is used to serialize uses of the `sbrk' function (in both |
malloc and free, sbrk may be used several times in succession, and |
things will break if these multiple calls are interleaved with another |
thread's use of sbrk!). */ |
extern malloc_mutex_t __malloc_sbrk_lock; |
# define __malloc_lock_sbrk() __pthread_mutex_lock (&__malloc_sbrk_lock) |
# define __malloc_unlock_sbrk() __pthread_mutex_unlock (&__malloc_sbrk_lock) |
# endif /* MALLOC_USE_SBRK */ |
|
#else /* !__UCLIBC_HAS_THREADS__ */ |
|
/* Without threads, mutex operations are a nop. */ |
# define __malloc_lock_sbrk() (void)0 |
# define __malloc_unlock_sbrk() (void)0 |
|
#endif /* __UCLIBC_HAS_THREADS__ */ |
|
|
/* branch-prediction macros; they may already be defined by libc. */ |
#ifndef likely |
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96) |
#define likely(cond) __builtin_expect(!!(int)(cond), 1) |
#define unlikely(cond) __builtin_expect((int)(cond), 0) |
#else |
#define likely(cond) (cond) |
#define unlikely(cond) (cond) |
#endif |
#endif /* !likely */ |
|
|
/* Define MALLOC_DEBUGGING to cause malloc to emit debugging info to stderr |
when the variable __malloc_debug is set to true. */ |
#ifdef MALLOC_DEBUGGING |
|
extern void __malloc_debug_init (void); |
|
/* The number of spaces in a malloc debug indent level. */ |
#define MALLOC_DEBUG_INDENT_SIZE 3 |
|
extern int __malloc_debug, __malloc_check; |
|
# define MALLOC_DEBUG(indent, fmt, args...) \ |
(__malloc_debug ? __malloc_debug_printf (indent, fmt , ##args) : 0) |
# define MALLOC_DEBUG_INDENT(indent) \ |
(__malloc_debug ? __malloc_debug_indent (indent) : 0) |
|
extern int __malloc_debug_cur_indent; |
|
/* Print FMT and args indented at the current debug print level, followed |
by a newline, and change the level by INDENT. */ |
extern void __malloc_debug_printf (int indent, const char *fmt, ...); |
|
/* Change the current debug print level by INDENT, and return the value. */ |
#define __malloc_debug_indent(indent) (__malloc_debug_cur_indent += indent) |
|
/* Set the current debug print level to LEVEL. */ |
#define __malloc_debug_set_indent(level) (__malloc_debug_cur_indent = level) |
|
#else /* !MALLOC_DEBUGGING */ |
# define MALLOC_DEBUG(fmt, args...) (void)0 |
# define MALLOC_DEBUG_INDENT(indent) (void)0 |
#endif /* MALLOC_DEBUGGING */ |
|
|
/* Return SZ rounded down to POWER_OF_2_SIZE (which must be power of 2). */ |
#define MALLOC_ROUND_DOWN(sz, power_of_2_size) \ |
((sz) & ~(power_of_2_size - 1)) |
/* Return SZ rounded to POWER_OF_2_SIZE (which must be power of 2). */ |
#define MALLOC_ROUND_UP(sz, power_of_2_size) \ |
MALLOC_ROUND_DOWN ((sz) + (power_of_2_size - 1), (power_of_2_size)) |
|
/* Return SZ rounded down to a multiple MALLOC_PAGE_SIZE. */ |
#define MALLOC_ROUND_DOWN_TO_PAGE_SIZE(sz) \ |
MALLOC_ROUND_DOWN (sz, MALLOC_PAGE_SIZE) |
/* Return SZ rounded up to a multiple MALLOC_PAGE_SIZE. */ |
#define MALLOC_ROUND_UP_TO_PAGE_SIZE(sz) \ |
MALLOC_ROUND_UP (sz, MALLOC_PAGE_SIZE) |
|
|
/* The malloc heap. */ |
extern struct heap __malloc_heap; |
/calloc.c
0,0 → 1,41
/* vi: set sw=4 ts=4: */ |
/* calloc for uClibc |
* |
* Copyright (C) 2002 by Erik Andersen <andersen@uclibc.org> |
* |
* This program is free software; you can redistribute it and/or modify it |
* under the terms of the GNU Library General Public License as published by |
* the Free Software Foundation; either version 2 of the License, or (at your |
* option) any later version. |
* |
* This program is distributed in the hope that it will be useful, but WITHOUT |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License |
* for more details. |
* |
* You should have received a copy of the GNU Library General Public License |
* along with this program; if not, write to the Free Software Foundation, |
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
*/ |
|
#include <stdlib.h> |
#include <string.h> |
#include <errno.h> |
|
void * calloc(size_t nmemb, size_t lsize) |
{ |
void *result; |
size_t size=lsize * nmemb; |
|
/* guard vs integer overflow, but allow nmemb |
* to fall through and call malloc(0) */ |
if (nmemb && lsize != (size / nmemb)) { |
__set_errno(ENOMEM); |
return NULL; |
} |
if ((result=malloc(size)) != NULL) { |
memset(result, 0, size); |
} |
return result; |
} |
|
/malloc_debug.c
0,0 → 1,86
/* |
* libc/stdlib/malloc/malloc_debug.c -- malloc debugging support |
* |
* Copyright (C) 2002 NEC Corporation |
* Copyright (C) 2002 Miles Bader <miles@gnu.org> |
* |
* This file is subject to the terms and conditions of the GNU Lesser |
* General Public License. See the file COPYING.LIB in the main |
* directory of this archive for more details. |
* |
* Written by Miles Bader <miles@gnu.org> |
*/ |
|
#include <stdlib.h> |
#include <stdio.h> |
#include <unistd.h> |
#include <stdarg.h> |
|
#include "malloc.h" |
#include "heap.h" |
|
int __malloc_debug = 0, __malloc_check = 0; |
|
#ifdef MALLOC_MMB_DEBUGGING |
int __malloc_mmb_debug = 0; |
#endif |
|
/* Debugging output is indented this may levels. */ |
int __malloc_debug_cur_indent = 0; |
|
|
/* Print FMT and args indented at the current debug print level, followed |
by a newline, and change the level by INDENT. */ |
void |
__malloc_debug_printf (int indent, const char *fmt, ...) |
{ |
unsigned spaces = __malloc_debug_cur_indent * MALLOC_DEBUG_INDENT_SIZE; |
va_list val; |
|
while (spaces > 0) |
{ |
putc (' ', stderr); |
spaces--; |
} |
|
va_start (val, fmt); |
vfprintf (stderr, fmt, val); |
va_end (val); |
|
putc ('\n', stderr); |
|
__malloc_debug_indent (indent); |
} |
|
void |
__malloc_debug_init (void) |
{ |
char *ev = getenv ("MALLOC_DEBUG"); |
if (ev) |
{ |
int val = atoi (ev); |
|
if (val & 1) |
__malloc_check = 1; |
|
if (val & 2) |
__malloc_debug = 1; |
|
#ifdef MALLOC_MMB_DEBUGGING |
if (val & 4) |
__malloc_mmb_debug = 1; |
#endif |
|
#ifdef HEAP_DEBUGGING |
if (val & 8) |
__heap_debug = 1; |
#endif |
|
if (val) |
__malloc_debug_printf |
(0, "malloc_debug: initialized to %d (check = %d, dump = %d, dump_mmb = %d, dump_heap = %d)", |
val, |
!!(val & 1), !!(val & 2), |
!!(val & 4), !!(val & 8)); |
} |
} |