aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/malloc.h129
-rw-r--r--src/malloc.c185
-rw-r--r--src/slibc-alloc.c11
3 files changed, 313 insertions, 12 deletions
diff --git a/include/malloc.h b/include/malloc.h
index c18f885..0e17817 100644
--- a/include/malloc.h
+++ b/include/malloc.h
@@ -97,7 +97,8 @@ void* realloc(void*, size_t)
* beginning of a memory allocation on the heap.
* However, if it is `NULL`, nothing will happen.
*/
-void free(void*) __slibc_warning("Use 'fast_free' or 'secure_free' instead.");
+void free(void*)
+ __slibc_warning("Use 'fast_free' or 'secure_free' instead.");
/**
* This function is identical to `free`.
@@ -112,7 +113,131 @@ void free(void*) __slibc_warning("Use 'fast_free' or 'secure_free' instead.");
* However, if it is `NULL`, nothing will happen.
*/
#ifndef _PORTABLE_SOURCE
-void cfree(void*, ...) __deprecated("'cfree' is deprecated and not portable, use 'free' instead.");
+void cfree(void*, ...)
+ __deprecated("'cfree' is deprecated and not portable, use 'free' instead.");
+#endif
+
+
+#ifndef _PORTABLE_SOURCE
+/**
+ * Variant of `malloc` that returns an address with a
+ * specified alignment.
+ *
+ * It is unspecified how the function works. This implemention
+ * will allocate a bit of extra memory and shift the returned
+ * pointer so that it is aligned.
+ *
+ * As a GNU-compliant slibc extension, memory allocated
+ * with this function can be freed with `free`.
+ *
+ * @param boundary The alignment.
+ * @param size The number of bytes to allocated.
+ * @return Pointer to the beginning of the new allocation.
+ * If `size` is zero, this function will either return
+ * `NULL` (that is what this implement does) or return
+ * a unique pointer that can later be freed with `free`.
+ * `NULL` is returned on error, and `errno` is set to
+ * indicate the error.
+ *
+ * @throws ENOMEM The process cannot allocate more memory.
+ * @throws EINVAL If `boundary` is not a power of two.
+ */
+void* memalign(size_t, size_t)
+#ifdef __C11__
+ __deprecated("'memalign' has be deprecated by 'aligned_alloc' in C11.")
+#endif
+ __GCC_ONLY(__attribute__((malloc, warn_unused_result)));
+#endif
+
+/**
+ * `posix_memalign(p, b, n)` is equivalent to
+ * `(*p = memalign(b, n), *p ? 0 : errno)`, except
+ * `boundary` must also be a multiple of `sizeof(void*)`,
+ * and `errno` is unspecified.
+ *
+ * As a GNU-compliant slibc extension, memory allocated
+ * with this function can be freed with `free`.
+ *
+ * @param ptrptr Output parameter for the allocated memory.
+ * @param boundary The alignment.
+ * @param size The number of bytes to allocated.
+ * @return Zero on success, a value for `errno` on error.
+ *
+ * @throws ENOMEM The process cannot allocate more memory.
+ * @throws EINVAL If `boundary` is not a power-of-two multiple of `sizeof(void*)`.
+ */
+int posix_memalign(void**, size_t, size_t)
+ __GCC_ONLY(__attribute__((nonnull)));
+
+#ifndef _PORTABLE_SOURCE
+/**
+ * `valloc(n)` is equivalent to `memalign(sysconf(_SC_PAGESIZE), n)`.
+ *
+ * As a GNU-compliant slibc extension, memory allocated
+ * with this function can be freed with `free`.
+ *
+ * @param size The number of bytes to allocated.
+ * @return Pointer to the beginning of the new allocation.
+ * If `size` is zero, this function will either return
+ * `NULL` (that is what this implement does) or return
+ * a unique pointer that can later be freed with `free`.
+ * `NULL` is returned on error, and `errno` is set to
+ * indicate the error.
+ *
+ * @throws ENOMEM The process cannot allocate more memory.
+ */
+void* valloc(size_t)
+ __GCC_ONLY(__attribute__((malloc, warn_unused_result)))
+ __deprecated("'valloc' is deprecated, use 'memalign' or 'posix_memalign' instead.");
+
+#ifdef _GNU_SOURCE
+/**
+ * This function works like `valloc`, except the allocation size,
+ * including auxiliary space, is rounded up to the next multiple
+ * of the page size.
+ *
+ * @param size The number of bytes to allocated.
+ * @return Pointer to the beginning of the new allocation.
+ * If `size` is zero, this function will either return
+ * `NULL` (that is what this implement does) or return
+ * a unique pointer that can later be freed with `free`.
+ * `NULL` is returned on error, and `errno` is set to
+ * indicate the error.
+ *
+ * @throws ENOMEM The process cannot allocate more memory.
+ */
+void* pvalloc(size_t)
+ __GCC_ONLY(__attribute__((malloc, warn_unused_result)))
+ __deprecated("'pvalloc' is deprecated, use 'memalign' or 'posix_memalign' instead.");
+#endif
+#endif
+
+#ifdef __C11__
+/**
+ * This function is identical to `memalign`,
+ * except it can be freed with `free`.
+ *
+ * Variant of `malloc` that returns an address with a
+ * specified alignment.
+ *
+ * It is unspecified how the function works. This implemention
+ * will allocate a bit of extra memory and shift the returned
+ * pointer so that it is aligned.
+ *
+ * @param boundary The alignment.
+ * @param size The number of bytes to allocated.
+ * @return Pointer to the beginning of the new allocation.
+ * If `size` is zero, this function will either return
+ * `NULL` (that is what this implement does) or return
+ * a unique pointer that can later be freed with `free`.
+ * `NULL` is returned on error, and `errno` is set to
+ * indicate the error.
+ *
+ * @throws ENOMEM The process cannot allocate more memory.
+ * @throws EINVAL If `boundary` is not a power of two.
+ */
+void* aligned_alloc(size_t, size_t)
+ __GCC_ONLY(__attribute__((malloc, warn_unused_result)));
#endif
diff --git a/src/malloc.c b/src/malloc.c
index 82a6f54..f7a2e95 100644
--- a/src/malloc.c
+++ b/src/malloc.c
@@ -19,6 +19,8 @@
#include <slibc-alloc.h>
#include <strings.h>
#include <sys/mman.h>
+#include <unistd.h>
+#include <errno.h>
@@ -40,12 +42,19 @@ void* malloc(size_t size)
{
/* TODO implement implementation of malloc */
char* ptr;
+ size_t full_size;
+
if (size == 0)
return NULL;
- ptr = mmap(NULL, sizeof(size_t) + size, (PROT_READ | PROT_WRITE),
+ if (__builtin_uaddl_overflow(2 * sizeof(size_t), size, &full_size))
+ return errno = ENOMEM, NULL;
+
+ ptr = mmap(NULL, full_size, (PROT_READ | PROT_WRITE),
(MAP_PRIVATE | MAP_ANONYMOUS), -1, 0);
- *(size_t*)ptr = size;
- return ptr + sizeof(size_t);
+
+ ((size_t*)ptr)[0] = size;
+ ((size_t*)ptr)[1] = 0;
+ return ptr + 2 * sizeof(size_t);
}
@@ -68,9 +77,16 @@ void* malloc(size_t size)
*/
void* calloc(size_t elem_count, size_t elem_size)
{
- void* ptr = malloc(elem_count * elem_size);
+ void* ptr;
+ size_t size;
+
+ if (__builtin_umull_overflow(elem_count, elem_size, &size))
+ return errno = ENOMEM, NULL;
+
+ ptr = malloc(size);
if (ptr != NULL)
- explicit_bzero(ptr, elem_count * elem_size);
+ explicit_bzero(ptr, size);
+
return ptr;
}
@@ -132,3 +148,162 @@ void cfree(void* ptr, ...)
fast_free(ptr);
}
+
+/**
+ * Variant of `malloc` that returns an address with a
+ * specified alignment.
+ *
+ * It is unspecified how the function works. This implemention
+ * will allocate a bit of extra memory and shift the returned
+ * pointer so that it is aligned.
+ *
+ * As a GNU-compliant slibc extension, memory allocated
+ * with this function can be freed with `free`.
+ *
+ * @param boundary The alignment.
+ * @param size The number of bytes to allocated.
+ * @return Pointer to the beginning of the new allocation.
+ * If `size` is zero, this function will either return
+ * `NULL` (that is what this implement does) or return
+ * a unique pointer that can later be freed with `free`.
+ * `NULL` is returned on error, and `errno` is set to
+ * indicate the error.
+ *
+ * @throws ENOMEM The process cannot allocate more memory.
+ * @throws EINVAL If `boundary` is not a power of two.
+ */
+void* memalign(size_t boundary, size_t size)
+{
+ char* ptr;
+ size_t full_size;
+ size_t address;
+ size_t shift = 0;
+
+ if (!boundary || (__builtin_ffsl(boundary) != boundary))
+ return errno = EINVAL, NULL;
+ if (__builtin_uaddl_overflow(boundary - 1, size, &full_size))
+ return errno = ENOMEM, NULL;
+
+ ptr = malloc(full_size);
+ if (ptr == NULL)
+ return NULL;
+
+ address = (size_t)ptr;
+ if (address % boundary != 0)
+ {
+ shift = boundary - (address % boundary);
+ ptr += shift;
+ *(size_t*)(ptr - sizeof(size_t)) = shift;
+ }
+
+ return ptr;
+}
+
+
+/**
+ * `posix_memalign(p, b, n)` is equivalent to
+ * `(*p = memalign(b, n), *p ? 0 : errno)`, except
+ * `boundary` must also be a multiple of `sizeof(void*)`,
+ * and `errno` is unspecified.
+ *
+ * As a GNU-compliant slibc extension, memory allocated
+ * with this function can be freed with `free`.
+ *
+ * @param ptrptr Output parameter for the allocated memory.
+ * @param boundary The alignment.
+ * @param size The number of bytes to allocated.
+ * @return Zero on success, a value for `errno` on error.
+ *
+ * @throws ENOMEM The process cannot allocate more memory.
+ * @throws EINVAL If `boundary` is not a power-of-two multiple of `sizeof(void*)`.
+ */
+int posix_memalign(void** ptrptr, size_t boundary, size_t size)
+{
+ if (boundary < sizeof(void*))
+ return EINVAL;
+ *ptrptr = memalign(boundary, size);
+ return *ptrptr ? 0 : errno;
+}
+
+
+/**
+ * `valloc(n)` is equivalent to `memalign(sysconf(_SC_PAGESIZE), n)`.
+ *
+ * As a GNU-compliant slibc extension, memory allocated
+ * with this function can be freed with `free`.
+ *
+ * @param size The number of bytes to allocated.
+ * @return Pointer to the beginning of the new allocation.
+ * If `size` is zero, this function will either return
+ * `NULL` (that is what this implement does) or return
+ * a unique pointer that can later be freed with `free`.
+ * `NULL` is returned on error, and `errno` is set to
+ * indicate the error.
+ *
+ * @throws ENOMEM The process cannot allocate more memory.
+ */
+void* valloc(size_t size)
+{
+ return memalign((size_t)sysconf(_SC_PAGESIZE), size);
+}
+
+
+/**
+ * This function works like `valloc`, except the allocation size,
+ * including auxiliary space, is rounded up to the next multiple
+ * of the page size.
+ *
+ * @param size The number of bytes to allocated.
+ * @return Pointer to the beginning of the new allocation.
+ * If `size` is zero, this function will either return
+ * `NULL` (that is what this implement does) or return
+ * a unique pointer that can later be freed with `free`.
+ * `NULL` is returned on error, and `errno` is set to
+ * indicate the error.
+ *
+ * @throws ENOMEM The process cannot allocate more memory.
+ */
+void* pvalloc(size_t size)
+{
+ size_t boundary = (size_t)sysconf(_SC_PAGESIZE);
+ size_t full_size = 2 * sizeof(size_t) + boundary - 1 + size;
+ size_t rounding = 0;
+
+ if (full_size % boundary != 0)
+ rounding = boundary - (full_size % boundary);
+
+ if (__builtin_uaddl_overflow(size, rounding, &full_size))
+ return errno = ENOMEM, NULL;
+
+ return memalign(boundary, full_size);
+}
+
+
+/**
+ * This function is identical to `memalign`,
+ * except it can be freed with `free`.
+ *
+ * Variant of `malloc` that returns an address with a
+ * specified alignment.
+ *
+ * It is unspecified how the function works. This implemention
+ * will allocate a bit of extra memory and shift the returned
+ * pointer so that it is aligned.
+ *
+ * @param boundary The alignment.
+ * @param size The number of bytes to allocated.
+ * @return Pointer to the beginning of the new allocation.
+ * If `size` is zero, this function will either return
+ * `NULL` (that is what this implement does) or return
+ * a unique pointer that can later be freed with `free`.
+ * `NULL` is returned on error, and `errno` is set to
+ * indicate the error.
+ *
+ * @throws ENOMEM The process cannot allocate more memory.
+ * @throws EINVAL If `boundary` is not a power of two.
+ */
+void* aligned_alloc(size_t boundary, size_t size)
+{
+ return memalign(boundary, size);
+}
+
diff --git a/src/slibc-alloc.c b/src/slibc-alloc.c
index 0cbf623..08e6209 100644
--- a/src/slibc-alloc.c
+++ b/src/slibc-alloc.c
@@ -23,8 +23,9 @@
-#define PURE_ALLOC(p) (((char*)(p)) - sizeof(size_t))
-#define PURE_SIZE(z) ((z) + sizeof(size_t))
+#define __ALIGN(p) (*(size_t*)(((char*)(p)) - sizeof(size_t)))
+#define PURE_ALLOC(p) (((char*)(p)) - (__ALIGN(p) + 2 * sizeof(size_t)))
+#define PURE_SIZE(p) (*(size_t*)PURE_ALLOC(p) + 2 * sizeof(size_t))
@@ -38,7 +39,7 @@ void fast_free(void* segment)
{
if (segument == NULL)
return;
- munmap(PURE_ALLOC(segment), PURE_SIZE(*(size_t*)segment));
+ munmap(PURE_ALLOC(segment), PURE_SIZE(segment));
}
@@ -52,7 +53,7 @@ void secure_free(void* segment)
{
if (segument == NULL)
return;
- explicit_bzero(PURE_ALLOC(segment), PURE_SIZE(allocsize(segment)));
+ explicit_bzero(PURE_ALLOC(segment), PURE_SIZE(segment));
fast_free(segment);
}
@@ -105,7 +106,7 @@ size_t allocsize(void* segment)
if (new_ptr != ptr) \
{ \
if (CLEAR_FREE) \
- explicit_bzero(PURE_ALLOC(ptr), PURE_SIZE(old_size)); \
+ explicit_bzero(PURE_ALLOC(ptr), PURE_SIZE(ptr)); \
fast_free(new_ptr); \
} \
\