mirror of
https://github.com/wiiu-env/WiiUModuleSystem.git
synced 2026-05-05 20:55:28 -05:00
Reimplement __wums_getreent to be per module and (hopefully) future proof
This commit is contained in:
parent
c7ddece866
commit
9916b10c8e
4
Makefile
4
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)/$@ \
|
||||
|
|
|
|||
34
include/wums/wums_debug.h
Normal file
34
include/wums/wums_debug.h
Normal file
|
|
@ -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__)
|
||||
|
|
@ -1,14 +1,18 @@
|
|||
#include "wums_reent.h"
|
||||
#include "wums_thread_specific.h"
|
||||
#include <cstring>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <wums/wums_debug.h>
|
||||
|
||||
#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)
|
||||
|
||||
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 const char wums_meta_info_dump[];
|
||||
|
||||
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);
|
||||
|
||||
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 (node->reentPtr) {
|
||||
_reclaim_reent(static_cast<_reent *>(node->reentPtr));
|
||||
free(node->reentPtr);
|
||||
}
|
||||
|
||||
if (context->savedCleanup) {
|
||||
context->savedCleanup(thread, stack);
|
||||
}
|
||||
|
||||
_reclaim_reent(&context->reent);
|
||||
|
||||
// Use global reent during free since the current reent is getting freed
|
||||
wums_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, _GLOBAL_REENT);
|
||||
|
||||
free(context);
|
||||
|
||||
wums_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, NULL);
|
||||
free(node);
|
||||
}
|
||||
|
||||
struct _reent *
|
||||
__wums_getreent(void) {
|
||||
struct __wums_thread_context *context;
|
||||
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));
|
||||
|
||||
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);
|
||||
if (!head || head == WUMS_REENT_ALLOC_SENTINEL) {
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
|
||||
OSThreadCleanupCallbackFn savedCleanup = nullptr;
|
||||
if (head->version >= 1) {
|
||||
savedCleanup = head->savedCleanup;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
_REENT_INIT_PTR(&context->reent);
|
||||
context->savedCleanup = OSSetThreadCleanupCallback(OSGetCurrentThread(), &__wums_thread_cleanup);
|
||||
|
||||
wums_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, context);
|
||||
curr = next;
|
||||
}
|
||||
|
||||
return &context->reent;
|
||||
wut_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, nullptr);
|
||||
|
||||
if (savedCleanup) {
|
||||
WUMS_DEBUG_REPORT("__wums_thread_cleanup: Chaining to saved cleanup for thread %p\n", thread);
|
||||
savedCleanup(thread, stack);
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
// 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 *newReent = static_cast<struct _reent *>(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;
|
||||
}
|
||||
|
||||
_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;
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user