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 /* Implementation of <<malloc>> <<free>> <<calloc>> <<realloc>>, optional
30  * as to be reenterable.
31  *
32  * Interface documentation refer to malloc.c.
33  */
34 
35 #define _DEFAULT_SOURCE
36 #include <stdio.h>
37 #include <string.h>
38 #include <stdbool.h>
39 #include <errno.h>
40 #include <malloc.h>
41 #include <stdlib.h>
42 #include <unistd.h>
43 #include <sys/config.h>
44 #include <sys/lock.h>
45 #include <stdint.h>
46 
47 #if MALLOC_DEBUG
48 #include <assert.h>
49 #define MALLOC_LOCK do { __LIBC_LOCK(); __malloc_validate(); } while(0)
50 #define MALLOC_UNLOCK do { __malloc_validate(); __LIBC_UNLOCK(); } while(0)
51 #else
52 #define MALLOC_LOCK __LIBC_LOCK()
53 #define MALLOC_UNLOCK __LIBC_UNLOCK()
54 #undef assert
55 #define assert(x) ((void)0)
56 #endif
57 
58 #ifndef MAX
59 #define MAX(a,b) ((a) >= (b) ? (a) : (b))
60 #endif
61 
62 #if __SIZEOF_POINTER__ == __SIZEOF_LONG__
63 #define ALIGN_TO(size, align) \
64     (((size) + (align) -1L) & ~((align) -1L))
65 #else
66 #define ALIGN_TO(size, align) \
67     (((size) + (align) -1) & ~((align) -1))
68 #endif
69 
70 #define ALIGN_PTR(ptr, align)	(void *) (uintptr_t) ALIGN_TO((uintptr_t) ptr, align)
71 
72 typedef struct {
73     char c;
74     union {
75 	void *p;
76 	double d;
77 	long long ll;
78 	size_t s;
79     } u;
80 } align_chunk_t;
81 
82 typedef struct {
83     char c;
84     size_t s;
85 } align_head_t;
86 
87 typedef struct malloc_chunk
88 {
89     /*          --------------------------------------
90      *   chunk->| size                               |
91      * mem_ptr->| When allocated: data               |
92      *          | When freed: pointer to next free   |
93      *          | chunk                              |
94      *          --------------------------------------
95      *
96      * mem_ptr is aligned to MALLOC_CHUNK_ALIGN. That means that
97      * the chunk may not be aligned to MALLOC_CHUNK_ALIGN. But
98      * it will be aligned to MALLOC_HEAD_ALIGN.
99      *
100      * size is set so that a chunk starting at chunk+size will be
101      * aligned correctly
102      */
103 
104     /* size of the allocated payload area */
105     size_t size;
106 
107     /* pointer to next chunk */
108     struct malloc_chunk * next;
109 } chunk_t;
110 
111 /* Alignment of allocated chunk. Compute the alignment required from a
112  * range of types */
113 #define MALLOC_CHUNK_ALIGN	(offsetof(align_chunk_t, u))
114 
115 /* Alignment of the header. Never larger than MALLOC_CHUNK_ALIGN */
116 #define MALLOC_HEAD_ALIGN	(offsetof(align_head_t, s))
117 
118 /* Size of malloc header. Keep it aligned. */
119 #define MALLOC_HEAD 		ALIGN_TO(sizeof(size_t), MALLOC_HEAD_ALIGN)
120 
121 /* nominal "page size" */
122 #define MALLOC_PAGE_ALIGN 	(0x1000)
123 
124 /* Minimum allocation size */
125 #define MALLOC_MINSIZE		ALIGN_TO(sizeof(chunk_t), MALLOC_HEAD_ALIGN)
126 
127 /* Maximum allocation size */
128 #define MALLOC_MAXSIZE 		(SIZE_MAX - (MALLOC_HEAD + 2*MALLOC_CHUNK_ALIGN))
129 
130 /* Forward data declarations */
131 extern chunk_t * __malloc_free_list;
132 extern char * __malloc_sbrk_start;
133 extern char * __malloc_sbrk_top;
134 
135 /* Forward function declarations */
136 void * malloc(size_t);
137 void free (void * free_p);
138 void cfree(void * ptr);
139 void * calloc(size_t n, size_t elem);
140 void malloc_stats(void);
141 size_t malloc_usable_size(void * ptr);
142 void * realloc(void * ptr, size_t size);
143 void * memalign(size_t align, size_t s);
144 int mallopt(int parameter_number, int parameter_value);
145 void * valloc(size_t s);
146 void * pvalloc(size_t s);
147 void __malloc_validate(void);
148 void __malloc_validate_block(chunk_t *r);
149 void * __malloc_sbrk_aligned(size_t s);
150 bool __malloc_grow_chunk(chunk_t *c, size_t new_size);
151 
152 /* Work around compiler optimizing away stores to 'size' field before
153  * call to free.
154  */
155 #ifdef _HAVE_ALIAS_ATTRIBUTE
156 extern void __malloc_free(void *);
157 extern void *__malloc_malloc(size_t);
158 #else
159 #define __malloc_free(x) free(x)
160 #define __malloc_malloc(x) malloc(x)
161 #endif
162 
163 /* convert storage pointer to chunk */
164 static inline chunk_t *
ptr_to_chunk(void * ptr)165 ptr_to_chunk(void * ptr)
166 {
167     return (chunk_t *) ((char *) ptr - MALLOC_HEAD);
168 }
169 
170 /* convert chunk to storage pointer */
171 static inline void *
chunk_to_ptr(chunk_t * c)172 chunk_to_ptr(chunk_t *c)
173 {
174     return (char *) c + MALLOC_HEAD;
175 }
176 
177 /* end of chunk -- address of first byte past chunk storage */
178 static inline void *
chunk_end(chunk_t * c)179 chunk_end(chunk_t *c)
180 {
181     return (char *) c + c->size;
182 }
183 
184 /* chunk size needed to hold 'malloc_size' bytes */
185 static inline size_t
chunk_size(size_t malloc_size)186 chunk_size(size_t malloc_size)
187 {
188     /* Keep all blocks aligned */
189     malloc_size = ALIGN_TO(malloc_size, MALLOC_CHUNK_ALIGN);
190 
191     /* Add space for header */
192     malloc_size += MALLOC_HEAD;
193 
194     /* fill the gap between chunks */
195     malloc_size += (MALLOC_CHUNK_ALIGN - MALLOC_HEAD_ALIGN);
196 
197     /* Make sure the requested size is big enough to hold a free chunk */
198     malloc_size = MAX(MALLOC_MINSIZE, malloc_size);
199     return malloc_size;
200 }
201 
202 /* available storage in chunk */
203 static inline size_t
chunk_usable(chunk_t * c)204 chunk_usable(chunk_t *c)
205 {
206     return c->size - MALLOC_HEAD;
207 }
208 
209 /* assign 'size' to the specified chunk and return it to the free
210  * pool */
211 static inline void
make_free_chunk(chunk_t * c,size_t size)212 make_free_chunk(chunk_t *c, size_t size)
213 {
214     c->size = size;
215     __malloc_free(chunk_to_ptr(c));
216 }
217 
218 #ifdef DEFINE_MALLOC
219 /* List list header of free blocks */
220 chunk_t * __malloc_free_list;
221 
222 /* Starting point of memory allocated from system */
223 char * __malloc_sbrk_start;
224 char * __malloc_sbrk_top;
225 
226 /** Function __malloc_sbrk_aligned
227   * Algorithm:
228   *   Use sbrk() to obtain more memory and ensure the storage is
229   *   MALLOC_CHUNK_ALIGN aligned. Optimise for the case that it is
230   *   already aligned - only ask for extra padding after we know we
231   *   need it
232   */
__malloc_sbrk_aligned(size_t s)233 void* __malloc_sbrk_aligned(size_t s)
234 {
235     char *p, *align_p;
236 
237 #ifdef __APPLE__
238     /* Mac OS X 'emulates' sbrk, but the
239      * parameter is int, not intptr_t or ptrdiff_t,
240      */
241     int d = (int) s;
242     if (d < 0 || (size_t) d != s)
243 	return (void *)-1;
244 #else
245     ptrdiff_t d = (ptrdiff_t)s;
246 
247     if (d < 0)
248 	return (void *)-1;
249 #endif
250     p = sbrk(d);
251 
252     /* sbrk returns -1 if fail to allocate */
253     if (p == (void *)-1)
254         return p;
255 
256     __malloc_sbrk_top = p + s;
257 
258     /* Adjust returned space so that the storage area
259      * is MALLOC_CHUNK_ALIGN aligned and the head is
260      * MALLOC_HEAD_ALIGN aligned.
261      */
262     align_p = (char*)ALIGN_PTR(p + MALLOC_HEAD, MALLOC_CHUNK_ALIGN) - MALLOC_HEAD;
263 
264     if (align_p != p)
265     {
266         /* p is not aligned, ask for a few more bytes so that we have
267          * s bytes reserved from align_p. This should only occur for
268          * the first sbrk in a chunk of memory as all others should be
269          * aligned to the right value as chunk sizes are selected to
270          * make them abut in memory
271 	 */
272 	intptr_t adjust = align_p - p;
273         char *extra = sbrk(adjust);
274         if (extra != p + s)
275             return (void *) -1;
276 	__malloc_sbrk_top = extra + adjust;
277     }
278     if (__malloc_sbrk_start == NULL)
279 	__malloc_sbrk_start = align_p;
280 
281     return align_p;
282 }
283 
284 bool
__malloc_grow_chunk(chunk_t * c,size_t new_size)285 __malloc_grow_chunk(chunk_t *c, size_t new_size)
286 {
287     char *chunk_e = chunk_end(c);
288 
289     if (chunk_e != __malloc_sbrk_top)
290 	return false;
291     size_t add_size = MAX(MALLOC_MINSIZE, new_size - c->size);
292 
293     /* Ask for the extra memory needed */
294     char *heap = __malloc_sbrk_aligned(add_size);
295 
296     /* Check if we got what we wanted */
297     if (heap == chunk_e)
298     {
299 	/* Set size and return */
300 	c->size += add_size;
301 	return true;
302     }
303 
304     if (heap != (char *) -1)
305     {
306 	/* sbrk returned unexpected memory, free it */
307 	make_free_chunk((chunk_t *) heap, add_size);
308     }
309     return false;
310 }
311 
312 /** Function malloc
313   * Algorithm:
314   *   Walk through the free list to find the first match. If fails to find
315   *   one, call sbrk to allocate a new chunk_t.
316   */
malloc(size_t s)317 void * malloc(size_t s)
318 {
319     chunk_t **p, *r;
320     char * ptr;
321     size_t alloc_size;
322 
323     if (s > MALLOC_MAXSIZE)
324     {
325         errno = ENOMEM;
326         return NULL;
327     }
328 
329     alloc_size = chunk_size(s);
330 
331     MALLOC_LOCK;
332 
333     for (p = &__malloc_free_list; (r = *p) != NULL; p = &r->next)
334     {
335         if (r->size >= alloc_size)
336         {
337 	    size_t rem = r->size - alloc_size;
338 
339             if (rem >= MALLOC_MINSIZE)
340             {
341                 /* Find a chunk_t that much larger than required size, break
342 		 * it into two chunks and return the first one
343 		 */
344 
345 		chunk_t *s = (chunk_t *)((char *)r + alloc_size);
346                 s->size = rem;
347 		s->next = r->next;
348 		*p = s;
349 
350                 r->size = alloc_size;
351             }
352 	    else
353 	    {
354 		/* Find a chunk_t that is exactly the size or slightly bigger
355 		 * than requested size, just return this chunk_t
356 		 */
357 		*p = r->next;
358 	    }
359             break;
360         }
361 	if (!r->next && __malloc_grow_chunk(r, alloc_size))
362 	{
363 	    /* Grow the last chunk in memory to the requested size,
364 	     * just return it
365 	     */
366 	    *p = r->next;
367 	    break;
368 	}
369     }
370 
371     /* Failed to find a appropriate chunk_t. Ask for more memory */
372     if (r == NULL)
373     {
374         r = __malloc_sbrk_aligned(alloc_size);
375 
376         /* sbrk returns -1 if fail to allocate */
377         if (r == (void *)-1)
378         {
379             errno = ENOMEM;
380             MALLOC_UNLOCK;
381             return NULL;
382         }
383         r->size = alloc_size;
384     }
385 
386     MALLOC_UNLOCK;
387 
388     ptr = (char *)r + MALLOC_HEAD;
389 
390     memset(ptr, '\0', alloc_size - MALLOC_HEAD);
391 
392     return ptr;
393 }
394 #ifdef _HAVE_ALIAS_ATTRIBUTE
395 #pragma GCC diagnostic push
396 #ifndef __clang__
397 #pragma GCC diagnostic ignored "-Wmissing-attributes"
398 #endif
399 __strong_reference(malloc, __malloc_malloc);
400 #pragma GCC diagnostic pop
401 #endif
402 #endif /* DEFINE_MALLOC */
403 
404 #ifdef DEFINE_FREE
405 
406 /** Function free
407   * Implementation of libc free.
408   * Algorithm:
409   *  Maintain a global free chunk_t single link list, headed by global
410   *  variable __malloc_free_list.
411   *  When free, insert the to-be-freed chunk_t into free list. The place to
412   *  insert should make sure all chunks are sorted by address from low to
413   *  high.  Then merge with neighbor chunks if adjacent.
414   */
free(void * free_p)415 void free (void * free_p)
416 {
417     chunk_t * p_to_free;
418     chunk_t ** p, * r;
419 
420     if (free_p == NULL) return;
421 
422     p_to_free = ptr_to_chunk(free_p);
423     p_to_free->next = NULL;
424 #if MALLOC_DEBUG
425     __malloc_validate_block(p_to_free);
426 #endif
427 
428     MALLOC_LOCK;
429 
430     for (p = &__malloc_free_list; (r = *p) != NULL; p = &r->next)
431     {
432 	/* Insert in address order */
433 	if (p_to_free < r)
434 	    break;
435 
436 	/* Merge blocks together */
437 	if (chunk_end(r) == p_to_free)
438 	{
439 	    r->size += p_to_free->size;
440 	    p_to_free = r;
441 	    r = r->next;
442 	    goto no_insert;
443 	}
444 
445 	/* Check for double free */
446 	if (p_to_free == r)
447 	{
448 	    errno = ENOMEM;
449 	    MALLOC_UNLOCK;
450 	    return;
451 	}
452 
453     }
454     p_to_free->next = r;
455     *p = p_to_free;
456 
457 no_insert:
458 
459     /* Merge blocks together */
460     if (chunk_end(p_to_free) == r)
461     {
462 	p_to_free->size += r->size;
463 	p_to_free->next = r->next;
464     }
465 
466     MALLOC_UNLOCK;
467 }
468 #ifdef _HAVE_ALIAS_ATTRIBUTE
469 #pragma GCC diagnostic push
470 #ifndef __clang__
471 #pragma GCC diagnostic ignored "-Wmissing-attributes"
472 #endif
473 __strong_reference(free, __malloc_free);
474 __strong_reference(free, cfree);
475 #pragma GCC diagnostic pop
476 #endif
477 #endif /* DEFINE_FREE */
478 
479 #ifdef DEFINE_CFREE
480 #ifndef _HAVE_ALIAS_ATTRIBUTE
cfree(void * ptr)481 void cfree(void * ptr)
482 {
483     free(ptr);
484 }
485 #endif
486 #endif /* DEFINE_CFREE */
487 
488 #ifdef DEFINE_CALLOC
489 #include "mul_overflow.h"
490 /* Function calloc
491  *
492  * Implement calloc by multiplying sizes (with overflow check) and
493  * calling malloc (which sets to zero)
494  */
495 
calloc(size_t n,size_t elem)496 void * calloc(size_t n, size_t elem)
497 {
498     size_t bytes;
499 
500     if (mul_overflow (n, elem, &bytes))
501     {
502         errno = ENOMEM;
503         return NULL;
504     }
505     return malloc(bytes);
506 }
507 #endif /* DEFINE_CALLOC */
508 
509 #ifdef DEFINE_REALLOC
510 
511 /* Function realloc
512  *
513  * Implement either by merging adjacent free memory
514  * or by calling malloc/memcpy
515  */
realloc(void * ptr,size_t size)516 void * realloc(void * ptr, size_t size)
517 {
518     void * mem;
519 
520     if (ptr == NULL)
521 	return malloc(size);
522 
523     if (size == 0)
524     {
525         free(ptr);
526         return NULL;
527     }
528 
529     if (size > MALLOC_MAXSIZE)
530     {
531         errno = ENOMEM;
532         return NULL;
533     }
534 
535     size_t new_size = chunk_size(size);
536     chunk_t *p_to_realloc = ptr_to_chunk(ptr);
537 
538 #if MALLOC_DEBUG
539     __malloc_validate_block(p_to_realloc);
540 #endif
541 
542     size_t old_size = p_to_realloc->size;
543 
544     /* See if we can avoid allocating new memory
545      * when increasing the size
546      */
547     if (new_size > old_size)
548     {
549 	void *chunk_e = chunk_end(p_to_realloc);
550 
551 	MALLOC_LOCK;
552 
553 	if (__malloc_grow_chunk(p_to_realloc, new_size))
554 	{
555 	    /* clear new memory */
556 	    memset(chunk_e, '\0', new_size - old_size);
557 	    /* adjust chunk_t size */
558 	    old_size = new_size;
559 	}
560 	else
561 	{
562 	    chunk_t **p, *r;
563 
564 	    /* Check to see if there's a chunk_t of free space just past
565 	     * the current block, merge it in in case that's useful
566 	     */
567 	    for (p = &__malloc_free_list; (r = *p) != NULL; p = &r->next)
568 	    {
569 		if (r == chunk_e)
570 		{
571 		    size_t r_size = r->size;
572 
573 		    /* remove R from the free list */
574 		    *p = r->next;
575 
576 		    /* clear the memory from r */
577 		    memset(r, '\0', r_size);
578 
579 		    /* add it's size to our block */
580 		    old_size += r_size;
581 		    p_to_realloc->size = old_size;
582 		    break;
583 		}
584 		if (p_to_realloc < r)
585 		    break;
586 	    }
587 	}
588 
589 	MALLOC_UNLOCK;
590     }
591 
592     if (new_size <= old_size)
593     {
594 	size_t extra = old_size - new_size;
595 
596 	/* If there's enough space left over, split it out
597 	 * and free it
598 	 */
599 	if (extra >= MALLOC_MINSIZE) {
600 	    p_to_realloc->size = new_size;
601 	    make_free_chunk(chunk_end(p_to_realloc), extra);
602 	}
603 	return ptr;
604     }
605 
606     /* No short cuts, allocate new memory and copy */
607 
608     mem = malloc(size);
609     if (!mem)
610         return NULL;
611 
612     memcpy(mem, ptr, old_size - MALLOC_HEAD);
613     free(ptr);
614 
615     return mem;
616 }
617 #endif /* DEFINE_REALLOC */
618 
619 #ifdef DEFINE_MALLINFO
620 
621 volatile chunk_t *__malloc_block;
622 
623 void
__malloc_validate_block(chunk_t * r)624 __malloc_validate_block(chunk_t *r)
625 {
626     __malloc_block = r;
627     assert (ALIGN_PTR(chunk_to_ptr(r), MALLOC_CHUNK_ALIGN) == chunk_to_ptr(r));
628     assert (ALIGN_PTR(r, MALLOC_HEAD_ALIGN) == r);
629     assert (r->size >= MALLOC_MINSIZE);
630     assert (r->size < 0x80000000UL);
631     assert (ALIGN_TO(r->size, MALLOC_HEAD_ALIGN) == r->size);
632 }
633 
634 void
__malloc_validate(void)635 __malloc_validate(void)
636 {
637     chunk_t *r;
638 
639     for (r = __malloc_free_list; r; r = r->next) {
640 	__malloc_validate_block(r);
641 	assert (r->next == NULL || (char *) r + r->size < (char *) r->next);
642     }
643 }
644 
mallinfo(void)645 struct mallinfo mallinfo(void)
646 {
647     char * sbrk_now;
648     chunk_t * pf;
649     size_t free_size = 0;
650     size_t total_size;
651     size_t ordblks = 0;
652     struct mallinfo current_mallinfo;
653 
654     MALLOC_LOCK;
655 
656     __malloc_validate();
657 
658     if (__malloc_sbrk_start == NULL) total_size = 0;
659     else {
660         sbrk_now = __malloc_sbrk_top;
661 
662         if (sbrk_now == NULL)
663             total_size = (size_t)-1;
664         else
665             total_size = (size_t) (sbrk_now - __malloc_sbrk_start);
666     }
667 
668     for (pf = __malloc_free_list; pf; pf = pf->next) {
669 	ordblks++;
670         free_size += pf->size;
671     }
672 
673     current_mallinfo.ordblks = ordblks;
674     current_mallinfo.arena = total_size;
675     current_mallinfo.fordblks = free_size;
676     current_mallinfo.uordblks = total_size - free_size;
677 
678     MALLOC_UNLOCK;
679     return current_mallinfo;
680 }
681 #endif /* DEFINE_MALLINFO */
682 
683 #ifdef DEFINE_MALLOC_STATS
684 
malloc_stats(void)685 void malloc_stats(void)
686 {
687     struct mallinfo current_mallinfo;
688 
689     current_mallinfo = mallinfo();
690     fprintf(stderr, "max system bytes = %10lu\n",
691             (long) current_mallinfo.arena);
692     fprintf(stderr, "system bytes     = %10lu\n",
693             (long) current_mallinfo.arena);
694     fprintf(stderr, "in use bytes     = %10lu\n",
695             (long) current_mallinfo.uordblks);
696     fprintf(stderr, "free blocks      = %10lu\n",
697 	    (long) current_mallinfo.ordblks);
698 }
699 #endif /* DEFINE_MALLOC_STATS */
700 
701 #ifdef DEFINE_MALLOC_USABLE_SIZE
malloc_usable_size(void * ptr)702 size_t malloc_usable_size(void * ptr)
703 {
704     return chunk_usable(ptr_to_chunk(ptr));
705 }
706 #endif /* DEFINE_MALLOC_USABLE_SIZE */
707 
708 #ifdef DEFINE_MEMALIGN
709 /* Function memalign
710  * Allocate memory block aligned at specific boundary.
711  *   align: required alignment. Must be power of 2. Return NULL
712  *          if not power of 2. Undefined behavior is bigger than
713  *          pointer value range.
714  *   s: required size.
715  * Return: allocated memory pointer aligned to align
716  * Algorithm: Malloc a big enough block, padding pointer to aligned
717  *            address, then truncate and free the tail if too big.
718  *            Record the offset of align pointer and original pointer
719  *            in the padding area.
720  */
memalign(size_t align,size_t s)721 void * memalign(size_t align, size_t s)
722 {
723     chunk_t * chunk_p;
724     size_t offset, size_with_padding;
725     char * allocated, * aligned_p;
726 
727     /* Return NULL if align isn't power of 2 */
728     if ((align & (align-1)) != 0)
729     {
730         errno = EINVAL;
731         return NULL;
732     }
733 
734     align = MAX(align, MALLOC_MINSIZE);
735 
736     if (s > MALLOC_MAXSIZE - align)
737     {
738         errno = ENOMEM;
739         return NULL;
740     }
741 
742     s = ALIGN_TO(MAX(s,1), MALLOC_CHUNK_ALIGN);
743 
744     /* Make sure there's space to align the allocation and split
745      * off chunk_t from the front
746      */
747     size_with_padding = s + align + MALLOC_MINSIZE;
748 
749     allocated = __malloc_malloc(size_with_padding);
750     if (allocated == NULL) return NULL;
751 
752     chunk_p = ptr_to_chunk(allocated);
753 
754     aligned_p = ALIGN_PTR(allocated, align);
755 
756     offset = (size_t) (aligned_p - allocated);
757 
758     /* Split off the front piece if necessary */
759     if (offset)
760     {
761 	if (offset < MALLOC_MINSIZE) {
762 	    aligned_p += align;
763 	    offset += align;
764 	}
765 
766 	chunk_t *new_chunk_p = ptr_to_chunk(aligned_p);
767 	new_chunk_p->size = chunk_p->size - offset;
768 
769 	make_free_chunk(chunk_p, offset);
770 
771 	chunk_p = new_chunk_p;
772     }
773 
774     offset = chunk_p->size - chunk_size(s);
775 
776     /* Split off the back piece if large enough */
777     if (offset >= MALLOC_MINSIZE)
778     {
779 	chunk_p->size -= offset;
780 
781 	make_free_chunk((chunk_t *) chunk_end(chunk_p), offset);
782     }
783     return aligned_p;
784 }
785 #ifdef _HAVE_ALIAS_ATTRIBUTE
786 __strong_reference(memalign, aligned_alloc);
787 #endif
788 #endif /* DEFINE_MEMALIGN */
789 
790 #ifdef DEFINE_MALLOPT
mallopt(int parameter_number,int parameter_value)791 int mallopt(int parameter_number, int parameter_value)
792 {
793     (void) parameter_number;
794     (void) parameter_value;
795     return 0;
796 }
797 #endif /* DEFINE_MALLOPT */
798 
799 #ifdef DEFINE_VALLOC
valloc(size_t s)800 void * valloc(size_t s)
801 {
802     return memalign(MALLOC_PAGE_ALIGN, s);
803 }
804 #endif /* DEFINE_VALLOC */
805 
806 #ifdef DEFINE_PVALLOC
pvalloc(size_t s)807 void * pvalloc(size_t s)
808 {
809     if (s > MALLOC_MAXSIZE - MALLOC_PAGE_ALIGN)
810     {
811         errno = ENOMEM;
812         return NULL;
813     }
814 
815     return valloc(ALIGN_TO(s, MALLOC_PAGE_ALIGN));
816 }
817 #endif /* DEFINE_PVALLOC */
818 
819 #ifdef DEFINE_GETPAGESIZE
getpagesize(void)820 int getpagesize(void)
821 {
822     return MALLOC_PAGE_ALIGN;
823 }
824 #endif /* DEFINE_GETPAGESIZE */
825 
826 #ifdef DEFINE_POSIX_MEMALIGN
posix_memalign(void ** memptr,size_t align,size_t size)827 int posix_memalign(void **memptr, size_t align, size_t size)
828 {
829     /* Return EINVAL if align isn't power of 2 or not a multiple of a pointer size */
830     if ((align & (align-1)) != 0 || align % sizeof(void*) != 0 || align == 0)
831         return EINVAL;
832 
833     void *mem = memalign(align, size);
834 
835     if (!mem)
836         return ENOMEM;
837 
838     *memptr = mem;
839     return 0;
840 }
841 #endif
842