From 6edac5489d56b909d7b38677e09a90ecfe6e40af Mon Sep 17 00:00:00 2001 From: Maschell Date: Fri, 3 Apr 2026 20:52:18 +0200 Subject: [PATCH 1/4] 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 5ca11444953a8e9c4f52b04b125be96f013a4f84 Mon Sep 17 00:00:00 2001 From: Maschell Date: Fri, 3 Apr 2026 20:52:37 +0200 Subject: [PATCH 2/4] 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 { From ae8bf4a995d7121f35a1b74bed90bc71c31b47b6 Mon Sep 17 00:00:00 2001 From: Maschell Date: Thu, 9 Apr 2026 16:42:40 +0200 Subject: [PATCH 3/4] Move reent logic into WUMSLoader --- Makefile | 2 +- include/wums/hooks.h | 13 +- include/wums/meta.h | 3 +- include/wums/reent_internal.h | 35 ++++ include/wums/wums_debug.h | 19 +- libraries/libwums/crt.cpp | 24 ++- libraries/libwums/wums_reent.cpp | 206 +++++++-------------- libraries/libwums/wums_thread_specific.cpp | 57 ------ libraries/libwums/wums_thread_specific.h | 18 -- 9 files changed, 137 insertions(+), 240 deletions(-) create mode 100644 include/wums/reent_internal.h delete mode 100644 libraries/libwums/wums_thread_specific.cpp delete mode 100644 libraries/libwums/wums_thread_specific.h diff --git a/Makefile b/Makefile index 7c7d254..2cabb1d 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ include $(TOPDIR)/share/wums_rules export WUMS_MAJOR := 0 export WUMS_MINOR := 3 -export WUMS_PATCH := 5 +export WUMS_PATCH := 6 VERSION := $(WUMS_MAJOR).$(WUMS_MINOR).$(WUMS_PATCH) diff --git a/include/wums/hooks.h b/include/wums/hooks.h index b3c84ac..484381f 100644 --- a/include/wums/hooks.h +++ b/include/wums/hooks.h @@ -27,6 +27,7 @@ #include "common.h" #include "defines/module_defines.h" +#include "reent_internal.h" #ifdef __cplusplus extern "C" { @@ -68,7 +69,9 @@ typedef enum wums_hook_type_t { WUMS_HOOK_GET_CUSTOM_RPL_ALLOCATOR, // for internal usage only WUMS_HOOK_CLEAR_ALLOCATED_RPL_MEMORY, // for internal usage only // Introduced in 0.3.5 - WUMS_HOOK_INIT_WUT_THREAD // for internal usage only + WUMS_HOOK_INIT_WUT_THREAD, // for internal usage only + // Introduced in 0.3.6 + WUMS_HOOK_INIT_REENT_FUNCTIONS // for internal usage only } wums_hook_type_t; typedef uint32_t (*WUMSRPLAllocatorAllocFn)(int32_t size, int32_t align, void **outAddr); @@ -196,6 +199,14 @@ typedef struct wums_relocs_done_args_t { } \ WUMS_HOOK_EX(WUMS_HOOK_INIT_WUT_THREAD, on_init_wut_thread); +#define WUMS_INIT_REENT_FUNCTIONS() \ + __EXTERN_C_MACRO void WUMSReentAPI_InitInternal(wums_loader_init_reent_args_t_); \ + void wums_init_reent_functions(wums_loader_init_reent_args_t_ args); \ + WUMS_HOOK_EX(WUMS_HOOK_INIT_REENT_FUNCTIONS, wums_init_reent_functions); \ + void wums_init_reent_functions(wums_loader_init_reent_args_t_ args) { \ + return WUMSReentAPI_InitInternal(args); \ + } + #define WUMS_USE_WUT_SOCKETS() \ __EXTERN_C_MACRO void __init_wut_socket(); \ WUMS_HOOK_EX(WUMS_HOOK_INIT_WUT_SOCKETS, __init_wut_socket); \ diff --git a/include/wums/meta.h b/include/wums/meta.h index ebe5da7..40a420e 100644 --- a/include/wums/meta.h +++ b/include/wums/meta.h @@ -39,7 +39,7 @@ extern "C" { #endif -#define WUMS_VERSION "0.3.5" +#define WUMS_VERSION "0.3.6" #define WUMS_MODULE_EXPORT_NAME(__module_name) \ WUMS_META(export_name, __module_name); \ @@ -50,6 +50,7 @@ extern "C" { WUMS_USE_WUT_THREAD(); \ WUMS___INIT_WRAPPER(); \ WUMS___FINI_WRAPPER(); \ + WUMS_INIT_REENT_FUNCTIONS(); \ WUMS_META(buildtimestamp, __DATE__ " " __TIME__); \ extern const char wums_meta_module_name[] WUMS_SECTION("meta"); \ const char wums_meta_module_name[] = __module_name; \ diff --git a/include/wums/reent_internal.h b/include/wums/reent_internal.h new file mode 100644 index 0000000..b758bb8 --- /dev/null +++ b/include/wums/reent_internal.h @@ -0,0 +1,35 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum wums_loader_init_reent_errors_t_ { + WUMSReent_ERROR_NONE = 0, + WUMSReent_ERROR_GLOBAL_REENT_REQUESTED = 1, + WUMSReent_ERROR_NO_THREAD = 2, +} wums_loader_init_reent_errors_t_; + +typedef void *(*WUMSReent_GetReentContext)(const void *moduleId, wums_loader_init_reent_errors_t_ *outError); +typedef void *(*WUMSReent_SetSentinel)(); +typedef void (*WUMSReent_RestoreHead)(void *oldHead); +typedef bool (*WUMSReent_AddReentContext)(const void *moduleId, void *reentPtr, void (*cleanupFn)(void *), void *oldHead); + +typedef uint32_t WUMS_REENT_API_VERSION; + +#define WUMS_REENT_CUR_API_VERSION 0x01 + +typedef struct wums_loader_init_reent_args_t_ { + WUMS_REENT_API_VERSION version; + WUMSReent_GetReentContext get_context_ptr; + WUMSReent_SetSentinel set_sentinel_ptr; + WUMSReent_RestoreHead restore_head_ptr; + WUMSReent_AddReentContext add_reent_context_ptr; +} wums_loader_init_reent_args_t_; + +void WUMSReentAPI_InitInternal(wums_loader_init_reent_args_t_ args); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/include/wums/wums_debug.h b/include/wums/wums_debug.h index 352e31a..87ded96 100644 --- a/include/wums/wums_debug.h +++ b/include/wums/wums_debug.h @@ -1,25 +1,10 @@ #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__ - +extern "C" void OSReport(const char *fmt, ...); #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 } @@ -31,4 +16,4 @@ extern const char wums_meta_info_dump[]; #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 +#define WUMS_DEBUG_WARN(fmt, ...) OSReport("\033[33m[%s] " fmt "\033[0m", wums_meta_info_dump, ##__VA_ARGS__) \ No newline at end of file diff --git a/libraries/libwums/crt.cpp b/libraries/libwums/crt.cpp index 2d44ec9..2b834c2 100644 --- a/libraries/libwums/crt.cpp +++ b/libraries/libwums/crt.cpp @@ -1,8 +1,10 @@ +#include "wums/wums_debug.h" #include "wums_reent.h" -#include "wums_thread_specific.h" + #include #include +extern "C" void OSFatal(const char *); extern "C" void OSFatal(const char *); int main(int argc, char **argv) { @@ -43,16 +45,22 @@ struct _reent *__getreent(void) { return __wums_getreent(); } -extern "C" void __attribute__((weak)) wut_set_thread_specific(__wut_thread_specific_id id, void *value); - -void wut_set_thread_specific(__wut_thread_specific_id id, void *value) { - return wums_set_thread_specific(id, value); -} +typedef enum __wut_thread_specific_id { + WUT_THREAD_SPECIFIC_0 = 0, + WUT_THREAD_SPECIFIC_1 = 1, +} __wut_thread_specific_id; extern "C" void *__attribute__((weak)) wut_get_thread_specific(__wut_thread_specific_id id); void *wut_get_thread_specific(__wut_thread_specific_id id) { - return wums_get_thread_specific(id); + if ((uint32_t) id == 0x13371337) { // Mechanism to detect if the function was overridden properly + return (void *) 0x42424242; + } + + WUMS_DEBUG_WARN("wums_get_thread_specific: NOT SUPPORTED\n"); + OSFatal("wums_get_thread_specific: NOT SUPPORTED\n"); + + return nullptr; } extern "C" const char wums_meta_module_name[]; @@ -100,6 +108,8 @@ __assert_func(const char *file, } OSFatal(buffer); + while (true) + ; /* NOTREACHED */ } diff --git a/libraries/libwums/wums_reent.cpp b/libraries/libwums/wums_reent.cpp index 319e175..f8e22ce 100644 --- a/libraries/libwums/wums_reent.cpp +++ b/libraries/libwums/wums_reent.cpp @@ -1,175 +1,105 @@ #include "wums_reent.h" -#include "wums_thread_specific.h" -#include -#include -#include -#include - -#define __WUMS_CONTEXT_THREAD_SPECIFIC_ID WUT_THREAD_SPECIFIC_0 -#define WUMS_REENT_ALLOC_SENTINEL ((__wums_reent_node *) 0xFFFFFFFF) - -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); - -typedef uint32_t OSThread; - -extern "C" void OSFatal(const char *); -extern "C" void OSReport(const char *, ...); -extern "C" OSThread *OSGetCurrentThread(); +#include "wums/hooks.h" +#include "wums/reent_internal.h" +#include "wums/wums_debug.h" -typedef void (*OSThreadCleanupCallbackFn)(OSThread *thread, void *stack); +extern "C" void OSFatal(const char *format, ...); +extern "C" void *OSGetCurrentThread(); -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; +#include -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; +struct wums_loader_init_reent_args_t { + WUMSReent_GetReentContext get_context_ptr = nullptr; + WUMSReent_SetSentinel set_sentinel_ptr = nullptr; + WUMSReent_RestoreHead restore_head_ptr = nullptr; + WUMSReent_AddReentContext add_reent_context_ptr = nullptr; }; -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)); +static wums_loader_init_reent_args_t __internal_functions = {}; - if (!head || head == WUMS_REENT_ALLOC_SENTINEL) { +void WUMSReentAPI_InitInternal(wums_loader_init_reent_args_t_ args) { + if (args.version > WUMS_REENT_CUR_API_VERSION) { + OSFatal("Incompatible reent api version"); return; } + WUMS_DEBUG_REPORT("WUMSReentAPI_InitInternal: Initializing reent module\n"); - 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; - } - - WUMS_DEBUG_REPORT("__wums_thread_cleanup: Triggered for thread %p\n", thread); + __internal_functions.get_context_ptr = args.get_context_ptr; + __internal_functions.set_sentinel_ptr = args.set_sentinel_ptr; + __internal_functions.add_reent_context_ptr = args.add_reent_context_ptr; + __internal_functions.restore_head_ptr = args.restore_head_ptr; +} - OSThreadCleanupCallbackFn savedCleanup = nullptr; - if (head->version >= 1) { - savedCleanup = head->savedCleanup; - } +// use variable in the .data section as unique module id +static const int sReentModuleId = 0; - // 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); +void *wums_backend_get_context(const void *id, wums_loader_init_reent_errors_t_ *outError) { + return __internal_functions.get_context_ptr(id, outError); +} - // 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; +void *wums_backend_set_sentinel() { + return __internal_functions.set_sentinel_ptr(); +} - // Trigger the self-destruct sequence. Frees curr - if (curr->cleanupFn) { - curr->cleanupFn(curr); - } +void wums_backend_restore_head(void *head) { + __internal_functions.restore_head_ptr(head); +} - curr = next; - } +bool wums_backend_register_context(const void *moduleId, void *reentPtr, void (*cleanupFn)(void *), void *oldHead) { + return __internal_functions.add_reent_context_ptr(moduleId, reentPtr, cleanupFn, oldHead); +} - wut_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, nullptr); +static void reclaim_reent_trampoline(void *payload) { + WUMS_DEBUG_REPORT("reclaim_reent_trampoline: Destroying reent payload: %p\n", payload); - if (savedCleanup) { - WUMS_DEBUG_REPORT("__wums_thread_cleanup: Chaining to saved cleanup for thread %p\n", thread); - savedCleanup(thread, stack); + if (payload) { + auto *reentPtr = static_cast<_reent *>(payload); + _reclaim_reent(reentPtr); + free(reentPtr); } } 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) { + if (!OSGetCurrentThread()) { 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; + wums_loader_init_reent_errors_t_ error = WUMSReent_ERROR_NONE; + if (void *existingCtx = wums_backend_get_context(&sReentModuleId, &error)) { + return static_cast<_reent *>(existingCtx); } - - // 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; + switch (error) { + case WUMSReent_ERROR_GLOBAL_REENT_REQUESTED: + case WUMSReent_ERROR_NO_THREAD: + return _GLOBAL_REENT; + case WUMSReent_ERROR_NONE: + break; } - WUMS_DEBUG_REPORT("__wums_getreent: Allocating new context for thread %p\n", OSGetCurrentThread()); - - // 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); - - auto *newNode = static_cast<__wums_reent_node *>(malloc(sizeof(__wums_reent_node))); + auto *oldHead = wums_backend_set_sentinel(); 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); + if (!newReent) { + wums_backend_restore_head(oldHead); return _GLOBAL_REENT; } + WUMS_DEBUG_REPORT("Allocated context! for thread %p: %p\n", OSGetCurrentThread(), newReent); + _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; - } + bool result = wums_backend_register_context( + &sReentModuleId, + newReent, + reclaim_reent_trampoline, + oldHead); + + if (!result) { + WUMS_DEBUG_WARN("Failed to register context for thread %p\n", OSGetCurrentThread()); + _reclaim_reent(newReent); + free(newReent); + wums_backend_restore_head(oldHead); + return _GLOBAL_REENT; } - 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 deleted file mode 100644 index 661cc74..0000000 --- a/libraries/libwums/wums_thread_specific.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "wums_thread_specific.h" -#include -#include - -extern "C" void OSFatal(const char *); -extern "C" void OSReport(const char *, ...); - -typedef struct OSThread { - uint8_t other[0x68c]; - void *reserved[5]; -} OSThread; - -static_assert(offsetof(OSThread, reserved) == 0x68c, "OSThread: \"reserved\" at wrong offset"); -static_assert(sizeof(OSThread) == 0x6a0, "OSThread: wrong size"); - -extern "C" const char wums_meta_info_dump[]; - -void wums_set_thread_specific(__wut_thread_specific_id id, void *value) { - OSThread *thread; - asm volatile("lwz %0, -0x20(0)" - : "=r"(thread)); // OSGetCurrentThread() - if (thread != nullptr) { - if (id == WUT_THREAD_SPECIFIC_0) { - thread->reserved[1] = value; - } else if (id == WUT_THREAD_SPECIFIC_1) { - 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"); - } - } else { - OSReport("[%s] wums_set_thread_specific: invalid thread\n", wums_meta_info_dump); - OSFatal("wums_set_thread_specific: invalid thread"); - } -} - -void *wums_get_thread_specific(__wut_thread_specific_id id) { - OSThread *thread; - asm volatile("lwz %0, -0x20(0)" - : "=r"(thread)); // OSGetCurrentThread() - if (thread != nullptr) { - if (id == WUT_THREAD_SPECIFIC_0) { - return thread->reserved[1]; - } else if (id == WUT_THREAD_SPECIFIC_1) { - return thread->reserved[2]; - } else if ((uint32_t) id == 0x13371337) { // Mechanism to detect if the function was overridden properly - return (void *) 0x42424242; - } else { - OSReport("[%s] wums_get_thread_specific: invalid id\n", wums_meta_info_dump); - OSFatal("wums_get_thread_specific: invalid id"); - } - } else { - OSReport("[%s] wums_get_thread_specific: invalid thread\n", wums_meta_info_dump); - OSFatal("wums_get_thread_specific: invalid thread\n"); - } - return nullptr; -} \ No newline at end of file diff --git a/libraries/libwums/wums_thread_specific.h b/libraries/libwums/wums_thread_specific.h deleted file mode 100644 index 6a7d922..0000000 --- a/libraries/libwums/wums_thread_specific.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -typedef enum __wut_thread_specific_id { - WUT_THREAD_SPECIFIC_0 = 0, - WUT_THREAD_SPECIFIC_1 = 1, -} __wut_thread_specific_id; - -#ifdef __cplusplus -extern "C" { -#endif - -void wums_set_thread_specific(__wut_thread_specific_id id, void *value); - -void *wums_get_thread_specific(__wut_thread_specific_id id); - -#ifdef __cplusplus -} -#endif \ No newline at end of file From cd66e5fc96d2c063db7c08d5f6326a8ff24d2358 Mon Sep 17 00:00:00 2001 From: Maschell Date: Tue, 14 Apr 2026 17:21:16 +0200 Subject: [PATCH 4/4] Use non-newlib function to allocate memory for reent structs to make things simpler --- include/wums/reent_internal.h | 16 ++------ libraries/libwums/crt.cpp | 1 - libraries/libwums/wums_reent.cpp | 64 +++++++++++++++----------------- 3 files changed, 32 insertions(+), 49 deletions(-) diff --git a/include/wums/reent_internal.h b/include/wums/reent_internal.h index b758bb8..0b85217 100644 --- a/include/wums/reent_internal.h +++ b/include/wums/reent_internal.h @@ -5,26 +5,16 @@ extern "C" { #endif -typedef enum wums_loader_init_reent_errors_t_ { - WUMSReent_ERROR_NONE = 0, - WUMSReent_ERROR_GLOBAL_REENT_REQUESTED = 1, - WUMSReent_ERROR_NO_THREAD = 2, -} wums_loader_init_reent_errors_t_; - -typedef void *(*WUMSReent_GetReentContext)(const void *moduleId, wums_loader_init_reent_errors_t_ *outError); -typedef void *(*WUMSReent_SetSentinel)(); -typedef void (*WUMSReent_RestoreHead)(void *oldHead); -typedef bool (*WUMSReent_AddReentContext)(const void *moduleId, void *reentPtr, void (*cleanupFn)(void *), void *oldHead); +typedef bool (*WUMSReent_GetReentContext)(const void *moduleId, void **outPtr); +typedef bool (*WUMSReent_AddReentContext)(const void *moduleId, void *reentPtr, void (*cleanupFn)(void *)); typedef uint32_t WUMS_REENT_API_VERSION; -#define WUMS_REENT_CUR_API_VERSION 0x01 +#define WUMS_REENT_CUR_API_VERSION 0x02 typedef struct wums_loader_init_reent_args_t_ { WUMS_REENT_API_VERSION version; WUMSReent_GetReentContext get_context_ptr; - WUMSReent_SetSentinel set_sentinel_ptr; - WUMSReent_RestoreHead restore_head_ptr; WUMSReent_AddReentContext add_reent_context_ptr; } wums_loader_init_reent_args_t_; diff --git a/libraries/libwums/crt.cpp b/libraries/libwums/crt.cpp index 2b834c2..0547304 100644 --- a/libraries/libwums/crt.cpp +++ b/libraries/libwums/crt.cpp @@ -4,7 +4,6 @@ #include #include -extern "C" void OSFatal(const char *); extern "C" void OSFatal(const char *); int main(int argc, char **argv) { diff --git a/libraries/libwums/wums_reent.cpp b/libraries/libwums/wums_reent.cpp index f8e22ce..2c4e06d 100644 --- a/libraries/libwums/wums_reent.cpp +++ b/libraries/libwums/wums_reent.cpp @@ -4,15 +4,22 @@ #include "wums/reent_internal.h" #include "wums/wums_debug.h" +#include + extern "C" void OSFatal(const char *format, ...); extern "C" void *OSGetCurrentThread(); -#include +typedef void *(*MEMAllocFromDefaultHeapFn)(uint32_t size); +typedef void *(*MEMAllocFromDefaultHeapExFn)(uint32_t size, int32_t alignment); +typedef void (*MEMFreeToDefaultHeapFn)(void *ptr); + +extern MEMAllocFromDefaultHeapFn MEMAllocFromDefaultHeap; +extern MEMAllocFromDefaultHeapExFn MEMAllocFromDefaultHeapEx; +extern MEMFreeToDefaultHeapFn MEMFreeToDefaultHeap; + struct wums_loader_init_reent_args_t { WUMSReent_GetReentContext get_context_ptr = nullptr; - WUMSReent_SetSentinel set_sentinel_ptr = nullptr; - WUMSReent_RestoreHead restore_head_ptr = nullptr; WUMSReent_AddReentContext add_reent_context_ptr = nullptr; }; @@ -26,28 +33,18 @@ void WUMSReentAPI_InitInternal(wums_loader_init_reent_args_t_ args) { WUMS_DEBUG_REPORT("WUMSReentAPI_InitInternal: Initializing reent module\n"); __internal_functions.get_context_ptr = args.get_context_ptr; - __internal_functions.set_sentinel_ptr = args.set_sentinel_ptr; __internal_functions.add_reent_context_ptr = args.add_reent_context_ptr; - __internal_functions.restore_head_ptr = args.restore_head_ptr; } // use variable in the .data section as unique module id static const int sReentModuleId = 0; -void *wums_backend_get_context(const void *id, wums_loader_init_reent_errors_t_ *outError) { - return __internal_functions.get_context_ptr(id, outError); -} - -void *wums_backend_set_sentinel() { - return __internal_functions.set_sentinel_ptr(); +bool wums_backend_get_context(const void *id, void **outPtr) { + return __internal_functions.get_context_ptr(id, outPtr); } -void wums_backend_restore_head(void *head) { - __internal_functions.restore_head_ptr(head); -} - -bool wums_backend_register_context(const void *moduleId, void *reentPtr, void (*cleanupFn)(void *), void *oldHead) { - return __internal_functions.add_reent_context_ptr(moduleId, reentPtr, cleanupFn, oldHead); +bool wums_backend_register_context(const void *moduleId, void *reentPtr, void (*cleanupFn)(void *)) { + return __internal_functions.add_reent_context_ptr(moduleId, reentPtr, cleanupFn); } static void reclaim_reent_trampoline(void *payload) { @@ -56,7 +53,9 @@ static void reclaim_reent_trampoline(void *payload) { if (payload) { auto *reentPtr = static_cast<_reent *>(payload); _reclaim_reent(reentPtr); - free(reentPtr); + + // Make sure to use MEMFreeToDefaultHeap + MEMFreeToDefaultHeap(reentPtr); } } @@ -64,22 +63,18 @@ struct _reent *__wums_getreent() { if (!OSGetCurrentThread()) { return _GLOBAL_REENT; } - wums_loader_init_reent_errors_t_ error = WUMSReent_ERROR_NONE; - if (void *existingCtx = wums_backend_get_context(&sReentModuleId, &error)) { - return static_cast<_reent *>(existingCtx); + void *existingCtx = nullptr; + if (!wums_backend_get_context(&sReentModuleId, &existingCtx)) { + return _GLOBAL_REENT; } - switch (error) { - case WUMSReent_ERROR_GLOBAL_REENT_REQUESTED: - case WUMSReent_ERROR_NO_THREAD: - return _GLOBAL_REENT; - case WUMSReent_ERROR_NONE: - break; + // if non-null we can use it + if (existingCtx) { + return static_cast<_reent *>(existingCtx); } - auto *oldHead = wums_backend_set_sentinel(); - auto *newReent = static_cast(malloc(sizeof(struct _reent))); + // Use `MEMAllocFromDefaultHeap` to avoid creating a new reent for allocating the reent + auto *newReent = static_cast(MEMAllocFromDefaultHeap(sizeof(struct _reent))); if (!newReent) { - wums_backend_restore_head(oldHead); return _GLOBAL_REENT; } @@ -87,17 +82,16 @@ struct _reent *__wums_getreent() { _REENT_INIT_PTR(newReent); - bool result = wums_backend_register_context( + const bool result = wums_backend_register_context( &sReentModuleId, newReent, - reclaim_reent_trampoline, - oldHead); + reclaim_reent_trampoline); if (!result) { WUMS_DEBUG_WARN("Failed to register context for thread %p\n", OSGetCurrentThread()); _reclaim_reent(newReent); - free(newReent); - wums_backend_restore_head(oldHead); + // Make sure to call the free function + MEMFreeToDefaultHeap(newReent); return _GLOBAL_REENT; }