From 888d50b19aca101ed0d35ceb10880080f66289a7 Mon Sep 17 00:00:00 2001 From: Maschell Date: Fri, 3 Apr 2026 20:52:18 +0200 Subject: [PATCH 1/2] Deprecate (s)bss field in module defines struct --- include/wums/defines/module_defines.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/wums/defines/module_defines.h b/include/wums/defines/module_defines.h index 2ebd6d3..cafaf4e 100644 --- a/include/wums/defines/module_defines.h +++ b/include/wums/defines/module_defines.h @@ -47,10 +47,10 @@ typedef struct module_information_single_t { uint32_t number_export_entries; hook_data_t * hook_entries; uint32_t number_hook_entries; - uint32_t bssAddr; - uint32_t bssSize; - uint32_t sbssAddr; - uint32_t sbssSize; + uint32_t bssAddr; // deprecated + uint32_t bssSize; // deprecated + uint32_t sbssAddr; // deprecated + uint32_t sbssSize; // deprecated uint32_t startAddress; uint32_t endAddress; uint32_t entrypoint; From 6b66776c07abb0888415d803760ea070e3d8f3e1 Mon Sep 17 00:00:00 2001 From: Maschell Date: Fri, 3 Apr 2026 20:52:37 +0200 Subject: [PATCH 2/2] Reimplement __wums_getreent to be per module and (hopefully) future proof --- Makefile | 4 +- include/wums/wums_debug.h | 34 ++++ libraries/libwums/wums_reent.cpp | 177 ++++++++++++++++----- libraries/libwums/wums_thread_specific.cpp | 8 +- 4 files changed, 177 insertions(+), 46 deletions(-) create mode 100644 include/wums/wums_debug.h diff --git a/Makefile b/Makefile index 8a1dbc8..7c7d254 100644 --- a/Makefile +++ b/Makefile @@ -102,10 +102,10 @@ lib: @[ -d $@ ] || mkdir -p $@ release: - @[ -d $@ ] || mkdir -p $@ + @$(shell [ ! -d 'release' ] && mkdir -p 'release') debug: - @[ -d $@ ] || mkdir -p $@ + @$(shell [ ! -d 'debug' ] && mkdir -p 'debug') lib/libwums.a : lib release $(SOURCES) $(INCLUDES) @$(MAKE) BUILD=release OUTPUT=$(CURDIR)/$@ \ diff --git a/include/wums/wums_debug.h b/include/wums/wums_debug.h new file mode 100644 index 0000000..352e31a --- /dev/null +++ b/include/wums/wums_debug.h @@ -0,0 +1,34 @@ +#pragma once + +#if defined(__GNUC__) || defined(__clang__) + +#define WUMS_FORMAT_PRINTF(fmt, args) __attribute__((__format__(__printf__, fmt, args))) + +#else // not __GNUC__ and not __clang__ + +#define WUMS_FORMAT_PRINTF(fmt, args) + +#endif //__GNUC__ or __clang__ + + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef DEBUG +extern void OSReport(const char *fmt, ...) WUMS_FORMAT_PRINTF(1, 2); +#endif + +extern const char wums_meta_info_dump[]; +#ifdef __cplusplus +} +#endif + +#ifdef DEBUG +#define WUMS_DEBUG_REPORT(fmt, ...) OSReport("[%s] " fmt, wums_meta_info_dump, ##__VA_ARGS__) +#else +#define WUMS_DEBUG_REPORT(fmt, ...) +#endif + +#define WUMS_DEBUG_WARN(fmt, ...) OSReport("[%s] " fmt, wums_meta_info_dump, ##__VA_ARGS__) \ No newline at end of file diff --git a/libraries/libwums/wums_reent.cpp b/libraries/libwums/wums_reent.cpp index 8b3d5ed..319e175 100644 --- a/libraries/libwums/wums_reent.cpp +++ b/libraries/libwums/wums_reent.cpp @@ -1,13 +1,17 @@ +#include "wums_reent.h" #include "wums_thread_specific.h" #include #include #include +#include -#define __WUMS_CONTEXT_THREAD_SPECIFIC_ID WUT_THREAD_SPECIFIC_1 +#define __WUMS_CONTEXT_THREAD_SPECIFIC_ID WUT_THREAD_SPECIFIC_0 +#define WUMS_REENT_ALLOC_SENTINEL ((__wums_reent_node *) 0xFFFFFFFF) -typedef uint32_t OSThread; +extern "C" __attribute__((weak)) void wut_set_thread_specific(__wut_thread_specific_id id, void *value); +extern "C" __attribute__((weak)) void *wut_get_thread_specific(__wut_thread_specific_id); -extern const char wums_meta_info_dump[]; +typedef uint32_t OSThread; extern "C" void OSFatal(const char *); extern "C" void OSReport(const char *, ...); @@ -20,59 +24,152 @@ extern "C" OSThreadCleanupCallbackFn OSSetThreadCleanupCallback(OSThread *thread, OSThreadCleanupCallbackFn callback); +#define WUMS_REENT_NODE_VERSION 1 +#define WUMS_REENT_NODE_MAGIC 0x57554D53 // WUMS +static const int sReentModuleId = 0; -struct __wums_thread_context { - struct _reent reent; +struct __wums_reent_node { + // FIXED HEADER (Never move or change these offsets!) + uint32_t magic; // Guarantees this is a __wums_reent_node + uint32_t version; + __wums_reent_node *next; + + // Node Version 1 Payload + const void *moduleId; + void *reentPtr; + void (*cleanupFn)(__wums_reent_node *); OSThreadCleanupCallbackFn savedCleanup; }; -static void -__wums_thread_cleanup(OSThread *thread, - void *stack) { - struct __wums_thread_context *context; +static void reclaim_reent_trampoline(__wums_reent_node *node) { + WUMS_DEBUG_REPORT("reclaim_reent_trampoline: Destroying node %p (reent: %p)\n", node, node->reentPtr); + + if (node->reentPtr) { + _reclaim_reent(static_cast<_reent *>(node->reentPtr)); + free(node->reentPtr); + } + free(node); +} + +static void __wums_thread_cleanup(OSThread *thread, void *stack) { + auto *head = static_cast<__wums_reent_node *>(wut_get_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID)); + + if (!head || head == WUMS_REENT_ALLOC_SENTINEL) { + return; + } - context = (struct __wums_thread_context *) wums_get_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID); - if (!context || &context->reent == _GLOBAL_REENT) { - OSReport("[%s] __wums_thread_cleanup: Context was NULL or reent was global\n", wums_meta_info_dump); - OSFatal("__wums_thread_cleanup: Context was NULL or reent was global"); + if (head->magic != WUMS_REENT_NODE_MAGIC) { + WUMS_DEBUG_WARN("__wums_thread_cleanup: Unexpected node magic word: %08X (expected %08X).\n", head->magic, WUMS_REENT_NODE_MAGIC); + return; } - if (context->savedCleanup) { - context->savedCleanup(thread, stack); + WUMS_DEBUG_REPORT("__wums_thread_cleanup: Triggered for thread %p\n", thread); + + OSThreadCleanupCallbackFn savedCleanup = nullptr; + if (head->version >= 1) { + savedCleanup = head->savedCleanup; } - _reclaim_reent(&context->reent); + // Set to effective global during free to prevent malloc re-entrancy loops + wut_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, WUMS_REENT_ALLOC_SENTINEL); + + // Safely iterate the ABI-stable list. + auto *curr = head; + while (curr) { + // Read the "next" pointer BEFORE destroying the current node. + __wums_reent_node *next = curr->next; + + // Trigger the self-destruct sequence. Frees curr + if (curr->cleanupFn) { + curr->cleanupFn(curr); + } - // Use global reent during free since the current reent is getting freed - wums_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, _GLOBAL_REENT); + curr = next; + } - free(context); + wut_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, nullptr); - wums_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, NULL); + if (savedCleanup) { + WUMS_DEBUG_REPORT("__wums_thread_cleanup: Chaining to saved cleanup for thread %p\n", thread); + savedCleanup(thread, stack); + } } -struct _reent * -__wums_getreent(void) { - struct __wums_thread_context *context; - - context = (struct __wums_thread_context *) wums_get_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID); - if (!context) { - // Temporarily use global reent during context allocation - wums_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, _GLOBAL_REENT); - - context = (struct __wums_thread_context *) malloc(sizeof(*context)); - if (!context) { - OSReport("[%s] __wums_getreent: Failed to allocate reent context\n", wums_meta_info_dump); - OSFatal("__wums_getreent: Failed to allocate reent context"); - wums_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, NULL); - return NULL; +struct _reent *__wums_getreent() { + if (!wut_get_thread_specific || !wut_set_thread_specific || OSGetCurrentThread() == nullptr) { + return _GLOBAL_REENT; + } + + auto head = static_cast<__wums_reent_node *>(wut_get_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID)); + + if (head == WUMS_REENT_ALLOC_SENTINEL) { + return _GLOBAL_REENT; + } + + if (head && head->magic != WUMS_REENT_NODE_MAGIC) { + WUMS_DEBUG_WARN("__wums_getreent: Unexpected node magic word: %08X (expected %08X).\n", head->magic, WUMS_REENT_NODE_MAGIC); + return _GLOBAL_REENT; + } + + // Check for already allocated reent ptr. + // (Intentionally not logging here to prevent console spam on the fast path) + const __wums_reent_node *curr = head; + while (curr) { + // Use a memory address as a unique id + if (curr->version >= 1 && curr->moduleId == &sReentModuleId) { + return static_cast<_reent *>(curr->reentPtr); } + curr = curr->next; + } + + WUMS_DEBUG_REPORT("__wums_getreent: Allocating new context for thread %p\n", OSGetCurrentThread()); - _REENT_INIT_PTR(&context->reent); - context->savedCleanup = OSSetThreadCleanupCallback(OSGetCurrentThread(), &__wums_thread_cleanup); + // If not found allocate a new for THIS module. + // Temporarily effectively use global reent during context allocation + wut_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, WUMS_REENT_ALLOC_SENTINEL); - wums_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, context); + auto *newNode = static_cast<__wums_reent_node *>(malloc(sizeof(__wums_reent_node))); + auto *newReent = static_cast(malloc(sizeof(struct _reent))); + + if (!newNode || !newReent) { + WUMS_DEBUG_WARN("__wums_getreent: Failed to allocate context! Falling back to _GLOBAL_REENT.\n"); + if (newNode) { + free(newNode); + } + if (newReent) { + free(newReent); + } + // reset on error + wut_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, head); + return _GLOBAL_REENT; } - return &context->reent; -} + _REENT_INIT_PTR(newReent); + + newNode->magic = WUMS_REENT_NODE_MAGIC; + newNode->version = WUMS_REENT_NODE_VERSION; + newNode->next = head; + newNode->moduleId = &sReentModuleId; + newNode->reentPtr = newReent; + newNode->cleanupFn = reclaim_reent_trampoline; + newNode->savedCleanup = nullptr; + + auto oldHead = head; + + // Hook cleanup logic + if (oldHead == nullptr) { + WUMS_DEBUG_REPORT("__wums_getreent: Hooking OSSetThreadCleanupCallback for thread %p\n", OSGetCurrentThread()); + newNode->savedCleanup = OSSetThreadCleanupCallback(OSGetCurrentThread(), &__wums_thread_cleanup); + } else { + WUMS_DEBUG_REPORT("__wums_getreent: Prepending to existing list for thread %p\n", OSGetCurrentThread()); + // We prepend, so we must inherit the saved cleanup from the previous head + if (oldHead->version >= 1) { + newNode->savedCleanup = oldHead->savedCleanup; + oldHead->savedCleanup = nullptr; + } + } + + wut_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, newNode); + + return newReent; +} \ No newline at end of file diff --git a/libraries/libwums/wums_thread_specific.cpp b/libraries/libwums/wums_thread_specific.cpp index 21fe6a1..661cc74 100644 --- a/libraries/libwums/wums_thread_specific.cpp +++ b/libraries/libwums/wums_thread_specific.cpp @@ -21,9 +21,9 @@ void wums_set_thread_specific(__wut_thread_specific_id id, void *value) { : "=r"(thread)); // OSGetCurrentThread() if (thread != nullptr) { if (id == WUT_THREAD_SPECIFIC_0) { - thread->reserved[3] = value; + thread->reserved[1] = value; } else if (id == WUT_THREAD_SPECIFIC_1) { - thread->reserved[4] = value; + thread->reserved[2] = value; } else { OSReport("[%s] wums_set_thread_specific: invalid id\n", wums_meta_info_dump); OSFatal("wut_set_thread_specific: invalid id"); @@ -40,9 +40,9 @@ void *wums_get_thread_specific(__wut_thread_specific_id id) { : "=r"(thread)); // OSGetCurrentThread() if (thread != nullptr) { if (id == WUT_THREAD_SPECIFIC_0) { - return thread->reserved[3]; + return thread->reserved[1]; } else if (id == WUT_THREAD_SPECIFIC_1) { - return thread->reserved[4]; + return thread->reserved[2]; } else if ((uint32_t) id == 0x13371337) { // Mechanism to detect if the function was overridden properly return (void *) 0x42424242; } else {