From: Matthias Kruk Date: Sun, 9 Aug 2020 07:23:27 +0000 (+0900) Subject: libc: Add simple mmap() based heap implementation X-Git-Url: https://git.corax.cc/?a=commitdiff_plain;h=753f16e01cf9263257af734ed9dae64f3a95a970;p=corax libc: Add simple mmap() based heap implementation --- diff --git a/include/stdlib.h b/include/stdlib.h new file mode 100644 index 0000000..6fbf6d7 --- /dev/null +++ b/include/stdlib.h @@ -0,0 +1,7 @@ +#ifndef STDLIB_H +#define STDLIB_H + +void *malloc(size_t); +void free(void*); + +#endif /* STDLIB_H */ diff --git a/libc/Makefile b/libc/Makefile index 6a80c91..5092ddd 100644 --- a/libc/Makefile +++ b/libc/Makefile @@ -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 index 0000000..a1020ff --- /dev/null +++ b/libc/heap.c @@ -0,0 +1,235 @@ +#include +#include +#include +#include +#include +#include + +#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 index 0000000..146fe9f --- /dev/null +++ b/libc/heap.h @@ -0,0 +1,10 @@ +#ifndef _HEAP_H +#define _HEAP_H + +#include + +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 index 0000000..7034332 --- /dev/null +++ b/libc/stdlib.c @@ -0,0 +1,44 @@ +#include +#include +#include "heap.h" +#include + +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; +}