From b2ae19f57296c37920411b131d172d426236bdfd Mon Sep 17 00:00:00 2001 From: Maschell Date: Wed, 21 Jun 2023 18:16:12 +0200 Subject: [PATCH] Fix reent support --- include/wups/meta.h | 39 ++++++----- libraries/libwups/main.cpp | 37 ++++++++-- libraries/libwups/wups_reent.cpp | 81 ++++++++++++++++++++++ libraries/libwups/wups_reent.h | 3 + libraries/libwups/wups_thread_specific.cpp | 55 +++++++++++++++ libraries/libwups/wups_thread_specific.h | 18 +++++ 6 files changed, 210 insertions(+), 23 deletions(-) create mode 100644 libraries/libwups/wups_reent.cpp create mode 100644 libraries/libwups/wups_reent.h create mode 100644 libraries/libwups/wups_thread_specific.cpp create mode 100644 libraries/libwups/wups_thread_specific.h diff --git a/include/wups/meta.h b/include/wups/meta.h index a102fc8..1b0f994 100644 --- a/include/wups/meta.h +++ b/include/wups/meta.h @@ -38,23 +38,28 @@ extern "C" { #endif -#define WUPS_PLUGIN_NAME(x) \ - WUPS_META(name, x); \ - WUPS_META(wups, "0.7.1"); \ - WUPS_USE_WUT_MALLOC(); \ - WUPS_USE_WUT_SOCKETS(); \ - WUPS_USE_WUT_NEWLIB(); \ - WUPS_USE_WUT_STDCPP(); \ - WUPS___INIT_WRAPPER(); \ - WUPS___FINI_WRAPPER(); \ - __EXTERN_C_MACRO void abort(); \ - __EXTERN_C_MACRO void OSFatal(const char *msg); \ - void abort() { \ - OSFatal(x ": abort() called. Uncaught exception?"); \ - while (1) \ - ; \ - } \ - WUPS_META(buildtimestamp, __DATE__ " " __TIME__); +#define WUPS_VERSION_STR "0.7.1" +#define WUPS_PLUGIN_NAME(__plugin_name) \ + WUPS_META(name, __plugin_name); \ + WUPS_META(wups, WUPS_VERSION_STR); \ + WUPS_USE_WUT_MALLOC(); \ + WUPS_USE_WUT_SOCKETS(); \ + WUPS_USE_WUT_NEWLIB(); \ + WUPS_USE_WUT_STDCPP(); \ + WUPS___INIT_WRAPPER(); \ + WUPS___FINI_WRAPPER(); \ + __EXTERN_C_MACRO void abort(); \ + __EXTERN_C_MACRO void OSFatal(const char *msg); \ + void abort() { \ + OSFatal(__plugin_name ": abort() called. Uncaught exception?"); \ + while (1) \ + ; \ + } \ + WUPS_META(buildtimestamp, __DATE__ " " __TIME__); \ + extern const char wups_meta_info_dump[] WUPS_SECTION("meta"); \ + const char wups_meta_info_dump[] = "(plugin: " __plugin_name ";" \ + "wups " WUPS_VERSION_STR ";" \ + "buildtime: " __DATE__ " " __TIME__ ")" #define WUPS_PLUGIN_AUTHOR(x) WUPS_META(author, x) #define WUPS_PLUGIN_VERSION(x) WUPS_META(version, x) diff --git a/libraries/libwups/main.cpp b/libraries/libwups/main.cpp index 3b482cf..b788672 100644 --- a/libraries/libwups/main.cpp +++ b/libraries/libwups/main.cpp @@ -1,19 +1,26 @@ -extern "C" void OSFatal(const char *msg); +#include "wups_reent.h" +#include "wups_thread_specific.h" +#include +#include + +extern "C" void OSFatal(const char *); extern "C" void __wups_start() { OSFatal("This file needs to be run with the Wii U Plugin System."); } -extern __attribute__((weak)) void __wut_socket_init_devoptab(); +extern "C" __attribute__((weak)) void __wut_socket_init_devoptab(); -extern __attribute__((weak)) void __wut_socket_fini_devoptab(); +extern "C" __attribute__((weak)) void __wut_socket_fini_devoptab(); static int __wut_socket_devoptab_added = 0; -extern void socket_lib_init(); +extern "C" __attribute__((weak)) void socket_lib_init(); + +extern "C" void __attribute__((weak)) __init_wut_socket(); void __attribute__((weak)) __init_wut_socket() { - if (!&__wut_socket_init_devoptab) return; + if (!&__wut_socket_init_devoptab || !&socket_lib_init) return; if (!__wut_socket_devoptab_added) { socket_lib_init(); __wut_socket_init_devoptab(); @@ -21,10 +28,28 @@ void __attribute__((weak)) __init_wut_socket() { } } +extern "C" void __attribute__((weak)) __fini_wut_socket(); + void __attribute__((weak)) __fini_wut_socket() { if (!&__wut_socket_init_devoptab || !&__wut_socket_fini_devoptab) return; if (__wut_socket_devoptab_added) { __wut_socket_fini_devoptab(); __wut_socket_devoptab_added = 0; } -} \ No newline at end of file +} + +extern "C" struct _reent *__getreent(void) { + return __wups_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 wups_set_thread_specific(id, value); +} + +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 wups_get_thread_specific(id); +} diff --git a/libraries/libwups/wups_reent.cpp b/libraries/libwups/wups_reent.cpp new file mode 100644 index 0000000..eac7013 --- /dev/null +++ b/libraries/libwups/wups_reent.cpp @@ -0,0 +1,81 @@ +#include "wups_reent.h" +#include "wups_thread_specific.h" +#include +#include +#include + +#define __WUPS_CONTEXT_THREAD_SPECIFIC_ID WUT_THREAD_SPECIFIC_1 + +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 wups_meta_info_dump[]; + +extern "C" void OSFatal(const char *); +extern "C" void OSReport(const char *, ...); + +extern "C" OSThread *OSGetCurrentThread(); + +typedef void (*OSThreadCleanupCallbackFn)(OSThread *thread, void *stack); + +extern "C" OSThreadCleanupCallbackFn +OSSetThreadCleanupCallback(OSThread *thread, + OSThreadCleanupCallbackFn callback); + +struct __wups_thread_context { + struct _reent reent; + OSThreadCleanupCallbackFn savedCleanup; +}; + +static void +__wups_thread_cleanup(OSThread *thread, + void *stack) { + struct __wups_thread_context *context; + + context = (struct __wups_thread_context *) wut_get_thread_specific(__WUPS_CONTEXT_THREAD_SPECIFIC_ID); + if (!context || &context->reent == _GLOBAL_REENT) { + OSReport("[%s] __wups_thread_cleanup: Context was NULL or reent was global\n", wups_meta_info_dump); + OSFatal("__wups_thread_cleanup: Context was NULL or reent was global"); + } + + if (context->savedCleanup) { + context->savedCleanup(thread, stack); + } + + _reclaim_reent(&context->reent); + + // Use global reent during free since the current reent is getting freed + wut_set_thread_specific(__WUPS_CONTEXT_THREAD_SPECIFIC_ID, _GLOBAL_REENT); + + free(context); + + wut_set_thread_specific(__WUPS_CONTEXT_THREAD_SPECIFIC_ID, NULL); +} + +struct _reent * +__wups_getreent(void) { + struct __wups_thread_context *context; + + context = (struct __wups_thread_context *) wut_get_thread_specific(__WUPS_CONTEXT_THREAD_SPECIFIC_ID); + if (!context) { + // Temporarily use global reent during context allocation + wut_set_thread_specific(__WUPS_CONTEXT_THREAD_SPECIFIC_ID, _GLOBAL_REENT); + + context = (struct __wups_thread_context *) malloc(sizeof(*context)); + if (!context) { + OSReport("[%s] __wups_getreent: Failed to allocate reent context\n", wups_meta_info_dump); + OSFatal("__wups_getreent: Failed to allocate reent context"); + wut_set_thread_specific(__WUPS_CONTEXT_THREAD_SPECIFIC_ID, NULL); + return NULL; + } + + _REENT_INIT_PTR(&context->reent); + context->savedCleanup = OSSetThreadCleanupCallback(OSGetCurrentThread(), &__wups_thread_cleanup); + + wut_set_thread_specific(__WUPS_CONTEXT_THREAD_SPECIFIC_ID, context); + } + + return &context->reent; +} diff --git a/libraries/libwups/wups_reent.h b/libraries/libwups/wups_reent.h new file mode 100644 index 0000000..efd1742 --- /dev/null +++ b/libraries/libwups/wups_reent.h @@ -0,0 +1,3 @@ +#pragma once + +struct _reent *__wups_getreent(); \ No newline at end of file diff --git a/libraries/libwups/wups_thread_specific.cpp b/libraries/libwups/wups_thread_specific.cpp new file mode 100644 index 0000000..52da322 --- /dev/null +++ b/libraries/libwups/wups_thread_specific.cpp @@ -0,0 +1,55 @@ +#include "wups_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 wups_meta_info_dump[]; + +void wups_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[3] = value; + } else if (id == WUT_THREAD_SPECIFIC_1) { + thread->reserved[4] = value; + } else { + OSReport("[%s] wups_set_thread_specific: invalid id\n", wups_meta_info_dump); + OSFatal("wups_set_thread_specific: invalid id"); + } + } else { + OSReport("[%s] wups_set_thread_specific: invalid thread\n", wups_meta_info_dump); + OSFatal("wups_set_thread_specific: invalid thread"); + } +} + +void *wups_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[3]; + } else if (id == WUT_THREAD_SPECIFIC_1) { + return thread->reserved[4]; + } else { + OSReport("[%s] wups_get_thread_specific: invalid id\n", wups_meta_info_dump); + OSFatal("wups_get_thread_specific: invalid id"); + } + } else { + OSReport("[%s] wups_get_thread_specific: invalid thread\n", wups_meta_info_dump); + OSFatal("wups_get_thread_specific: invalid thread\n"); + } + return nullptr; +} \ No newline at end of file diff --git a/libraries/libwups/wups_thread_specific.h b/libraries/libwups/wups_thread_specific.h new file mode 100644 index 0000000..d534180 --- /dev/null +++ b/libraries/libwups/wups_thread_specific.h @@ -0,0 +1,18 @@ +#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 wups_set_thread_specific(__wut_thread_specific_id id, void *value); + +void *wups_get_thread_specific(__wut_thread_specific_id id); + +#ifdef __cplusplus +} +#endif \ No newline at end of file