Repository navigation
Expand file tree
/
Copy pathExport.cpp
More file actions
133 lines (112 loc) · 3.91 KB
/
Copy pathExport.cpp
File metadata and controls
133 lines (112 loc) · 3.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#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;
}
}