]> git.corax.cc Git - corax/commitdiff
libc: Add simple mmap() based heap implementation
authorMatthias Kruk <m@m10k.eu>
Sun, 9 Aug 2020 07:23:27 +0000 (16:23 +0900)
committerMatthias Kruk <m@m10k.eu>
Sun, 9 Aug 2020 07:23:27 +0000 (16:23 +0900)
include/stdlib.h [new file with mode: 0644]
libc/Makefile
libc/heap.c [new file with mode: 0644]
libc/heap.h [new file with mode: 0644]
libc/stdlib.c [new file with mode: 0644]

diff --git a/include/stdlib.h b/include/stdlib.h
new file mode 100644 (file)
index 0000000..6fbf6d7
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef STDLIB_H
+#define STDLIB_H
+
+void *malloc(size_t);
+void free(void*);
+
+#endif /* STDLIB_H */
index 6a80c91dc26863c8b7e0cdd979fdffdd0f1064a5..5092ddd4a7bf7393694c51f458719b92250178a9 100644 (file)
@@ -1,5 +1,6 @@
 OUTPUT = libc.a
-OBJECTS = syscall.o string.o signal.o start.o stdio.o string_ia32.o unistd.o spinlock.o
+OBJECTS = syscall.o string.o signal.o start.o stdio.o string_ia32.o unistd.o spinlock.o stdlib.o \
+         heap.o
 PHONY = clean
 INCLUDES = -I../include -I..
 CFLAGS = -m32 -Wall -nostdlib -nodefaultlibs -nostartfiles -ffreestanding $(INCLUDES)
diff --git a/libc/heap.c b/libc/heap.c
new file mode 100644 (file)
index 0000000..a1020ff
--- /dev/null
@@ -0,0 +1,235 @@
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <string.h>
+#include <errno.h>
+#include <corax/spinlock.h>
+#include <crxstd.h>
+
+#define FLAG_IN_USE        (1 << 0)
+#define FLAG_INSECURE      (1 << 1)
+#define WASTE_MAX          32
+#define HEAP_FLAGS_DEFAULT 0
+#define PAGE_SIZE          4096
+
+struct heap_node {
+       struct heap_node *next;
+       size_t size;
+       unsigned int flags;
+       unsigned char data[];
+};
+
+static struct {
+       void *base;
+       size_t size;
+       size_t free;
+       struct heap_node *nodes;
+       cx_spinlock_t lock;
+       unsigned int flags;
+} _heap;
+
+#define HEAP_ADDR     ((void*)0x80000000)
+#define HEAP_INITSIZE 8192
+#define HEAP_MAXSIZE  (1024 * 1024 * 1024)
+
+static inline void _heap_lock(void)
+{
+       cx_spinlock_lock(&_heap.lock);
+       return;
+}
+
+static inline void _heap_unlock(void)
+{
+       cx_spinlock_unlock(&_heap.lock);
+       return;
+}
+
+/*
+ * _heap_grow() - Increase the size of the heap
+ *
+ * SYNOPSIS
+ *  int _heap_grow(size_t size);
+ *
+ * DESCRIPTION
+ *  The _heap_grow() function increases the size of the heap by `size' bytes. Internally, memory
+ *  is allocated by adding new pages to the end of the heap by means of the mmap() syscall. For
+ *  that reason, the value of `size' will be rounded up to multiples of the page size of the
+ *  process's page directory.
+ *
+ */
+int _heap_grow(size_t size)
+{
+       void *map_base;
+       int ret_val;
+       size_t new_size;
+       size_t map_size;
+
+       new_size = _heap.size + size;
+       map_size = size + sizeof(struct heap_node);
+
+       if(map_size & (PAGE_SIZE - 1)) {
+               map_size &= ~(PAGE_SIZE - 1);
+               map_size += PAGE_SIZE;
+       }
+
+       if(new_size < _heap.size || map_size < size) {
+               return(-EOVERFLOW);
+       }
+
+       ret_val = -ENOMEM;
+
+       if(new_size < HEAP_MAXSIZE) {
+               map_base = _heap.base + _heap.size;
+
+               map_base = mmap(map_base, map_size, PROT_READ | PROT_WRITE, 0, -1, 0);
+
+               if(map_base != (void*)-1) {
+                       struct heap_node *node;
+                       struct heap_node **cur;
+
+                       node = (struct heap_node*)map_base;
+                       node->flags = 0;
+                       node->size = map_size - sizeof(*node);
+
+                       _heap.size += node->size;
+                       _heap.free += node->size;
+
+                       for(cur = &_heap.nodes; *cur; cur = &(*cur)->next);
+                       *cur = node;
+
+                       ret_val = 0;
+               }
+       }
+
+       return(ret_val);
+}
+
+int heap_init(void)
+{
+       int ret_val;
+
+       ret_val = -EALREADY;
+
+       if(!_heap.base) {
+               _heap.base = HEAP_ADDR;
+               _heap.size = 0;
+               _heap.nodes = NULL;
+               _heap.flags = HEAP_FLAGS_DEFAULT;
+
+               ret_val = _heap_grow(HEAP_INITSIZE);
+       }
+
+       return(ret_val);
+}
+
+/*
+ * _heap_node_split() - Split a heap node
+ *
+ * SYNOPSIS
+ *  int _heap_node_split(struct heap_node *old_node, const size_t size);
+ *
+ * DESCRIPTION
+ *  The _heap_node_split() function splits the heap node pointed to by `old_node' into two nodes,
+ *  reducing the size of `old_node' to `size' and creating a new node from the remaining memory.
+ *
+ * RETURN VALUE
+ *  This function returns zero upon success, or a negative error number if an error has occured.
+ *
+ * ERRORS
+ *  -ERANGE The value of `size' is too large
+ *  -ENOMEM The node is too small to be split
+ */
+static inline int _heap_node_split(struct heap_node *old_node, const size_t size)
+{
+       struct heap_node *new_node;
+       size_t threshold;
+       int ret_val;
+
+       ret_val = -ERANGE;
+       threshold = size + sizeof(struct heap_node) + WASTE_MAX;
+
+       if(threshold > size) {
+               ret_val = -ENOMEM;
+
+               if(old_node->size > threshold) {
+                       _heap.size -= old_node->size;
+
+                       new_node = (struct heap_node*)(old_node->data + size);
+                       new_node->next = old_node->next;
+                       new_node->size = old_node->size - size - sizeof(*new_node);
+                       new_node->flags = 0;
+
+                       _heap.size += new_node->size;
+
+                       old_node->next = new_node;
+                       old_node->size = size;
+
+                       ret_val = 0;
+               }
+       }
+
+       return(ret_val);
+}
+
+int heap_alloc(size_t size, void **dst)
+{
+       struct heap_node *cur;
+       int ret_val;
+
+       ret_val = -ENOMEM;
+
+       if(_heap.free < size) {
+               _heap_grow(size);
+       }
+
+       for(cur = _heap.nodes; cur; cur = cur->next) {
+               if(cur->flags & FLAG_IN_USE) {
+                       continue;
+               }
+
+               if(cur->size < size) {
+                       continue;
+               }
+
+               ret_val = _heap_node_split(cur, size);
+
+               /*
+                * The node is definitely big enough for us, but it is possible that it is not big
+                * enough to be split. The latter case is not fatal for the allocation though.
+                */
+               if(ret_val == 0 || ret_val == -ENOMEM) {
+                       cur->flags = _heap.flags | FLAG_IN_USE;
+                       *dst = (void*)cur->data;
+                       ret_val = 0;
+               }
+
+               break;
+       }
+
+       return(ret_val);
+}
+
+int heap_free(void *ptr)
+{
+       struct heap_node *cur;
+       int ret_val;
+
+       ret_val = -EINVAL;
+
+       for(cur = _heap.nodes; cur; cur = cur->next) {
+               if(ptr >= (void*)cur->data && ptr < (void*)cur->data + cur->size) {
+                       ret_val = -EALREADY;
+
+                       if(cur->flags & FLAG_IN_USE) {
+                               cur->flags &= ~FLAG_IN_USE;
+
+                               if(!(cur->flags & FLAG_INSECURE)) {
+                                       memset(cur->data, 0, cur->size);
+                               }
+
+                               ret_val = 0;
+                       }
+               }
+       }
+
+       return(ret_val);
+}
diff --git a/libc/heap.h b/libc/heap.h
new file mode 100644 (file)
index 0000000..146fe9f
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _HEAP_H
+#define _HEAP_H
+
+#include <sys/types.h>
+
+int heap_init(void);
+int heap_alloc(size_t, void**);
+int heap_free(void*);
+
+#endif /* _HEAP_H */
diff --git a/libc/stdlib.c b/libc/stdlib.c
new file mode 100644 (file)
index 0000000..7034332
--- /dev/null
@@ -0,0 +1,44 @@
+#include <unistd.h>
+#include <errno.h>
+#include "heap.h"
+#include <crxstd.h>
+
+static int _initialized = 0;
+
+void *malloc(size_t size)
+{
+       void *ret_val;
+       int err;
+
+       if(!_initialized) {
+               err = heap_init();
+
+               if(err < 0) {
+                       errno = -err;
+                       return(NULL);
+               }
+
+               _initialized = 1;
+       }
+
+       err = heap_alloc(size, &ret_val);
+
+       if(err < 0) {
+               ret_val = NULL;
+       }
+
+       errno = -err;
+
+       return(ret_val);
+}
+
+void free(void *ptr)
+{
+       errno = EBADFD;
+
+       if(_initialized) {
+               errno = -heap_free(ptr);
+       }
+
+       return;
+}