mirror of
https://github.com/wiiu-env/WUMSLoader.git
synced 2026-05-06 05:15:32 -05:00
Support for WUMS 0.3.6 - implement reent for modules
This commit is contained in:
parent
f00d4a9dc0
commit
b41a83d9bb
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
#include "utils/SegmentedTimer.h"
|
||||
#include "utils/dynamic.h"
|
||||
#include "utils/logger.h"
|
||||
#include "utils/reent.h"
|
||||
|
||||
#include <coreinit/debug.h>
|
||||
#include <coreinit/interrupts.h>
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -4,10 +4,12 @@
|
|||
#include "ModuleDataPersistence.h"
|
||||
#include "globals.h"
|
||||
#include "utils/logger.h"
|
||||
#include "utils/reent.h"
|
||||
|
||||
#include <wums/reent_internal.h>
|
||||
|
||||
#include <coreinit/memexpheap.h>
|
||||
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -150,6 +150,7 @@ std::vector<ModuleInfoWithLoadedFileWrapper> 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);
|
||||
|
|
|
|||
|
|
@ -102,6 +102,8 @@ std::optional<ModuleMetaInformation> 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<int>(value.length()), value.data());
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
345
wumsloader/src/utils/reent.cpp
Normal file
345
wumsloader/src/utils/reent.cpp
Normal file
|
|
@ -0,0 +1,345 @@
|
|||
#include "reent.h"
|
||||
|
||||
#include "globals.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <wums/reent_internal.h>
|
||||
|
||||
#include <coreinit/debug.h>
|
||||
#include <coreinit/thread.h>
|
||||
|
||||
#include <coreinit/memexpheap.h>
|
||||
#include <cstdlib>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#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<uint32_t>(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<void *>(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<WUMSLoader::Modules::ModuleContainer> &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<PendingRestore> 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<OSThread **>(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<uint32_t>(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);
|
||||
}
|
||||
}
|
||||
*/
|
||||
13
wumsloader/src/utils/reent.h
Normal file
13
wumsloader/src/utils/reent.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <wums/reent_internal.h>
|
||||
|
||||
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();
|
||||
Loading…
Reference in New Issue
Block a user