OSDN Git Service

hidden_def/hidden_proto: convert all users (I hope) termios split, add some missing...
[uclinux-h8/uClibc.git] / libc / stdlib / malloc / malloc.c
1 /*
2  * libc/stdlib/malloc/malloc.c -- malloc function
3  *
4  *  Copyright (C) 2002,03  NEC Electronics Corporation
5  *  Copyright (C) 2002,03  Miles Bader <miles@gnu.org>
6  *
7  * This file is subject to the terms and conditions of the GNU Lesser
8  * General Public License.  See the file COPYING.LIB in the main
9  * directory of this archive for more details.
10  * 
11  * Written by Miles Bader <miles@gnu.org>
12  */
13
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <errno.h>
17 #include <sys/mman.h>
18
19 libc_hidden_proto(mmap)
20 libc_hidden_proto(sbrk)
21
22 #include "malloc.h"
23 #include "heap.h"
24
25
26 /* The malloc heap.  We provide a bit of initial static space so that
27    programs can do a little mallocing without mmaping in more space.  */
28 HEAP_DECLARE_STATIC_FREE_AREA (initial_fa, 256);
29 struct heap __malloc_heap = HEAP_INIT_WITH_FA (initial_fa);
30
31 #if defined(MALLOC_USE_LOCKING) && defined(MALLOC_USE_SBRK)
32 /* A lock protecting our use of sbrk.  */
33 malloc_mutex_t __malloc_sbrk_lock;
34 #endif /* MALLOC_USE_LOCKING && MALLOC_USE_SBRK */
35
36
37 #ifdef __UCLIBC_UCLINUX_BROKEN_MUNMAP__
38 /* A list of all malloc_mmb structures describing blocsk that
39    malloc has mmapped, ordered by the block address.  */
40 struct malloc_mmb *__malloc_mmapped_blocks = 0;
41
42 /* A heap used for allocating malloc_mmb structures.  We could allocate
43    them from the main heap, but that tends to cause heap fragmentation in
44    annoying ways.  */
45 HEAP_DECLARE_STATIC_FREE_AREA (initial_mmb_fa, 48); /* enough for 3 mmbs */
46 struct heap __malloc_mmb_heap = HEAP_INIT_WITH_FA (initial_mmb_fa);
47 #endif /* __UCLIBC_UCLINUX_BROKEN_MUNMAP__ */
48
49
50 static void *
51 malloc_from_heap (size_t size, struct heap *heap)
52 {
53   void *mem;
54
55   MALLOC_DEBUG (1, "malloc: %d bytes", size);
56
57   /* Include extra space to record the size of the allocated block.  */
58   size += MALLOC_HEADER_SIZE;
59
60   __heap_lock (heap);
61
62   /* First try to get memory that's already in our heap.  */
63   mem = __heap_alloc (heap, &size);
64
65   __heap_unlock (heap);
66
67   if (unlikely (! mem))
68     /* We couldn't allocate from the heap, so grab some more
69        from the system, add it to the heap, and try again.  */
70     {
71       /* If we're trying to allocate a block bigger than the default
72          MALLOC_HEAP_EXTEND_SIZE, make sure we get enough to hold it. */
73       void *block;
74       size_t block_size
75         = (size < MALLOC_HEAP_EXTEND_SIZE
76            ? MALLOC_HEAP_EXTEND_SIZE
77            : MALLOC_ROUND_UP_TO_PAGE_SIZE (size));
78
79       /* Allocate the new heap block.  */
80 #ifdef MALLOC_USE_SBRK
81
82       __malloc_lock_sbrk ();
83
84       /* Use sbrk we can, as it's faster than mmap, and guarantees
85          contiguous allocation.  */
86       block = sbrk (block_size);
87       if (likely (block != (void *)-1))
88         {
89           /* Because sbrk can return results of arbitrary
90              alignment, align the result to a MALLOC_ALIGNMENT boundary.  */
91           long aligned_block = MALLOC_ROUND_UP ((long)block, MALLOC_ALIGNMENT);
92           if (block != (void *)aligned_block)
93             /* Have to adjust.  We should only have to actually do this
94                the first time (after which we will have aligned the brk
95                correctly).  */
96             {
97               /* Move the brk to reflect the alignment; our next allocation
98                  should start on exactly the right alignment.  */
99               sbrk (aligned_block - (long)block);
100               block = (void *)aligned_block;
101             }
102         }
103
104       __malloc_unlock_sbrk ();
105
106 #else /* !MALLOC_USE_SBRK */
107
108       /* Otherwise, use mmap.  */
109 #ifdef __ARCH_HAS_MMU__
110       block = mmap ((void *)0, block_size, PROT_READ | PROT_WRITE,
111                     MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
112 #else
113       block = mmap ((void *)0, block_size, PROT_READ | PROT_WRITE,
114                     MAP_SHARED | MAP_ANONYMOUS, 0, 0);
115 #endif
116
117 #endif /* MALLOC_USE_SBRK */
118
119       if (likely (block != (void *)-1))
120         {
121 #if !defined(MALLOC_USE_SBRK) && defined(__UCLIBC_UCLINUX_BROKEN_MUNMAP__)
122           struct malloc_mmb *mmb, *prev_mmb, *new_mmb;
123 #endif
124
125           MALLOC_DEBUG (1, "adding system memroy to heap: 0x%lx - 0x%lx (%d bytes)",
126                         (long)block, (long)block + block_size, block_size);
127
128           /* Get back the heap lock.  */
129           __heap_lock (heap);
130
131           /* Put BLOCK into the heap.  */
132           __heap_free (heap, block, block_size);
133
134           MALLOC_DEBUG_INDENT (-1);
135
136           /* Try again to allocate.  */
137           mem = __heap_alloc (heap, &size);
138
139           __heap_unlock (heap);
140
141 #if !defined(MALLOC_USE_SBRK) && defined(__UCLIBC_UCLINUX_BROKEN_MUNMAP__)
142           /* Insert a record of BLOCK in sorted order into the
143              __malloc_mmapped_blocks list.  */
144
145           for (prev_mmb = 0, mmb = __malloc_mmapped_blocks;
146                mmb;
147                prev_mmb = mmb, mmb = mmb->next)
148             if (block < mmb->mem)
149               break;
150
151           new_mmb = malloc_from_heap (sizeof *new_mmb, &__malloc_mmb_heap);
152           new_mmb->next = mmb;
153           new_mmb->mem = block;
154           new_mmb->size = block_size;
155
156           if (prev_mmb)
157             prev_mmb->next = new_mmb;
158           else
159             __malloc_mmapped_blocks = new_mmb;
160
161           MALLOC_MMB_DEBUG (0, "new mmb at 0x%x: 0x%x[%d]",
162                             (unsigned)new_mmb,
163                             (unsigned)new_mmb->mem, block_size);
164 #endif /* !MALLOC_USE_SBRK && __UCLIBC_UCLINUX_BROKEN_MUNMAP__ */
165         }
166     }
167
168   if (likely (mem))
169     /* Record the size of the block and get the user address.  */
170     {
171       mem = MALLOC_SETUP (mem, size);
172
173       MALLOC_DEBUG (-1, "malloc: returning 0x%lx (base:0x%lx, total_size:%ld)",
174                     (long)mem, (long)MALLOC_BASE(mem), (long)MALLOC_SIZE(mem));
175     }
176   else
177     MALLOC_DEBUG (-1, "malloc: returning 0");
178
179   return mem;
180 }
181
182 void *
183 malloc (size_t size)
184 {
185   void *mem;
186 #ifdef MALLOC_DEBUGGING
187   static int debugging_initialized = 0;
188   if (! debugging_initialized)
189     {
190       debugging_initialized = 1;
191       __malloc_debug_init ();
192     }
193   if (__malloc_check)
194     __heap_check (&__malloc_heap, "malloc");
195 #endif
196
197 #ifdef __MALLOC_GLIBC_COMPAT__
198   if (unlikely (size == 0))
199     size++;
200 #else
201   /* Some programs will call malloc (0).  Lets be strict and return NULL */
202   if (unlikely (size == 0))
203     return 0;
204 #endif
205
206   /* Check if they are doing something dumb like malloc(-1) */
207   if (unlikely(((unsigned long)size > (unsigned long)(MALLOC_HEADER_SIZE*-2))))
208     goto oom;
209
210   mem = malloc_from_heap (size, &__malloc_heap);
211   if (unlikely (!mem))
212     {
213     oom:
214       __set_errno (ENOMEM);
215       return 0;
216     }
217
218   return mem;
219 }