memalign.c 1.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include "malloc_impl.h"
#include "malloc_config.h"

void *__memalign(size_t align, size_t len)
{
	unsigned char *mem, *new;

	if ((align & -align) != align) {
		errno = EINVAL;
		return 0;
	}

	if (len > SIZE_MAX - align || __malloc_replaced) {
		errno = ENOMEM;
		return 0;
	}

	if (align <= SIZE_ALIGN)
		return malloc(len);

	if (!(mem = malloc(len + align-1)))
		return 0;

	new = (void *)((uintptr_t)mem + align-1 & -align);

	if (new == mem) return mem;
	struct chunk *c = MEM_TO_CHUNK(mem);
	struct chunk *n = MEM_TO_CHUNK(new);

	if (IS_MMAPPED(c)) {
		/* Apply difference between aligned and original
		 * address to the "extra" field of mmapped chunk.
		 */
		n->psize = c->psize + (new-mem);
		n->csize = c->csize - (new-mem);
#ifdef MALLOC_RED_ZONE
		n->usize = len;
		n->state = M_STATE_MMAP | M_STATE_USED;
		chunk_checksum_set(n);
#endif
		return new;
	}

	struct chunk *t = NEXT_CHUNK(c);

	/* Split the allocated chunk into two chunks. The aligned part
	 * that will be used has the size in its footer reduced by the
	 * difference between the aligned and original addresses, and
	 * the resulting size copied to its header. A new header and
	 * footer are written for the split-off part to be freed. */
	n->psize = c->csize = C_INUSE | (new-mem);
	n->csize = t->psize -= new-mem;

#ifdef MALLOC_RED_ZONE
	/* Update extra overhead */
	c->usize = OVERHEAD;
	c->state = M_STATE_BRK;
	chunk_checksum_set(c);
	n->usize = len;
	n->state = M_STATE_BRK | M_STATE_USED;
	chunk_checksum_set(n);
#endif

	__bin_chunk(c);
	return new;
}

weak_alias(__memalign, memalign);