1 /*
2 * Copyright (c) 2012, 2013 ARM Ltd
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The name of the company may not be used to endorse or promote
14 * products derived from this software without specific prior written
15 * permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY ARM LTD ``AS IS'' AND ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL ARM LTD BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
22 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "nano-malloc.h"
30
31 /* List list header of free blocks */
32 chunk_t *__malloc_free_list;
33
34 /* Starting point of memory allocated from system */
35 char * __malloc_sbrk_start;
36 char * __malloc_sbrk_top;
37
38 /*
39 * Algorithm:
40 * Use sbrk() to obtain more memory and ensure the storage is
41 * MALLOC_CHUNK_ALIGN aligned. Optimise for the case that it is
42 * already aligned - only ask for extra padding after we know we
43 * need it
44 */
45 static void *
__malloc_sbrk_aligned(size_t s)46 __malloc_sbrk_aligned(size_t s)
47 {
48 char *p, *align_p;
49
50 #ifdef __APPLE__
51 /* Mac OS X 'emulates' sbrk, but the
52 * parameter is int, not intptr_t or ptrdiff_t,
53 */
54 int d = (int) s;
55 if (d < 0 || (size_t) d != s)
56 return (void *)-1;
57 #else
58 intptr_t d = (intptr_t)s;
59
60 if (d < 0)
61 return (void *)-1;
62 #endif
63 p = sbrk(d);
64
65 /* sbrk returns -1 if fail to allocate */
66 if (p == (void *)-1)
67 return p;
68
69 __malloc_sbrk_top = p + s;
70
71 /* Adjust returned space so that the storage area
72 * is MALLOC_CHUNK_ALIGN aligned and the head is
73 * MALLOC_HEAD_ALIGN aligned.
74 */
75 align_p = __align_up(p + MALLOC_HEAD, MALLOC_CHUNK_ALIGN) - MALLOC_HEAD;
76
77 if (align_p != p)
78 {
79 /* p is not aligned, ask for a few more bytes so that we have
80 * s bytes reserved from align_p. This should only occur for
81 * the first sbrk in a chunk of memory as all others should be
82 * aligned to the right value as chunk sizes are selected to
83 * make them abut in memory
84 */
85 intptr_t adjust = align_p - p;
86 char *extra = sbrk(adjust);
87 if (extra != p + s)
88 return (void *) -1;
89 __malloc_sbrk_top = extra + adjust;
90 }
91 if (__malloc_sbrk_start == NULL)
92 __malloc_sbrk_start = align_p;
93
94 return align_p;
95 }
96
97 bool
__malloc_grow_chunk(chunk_t * c,size_t new_size)98 __malloc_grow_chunk(chunk_t *c, size_t new_size)
99 {
100 char *chunk_e = chunk_end(c);
101
102 if (chunk_e != __malloc_sbrk_top)
103 return false;
104 size_t add_size = MAX(MALLOC_MINSIZE, new_size - _size(c));
105
106 /* Ask for the extra memory needed */
107 char *heap = __malloc_sbrk_aligned(add_size);
108
109 /* Check if we got what we wanted */
110 if (heap == chunk_e)
111 {
112 /* Set size and return */
113 *_size_ref(c) += add_size;
114 return true;
115 }
116
117 if (heap != (char *) -1)
118 {
119 /* sbrk returned unexpected memory, free it */
120 make_free_chunk((chunk_t *) (heap + MALLOC_HEAD), add_size);
121 }
122 return false;
123 }
124
125 /** Function malloc
126 * Algorithm:
127 * Walk through the free list to find the first match. If fails to find
128 * one, call sbrk to allocate a new chunk_t.
129 */
130 void *
malloc(size_t s)131 malloc(size_t s)
132 {
133 chunk_t **p, *r;
134 char * ptr;
135 size_t alloc_size;
136
137 if (s > MALLOC_MAXSIZE)
138 {
139 errno = ENOMEM;
140 return NULL;
141 }
142
143 alloc_size = chunk_size(s);
144
145 MALLOC_LOCK;
146
147 for (p = &__malloc_free_list; (r = *p) != NULL; p = &r->next)
148 {
149 if (_size(r) >= alloc_size)
150 {
151 size_t rem = _size(r) - alloc_size;
152
153 if (rem >= MALLOC_MINSIZE)
154 {
155 /* Find a chunk_t that much larger than required size, break
156 * it into two chunks and return the first one
157 */
158
159 chunk_t *s = (chunk_t *)((char *)r + alloc_size);
160 _set_size(s, rem);
161 s->next = r->next;
162 *p = s;
163
164 _set_size(r, alloc_size);
165 }
166 else
167 {
168 /* Find a chunk_t that is exactly the size or slightly bigger
169 * than requested size, just return this chunk_t
170 */
171 *p = r->next;
172 }
173 break;
174 }
175 if (!r->next && __malloc_grow_chunk(r, alloc_size))
176 {
177 /* Grow the last chunk in memory to the requested size,
178 * just return it
179 */
180 *p = r->next;
181 break;
182 }
183 }
184
185 /* Failed to find a appropriate chunk_t. Ask for more memory */
186 if (r == NULL)
187 {
188 void *blob = __malloc_sbrk_aligned(alloc_size);
189
190 /* sbrk returns -1 if fail to allocate */
191 if (blob == (void *)-1)
192 {
193 errno = ENOMEM;
194 MALLOC_UNLOCK;
195 return NULL;
196 }
197 r = blob_to_chunk(blob);
198 _set_size(r, alloc_size);
199 }
200
201 MALLOC_UNLOCK;
202
203 ptr = chunk_to_ptr(r);
204
205 memset(ptr, '\0', alloc_size - MALLOC_HEAD);
206
207 return ptr;
208 }
209
210 #ifdef _HAVE_ALIAS_ATTRIBUTE
211 #ifndef __clang__
212 #pragma GCC diagnostic ignored "-Wmissing-attributes"
213 #endif
214 __strong_reference(malloc, __malloc_malloc);
215 #endif
216
217 #if MALLOC_DEBUG
218
219 #include <assert.h>
220
221 void
__malloc_validate_block(chunk_t * r)222 __malloc_validate_block(chunk_t *r)
223 {
224 assert (__align_up(chunk_to_ptr(r), MALLOC_CHUNK_ALIGN) == chunk_to_ptr(r));
225 assert (__align_up(r, MALLOC_HEAD_ALIGN) == r);
226 assert (_size(r) >= MALLOC_MINSIZE);
227 assert (_size(r) < 0x80000000UL);
228 assert (__align_up(_size(r), MALLOC_HEAD_ALIGN) == _size(r));
229 }
230
231 void
__malloc_validate(void)232 __malloc_validate(void)
233 {
234 chunk_t *r;
235
236 for (r = __malloc_free_list; r; r = r->next) {
237 __malloc_validate_block(r);
238 assert (r->next == NULL || (char *) r + _size(r) <= (char *) r->next);
239 }
240 }
241
242 #endif
243