crt0/malloc.c

143 lines
3.5 KiB
C
Raw Permalink Normal View History

2024-03-25 22:52:21 +00:00
#include <sys/mman.h>
2024-04-12 22:09:14 +00:00
#include <unistd.h>
2024-03-25 22:52:21 +00:00
#include <stdlib.h>
#include <string.h>
2024-04-15 22:51:44 +00:00
#include <errno.h>
2024-02-27 01:06:08 +00:00
2024-03-25 22:52:21 +00:00
#define BLOCK_SIZE (1 << 22) /* 4M */
#define MMAP_THRESHOLD (1 << 18) /* 256k */
#define WORD_SZ (sizeof(void*))
#define MIN_ALIGNMENT (2 * WORD_SZ)
2024-02-27 01:06:08 +00:00
2024-03-25 22:52:21 +00:00
#define CHUNK_START(p) *((void **)(p - WORD_SZ))
#define CHUNK_SIZE(p) *((size_t *) CHUNK_START(p))
2024-02-27 01:06:08 +00:00
2024-03-25 22:52:21 +00:00
#define sys_alloc(s) mmap(NULL, s, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)
2024-02-27 01:06:08 +00:00
2024-03-25 22:52:21 +00:00
/*
* Allocated memory chunk layout
*
* +-------------+ <----------- chunk_start
* | chunk_size | size_t
* +-------------+
* | padding | n words (n can be 0)
* +-------------+
* | chunk_start | void *
* +-------------+ <----------- data_start
* | data | chunk_size
* +-------------+
*
*/
static inline size_t align(size_t l, size_t align) {
return (l + align - 1) / align * align;
}
// Prefix is chunk_size + padding + chunk_start
static size_t calculate_prefix(size_t start, size_t alignment) {
size_t p = start;
// Reserve 2 words, one for chunk_size, one for chunk_start
p += 2 * WORD_SZ;
p = align(p, alignment);
return p - start;
}
void *memalign(size_t alignment, size_t size) {
static struct {
void *curr;
void *end;
} block = { NULL, NULL };
if (alignment < MIN_ALIGNMENT) alignment = MIN_ALIGNMENT;
2024-04-12 22:09:14 +00:00
if (alignment > getpagesize()) alignment = getpagesize();
2024-03-25 22:52:21 +00:00
size = align(size, MIN_ALIGNMENT);
if (size >= MMAP_THRESHOLD) {
size_t prefix = calculate_prefix(0, alignment);
void *chunk = sys_alloc(size + prefix);
2024-04-15 22:51:44 +00:00
if (chunk == MAP_FAILED) {
errno = ENOMEM;
return NULL;
}
2024-03-25 22:52:21 +00:00
void *p = chunk + prefix;
CHUNK_START(p) = chunk;
CHUNK_SIZE(p) = size;
return p;
}
size_t prefix = calculate_prefix((size_t) block.curr, alignment);
if (block.curr == NULL || block.curr + prefix + size > block.end) {
void *b = sys_alloc(BLOCK_SIZE);
2024-04-15 22:51:44 +00:00
if (b == MAP_FAILED) {
errno = ENOMEM;
return NULL;
}
2024-03-25 22:52:21 +00:00
block.curr = b;
block.end = b + BLOCK_SIZE;
prefix = calculate_prefix((size_t) b, alignment);
}
void *p = block.curr + prefix;
CHUNK_START(p) = block.curr;
CHUNK_SIZE(p) = size;
block.curr += prefix + size;
return p;
}
2024-08-11 11:14:29 +00:00
int posix_memalign(void **memptr, size_t alignment, size_t size) {
void *ptr = memalign(alignment, size);
if (ptr) {
*memptr = ptr;
return 0;
} else {
return ENOMEM;
}
}
2024-03-25 22:52:21 +00:00
void *malloc(size_t size) {
return memalign(MIN_ALIGNMENT, size);
}
void free(void *ptr) {
if (ptr == NULL) return;
size_t size = CHUNK_SIZE(ptr);
if (size >= MMAP_THRESHOLD) {
munmap(CHUNK_START(ptr), size);
}
}
void *crt0_calloc(size_t nmemb, size_t size) {
2024-04-15 22:51:44 +00:00
size_t sz;
if (__builtin_mul_overflow(nmemb, size, &sz)) {
errno = ENOMEM;
return NULL;
}
return malloc(sz);
2024-03-25 22:52:21 +00:00
}
// For some reason calloc does not export properly
__asm__(".global calloc \n calloc = crt0_calloc");
void *realloc(void *ptr, size_t size) {
if (ptr) {
size_t old_sz = CHUNK_SIZE(ptr);
if (old_sz >= size)
return ptr;
void *p = malloc(size);
memcpy(p, ptr, old_sz);
free(ptr);
return p;
} else {
return malloc(size);
}
}
2024-04-15 22:51:44 +00:00
void *reallocarray(void *ptr, size_t nmemb, size_t size) {
size_t sz;
if (__builtin_mul_overflow(nmemb, size, &sz)) {
errno = ENOMEM;
return NULL;
}
return realloc(ptr, sz);
}