aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Bucur <stefanb@zytor.com>2008-07-21 21:04:49 -0700
committerStefan Bucur <stefan@stefan-ubumac.(none)>2009-03-15 10:03:13 +0200
commit8e1a02afe4ed103b474142d725f8e1ee0d9ea56b (patch)
tree8c969cbdc7e9ca93e03da7a7f34d1b18954824ec
parent683cdc4067eeb92c3117584c570d66b11e3aa107 (diff)
downloadsyslinux-elf-8e1a02afe4ed103b474142d725f8e1ee0d9ea56b.tar.gz
syslinux-elf-8e1a02afe4ed103b474142d725f8e1ee0d9ea56b.tar.xz
syslinux-elf-8e1a02afe4ed103b474142d725f8e1ee0d9ea56b.zip
Implemented a native version of posix_memalign().
-rw-r--r--com32/include/stdlib.h2
-rw-r--r--com32/lib/malloc.c80
-rw-r--r--com32/lib/malloc.h5
3 files changed, 85 insertions, 2 deletions
diff --git a/com32/include/stdlib.h b/com32/include/stdlib.h
index 24cf602a..d822b9b0 100644
--- a/com32/include/stdlib.h
+++ b/com32/include/stdlib.h
@@ -36,6 +36,8 @@ __extern __mallocfunc void *malloc(size_t);
__extern __mallocfunc void *zalloc(size_t);
__extern __mallocfunc void *calloc(size_t, size_t);
__extern __mallocfunc void *realloc(void *, size_t);
+__extern int posix_memalign(void **memptr, size_t alignment,
+ size_t size);
__extern long strtol(const char *, char **, int);
__extern long long strtoll(const char *, char **, int);
__extern unsigned long strtoul(const char *, char **, int);
diff --git a/com32/lib/malloc.c b/com32/lib/malloc.c
index 2f8362b4..5841ad13 100644
--- a/com32/lib/malloc.c
+++ b/com32/lib/malloc.c
@@ -5,6 +5,7 @@
*/
#include <stdlib.h>
+#include <errno.h>
#include "init.h"
#include "malloc.h"
@@ -118,3 +119,82 @@ void *malloc(size_t size)
/* Nothing found... need to request a block from the kernel */
return NULL; /* No kernel to get stuff from */
}
+
+int posix_memalign(void **memptr, size_t alignment, size_t size) {
+ struct free_arena_header *fp, *nfp;
+ uintptr_t align_mask, align_addr;
+
+ if (size == 0 || memptr == NULL) {
+ return EINVAL;
+ }
+
+ if ((alignment & (alignment - 1)) != 0)
+ return EINVAL;
+
+ // POSIX says to refuse alignments smaller than sizeof(void*)
+ if (alignment % sizeof(void*) != 0)
+ return EINVAL;
+
+ // The arena allocator can't handle alignments smaller than this
+ if (alignment < sizeof(struct arena_header)) {
+ alignment = sizeof(struct arena_header);
+ }
+ align_mask = ~(uintptr_t)(alignment - 1);
+
+ // Round up
+ size = (size + sizeof(struct arena_header) - 1) & ARENA_SIZE_MASK;
+
+ *memptr = NULL;
+
+ for (fp = __malloc_head.next_free; fp->a.type != ARENA_TYPE_HEAD;
+ fp = fp->next_free) {
+
+ if (fp->a.size <= size)
+ continue;
+
+ align_addr = (uintptr_t)fp;
+
+ // Ensure the alignment leaves some space before for the header
+ if (align_addr % alignment == 0) {
+ align_addr += alignment;
+ } else {
+ align_addr = (align_addr + alignment - 1) & align_mask;
+ }
+ if (align_addr - (uintptr_t)fp < 2*sizeof(struct arena_header))
+ align_addr += alignment;
+
+ // See if now we have enough space
+ if (align_addr + size > (uintptr_t)fp + fp->a.size)
+ continue;
+
+ // We have a winner...
+ if (align_addr - (uintptr_t)fp > sizeof(struct arena_header)) {
+ // We must split the block before the alignment point
+ nfp = (struct free_arena_header*)(align_addr - sizeof(struct arena_header));
+ nfp->a.type = ARENA_TYPE_FREE;
+ nfp->a.size = fp->a.size - ((uintptr_t)nfp - (uintptr_t)fp);
+ nfp->a.prev = fp;
+ nfp->a.next = fp->a.next;
+ nfp->prev_free = fp;
+ nfp->next_free = fp->next_free;
+
+ nfp->a.next->a.prev = nfp;
+ nfp->next_free->prev_free = nfp;
+
+ fp->a.size = (uintptr_t)nfp - (uintptr_t)fp;
+
+ fp->a.next = nfp;
+ fp->next_free = nfp;
+
+ *memptr = __malloc_from_block(nfp, size + sizeof(struct arena_header));
+ } else {
+ *memptr = __malloc_from_block(fp, size + sizeof(struct arena_header));
+ }
+
+ }
+
+ if (*memptr == NULL)
+ return ENOMEM;
+
+ return 0;
+}
diff --git a/com32/lib/malloc.h b/com32/lib/malloc.h
index 830377d9..b873ac05 100644
--- a/com32/lib/malloc.h
+++ b/com32/lib/malloc.h
@@ -14,12 +14,13 @@
#define MALLOC_CHUNK_SIZE 65536
#define MALLOC_CHUNK_MASK (MALLOC_CHUNK_SIZE-1)
+
+struct free_arena_header;
+
/*
* This structure should be a power of two. This becomes the
* alignment unit.
*/
-struct free_arena_header;
-
struct arena_header {
size_t type;
size_t size; /* Also gives the location of the next entry */