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