From 72d238ef03866254982cebeacf9bf9297e95ab97 Mon Sep 17 00:00:00 2001 From: Maschell Date: Fri, 10 Apr 2026 13:21:08 +0200 Subject: [PATCH] Support for WUMS 0.3.6 - implement reent for modules --- wumsloader/src/crt.c | 48 +-- wumsloader/src/entry.cpp | 4 + wumsloader/src/module/HooksManagement.cpp | 23 +- wumsloader/src/module/ModuleManagement.cpp | 1 + .../module/ModuleMetaInformationFactory.cpp | 2 + wumsloader/src/utils/logger.h | 69 ++-- wumsloader/src/utils/reent.cpp | 345 ++++++++++++++++++ wumsloader/src/utils/reent.h | 13 + 8 files changed, 423 insertions(+), 82 deletions(-) create mode 100644 wumsloader/src/utils/reent.cpp create mode 100644 wumsloader/src/utils/reent.h diff --git a/wumsloader/src/crt.c b/wumsloader/src/crt.c index c281f5c..f87adc3 100644 --- a/wumsloader/src/crt.c +++ b/wumsloader/src/crt.c @@ -35,50 +35,6 @@ fini_wut() { __fini_wut_malloc(); } -typedef enum __wut_thread_specific_id { - WUT_THREAD_SPECIFIC_0 = 0, - WUT_THREAD_SPECIFIC_1 = 1, -} __wut_thread_specific_id; - -extern 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) { - OSThread *thread; - asm volatile("lwz %0, -0x20(0)" - : "=r"(thread)); // OSGetCurrentThread() - if (thread != NULL) { - if (id == WUT_THREAD_SPECIFIC_0) { - thread->reserved[3] = (uint32_t) value; - } else if (id == WUT_THREAD_SPECIFIC_1) { - thread->reserved[4] = (uint32_t) value; - } else { - OSReport("[WUMSLOADER] wut_set_thread_specific: invalid id\n"); - OSFatal("[WUMSLOADER] wut_set_thread_specific: invalid id"); - } - } else { - OSReport("[WUMSLOADER] wut_set_thread_specific: invalid thread\n"); - OSFatal("[WUMSLOADER] wut_set_thread_specific: invalid thread"); - } +struct _reent *__getreent(void) { + return _GLOBAL_REENT; } - -extern void *__attribute__((weak)) wut_get_thread_specific(__wut_thread_specific_id id); - -void *wut_get_thread_specific(__wut_thread_specific_id id) { - OSThread *thread; - asm volatile("lwz %0, -0x20(0)" - : "=r"(thread)); // OSGetCurrentThread() - if (thread != NULL) { - if (id == WUT_THREAD_SPECIFIC_0) { - return (void *) thread->reserved[3]; - } else if (id == WUT_THREAD_SPECIFIC_1) { - return (void *) thread->reserved[4]; - } else { - OSReport("[WUMSLOADER] wut_get_thread_specific: invalid id\n"); - OSFatal("[WUMSLOADER] wut_get_thread_specific: invalid id"); - } - } else { - OSReport("[WUMSLOADER] wut_get_thread_specific: invalid thread\n"); - OSFatal("[WUMSLOADER] wut_get_thread_specific: invalid thread"); - } - return NULL; -} \ No newline at end of file diff --git a/wumsloader/src/entry.cpp b/wumsloader/src/entry.cpp index e88bff8..800ad07 100644 --- a/wumsloader/src/entry.cpp +++ b/wumsloader/src/entry.cpp @@ -11,6 +11,7 @@ #include "utils/SegmentedTimer.h" #include "utils/dynamic.h" #include "utils/logger.h" +#include "utils/reent.h" #include #include @@ -101,6 +102,9 @@ void doStart(int argc, char **argv) { init_wut(); initLogging(); + // We must be 100% sure we never call this more than once per application cycle + ClearDanglingReentPtr(); + OSReport("Running WUMSLoader " VERSION VERSION_EXTRA "\n"); #ifdef WUMS_ENABLE_PROFILING diff --git a/wumsloader/src/module/HooksManagement.cpp b/wumsloader/src/module/HooksManagement.cpp index 8b01fda..6158d6c 100644 --- a/wumsloader/src/module/HooksManagement.cpp +++ b/wumsloader/src/module/HooksManagement.cpp @@ -4,10 +4,12 @@ #include "ModuleDataPersistence.h" #include "globals.h" #include "utils/logger.h" +#include "utils/reent.h" + +#include #include - namespace WUMSLoader::Modules::HooksManagement { namespace { @@ -63,6 +65,8 @@ constexpr std::string_view hookNameToStr(wums_hook_type_t hook) { return "WUMS_HOOK_CLEAR_ALLOCATED_RPL_MEMORY"; case WUMS_HOOK_INIT_WUT_THREAD: return "WUMS_HOOK_INIT_WUT_THREAD"; + case WUMS_HOOK_INIT_REENT_FUNCTIONS: + return "WUMS_HOOK_INIT_REENT_FUNCTIONS"; } return ""; } @@ -147,6 +151,23 @@ void CallHook(const ModuleContainer &module, wums_hook_type_t type) { gCustomRPLAllocatorAllocFn = allocFn; gCustomRPLAllocatorFreeFn = freeFn; + } else if (type == WUMS_HOOK_INIT_REENT_FUNCTIONS) { + if (module.getMetaInformation().getWUMSVersion() <= WUMSVersion(0, 3, 5)) { + break; + } + wums_loader_init_reent_args_t_ args; + args.version = WUMS_REENT_CUR_API_VERSION; + args.restore_head_ptr = &wums_backend_restore_head; + args.get_context_ptr = &wums_backend_get_context; + args.set_sentinel_ptr = &wums_backend_set_sentinel; + args.add_reent_context_ptr = &wums_backend_register_context; + + // clang-format off + ((void (*)(wums_loader_init_reent_args_t_))((uint32_t *) func_ptr))(args); + // clang-format on + + break; + } else { DEBUG_FUNCTION_LINE_ERR("#########################################"); DEBUG_FUNCTION_LINE_ERR("#########HOOK NOT IMPLEMENTED %d#########", type); diff --git a/wumsloader/src/module/ModuleManagement.cpp b/wumsloader/src/module/ModuleManagement.cpp index 124e05f..a312a5d 100644 --- a/wumsloader/src/module/ModuleManagement.cpp +++ b/wumsloader/src/module/ModuleManagement.cpp @@ -150,6 +150,7 @@ std::vector OrderModulesMetaByDependencies(std: } void CallInitHooksForModule(const ModuleContainer &curModule) { + HooksManagement::CallHook(curModule, WUMS_HOOK_INIT_REENT_FUNCTIONS); HooksManagement::CallHook(curModule, WUMS_HOOK_INIT_WUT_THREAD); HooksManagement::CallHook(curModule, WUMS_HOOK_INIT_WUT_MALLOC); HooksManagement::CallHook(curModule, WUMS_HOOK_INIT_WUT_NEWLIB); diff --git a/wumsloader/src/module/ModuleMetaInformationFactory.cpp b/wumsloader/src/module/ModuleMetaInformationFactory.cpp index b484552..a9ab97c 100644 --- a/wumsloader/src/module/ModuleMetaInformationFactory.cpp +++ b/wumsloader/src/module/ModuleMetaInformationFactory.cpp @@ -102,6 +102,8 @@ std::optional ModuleMetaInformationFactory::loadModuleMet moduleInfo.setWUMSVersion(0, 3, 4); } else if (value == "0.3.5") { moduleInfo.setWUMSVersion(0, 3, 5); + } else if (value == "0.3.6") { + moduleInfo.setWUMSVersion(0, 3, 6); } else { error = MODULE_PARSE_ERROR_INCOMPATIBLE_VERSION; DEBUG_FUNCTION_LINE_WARN("Ignoring module - Unsupported WUMS version: %.*s.", static_cast(value.length()), value.data()); diff --git a/wumsloader/src/utils/logger.h b/wumsloader/src/utils/logger.h index ef67468..cbb4736 100644 --- a/wumsloader/src/utils/logger.h +++ b/wumsloader/src/utils/logger.h @@ -8,62 +8,61 @@ extern "C" { #endif -#define LOG_APP_TYPE "M" -#define LOG_APP_NAME "WUMSLoader" +#define LOG_APP_TYPE "M" +#define LOG_APP_NAME "WUMSLoader" -#ifdef __cplusplus -constexpr const char *ConstexprFileName(const char *path) { - const char *file = path; - for (const char *p = path; *p; ++p) { - if (*p == '/' || *p == '\\') { - file = p + 1; - } - } - return file; -} -#define __FILENAME__ ConstexprFileName(__FILE__) -#else -#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) -#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__) -#endif +#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) +#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__) -#define LOG(LOG_FUNC, FMT, ARGS...) LOG_EX(LOG_FUNC, "", "", FMT, ##ARGS) +#define LOG(LOG_FUNC, FMT, ARGS...) LOG_EX_DEFAULT(LOG_FUNC, "", "", "", FMT, ##ARGS) -#define LOG_EX(LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ARGS...) \ - do { \ - LOG_FUNC("[(%s)%18s][%23s]%30s@L%04d: " LOG_LEVEL "" FMT "" LINE_END, LOG_APP_TYPE, LOG_APP_NAME, __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \ +#define CONSOLE_COLOR_RED "\033[31m" +#define CONSOLE_COLOR_YELLOW "\033[33m" +#define CONSOLE_COLOR_CYAN "\033[36m" +#define CONSOLE_COLOR_RESET "\033[0m" + +#define LOG_EX_DEFAULT(LOG_FUNC, LOG_COLOR, LOG_LEVEL, LINE_END, FMT, ARGS...) LOG_EX(__FILENAME__, __FUNCTION__, __LINE__, LOG_FUNC, LOG_COLOR, LOG_LEVEL, LINE_END, FMT, ##ARGS) + +#define LOG_EX(FILENAME, FUNCTION, LINE, LOG_FUNC, LOG_COLOR, LOG_LEVEL, LINE_END, FMT, ARGS...) \ + do { \ + LOG_FUNC(LOG_COLOR "[(%s)%18s][%23s]%30s@L%04d: " LOG_LEVEL "" FMT "" LINE_END, LOG_APP_TYPE, LOG_APP_NAME, FILENAME, FUNCTION, LINE, ##ARGS); \ } while (0) - #ifdef DEBUG #ifdef VERBOSE_DEBUG -#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) LOG(WHBLogPrintf, FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) LOG(WHBLogPrintf, FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, WHBLogPrintf, "", "", FMT, ##ARGS); #else -#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0) +#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0) +#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FMT, ARGS...) while (0) #endif -#define DEBUG_FUNCTION_LINE(FMT, ARGS...) LOG(WHBLogPrintf, FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE(FMT, ARGS...) LOG(WHBLogPrintf, FMT, ##ARGS) -#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) LOG(WHBLogWritef, FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) LOG(WHBLogWritef, FMT, ##ARGS) -#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX(WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, CONSOLE_COLOR_RED, "## ERROR## ", CONSOLE_COLOR_RESET, FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, CONSOLE_COLOR_YELLOW, "##WARN ## ", CONSOLE_COLOR_RESET, FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, CONSOLE_COLOR_CYAN, "##INFO ## ", CONSOLE_COLOR_RESET, FMT, ##ARGS) -#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX(WHBLogPrintf, "##WARN ## ", "", FMT, ##ARGS) -#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX(OSReport, "##INFO ## ", "\n", FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, WHBLogPrintf, CONSOLE_COLOR_RED, "##ERROR## ", CONSOLE_COLOR_RESET, FMT, ##ARGS); #else -#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0) +#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FMT, ARGS...) while (0) -#define DEBUG_FUNCTION_LINE(FMT, ARGS...) while (0) +#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0) -#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) while (0) +#define DEBUG_FUNCTION_LINE(FMT, ARGS...) while (0) -#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX(OSReport, "##ERROR## ", "\n", FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) while (0) -#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX(OSReport, "##WARN ## ", "\n", FMT, ##ARGS) -#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX(OSReport, "##INFO ## ", "\n", FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, CONSOLE_COLOR_RED, "##ERROR## ", CONSOLE_COLOR_RESET "\n", FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, CONSOLE_COLOR_YELLOW, "##WARN ## ", CONSOLE_COLOR_RESET "\n", FMT, ##ARGS) +#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, CONSOLE_COLOR_CYAN, "##INFO ## ", CONSOLE_COLOR_RESET "\n", FMT, ##ARGS) + +#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, OSReport, CONSOLE_COLOR_RED, "##ERROR## ", CONSOLE_COLOR_RESET "\n", FMT, ##ARGS); #endif diff --git a/wumsloader/src/utils/reent.cpp b/wumsloader/src/utils/reent.cpp new file mode 100644 index 0000000..1caa532 --- /dev/null +++ b/wumsloader/src/utils/reent.cpp @@ -0,0 +1,345 @@ +#include "reent.h" + +#include "globals.h" +#include "logger.h" + +#include + +#include +#include + +#include +#include +#include +#include + +#define __WUMS_CONTEXT_THREAD_SPECIFIC_ID 0 +#define WUMS_REENT_ALLOC_SENTINEL ((__wums_reent_node *) 0xFFFFFFFF) +#define WUMS_REENT_NODE_VERSION 1 +#define WUMS_REENT_NODE_MAGIC 0x57554D53 // WUMS + +void wums_set_thread_specific_ex(const int id, void *value, OSThread *thread) { + if (thread != nullptr) { + if (id == 0) { + thread->reserved[1] = reinterpret_cast(value); + } else { + OSReport("wums_set_thread_specific: invalid id\n"); + OSFatal("wums_set_thread_specific: invalid id"); + } + } else { + OSReport("wums_set_thread_specific: invalid thread\n"); + OSFatal("wums_set_thread_specific: invalid thread"); + } +} + +void *wums_get_thread_specific_ex(const int id, const OSThread *thread) { + if (thread != nullptr) { + if (id == 0) { + return reinterpret_cast(thread->reserved[1]); + } else { + OSReport("wums_get_thread_specific: invalid id\n"); + OSFatal("wums_get_thread_specific: invalid id"); + } + } else { + OSReport("wums_get_thread_specific: invalid thread\n"); + OSFatal("wums_get_thread_specific: invalid thread\n"); + } + return nullptr; +} + +void wums_set_thread_specific(int id, void *value) { + return wums_set_thread_specific_ex(id, value, OSGetCurrentThread()); +} + +void *wums_get_thread_specific(int id) { + return wums_get_thread_specific_ex(id, OSGetCurrentThread()); +} + +struct __wums_reent_node { + uint32_t magic; + uint32_t version; + __wums_reent_node *next; + + const void *moduleId; + void *reentPtr; // The ABI payload + void (*cleanupFn)(void *); // The trampoline to clean up the payload + OSThreadCleanupCallbackFn savedCleanup; +}; + +namespace { +std::unordered_set<__wums_reent_node *> sGlobalNodesSeen; +std::vector<__wums_reent_node *> sGlobalNodes; +std::recursive_mutex sGlobalNodesMutex; + +void removeNodeFromListsLocked(__wums_reent_node *curr) { + std::lock_guard lock(sGlobalNodesMutex); + if (const auto it = std::ranges::find(sGlobalNodes, curr); it != sGlobalNodes.end()) { + *it = sGlobalNodes.back(); + sGlobalNodes.pop_back(); + } + sGlobalNodesSeen.erase(curr); +} +} // namespace + +void ClearDanglingReentPtr() { + std::lock_guard lock(sGlobalNodesMutex); + + // This function is expected to be called exactly once at the start of each new application cycle. + // It acts as a garbage collector for nodes left behind by the previous application. + // Leftover nodes typically occur when threads are forcefully killed before they can execute + // their cleanup callbacks, or if a thread's cleanup function was wrongly overridden. + // + // Mechanism: Since threads do not survive across application boundaries, any node we + // observe across multiple cycles is guaranteed to be a dangling pointer from a dead thread. + std::erase_if(sGlobalNodes, [](__wums_reent_node *ptr) { + if (ptr == nullptr) { + return true; + } + + // Try to register the pointer in our historical "seen" tracker. + auto [iterator, isNewValue] = sGlobalNodesSeen.insert(ptr); + + if (isNewValue) { + // If it was newly inserted, it might be a valid node created during the current + // application cycle (e.g., initialized via a hook in an RPL's init function). + // We keep it in the vector for now. + return false; + } + + // Otherwise, we have already seen this address in a previous cycle. + // This means the node belongs to a dead thread from an older application. + // It is now safe to execute its payload cleanup and free the memory. + auto *nodeToFree = *iterator; + if (nodeToFree->cleanupFn) { + nodeToFree->cleanupFn(nodeToFree->reentPtr); + } + free(nodeToFree); + + // Make to remove it from the "seen" list as well. + sGlobalNodesSeen.erase(iterator); + return true; + }); +} + + +static void __wums_thread_cleanup(OSThread *thread, void *stack) { + auto *head = static_cast<__wums_reent_node *>(wums_get_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID)); + + if (!head || head == WUMS_REENT_ALLOC_SENTINEL || head->magic != WUMS_REENT_NODE_MAGIC) { + return; + } + + OSThreadCleanupCallbackFn savedCleanup = head->savedCleanup; + + // Set to effective global during free to prevent malloc re-entrancy loops + wums_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, WUMS_REENT_ALLOC_SENTINEL); + + auto *curr = head; + while (curr) { + __wums_reent_node *next = curr->next; + + if (curr->cleanupFn) { + curr->cleanupFn(curr->reentPtr); + } + + removeNodeFromListsLocked(curr); + + free(curr); + curr = next; + } + + wums_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, nullptr); + + // Chain to previous OS callback + if (savedCleanup) { + savedCleanup(thread, stack); + } +} + +void *wums_backend_get_context(const void *moduleId, wums_loader_init_reent_errors_t_ *outError) { + if (!outError) { + OSFatal("Called wums_backend_get_context with error nullptr"); + return nullptr; + } + + if (!OSGetCurrentThread()) { + *outError = WUMSReent_ERROR_NO_THREAD; + return nullptr; + } + + auto *head = static_cast<__wums_reent_node *>(wums_get_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID)); + + if (head == WUMS_REENT_ALLOC_SENTINEL) { + *outError = WUMSReent_ERROR_GLOBAL_REENT_REQUESTED; + return nullptr; + } + if (head && head->magic != WUMS_REENT_NODE_MAGIC) { + *outError = WUMSReent_ERROR_GLOBAL_REENT_REQUESTED; + return nullptr; + } + + const __wums_reent_node *curr = head; + while (curr) { + if (curr->version >= 1 && curr->moduleId == moduleId) { + return curr->reentPtr; + } + curr = curr->next; + } + + *outError = WUMSReent_ERROR_NONE; + + return nullptr; +} + +void *wums_backend_set_sentinel() { + auto *head = wums_get_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID); + wums_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, WUMS_REENT_ALLOC_SENTINEL); + return head; +} + +void wums_backend_restore_head(void *oldHead) { + wums_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, oldHead); +} + +bool wums_backend_register_context(const void *moduleId, void *reentPtr, void (*cleanupFn)(void *), void *oldHeadVoid) { + auto *oldHead = static_cast<__wums_reent_node *>(oldHeadVoid); + + auto *newNode = static_cast<__wums_reent_node *>(malloc(sizeof(__wums_reent_node))); + if (!newNode) { + return false; + } + + newNode->magic = WUMS_REENT_NODE_MAGIC; + newNode->version = WUMS_REENT_NODE_VERSION; + newNode->next = oldHead; + newNode->moduleId = moduleId; + newNode->reentPtr = reentPtr; + newNode->cleanupFn = cleanupFn; + newNode->savedCleanup = nullptr; + + if (oldHead == nullptr || oldHead == WUMS_REENT_ALLOC_SENTINEL || oldHead->magic != WUMS_REENT_NODE_MAGIC || oldHead->version < WUMS_REENT_NODE_VERSION) { + newNode->savedCleanup = OSSetThreadCleanupCallback(OSGetCurrentThread(), &__wums_thread_cleanup); + } else { + newNode->savedCleanup = oldHead->savedCleanup; + oldHead->savedCleanup = nullptr; + } + + wums_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, newNode); + + { + std::lock_guard lock(sGlobalNodesMutex); + sGlobalNodes.push_back(newNode); + } + + return true; +} + +/* +void ClearReentDataForModules(const std::vector &modules) { + auto *curThread = OSGetCurrentThread(); + + for (const auto &cur : modules) { + if (!cur.isLinkedAndLoaded()) { + continue; + } + + if (cur.getMetaInformation().getWUMSVersion() <= WUMSLoader::Modules::WUMSVersion(0, 3, 5)) { + continue; + } + + const auto startAddress = cur.getLinkInformation().getStartAddress(); + const auto endAddress = cur.getLinkInformation().getEndAddress(); + + // Zero-allocation deferred free list + __wums_reent_node *deferredFreeHead = nullptr; + + struct PendingRestore { + OSThread *thread; + OSThreadCleanupCallbackFn callback; + }; + std::vector pendingRestores; + constexpr int PENDING_RESTORES_SIZE = 128; + // Pre-allocate to prevent malloc() from firing while the scheduler is locked! + pendingRestores.reserve(PENDING_RESTORES_SIZE); + { + // Acquire GLOBAL scheduler lock + const int state = OSDisableInterrupts(); + __OSLockScheduler(curThread); + + OSThread *t = *reinterpret_cast(0x100567F8); + + while (t) { + auto *head = static_cast<__wums_reent_node *>(wums_get_thread_specific_ex(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, t)); + + // Safety checks with Sentinel/Magic + if (!head || head == WUMS_REENT_ALLOC_SENTINEL || head->magic != WUMS_REENT_NODE_MAGIC) { + t = t->activeLink.next; + continue; + } + + __wums_reent_node *prev = nullptr; + auto *curr = head; + + while (curr) { + __wums_reent_node *next = curr->next; + auto moduleIdAddr = reinterpret_cast(curr->moduleId); + + // module id lives in the .data/.bss section of a module + if (moduleIdAddr >= startAddress && moduleIdAddr < endAddress) { + // remove from linked list + if (prev) { + prev->next = next; + } else { + head = next; + if (curr->savedCleanup) { + if (head) { + head->savedCleanup = curr->savedCleanup; + } else { + // No WUMS nodes left, mark for restoring. + if (pendingRestores.size() == PENDING_RESTORES_SIZE) { + OSFatal("WUMSBackend pendingRestores size limit hit"); + } + pendingRestores.push_back({t, curr->savedCleanup}); + } + } + } + + curr->next = deferredFreeHead; + deferredFreeHead = curr; + } else { + prev = curr; + } + curr = next; + } + + // Restore the updated head to the thread + wums_set_thread_specific_ex(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, head, t); + t = t->activeLink.next; + } + + __OSUnlockScheduler(curThread); + OSRestoreInterrupts(state); + } + + for (const auto &restore : pendingRestores) { + DEBUG_FUNCTION_LINE_VERBOSE("Set cleanup function for thread %p to %p", restore.thread, restore.callback); + OSSetThreadCleanupCallback(restore.thread, restore.callback); + } + + // Free removed entries + auto *oldHead = wums_backend_set_sentinel(); + __wums_reent_node *nodeToFree = deferredFreeHead; + while (nodeToFree) { + __wums_reent_node *nextNode = nodeToFree->next; + if (nodeToFree->cleanupFn) { + nodeToFree->cleanupFn(nodeToFree->reentPtr); + } + removeNodeFromListsLocked(nodeToFree); + free(nodeToFree); + + nodeToFree = nextNode; + } + wums_backend_restore_head(oldHead); + } +} +*/ \ No newline at end of file diff --git a/wumsloader/src/utils/reent.h b/wumsloader/src/utils/reent.h new file mode 100644 index 0000000..e814060 --- /dev/null +++ b/wumsloader/src/utils/reent.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +void *wums_backend_get_context(const void *pluginId, wums_loader_init_reent_errors_t_ *outError); + +void *wums_backend_set_sentinel(); + +void wums_backend_restore_head(void *oldHead); + +bool wums_backend_register_context(const void *pluginId, void *reentPtr, void (*cleanupFn)(void *), void *oldHead); + +void ClearDanglingReentPtr(); \ No newline at end of file