AnyPS5/core/libs/prx/libSceLibcInternal/Export.cpp

140 lines
4.1 KiB
C++

#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
#include "prx/libc/include/HeapDiagnostics.hpp"
#include "prx/libc/include/HostThreadLocal.hpp"
#include <cstddef>
#include <cstdint>
#include <sstream>
#include <stdexcept>
#include <vector>
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif
extern "C" void APS5_VABI sceKernelSetThreadDtors(thread_dtors_func_t dtors);
extern "C" int APS5_VABI sceKernelGetModuleInfoFromAddr(std::uint64_t address, int flags, ModuleInfoEx* info);
namespace {
using ThreadDestructorFunction = void (APS5_VABI*)(void*);
struct ThreadDestructor {
ThreadDestructorFunction function;
void* object;
void* dsoSymbol;
};
struct ThreadDestructorsTag {};
std::vector<ThreadDestructor>& ThreadDestructors() {
return HostThreadLocal<std::vector<ThreadDestructor>, ThreadDestructorsTag>();
}
bool IsInLoadedImage(const void* address) {
if (address == nullptr)
return false;
#ifdef _WIN32
HMODULE module = nullptr;
return GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, static_cast<LPCSTR>(address), &module) != 0;
#else
Dl_info info{};
return dladdr(address, &info) != 0;
#endif
}
void CallThreadDestructor(const ThreadDestructor& destructor) {
const auto* function = reinterpret_cast<const void*>(destructor.function);
if (!IsInLoadedImage(function)) {
std::ostringstream message;
message << "thread_local destructor " << function << " of dso " << destructor.dsoSymbol << " is not in a loaded image";
throw std::runtime_error(message.str());
}
destructor.function(destructor.object);
}
void APS5_VABI RunThreadDestructors_nid_no_patch() {
auto& destructors = ThreadDestructors();
while (!destructors.empty()) {
const ThreadDestructor destructor = destructors.back();
destructors.pop_back();
CallThreadDestructor(destructor);
}
}
bool FindDsoModule(const void* dsoSymbol, KernelModule& handle) {
ModuleInfoEx info{};
info.st_size = sizeof(ModuleInfoEx);
if (sceKernelGetModuleInfoFromAddr(reinterpret_cast<std::uintptr_t>(dsoSymbol), 2, &info) != 0) {
handle = 0;
return false;
}
handle = info.id;
return true;
}
bool ForceThreadDestructorPass(KernelModule handle) {
auto& destructors = ThreadDestructors();
bool found = false;
for (std::size_t index = destructors.size(); index-- > 0;) {
const ThreadDestructor destructor = destructors[index];
KernelModule module = 0;
const bool loaded = FindDsoModule(destructor.dsoSymbol, module);
if (module != handle)
continue;
found = true;
destructors.erase(destructors.begin() + static_cast<std::ptrdiff_t>(index));
if (loaded && *static_cast<void* const*>(destructor.dsoSymbol) == destructor.dsoSymbol)
CallThreadDestructor(destructor);
}
return found;
}
void RegisterThreadExitHook() {
[[maybe_unused]] static const bool registered = [] {
sceKernelSetThreadDtors(RunThreadDestructors_nid_no_patch);
return true;
}();
}
}
extern "C" {
int Need_sceLibcInternal_nid_postfix = 1;
void APS5_VABI __cxa_finalize_nid_postfix(void* dsoHandle) {
CxaFinalize_nid_no_patch(dsoHandle);
}
void APS5_VABI sceLibcHeapGetTraceInfo_nid_postfix(Info* info) {
LibcHeapTraceInfo_nid_no_patch(info);
}
int APS5_VABI _sceLibcInternalThreadAtexit_nid_postfix(ThreadDestructorFunction destructor, void* object, void* dsoSymbol) {
RegisterThreadExitHook();
ThreadDestructors().push_back({destructor, object, dsoSymbol});
return 0;
}
void APS5_VABI _sceLibcInternalThreadDtors_nid_postfix() {
RunThreadDestructors_nid_no_patch();
}
int APS5_VABI _sceLibcInternalForceTlsDestructor_nid_postfix(KernelModule handle) {
for (int pass = 0; pass < 4; ++pass) {
if (!ForceThreadDestructorPass(handle))
break;
}
return 0;
}
unsigned int APS5_VABI GuestAlarm_nid_no_patch(unsigned int seconds);
unsigned int APS5_VABI alarm_nid_postfix(unsigned int seconds) {
return GuestAlarm_nid_no_patch(seconds);
}
}