mirror of
https://github.com/wiiu-env/WiiUPluginLoaderBackend.git
synced 2026-04-17 14:05:49 -05:00
Implement support for WUPS 0.9.1, add proper plugin reent handling
This commit is contained in:
parent
8c973db737
commit
2e5de3e6e7
14
Dockerfile
14
Dockerfile
|
|
@ -1,12 +1,12 @@
|
|||
FROM ghcr.io/wiiu-env/devkitppc:20260225
|
||||
|
||||
COPY --from=ghcr.io/wiiu-env/wiiumodulesystem:20260225 /artifacts $DEVKITPRO
|
||||
COPY --from=ghcr.io/wiiu-env/wiiupluginsystem:20260225 /artifacts $DEVKITPRO
|
||||
COPY --from=ghcr.io/wiiu-env/libfunctionpatcher:20260208 /artifacts $DEVKITPRO
|
||||
COPY --from=ghcr.io/wiiu-env/libmappedmemory:20260131 /artifacts $DEVKITPRO
|
||||
COPY --from=ghcr.io/wiiu-env/libwupsbackend:20260131 /artifacts $DEVKITPRO
|
||||
COPY --from=ghcr.io/wiiu-env/libnotifications:20260131 /artifacts $DEVKITPRO
|
||||
COPY --from=ghcr.io/wiiu-env/libbuttoncombo:20260112 /artifacts $DEVKITPRO
|
||||
COPY --from=ghcr.io/wiiu-env/wiiumodulesystem:reentfix-dev-20260403-5ca1144 /artifacts $DEVKITPRO
|
||||
COPY --from=ghcr.io/wiiu-env/wiiupluginsystem:abifix-dev-20260408-77ab748 /artifacts $DEVKITPRO
|
||||
COPY --from=ghcr.io/wiiu-env/libfunctionpatcher:20260331 /artifacts $DEVKITPRO
|
||||
COPY --from=ghcr.io/wiiu-env/libmappedmemory:20260331 /artifacts $DEVKITPRO
|
||||
COPY --from=ghcr.io/wiiu-env/libwupsbackend:20260331 /artifacts $DEVKITPRO
|
||||
COPY --from=ghcr.io/wiiu-env/libnotifications:20260331 /artifacts $DEVKITPRO
|
||||
COPY --from=ghcr.io/wiiu-env/libbuttoncombo:20260331 /artifacts $DEVKITPRO
|
||||
COPY --from=ghcr.io/wiiu-env/libiopshell:20260318 /artifacts $DEVKITPRO
|
||||
|
||||
WORKDIR project
|
||||
|
|
|
|||
|
|
@ -7,11 +7,13 @@
|
|||
#include "utils/StorageUtilsDeprecated.h"
|
||||
#include "utils/buttoncombo/ButtonComboUtils.h"
|
||||
#include "utils/logger.h"
|
||||
#include "utils/reent.h"
|
||||
#include "utils/storage/StorageUtils.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
#include <wups/button_combo/api.h>
|
||||
#include <wups/button_combo_internal.h>
|
||||
#include <wups/reent_internal.h>
|
||||
#include <wups/storage.h>
|
||||
|
||||
#include <functional>
|
||||
|
|
@ -102,7 +104,8 @@ void CallHook(const PluginContainer &plugin, const wups_loader_hook_type_t hook_
|
|||
}
|
||||
case WUPS_LOADER_HOOK_INIT_CONFIG: {
|
||||
wups_loader_init_config_args_t args{.arg_version = 1, .plugin_identifier = plugin.getHandle()};
|
||||
auto res = ((WUPSConfigAPIStatus(*)(wups_loader_init_config_args_t))((uint32_t *) func_ptr))(args);
|
||||
// clang-format off
|
||||
auto res = ((WUPSConfigAPIStatus (*)(wups_loader_init_config_args_t))((uint32_t *) func_ptr))(args);
|
||||
// clang-format on
|
||||
if (res != WUPSCONFIG_API_RESULT_SUCCESS) {
|
||||
// TODO: More error handling? Notification?
|
||||
|
|
@ -131,7 +134,8 @@ void CallHook(const PluginContainer &plugin, const wups_loader_hook_type_t hook_
|
|||
args.check_button_combo_available_function_ptr = &ButtonComboUtils::API::CheckComboAvailable;
|
||||
args.detect_button_combo_blocking_function_ptr = &ButtonComboUtils::API::DetectButtonCombo_Blocking;
|
||||
|
||||
auto res = ((WUPSButtonCombo_Error(*)(wups_loader_init_button_combo_args_t))((uint32_t *) func_ptr))(args);
|
||||
// clang-format off
|
||||
auto res = ((WUPSButtonCombo_Error (*)(wups_loader_init_button_combo_args_t))((uint32_t *) func_ptr))(args);
|
||||
// clang-format on
|
||||
if (res != WUPS_BUTTON_COMBO_ERROR_SUCCESS) {
|
||||
// TODO: More error handling? Notification?
|
||||
|
|
@ -139,6 +143,23 @@ void CallHook(const PluginContainer &plugin, const wups_loader_hook_type_t hook_
|
|||
}
|
||||
break;
|
||||
}
|
||||
case WUPS_LOADER_HOOK_INIT_REENT_FUNCTIONS: {
|
||||
if (plugin.getMetaInformation().getWUPSVersion() <= WUPSVersion(0, 9, 0)) {
|
||||
break;
|
||||
}
|
||||
wups_loader_init_reent_args_t_ args;
|
||||
args.version = WUPS_REENT_CUR_API_VERSION;
|
||||
args.restore_head_ptr = &wups_backend_restore_head;
|
||||
args.get_context_ptr = &wups_backend_get_context;
|
||||
args.set_sentinel_ptr = &wups_backend_set_sentinel;
|
||||
args.add_reent_context_ptr = &wups_backend_register_context;
|
||||
|
||||
// clang-format off
|
||||
((void (*)(wups_loader_init_reent_args_t_))((uint32_t *) func_ptr))(args);
|
||||
// clang-format on
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
DEBUG_FUNCTION_LINE_ERR("######################################");
|
||||
DEBUG_FUNCTION_LINE_ERR("Hook is not implemented %s [%d]", hookNameToString(hook_type), hook_type);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include "utils/WUPSBackendSettings.h"
|
||||
#include "utils/input/VPADInput.h"
|
||||
#include "utils/logger.h"
|
||||
#include "utils/reent.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
#include <buttoncombo/api.h>
|
||||
|
|
@ -164,7 +165,6 @@ WUMS_APPLICATION_ENDS() {
|
|||
deinitLogging();
|
||||
}
|
||||
|
||||
void CheckCleanupCallbackUsage(const std::vector<PluginContainer> &plugins);
|
||||
void CleanupPlugins(std::vector<PluginContainer> &pluginsToDeinit);
|
||||
|
||||
|
||||
|
|
@ -190,6 +190,8 @@ WUMS_APPLICATION_STARTS() {
|
|||
|
||||
initLogging();
|
||||
|
||||
ClearDanglingReentPtr();
|
||||
|
||||
std::lock_guard lock(gLoadedDataMutex);
|
||||
|
||||
std::vector<PluginContainer> newLoadedPlugins;
|
||||
|
|
@ -332,6 +334,7 @@ WUMS_APPLICATION_STARTS() {
|
|||
// PluginManagement::memsetBSS(plugins);
|
||||
|
||||
const auto &needsInitsCheck = [](const PluginContainer &container) { return !container.isInitDone(); };
|
||||
CallHook(gLoadedPlugins, WUPS_LOADER_HOOK_INIT_REENT_FUNCTIONS, needsInitsCheck);
|
||||
CallHook(gLoadedPlugins, WUPS_LOADER_HOOK_INIT_WUT_MALLOC, needsInitsCheck);
|
||||
CallHook(gLoadedPlugins, WUPS_LOADER_HOOK_INIT_WUT_NEWLIB, needsInitsCheck);
|
||||
CallHook(gLoadedPlugins, WUPS_LOADER_HOOK_INIT_WUT_STDCPP, needsInitsCheck);
|
||||
|
|
@ -367,20 +370,21 @@ void CleanupPlugins(std::vector<PluginContainer> &pluginsToDeinit) {
|
|||
DEBUG_FUNCTION_LINE_INFO("De-init plugin %s from %s. PluginData: %08X", pluginContainer.getMetaInformation().getName().c_str(), pluginContainer.getMetaInformation().getAuthor().c_str(), pluginContainer.getPluginDataCopy()->getHandle());
|
||||
}
|
||||
|
||||
currentThread->reserved[4] = 0;
|
||||
{ // legacy reent code
|
||||
currentThread->reserved[4] = 0;
|
||||
}
|
||||
|
||||
CallHook(pluginsToDeinit, WUPS_LOADER_HOOK_DEINIT_PLUGIN);
|
||||
CallHook(pluginsToDeinit, WUPS_LOADER_HOOK_FINI_WRAPPER);
|
||||
|
||||
CheckCleanupCallbackUsage(pluginsToDeinit);
|
||||
|
||||
if (currentThread->cleanupCallback != saved_cleanupCallback) {
|
||||
DEBUG_FUNCTION_LINE_WARN("WUPS_LOADER_HOOK_DEINIT_PLUGIN overwrote the ThreadCleanupCallback, we need to restore it!\n");
|
||||
OSSetThreadCleanupCallback(OSGetCurrentThread(), saved_cleanupCallback);
|
||||
{ // legacy reent code
|
||||
if (currentThread->cleanupCallback != saved_cleanupCallback) {
|
||||
DEBUG_FUNCTION_LINE_WARN("WUPS_LOADER_HOOK_DEINIT_PLUGIN overwrote the ThreadCleanupCallback, we need to restore it!\n");
|
||||
OSSetThreadCleanupCallback(OSGetCurrentThread(), saved_cleanupCallback);
|
||||
}
|
||||
currentThread->reserved[4] = saved_reent;
|
||||
}
|
||||
|
||||
currentThread->reserved[4] = saved_reent;
|
||||
|
||||
DEBUG_FUNCTION_LINE("Restore function patches of plugins.");
|
||||
PluginManagement::RestoreFunctionPatches(pluginsToDeinit);
|
||||
|
||||
|
|
@ -392,6 +396,8 @@ void CleanupPlugins(std::vector<PluginContainer> &pluginsToDeinit) {
|
|||
plugin.DeinitButtonComboData();
|
||||
}
|
||||
|
||||
ClearReentDataForPlugins(pluginsToDeinit);
|
||||
|
||||
// Check for leaked memory
|
||||
for (auto &plugin : pluginsToDeinit) {
|
||||
if (const auto tracking = plugin.getTrackingMemoryAllocator()) {
|
||||
|
|
@ -406,34 +412,4 @@ void CleanupPlugins(std::vector<PluginContainer> &pluginsToDeinit) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void CheckCleanupCallbackUsage(const std::vector<PluginContainer> &plugins) {
|
||||
auto *curThread = OSGetCurrentThread();
|
||||
for (const auto &cur : plugins) {
|
||||
if (!cur.isLinkedAndLoaded()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto textSection = cur.getPluginLinkInformation().getSectionInfo(".text");
|
||||
if (!textSection) {
|
||||
continue;
|
||||
}
|
||||
const uint32_t startAddress = textSection->getAddress();
|
||||
const uint32_t endAddress = textSection->getAddress() + textSection->getSize();
|
||||
auto *pluginName = cur.getMetaInformation().getName().c_str();
|
||||
{
|
||||
__OSLockScheduler(curThread);
|
||||
const int state = OSDisableInterrupts();
|
||||
OSThread *t = *reinterpret_cast<OSThread **>(0x100567F8);
|
||||
while (t) {
|
||||
const auto address = reinterpret_cast<uint32_t>(t->cleanupCallback);
|
||||
if (address != 0 && address >= startAddress && address <= endAddress) {
|
||||
OSReport("[WARN] PluginBackend: Thread 0x%p is using a function from plugin %s for the threadCleanupCallback\n", t, pluginName);
|
||||
}
|
||||
t = t->activeLink.next;
|
||||
}
|
||||
OSRestoreInterrupts(state);
|
||||
__OSUnlockScheduler(curThread);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
#include "plugin/SectionInfo.h"
|
||||
#include "utils/config/ConfigUtils.h"
|
||||
#include "utils/logger.h"
|
||||
#include "utils/reent.h"
|
||||
|
||||
#include <coreinit/cache.h>
|
||||
#include <coreinit/core.h>
|
||||
|
|
@ -276,6 +277,15 @@ DECL_FUNCTION(uint32_t, KiGetAppSymbolName, uint32_t addr, char *buffer, int32_t
|
|||
|
||||
#pragma GCC pop_options
|
||||
|
||||
|
||||
DECL_FUNCTION(uint32_t, SomeExitHook) {
|
||||
// Thats the last thing called in __PPCExit
|
||||
const auto res = real_SomeExitHook();
|
||||
|
||||
MarkReentNodesForDeletion();
|
||||
return res;
|
||||
}
|
||||
|
||||
function_replacement_data_t method_hooks_static[] __attribute__((section(".data"))) = {
|
||||
REPLACE_FUNCTION(GX2SwapScanBuffers, LIBRARY_GX2, GX2SwapScanBuffers),
|
||||
REPLACE_FUNCTION(GX2SetTVBuffer, LIBRARY_GX2, GX2SetTVBuffer),
|
||||
|
|
@ -287,6 +297,7 @@ function_replacement_data_t method_hooks_static[] __attribute__((section(".data"
|
|||
REPLACE_FUNCTION(WPADRead, LIBRARY_PADSCORE, WPADRead),
|
||||
REPLACE_FUNCTION_VIA_ADDRESS(SC17_FindClosestSymbol, 0xfff10218, 0xfff10218),
|
||||
REPLACE_FUNCTION_VIA_ADDRESS(KiGetAppSymbolName, 0xfff0e3a0, 0xfff0e3a0),
|
||||
REPLACE_FUNCTION_VIA_ADDRESS(SomeExitHook, 0x3201C400 + 0x40b4c, 0x101C400 + 0x40b4c),
|
||||
};
|
||||
|
||||
uint32_t method_hooks_static_size __attribute__((section(".data"))) = sizeof(method_hooks_static) / sizeof(function_replacement_data_t);
|
||||
|
|
|
|||
|
|
@ -123,6 +123,8 @@ std::optional<PluginMetaInformation> PluginMetaInformationFactory::loadPlugin(st
|
|||
pluginInfo.setWUPSVersion(0, 8, 2);
|
||||
} else if (value == "0.9.0") {
|
||||
pluginInfo.setWUPSVersion(0, 9, 0);
|
||||
} else if (value == "0.9.1") {
|
||||
pluginInfo.setWUPSVersion(0, 9, 1);
|
||||
} else {
|
||||
error = PLUGIN_PARSE_ERROR_INCOMPATIBLE_VERSION;
|
||||
DEBUG_FUNCTION_LINE_ERR("Warning: Ignoring plugin - Unsupported WUPS version: %s.", value.c_str());
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
|
||||
#include <coreinit/debug.h>
|
||||
#include <string.h>
|
||||
#include <whb/log.h>
|
||||
|
|
@ -9,19 +8,24 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define LOG_APP_TYPE "M"
|
||||
#define LOG_APP_NAME "wupsbackend"
|
||||
#define LOG_APP_TYPE "M"
|
||||
#define LOG_APP_NAME "wupsbackend"
|
||||
|
||||
#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
|
||||
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__)
|
||||
#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_DEFAULT(LOG_FUNC, "", "", FMT, ##ARGS)
|
||||
#define LOG(LOG_FUNC, FMT, ARGS...) LOG_EX_DEFAULT(LOG_FUNC, "", "", "", FMT, ##ARGS)
|
||||
|
||||
#define LOG_EX_DEFAULT(LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ARGS...) LOG_EX(__FILENAME__, __FUNCTION__, __LINE__, LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ##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(FILENAME, FUNCTION, LINE, 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 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
|
||||
|
|
@ -38,11 +42,11 @@ extern "C" {
|
|||
|
||||
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) LOG(WHBLogWritef, FMT, ##ARGS)
|
||||
|
||||
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS)
|
||||
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##WARN ## ", "", FMT, ##ARGS)
|
||||
#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##INFO ## ", "", 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_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, WHBLogPrintf, "##ERROR## ", "", 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
|
||||
|
||||
|
|
@ -54,11 +58,11 @@ extern "C" {
|
|||
|
||||
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) while (0)
|
||||
|
||||
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##ERROR## ", "\n", FMT, ##ARGS)
|
||||
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##WARN ## ", "\n", FMT, ##ARGS)
|
||||
#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX_DEFAULT(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, "##ERROR## ", "\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
|
||||
|
||||
|
|
|
|||
311
source/utils/reent.cpp
Normal file
311
source/utils/reent.cpp
Normal file
|
|
@ -0,0 +1,311 @@
|
|||
#include "reent.h"
|
||||
|
||||
#include "logger.h"
|
||||
#include "plugin/PluginContainer.h"
|
||||
#include "plugin/SectionInfo.h"
|
||||
|
||||
#include <coreinit/debug.h>
|
||||
#include <coreinit/interrupts.h>
|
||||
#include <coreinit/scheduler.h>
|
||||
#include <coreinit/thread.h>
|
||||
#include <forward_list>
|
||||
#include <wups/reent_internal.h>
|
||||
|
||||
#define __WUPS_CONTEXT_THREAD_SPECIFIC_ID 0
|
||||
#define WUPS_REENT_ALLOC_SENTINEL ((__wups_reent_node *) 0xFFFFFFFF)
|
||||
#define WUPS_REENT_NODE_VERSION 1
|
||||
#define WUPS_REENT_NODE_MAGIC 0x57555053 // WUPS
|
||||
|
||||
void wups_set_thread_specific_ex(const int id, void *value, OSThread *thread) {
|
||||
if (thread != nullptr) {
|
||||
if (id == 0) {
|
||||
thread->reserved[0] = reinterpret_cast<uint32_t>(value);
|
||||
} else {
|
||||
OSReport("wups_set_thread_specific: invalid id\n");
|
||||
OSFatal("wups_set_thread_specific: invalid id");
|
||||
}
|
||||
} else {
|
||||
OSReport("wups_set_thread_specific: invalid thread\n");
|
||||
OSFatal("wups_set_thread_specific: invalid thread");
|
||||
}
|
||||
}
|
||||
|
||||
void *wups_get_thread_specific_ex(const int id, const OSThread *thread) {
|
||||
if (thread != nullptr) {
|
||||
if (id == 0) {
|
||||
return reinterpret_cast<void *>(thread->reserved[0]);
|
||||
} else {
|
||||
OSReport("wups_get_thread_specific: invalid id\n");
|
||||
OSFatal("wups_get_thread_specific: invalid id");
|
||||
}
|
||||
} else {
|
||||
OSReport("wups_get_thread_specific: invalid thread\n");
|
||||
OSFatal("wups_get_thread_specific: invalid thread\n");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void wups_set_thread_specific(int id, void *value) {
|
||||
return wups_set_thread_specific_ex(id, value, OSGetCurrentThread());
|
||||
}
|
||||
|
||||
void *wups_get_thread_specific(int id) {
|
||||
return wups_get_thread_specific_ex(id, OSGetCurrentThread());
|
||||
}
|
||||
|
||||
struct __wups_reent_node {
|
||||
uint32_t magic;
|
||||
uint32_t version;
|
||||
__wups_reent_node *next;
|
||||
|
||||
const void *pluginId;
|
||||
void *reentPtr; // The ABI payload
|
||||
void (*cleanupFn)(void *); // The trampoline to clean up the payload
|
||||
OSThreadCleanupCallbackFn savedCleanup;
|
||||
};
|
||||
|
||||
namespace {
|
||||
std::vector<__wups_reent_node *> sGlobalNodesCopy;
|
||||
std::vector<__wups_reent_node *> sGlobalNodes;
|
||||
std::recursive_mutex sGlobalNodesMutex;
|
||||
} // namespace
|
||||
|
||||
void MarkReentNodesForDeletion() {
|
||||
sGlobalNodesCopy = std::move(sGlobalNodes);
|
||||
}
|
||||
|
||||
void ClearDanglingReentPtr() {
|
||||
for (auto nodeToFree : sGlobalNodesCopy) {
|
||||
if (nodeToFree->cleanupFn) {
|
||||
nodeToFree->cleanupFn(nodeToFree->reentPtr);
|
||||
}
|
||||
free(nodeToFree);
|
||||
}
|
||||
sGlobalNodesCopy.clear();
|
||||
}
|
||||
|
||||
static void __wups_thread_cleanup(OSThread *thread, void *stack) {
|
||||
auto *head = static_cast<__wups_reent_node *>(wups_get_thread_specific(__WUPS_CONTEXT_THREAD_SPECIFIC_ID));
|
||||
|
||||
if (!head || head == WUPS_REENT_ALLOC_SENTINEL || head->magic != WUPS_REENT_NODE_MAGIC) {
|
||||
return;
|
||||
}
|
||||
|
||||
OSThreadCleanupCallbackFn savedCleanup = head->savedCleanup;
|
||||
|
||||
// Set to effective global during free to prevent malloc re-entrancy loops
|
||||
wups_set_thread_specific(__WUPS_CONTEXT_THREAD_SPECIFIC_ID, WUPS_REENT_ALLOC_SENTINEL);
|
||||
|
||||
auto *curr = head;
|
||||
while (curr) {
|
||||
__wups_reent_node *next = curr->next;
|
||||
|
||||
if (curr->cleanupFn) {
|
||||
curr->cleanupFn(curr->reentPtr);
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard lock(sGlobalNodesMutex);
|
||||
std::erase(sGlobalNodes, curr);
|
||||
}
|
||||
|
||||
free(curr);
|
||||
curr = next;
|
||||
}
|
||||
|
||||
wups_set_thread_specific(__WUPS_CONTEXT_THREAD_SPECIFIC_ID, nullptr);
|
||||
|
||||
// Chain to previous OS callback
|
||||
if (savedCleanup) {
|
||||
savedCleanup(thread, stack);
|
||||
}
|
||||
}
|
||||
|
||||
void *wups_backend_get_context(const void *pluginId, wups_loader_init_reent_errors_t_ *outError) {
|
||||
if (!outError) {
|
||||
OSFatal("Called wups_backend_get_context with error nullptr");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!OSGetCurrentThread()) {
|
||||
*outError = WUPSReent_ERROR_NO_THREAD;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto *head = static_cast<__wups_reent_node *>(wups_get_thread_specific(__WUPS_CONTEXT_THREAD_SPECIFIC_ID));
|
||||
|
||||
if (head == WUPS_REENT_ALLOC_SENTINEL) {
|
||||
*outError = WUPSReent_ERROR_GLOBAL_REENT_REQUESTED;
|
||||
return nullptr;
|
||||
}
|
||||
if (head && head->magic != WUPS_REENT_NODE_MAGIC) {
|
||||
*outError = WUPSReent_ERROR_GLOBAL_REENT_REQUESTED;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const __wups_reent_node *curr = head;
|
||||
while (curr) {
|
||||
if (curr->version >= 1 && curr->pluginId == pluginId) {
|
||||
return curr->reentPtr;
|
||||
}
|
||||
curr = curr->next;
|
||||
}
|
||||
|
||||
*outError = WUPSReent_ERROR_NONE;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void *wups_backend_set_sentinel() {
|
||||
auto *head = wups_get_thread_specific(__WUPS_CONTEXT_THREAD_SPECIFIC_ID);
|
||||
wups_set_thread_specific(__WUPS_CONTEXT_THREAD_SPECIFIC_ID, WUPS_REENT_ALLOC_SENTINEL);
|
||||
return head;
|
||||
}
|
||||
|
||||
void wups_backend_restore_head(void *oldHead) {
|
||||
wups_set_thread_specific(__WUPS_CONTEXT_THREAD_SPECIFIC_ID, oldHead);
|
||||
}
|
||||
|
||||
bool wups_backend_register_context(const void *pluginId, void *reentPtr, void (*cleanupFn)(void *), void *oldHeadVoid) {
|
||||
auto *oldHead = static_cast<__wups_reent_node *>(oldHeadVoid);
|
||||
|
||||
auto *newNode = static_cast<__wups_reent_node *>(malloc(sizeof(__wups_reent_node)));
|
||||
if (!newNode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
newNode->magic = WUPS_REENT_NODE_MAGIC;
|
||||
newNode->version = WUPS_REENT_NODE_VERSION;
|
||||
newNode->next = oldHead;
|
||||
newNode->pluginId = pluginId;
|
||||
newNode->reentPtr = reentPtr;
|
||||
newNode->cleanupFn = cleanupFn;
|
||||
newNode->savedCleanup = nullptr;
|
||||
|
||||
if (oldHead == nullptr || oldHead == WUPS_REENT_ALLOC_SENTINEL || oldHead->magic != WUPS_REENT_NODE_MAGIC || oldHead->version < WUPS_REENT_NODE_VERSION) {
|
||||
newNode->savedCleanup = OSSetThreadCleanupCallback(OSGetCurrentThread(), &__wups_thread_cleanup);
|
||||
} else {
|
||||
newNode->savedCleanup = oldHead->savedCleanup;
|
||||
oldHead->savedCleanup = nullptr;
|
||||
}
|
||||
|
||||
wups_set_thread_specific(__WUPS_CONTEXT_THREAD_SPECIFIC_ID, newNode);
|
||||
|
||||
{
|
||||
std::lock_guard lock(sGlobalNodesMutex);
|
||||
sGlobalNodes.push_back(newNode);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClearReentDataForPlugins(const std::vector<PluginContainer> &plugins) {
|
||||
auto *curThread = OSGetCurrentThread();
|
||||
|
||||
for (const auto &cur : plugins) {
|
||||
if (!cur.isLinkedAndLoaded()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur.getMetaInformation().getWUPSVersion() <= WUPSVersion(0, 9, 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto dataMemory = cur.getPluginLinkInformation().getDataMemory();
|
||||
const auto startAddress = reinterpret_cast<uint32_t>(dataMemory.data());
|
||||
const auto endAddress = reinterpret_cast<uint32_t>(dataMemory.data()) + dataMemory.size();
|
||||
|
||||
// Zero-allocation deferred free list
|
||||
__wups_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<__wups_reent_node *>(wups_get_thread_specific_ex(__WUPS_CONTEXT_THREAD_SPECIFIC_ID, t));
|
||||
|
||||
// Safety checks with Sentinel/Magic
|
||||
if (!head || head == WUPS_REENT_ALLOC_SENTINEL || head->magic != WUPS_REENT_NODE_MAGIC) {
|
||||
t = t->activeLink.next;
|
||||
continue;
|
||||
}
|
||||
|
||||
__wups_reent_node *prev = nullptr;
|
||||
auto *curr = head;
|
||||
|
||||
while (curr) {
|
||||
__wups_reent_node *next = curr->next;
|
||||
auto pluginIdAddr = reinterpret_cast<uint32_t>(curr->pluginId);
|
||||
|
||||
// plugin id lives in the .data/.bss section of a plugin
|
||||
if (pluginIdAddr >= startAddress && pluginIdAddr < endAddress) {
|
||||
// remove from linked list
|
||||
if (prev) {
|
||||
prev->next = next;
|
||||
} else {
|
||||
head = next;
|
||||
if (curr->savedCleanup) {
|
||||
if (head) {
|
||||
head->savedCleanup = curr->savedCleanup;
|
||||
} else {
|
||||
// No WUPS nodes left, mark for restoring.
|
||||
if (pendingRestores.size() == PENDING_RESTORES_SIZE) {
|
||||
OSFatal("WUPSBackend 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
|
||||
wups_set_thread_specific_ex(__WUPS_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 = wups_backend_set_sentinel();
|
||||
__wups_reent_node *nodeToFree = deferredFreeHead;
|
||||
while (nodeToFree) {
|
||||
__wups_reent_node *nextNode = nodeToFree->next;
|
||||
if (nodeToFree->cleanupFn) {
|
||||
nodeToFree->cleanupFn(nodeToFree->reentPtr);
|
||||
}
|
||||
free(nodeToFree);
|
||||
{
|
||||
std::lock_guard lock(sGlobalNodesMutex);
|
||||
std::erase(sGlobalNodes, nodeToFree);
|
||||
}
|
||||
nodeToFree = nextNode;
|
||||
}
|
||||
wups_backend_restore_head(oldHead);
|
||||
}
|
||||
}
|
||||
19
source/utils/reent.h
Normal file
19
source/utils/reent.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
#include <vector>
|
||||
#include <wups/reent_internal.h>
|
||||
|
||||
class PluginContainer;
|
||||
|
||||
void *wups_backend_get_context(const void *pluginId, wups_loader_init_reent_errors_t_ *outError);
|
||||
|
||||
void *wups_backend_set_sentinel();
|
||||
|
||||
void wups_backend_restore_head(void *oldHead);
|
||||
|
||||
bool wups_backend_register_context(const void *pluginId, void *reentPtr, void (*cleanupFn)(void *), void *oldHead);
|
||||
|
||||
void ClearReentDataForPlugins(const std::vector<PluginContainer> &plugins);
|
||||
|
||||
void MarkReentNodesForDeletion();
|
||||
|
||||
void ClearDanglingReentPtr();
|
||||
|
|
@ -339,6 +339,8 @@ const char *hookNameToString(const wups_loader_hook_type_t type) {
|
|||
return "WUPS_LOADER_HOOK_INIT_BUTTON_COMBO";
|
||||
case WUPS_LOADER_HOOK_INIT_WUT_THREAD:
|
||||
return "WUPS_LOADER_HOOK_INIT_WUT_THREAD";
|
||||
case WUPS_LOADER_HOOK_INIT_REENT_FUNCTIONS:
|
||||
return "WUPS_LOADER_HOOK_INIT_REENT_FUNCTIONS";
|
||||
}
|
||||
return "<UNKNOWN>";
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user