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 #ifdef __GNUC__
407 #pragma GCC diagnostic ignored "-Wpragmas"
408 #pragma GCC diagnostic ignored "-Wunknown-warning-option"
409 #pragma GCC diagnostic ignored "-Wanalyzer-null-dereference"
410 #endif
411 
412 /** Function free
413   * Implementation of libc free.
414   * Algorithm:
415   *  Maintain a global free chunk_t single link list, headed by global
416   *  variable __malloc_free_list.
417   *  When free, insert the to-be-freed chunk_t into free list. The place to
418   *  insert should make sure all chunks are sorted by address from low to
419   *  high.  Then merge with neighbor chunks if adjacent.
420   */
free(void * free_p)421 void free (void * free_p)
422 {
423     chunk_t * p_to_free;
424     chunk_t ** p, * r;
425 
426     if (free_p == NULL) return;
427 
428     p_to_free = ptr_to_chunk(free_p);
429     p_to_free->next = NULL;
430 #if MALLOC_DEBUG
431     __malloc_validate_block(p_to_free);
432 #endif
433 
434     MALLOC_LOCK;
435 
436     for (p = &__malloc_free_list; (r = *p) != NULL; p = &r->next)
437     {
438 	/* Insert in address order */
439 	if (p_to_free < r)
440 	    break;
441 
442 	/* Merge blocks together */
443 	if (chunk_end(r) == p_to_free)
444 	{
445 	    r->size += p_to_free->size;
446 	    p_to_free = r;
447 	    r = r->next;
448 	    goto no_insert;
449 	}
450 
451 	/* Check for double free */
452 	if (p_to_free == r)
453 	{
454 	    errno = ENOMEM;
455 	    MALLOC_UNLOCK;
456 	    return;
457 	}
458 
459     }
460     p_to_free->next = r;
461     *p = p_to_free;
462 
463 no_insert:
464 
465     /* Merge blocks together */
466     if (chunk_end(p_to_free) == r)
467     {
468 	p_to_free->size += r->size;
469 	p_to_free->next = r->next;
470     }
471 
472     MALLOC_UNLOCK;
473 }
474 #ifdef _HAVE_ALIAS_ATTRIBUTE
475 #pragma GCC diagnostic push
476 #ifndef __clang__
477 #pragma GCC diagnostic ignored "-Wmissing-attributes"
478 #endif
479 __strong_reference(free, __malloc_free);
480 __strong_reference(free, cfree);
481 #pragma GCC diagnostic pop
482 #endif
483 #endif /* DEFINE_FREE */
484 
485 #ifdef DEFINE_CFREE
486 #ifndef _HAVE_ALIAS_ATTRIBUTE
cfree(void * ptr)487 void cfree(void * ptr)
488 {
489     free(ptr);
490 }
491 #endif
492 #endif /* DEFINE_CFREE */
493 
494 #ifdef DEFINE_CALLOC
495 #include "mul_overflow.h"
496 /* Function calloc
497  *
498  * Implement calloc by multiplying sizes (with overflow check) and
499  * calling malloc (which sets to zero)
500  */
501 
calloc(size_t n,size_t elem)502 void * calloc(size_t n, size_t elem)
503 {
504     size_t bytes;
505 
506     if (mul_overflow (n, elem, &bytes))
507     {
508         errno = ENOMEM;
509         return NULL;
510     }
511     return malloc(bytes);
512 }
513 #endif /* DEFINE_CALLOC */
514 
515 #ifdef DEFINE_REALLOC
516 
517 /* Function realloc
518  *
519  * Implement either by merging adjacent free memory
520  * or by calling malloc/memcpy
521  */
realloc(void * ptr,size_t size)522 void * realloc(void * ptr, size_t size)
523 {
524     void * mem;
525 
526     if (ptr == NULL)
527 	return malloc(size);
528 
529     if (size == 0)
530     {
531         free(ptr);
532         return NULL;
533     }
534 
535     if (size > MALLOC_MAXSIZE)
536     {
537         errno = ENOMEM;
538         return NULL;
539     }
540 
541     size_t new_size = chunk_size(size);
542     chunk_t *p_to_realloc = ptr_to_chunk(ptr);
543 
544 #if MALLOC_DEBUG
545     __malloc_validate_block(p_to_realloc);
546 #endif
547 
548     size_t old_size = p_to_realloc->size;
549 
550     /* See if we can avoid allocating new memory
551      * when increasing the size
552      */
553     if (new_size > old_size)
554     {
555 	void *chunk_e = chunk_end(p_to_realloc);
556 
557 	MALLOC_LOCK;
558 
559 	if (__malloc_grow_chunk(p_to_realloc, new_size))
560 	{
561 	    /* clear new memory */
562 	    memset(chunk_e, '\0', new_size - old_size);
563 	    /* adjust chunk_t size */
564 	    old_size = new_size;
565 	}
566 	else
567 	{
568 	    chunk_t **p, *r;
569 
570 	    /* Check to see if there's a chunk_t of free space just past
571 	     * the current block, merge it in in case that's useful
572 	     */
573 	    for (p = &__malloc_free_list; (r = *p) != NULL; p = &r->next)
574 	    {
575 		if (r == chunk_e)
576 		{
577 		    size_t r_size = r->size;
578 
579 		    /* remove R from the free list */
580 		    *p = r->next;
581 
582 		    /* clear the memory from r */
583 		    memset(r, '\0', r_size);
584 
585 		    /* add it's size to our block */
586 		    old_size += r_size;
587 		    p_to_realloc->size = old_size;
588 		    break;
589 		}
590 		if (p_to_realloc < r)
591 		    break;
592 	    }
593 	}
594 
595 	MALLOC_UNLOCK;
596     }
597 
598     if (new_size <= old_size)
599     {
600 	size_t extra = old_size - new_size;
601 
602 	/* If there's enough space left over, split it out
603 	 * and free it
604 	 */
605 	if (extra >= MALLOC_MINSIZE) {
606 	    p_to_realloc->size = new_size;
607 	    make_free_chunk(chunk_end(p_to_realloc), extra);
608 	}
609 	return ptr;
610     }
611 
612     /* No short cuts, allocate new memory and copy */
613 
614     mem = malloc(size);
615     if (!mem)
616         return NULL;
617 
618     memcpy(mem, ptr, old_size - MALLOC_HEAD);
619     free(ptr);
620 
621     return mem;
622 }
623 #endif /* DEFINE_REALLOC */
624 
625 #ifdef DEFINE_MALLINFO
626 
627 volatile chunk_t *__malloc_block;
628 
629 void
__malloc_validate_block(chunk_t * r)630 __malloc_validate_block(chunk_t *r)
631 {
632     __malloc_block = r;
633     assert (ALIGN_PTR(chunk_to_ptr(r), MALLOC_CHUNK_ALIGN) == chunk_to_ptr(r));
634     assert (ALIGN_PTR(r, MALLOC_HEAD_ALIGN) == r);
635     assert (r->size >= MALLOC_MINSIZE);
636     assert (r->size < 0x80000000UL);
637     assert (ALIGN_TO(r->size, MALLOC_HEAD_ALIGN) == r->size);
638 }
639 
640 void
__malloc_validate(void)641 __malloc_validate(void)
642 {
643     chunk_t *r;
644 
645     for (r = __malloc_free_list; r; r = r->next) {
646 	__malloc_validate_block(r);
647 	assert (r->next == NULL || (char *) r + r->size < (char *) r->next);
648     }
649 }
650 
mallinfo(void)651 struct mallinfo mallinfo(void)
652 {
653     char * sbrk_now;
654     chunk_t * pf;
655     size_t free_size = 0;
656     size_t total_size;
657     size_t ordblks = 0;
658     struct mallinfo current_mallinfo = {};
659 
660     MALLOC_LOCK;
661 
662     __malloc_validate();
663 
664     if (__malloc_sbrk_start == NULL) total_size = 0;
665     else {
666         sbrk_now = __malloc_sbrk_top;
667 
668         if (sbrk_now == NULL)
669             total_size = (size_t)-1;
670         else
671             total_size = (size_t) (sbrk_now - __malloc_sbrk_start);
672     }
673 
674     for (pf = __malloc_free_list; pf; pf = pf->next) {
675 	ordblks++;
676         free_size += pf->size;
677     }
678 
679     current_mallinfo.ordblks = ordblks;
680     current_mallinfo.arena = total_size;
681     current_mallinfo.fordblks = free_size;
682     current_mallinfo.uordblks = total_size - free_size;
683 
684     MALLOC_UNLOCK;
685     return current_mallinfo;
686 }
687 #endif /* DEFINE_MALLINFO */
688 
689 #ifdef DEFINE_MALLOC_STATS
690 
malloc_stats(void)691 void malloc_stats(void)
692 {
693     struct mallinfo current_mallinfo;
694 
695     current_mallinfo = mallinfo();
696     fprintf(stderr, "max system bytes = %10lu\n",
697             (long) current_mallinfo.arena);
698     fprintf(stderr, "system bytes     = %10lu\n",
699             (long) current_mallinfo.arena);
700     fprintf(stderr, "in use bytes     = %10lu\n",
701             (long) current_mallinfo.uordblks);
702     fprintf(stderr, "free blocks      = %10lu\n",
703 	    (long) current_mallinfo.ordblks);
704 }
705 #endif /* DEFINE_MALLOC_STATS */
706 
707 #ifdef DEFINE_MALLOC_USABLE_SIZE
malloc_usable_size(void * ptr)708 size_t malloc_usable_size(void * ptr)
709 {
710     return chunk_usable(ptr_to_chunk(ptr));
711 }
712 #endif /* DEFINE_MALLOC_USABLE_SIZE */
713 
714 #ifdef DEFINE_MEMALIGN
715 /* Function memalign
716  * Allocate memory block aligned at specific boundary.
717  *   align: required alignment. Must be power of 2. Return NULL
718  *          if not power of 2. Undefined behavior is bigger than
719  *          pointer value range.
720  *   s: required size.
721  * Return: allocated memory pointer aligned to align
722  * Algorithm: Malloc a big enough block, padding pointer to aligned
723  *            address, then truncate and free the tail if too big.
724  *            Record the offset of align pointer and original pointer
725  *            in the padding area.
726  */
memalign(size_t align,size_t s)727 void * memalign(size_t align, size_t s)
728 {
729     chunk_t * chunk_p;
730     size_t offset, size_with_padding;
731     char * allocated, * aligned_p;
732 
733     /* Return NULL if align isn't power of 2 */
734     if ((align & (align-1)) != 0)
735     {
736         errno = EINVAL;
737         return NULL;
738     }
739 
740     align = MAX(align, MALLOC_MINSIZE);
741 
742     if (s > MALLOC_MAXSIZE - align)
743     {
744         errno = ENOMEM;
745         return NULL;
746     }
747 
748     s = ALIGN_TO(MAX(s,1), MALLOC_CHUNK_ALIGN);
749 
750     /* Make sure there's space to align the allocation and split
751      * off chunk_t from the front
752      */
753     size_with_padding = s + align + MALLOC_MINSIZE;
754 
755     allocated = __malloc_malloc(size_with_padding);
756     if (allocated == NULL) return NULL;
757 
758     chunk_p = ptr_to_chunk(allocated);
759 
760     aligned_p = ALIGN_PTR(allocated, align);
761 
762     offset = (size_t) (aligned_p - allocated);
763 
764     /* Split off the front piece if necessary */
765     if (offset)
766     {
767 	if (offset < MALLOC_MINSIZE) {
768 	    aligned_p += align;
769 	    offset += align;
770 	}
771 
772 	chunk_t *new_chunk_p = ptr_to_chunk(aligned_p);
773 	new_chunk_p->size = chunk_p->size - offset;
774 
775 	make_free_chunk(chunk_p, offset);
776 
777 	chunk_p = new_chunk_p;
778     }
779 
780     offset = chunk_p->size - chunk_size(s);
781 
782     /* Split off the back piece if large enough */
783     if (offset >= MALLOC_MINSIZE)
784     {
785 	chunk_p->size -= offset;
786 
787 	make_free_chunk((chunk_t *) chunk_end(chunk_p), offset);
788     }
789     return aligned_p;
790 }
791 #ifdef _HAVE_ALIAS_ATTRIBUTE
792 __strong_reference(memalign, aligned_alloc);
793 #endif
794 #endif /* DEFINE_MEMALIGN */
795 
796 #ifdef DEFINE_MALLOPT
mallopt(int parameter_number,int parameter_value)797 int mallopt(int parameter_number, int parameter_value)
798 {
799     (void) parameter_number;
800     (void) parameter_value;
801     return 0;
802 }
803 #endif /* DEFINE_MALLOPT */
804 
805 #ifdef DEFINE_VALLOC
valloc(size_t s)806 void * valloc(size_t s)
807 {
808     return memalign(MALLOC_PAGE_ALIGN, s);
809 }
810 #endif /* DEFINE_VALLOC */
811 
812 #ifdef DEFINE_PVALLOC
pvalloc(size_t s)813 void * pvalloc(size_t s)
814 {
815     if (s > MALLOC_MAXSIZE - MALLOC_PAGE_ALIGN)
816     {
817         errno = ENOMEM;
818         return NULL;
819     }
820 
821     return valloc(ALIGN_TO(s, MALLOC_PAGE_ALIGN));
822 }
823 #endif /* DEFINE_PVALLOC */
824 
825 #ifdef DEFINE_GETPAGESIZE
getpagesize(void)826 int getpagesize(void)
827 {
828     return MALLOC_PAGE_ALIGN;
829 }
830 #endif /* DEFINE_GETPAGESIZE */
831 
832 #ifdef DEFINE_POSIX_MEMALIGN
posix_memalign(void ** memptr,size_t align,size_t size)833 int posix_memalign(void **memptr, size_t align, size_t size)
834 {
835     /* Return EINVAL if align isn't power of 2 or not a multiple of a pointer size */
836     if ((align & (align-1)) != 0 || align % sizeof(void*) != 0 || align == 0)
837         return EINVAL;
838 
839     void *mem = memalign(align, size);
840 
841     if (!mem)
842         return ENOMEM;
843 
844     *memptr = mem;
845     return 0;
846 }
847 #endif
848