summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2023-01-02 22:48:07 +0000
committerAidan MacDonald <amachronic@protonmail.com>2023-01-15 10:04:13 +0000
commit92565e9246f3a47b90fea4a436ecfd8e7a1198b8 (patch)
tree5e2fa26fff921296729b74bd8947dd225e69c84f
parentf2f198663edc01a1f19e35b8a0c302f8ee47ae5e (diff)
downloadrockbox-92565e9246.tar.gz
rockbox-92565e9246.zip
buflib: Add malloc-backed buflib
This is intended for improving the effectiveness of tools like ASAN when debugging memory errors in the sim. It's not meant to be a serious allocator for hosted targets. Enable it by changing the buflib backend in config.h. Change-Id: I0cf23cefa47ee35dede7b49e0e5b72dac60e8d3e
-rw-r--r--firmware/SOURCES2
-rw-r--r--firmware/buflib_malloc.c251
-rw-r--r--firmware/export/config.h1
-rw-r--r--firmware/include/buflib.h2
-rw-r--r--firmware/include/buflib_malloc.h53
5 files changed, 309 insertions, 0 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 02e962c0c3..7e2ffb323e 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -6,6 +6,8 @@ events.c
backlight.c
#if CONFIG_BUFLIB_BACKEND == BUFLIB_BACKEND_MEMPOOL
buflib_mempool.c
+#elif CONFIG_BUFLIB_BACKEND == BUFLIB_BACKEND_MALLOC
+buflib_malloc.c
#endif
core_alloc.c
general.c
diff --git a/firmware/buflib_malloc.c b/firmware/buflib_malloc.c
new file mode 100644
index 0000000000..fdc2b5b925
--- /dev/null
+++ b/firmware/buflib_malloc.c
@@ -0,0 +1,251 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2023 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/*
+ * Malloc backed buflib. This is intended for debugging rather than for
+ * serious use - the buffer passed to the context is wasted, and memory
+ * is acquired from malloc() instead. The main point is to make ASAN more
+ * effective by isolating buflib allocations from each other.
+ *
+ * Currently this is a bare-minimum implementation, it doesn't even run
+ * buflib callbacks since it never moves anything. It could later be
+ * extended with stress-testing options, for example by randomly moving
+ * allocations around.
+ */
+
+#include "buflib.h"
+#include "panic.h"
+#include <stdlib.h>
+
+static struct buflib_malloc_handle *get_free_handle(struct buflib_context *ctx)
+{
+ struct buflib_malloc_handle *h;
+ for (size_t i = 0; i < ctx->num_allocs; ++i)
+ {
+ h = &ctx->allocs[i];
+ if (h->size == 0)
+ return h;
+ }
+
+ ctx->num_allocs++;
+ ctx->allocs = realloc(ctx->allocs, ctx->num_allocs * sizeof(*ctx->allocs));
+ if (!ctx->allocs)
+ panicf("buflib %p handle OOM", ctx);
+
+ h = &ctx->allocs[ctx->num_allocs - 1];
+ h->size = 0;
+ return h;
+}
+
+static int get_handle_num(struct buflib_context *ctx,
+ struct buflib_malloc_handle *handle)
+{
+ return (handle - ctx->allocs) + 1;
+}
+
+static struct buflib_malloc_handle *get_handle(struct buflib_context *ctx,
+ int handle)
+{
+ return &ctx->allocs[handle - 1];
+}
+
+struct buflib_callbacks buflib_ops_locked = {
+ .move_callback = NULL,
+ .shrink_callback = NULL,
+ .sync_callback = NULL,
+};
+
+void buflib_init(struct buflib_context *ctx, void *buf, size_t size)
+{
+ ctx->allocs = NULL;
+ ctx->num_allocs = 0;
+ ctx->buf = buf;
+ ctx->bufsize = size;
+}
+
+size_t buflib_available(struct buflib_context *ctx)
+{
+ return ctx->bufsize;
+}
+
+size_t buflib_allocatable(struct buflib_context *ctx)
+{
+ return ctx->bufsize;
+}
+
+bool buflib_context_relocate(struct buflib_context *ctx, void *buf)
+{
+ ctx->buf = buf;
+ return true;
+}
+
+int buflib_alloc(struct buflib_context *ctx, size_t size)
+{
+ return buflib_alloc_ex(ctx, size, NULL);
+}
+
+int buflib_alloc_ex(struct buflib_context *ctx, size_t size,
+ struct buflib_callbacks *ops)
+{
+ struct buflib_malloc_handle *handle = get_free_handle(ctx);
+
+ handle->data = malloc(size);
+ handle->user = handle->data;
+ handle->size = size;
+ handle->pin_count = 0;
+ handle->ops = ops;
+
+ if (!handle->data)
+ panicf("buflib %p data OOM", ctx);
+
+ return get_handle_num(ctx, handle);
+}
+
+int buflib_alloc_maximum(struct buflib_context* ctx,
+ size_t *size, struct buflib_callbacks *ops)
+{
+ *size = ctx->bufsize;
+
+ return buflib_alloc_ex(ctx, *size, ops);
+}
+
+bool buflib_shrink(struct buflib_context *ctx, int handle,
+ void *newstart, size_t new_size)
+{
+ struct buflib_malloc_handle *h = get_handle(ctx, handle);
+ if (newstart < h->user || new_size > h->size - (newstart - h->user))
+ return false;
+
+ /* XXX: this might be allowed, but what would be the point... */
+ if (new_size == 0)
+ {
+ panicf("weird shrink");
+ return false;
+ }
+
+ /* due to buflib semantics we must not realloc */
+ h->user = newstart;
+ h->size = new_size;
+ return true;
+}
+
+void buflib_pin(struct buflib_context *ctx, int handle)
+{
+ struct buflib_malloc_handle *h = get_handle(ctx, handle);
+
+ h->pin_count++;
+}
+
+void buflib_unpin(struct buflib_context *ctx, int handle)
+{
+ struct buflib_malloc_handle *h = get_handle(ctx, handle);
+
+ h->pin_count--;
+}
+
+unsigned buflib_pin_count(struct buflib_context *ctx, int handle)
+{
+ struct buflib_malloc_handle *h = get_handle(ctx, handle);
+
+ return h->pin_count;
+}
+
+int buflib_free(struct buflib_context *ctx, int handle)
+{
+ if (handle <= 0)
+ return 0;
+
+ struct buflib_malloc_handle *h = get_handle(ctx, handle);
+
+ free(h->data);
+ h->size = 0;
+
+ return 0;
+}
+
+#ifdef BUFLIB_DEBUG_GET_DATA
+void *buflib_get_data(struct buflib_context *ctx, int handle)
+{
+ /* kind of silly since it's better for ASAN to catch this but... */
+ if (handle <= 0 || handle > ctx->num_allocs)
+ panicf("buflib %p: invalid handle %d", ctx, handle);
+
+ struct buflib_malloc_handle *h = get_handle(ctx, handle);
+ if (h->user == NULL)
+ panicf("buflib %p: handle %d use after free", ctx, handle);
+
+ return h->user;
+}
+#endif
+
+void *buflib_buffer_out(struct buflib_context *ctx, size_t *size)
+{
+ if (*size == 0)
+ *size = ctx->bufsize;
+
+ void *ret = ctx->buf;
+
+ ctx->buf += *size;
+ return ret;
+}
+
+void buflib_buffer_in(struct buflib_context *ctx, int size)
+{
+ ctx->buf -= size;
+}
+
+#ifdef BUFLIB_DEBUG_PRINT
+int buflib_get_num_blocks(struct buflib_context *ctx)
+{
+ return ctx->num_allocs;
+}
+
+bool buflib_print_block_at(struct buflib_context *ctx, int block_num,
+ char *buf, size_t bufsize)
+{
+ if (block_num >= ctx->num_allocs)
+ {
+ if (bufsize > 0)
+ *buf = '\0';
+ return false;
+ }
+
+ struct buflib_malloc_handle *handle = &ctx->allocs[block_num];
+ if (handle->data)
+ {
+ snprintf(buf, bufsize, "%03d addr:%8p length:%zu",
+ block_num, handle->data, handle->size);
+ }
+ else
+ {
+ snprintf(buf, bufsize, "%03d (unallocated)", block_num);
+ }
+
+ return true;
+}
+#endif
+
+#ifdef BUFLIB_DEBUG_CHECK_VALID
+void buflib_check_valid(struct buflib_context *ctx)
+{
+ (void)ctx;
+}
+#endif
diff --git a/firmware/export/config.h b/firmware/export/config.h
index d8674c40f2..2ec0b7878f 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -382,6 +382,7 @@ Lyre prototype 1 */
/* CONFIG_BUFLIB_BACKEND */
#define BUFLIB_BACKEND_MEMPOOL 0 /* Default memory pool backed buflib */
+#define BUFLIB_BACKEND_MALLOC 1 /* malloc() buflib (for debugging) */
/* now go and pick yours */
#if defined(IRIVER_H100)
diff --git a/firmware/include/buflib.h b/firmware/include/buflib.h
index 32a5a6abe0..c4865b6d9b 100644
--- a/firmware/include/buflib.h
+++ b/firmware/include/buflib.h
@@ -380,6 +380,8 @@ void buflib_check_valid(struct buflib_context *ctx);
#if CONFIG_BUFLIB_BACKEND == BUFLIB_BACKEND_MEMPOOL
#include "buflib_mempool.h"
+#elif CONFIG_BUFLIB_BACKEND == BUFLIB_BACKEND_MALLOC
+#include "buflib_malloc.h"
#endif
#ifndef BUFLIB_ALLOC_OVERHEAD
diff --git a/firmware/include/buflib_malloc.h b/firmware/include/buflib_malloc.h
new file mode 100644
index 0000000000..32c837e7b7
--- /dev/null
+++ b/firmware/include/buflib_malloc.h
@@ -0,0 +1,53 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2023 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef _BUFLIB_MALLOC_H_
+#define _BUFLIB_MALLOC_H_
+
+#ifndef _BUFLIB_H_
+# error "include buflib.h instead"
+#endif
+
+struct buflib_malloc_handle
+{
+ void *data;
+ void *user;
+ size_t size;
+ unsigned int pin_count;
+ struct buflib_callbacks *ops;
+};
+
+struct buflib_context
+{
+ struct buflib_malloc_handle *allocs;
+ size_t num_allocs;
+
+ void *buf;
+ size_t bufsize;
+};
+
+#ifndef BUFLIB_DEBUG_GET_DATA
+static inline void *buflib_get_data(struct buflib_context *ctx, int handle)
+{
+ return ctx->allocs[handle - 1].user;
+}
+#endif
+
+#endif /* _BUFLIB_MALLOC_H_ */