Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)/$@ \
Expand Down
8 changes: 4 additions & 4 deletions include/wums/defines/module_defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ typedef struct module_information_single_t {
uint32_t number_export_entries;
hook_data_t * hook_entries;
uint32_t number_hook_entries;
uint32_t bssAddr;
uint32_t bssSize;
uint32_t sbssAddr;
uint32_t sbssSize;
uint32_t bssAddr; // deprecated
uint32_t bssSize; // deprecated
uint32_t sbssAddr; // deprecated
uint32_t sbssSize; // deprecated
uint32_t startAddress;
uint32_t endAddress;
uint32_t entrypoint;
Expand Down
34 changes: 34 additions & 0 deletions include/wums/wums_debug.h
Original file line number Diff line number Diff line change
@@ -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__)
177 changes: 137 additions & 40 deletions libraries/libwums/wums_reent.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
#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)

typedef uint32_t OSThread;
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);

extern const char wums_meta_info_dump[];
typedef uint32_t OSThread;

extern "C" void OSFatal(const char *);
extern "C" void OSReport(const char *, ...);
Expand All @@ -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);

if (node->reentPtr) {
_reclaim_reent(static_cast<_reent *>(node->reentPtr));
free(node->reentPtr);
}
free(node);
}

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));

if (!head || head == WUMS_REENT_ALLOC_SENTINEL) {
return;
}

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 (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;
}

if (context->savedCleanup) {
context->savedCleanup(thread, stack);
WUMS_DEBUG_REPORT("__wums_thread_cleanup: Triggered for thread %p\n", thread);

OSThreadCleanupCallbackFn savedCleanup = nullptr;
if (head->version >= 1) {
savedCleanup = head->savedCleanup;
}

_reclaim_reent(&context->reent);
// 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);
}

// Use global reent during free since the current reent is getting freed
wums_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, _GLOBAL_REENT);
curr = next;
}

free(context);
wut_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, nullptr);

wums_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, NULL);
if (savedCleanup) {
WUMS_DEBUG_REPORT("__wums_thread_cleanup: Chaining to saved cleanup for thread %p\n", thread);
savedCleanup(thread, stack);
}
}

struct _reent *
__wums_getreent(void) {
struct __wums_thread_context *context;

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);

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;
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());

_REENT_INIT_PTR(&context->reent);
context->savedCleanup = OSSetThreadCleanupCallback(OSGetCurrentThread(), &__wums_thread_cleanup);
// 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);

wums_set_thread_specific(__WUMS_CONTEXT_THREAD_SPECIFIC_ID, context);
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;
}

return &context->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;
}
8 changes: 4 additions & 4 deletions libraries/libwums/wums_thread_specific.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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 {
Expand Down
Loading