3315 lines
105 KiB
C++
3315 lines
105 KiB
C++
#include "common/abi.h"
|
|
#include "common/assert.h"
|
|
#include "common/common.h"
|
|
#include "common/emulatorConfig.h"
|
|
#include "common/file.h"
|
|
#include "common/logging/log.h"
|
|
#include "common/singleton.h"
|
|
#include "common/stringUtils.h"
|
|
#include "common/threads.h"
|
|
#include "kernel/eventFlag.h"
|
|
#include "kernel/eventQueue.h"
|
|
#include "kernel/fileSystem.h"
|
|
#include "kernel/memory.h"
|
|
#include "kernel/pthread.h"
|
|
#include "kernel/semaphore.h"
|
|
#include "kernel/syncOnAddress.h"
|
|
#include "libs/errno.h"
|
|
#include "libs/libs.h"
|
|
#include "libs/network.h"
|
|
#include "loader/elf.h"
|
|
#include "loader/runtimeLinker.h"
|
|
#include "loader/symbolDatabase.h"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <cstddef>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <random>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <vector>
|
|
|
|
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
|
#ifndef NOMINMAX
|
|
#define NOMINMAX
|
|
#endif
|
|
#include <windows.h>
|
|
#elif defined(__APPLE__)
|
|
#include <csignal>
|
|
#include <sys/ucontext.h>
|
|
#else
|
|
#include <csignal>
|
|
#include <ucontext.h>
|
|
#endif
|
|
|
|
namespace Libs {
|
|
|
|
namespace LibC {
|
|
int GetArgc();
|
|
const char** GetArgv();
|
|
} // namespace LibC
|
|
|
|
LIB_VERSION("libkernel", 1, "libkernel", 1, 1);
|
|
|
|
namespace LibKernelApr {
|
|
LIB_DEFINE(InitLibKernel_1_Apr);
|
|
} // namespace LibKernelApr
|
|
|
|
namespace LibKernel {
|
|
|
|
using KernelModule = int32_t;
|
|
using get_thread_atexit_count_func_t = KYTY_SYSV_ABI int (*)(KernelModule);
|
|
using thread_atexit_report_func_t = KYTY_SYSV_ABI void (*)(KernelModule);
|
|
|
|
static uint32_t sha1_rol(uint32_t value, uint32_t bits) {
|
|
return (value << bits) | (value >> (32u - bits));
|
|
}
|
|
|
|
static std::array<uint8_t, 20> sha1_digest(const uint8_t* data, size_t size) {
|
|
uint32_t h0 = 0x67452301u;
|
|
uint32_t h1 = 0xefcdab89u;
|
|
uint32_t h2 = 0x98badcfeu;
|
|
uint32_t h3 = 0x10325476u;
|
|
uint32_t h4 = 0xc3d2e1f0u;
|
|
|
|
std::vector<uint8_t> msg(data, data + size);
|
|
const uint64_t bit_len = static_cast<uint64_t>(size) * 8u;
|
|
|
|
msg.push_back(0x80u);
|
|
while ((msg.size() % 64u) != 56u) {
|
|
msg.push_back(0u);
|
|
}
|
|
for (int i = 7; i >= 0; i--) {
|
|
msg.push_back(static_cast<uint8_t>((bit_len >> (i * 8)) & 0xffu));
|
|
}
|
|
|
|
for (size_t offset = 0; offset < msg.size(); offset += 64u) {
|
|
uint32_t w[80] = {};
|
|
for (int i = 0; i < 16; i++) {
|
|
const auto j = offset + static_cast<size_t>(i) * 4u;
|
|
w[i] = (static_cast<uint32_t>(msg[j + 0]) << 24u) |
|
|
(static_cast<uint32_t>(msg[j + 1]) << 16u) |
|
|
(static_cast<uint32_t>(msg[j + 2]) << 8u) | static_cast<uint32_t>(msg[j + 3]);
|
|
}
|
|
for (int i = 16; i < 80; i++) {
|
|
w[i] = sha1_rol(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
|
|
}
|
|
|
|
uint32_t a = h0;
|
|
uint32_t b = h1;
|
|
uint32_t c = h2;
|
|
uint32_t d = h3;
|
|
uint32_t e = h4;
|
|
|
|
for (int i = 0; i < 80; i++) {
|
|
uint32_t f = 0;
|
|
uint32_t k = 0;
|
|
if (i < 20) {
|
|
f = (b & c) | ((~b) & d);
|
|
k = 0x5a827999u;
|
|
} else if (i < 40) {
|
|
f = b ^ c ^ d;
|
|
k = 0x6ed9eba1u;
|
|
} else if (i < 60) {
|
|
f = (b & c) | (b & d) | (c & d);
|
|
k = 0x8f1bbcdcu;
|
|
} else {
|
|
f = b ^ c ^ d;
|
|
k = 0xca62c1d6u;
|
|
}
|
|
|
|
const auto temp = sha1_rol(a, 5) + f + e + k + w[i];
|
|
e = d;
|
|
d = c;
|
|
c = sha1_rol(b, 30);
|
|
b = a;
|
|
a = temp;
|
|
}
|
|
|
|
h0 += a;
|
|
h1 += b;
|
|
h2 += c;
|
|
h3 += d;
|
|
h4 += e;
|
|
}
|
|
|
|
std::array<uint8_t, 20> digest = {};
|
|
const uint32_t h[5] = {h0, h1, h2, h3, h4};
|
|
for (int i = 0; i < 5; i++) {
|
|
digest[static_cast<size_t>(i) * 4u + 0u] = static_cast<uint8_t>((h[i] >> 24u) & 0xffu);
|
|
digest[static_cast<size_t>(i) * 4u + 1u] = static_cast<uint8_t>((h[i] >> 16u) & 0xffu);
|
|
digest[static_cast<size_t>(i) * 4u + 2u] = static_cast<uint8_t>((h[i] >> 8u) & 0xffu);
|
|
digest[static_cast<size_t>(i) * 4u + 3u] = static_cast<uint8_t>(h[i] & 0xffu);
|
|
}
|
|
return digest;
|
|
}
|
|
|
|
static std::string kernel_symbol_to_nid(const char* symbol) {
|
|
static constexpr std::array<uint8_t, 16> salt = {0x51, 0x8d, 0x64, 0xa6, 0x35, 0xde,
|
|
0xd8, 0xc1, 0xe6, 0xb0, 0x39, 0xb1,
|
|
0xc3, 0xe5, 0x52, 0x30};
|
|
static constexpr char codes[] =
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
|
|
|
|
const auto symbol_size = std::strlen(symbol);
|
|
std::vector<uint8_t> input(symbol_size + salt.size());
|
|
std::memcpy(input.data(), symbol, symbol_size);
|
|
std::memcpy(input.data() + symbol_size, salt.data(), salt.size());
|
|
|
|
const auto hash = sha1_digest(input.data(), input.size());
|
|
|
|
uint64_t digest = 0;
|
|
std::memcpy(&digest, hash.data(), sizeof(digest));
|
|
|
|
char nid[12] = {};
|
|
for (int i = 0; i < 10; i++) {
|
|
nid[i] = codes[(digest >> (58 - i * 6)) & 0x3fu];
|
|
}
|
|
nid[10] = codes[(digest & 0xfu) * 4u];
|
|
return std::string(nid);
|
|
}
|
|
|
|
static const Loader::SymbolRecord* kernel_find_export_symbol(const Loader::SymbolDatabase* symbols,
|
|
const char* symbol) {
|
|
EXIT_IF(symbols == nullptr);
|
|
EXIT_IF(symbol == nullptr);
|
|
|
|
const auto nid = kernel_symbol_to_nid(symbol);
|
|
|
|
if (const auto* record = symbols->FindByNid(nid, Loader::SymbolType::Func); record != nullptr) {
|
|
return record;
|
|
}
|
|
if (const auto* record = symbols->FindByNid(nid, Loader::SymbolType::Object);
|
|
record != nullptr) {
|
|
return record;
|
|
}
|
|
if (const auto* record = symbols->FindByNid(nid, Loader::SymbolType::NoType);
|
|
record != nullptr) {
|
|
return record;
|
|
}
|
|
|
|
const auto symbol_name = std::string(symbol);
|
|
if (const auto* record = symbols->FindByName(symbol_name, Loader::SymbolType::Func);
|
|
record != nullptr) {
|
|
return record;
|
|
}
|
|
if (const auto* record = symbols->FindByName(symbol_name, Loader::SymbolType::Object);
|
|
record != nullptr) {
|
|
return record;
|
|
}
|
|
return symbols->FindByName(symbol_name, Loader::SymbolType::NoType);
|
|
}
|
|
|
|
static void* KYTY_SYSV_ABI KernelApplicationHeapGetMem(uint64_t alignment, uint64_t size) {
|
|
static std::atomic_uint32_t log_count = 0;
|
|
|
|
if (alignment < 0x10u) {
|
|
alignment = 0x10u;
|
|
}
|
|
if ((alignment & (alignment - 1u)) != 0u) {
|
|
return nullptr;
|
|
}
|
|
|
|
auto* rt = Common::Singleton<Loader::RuntimeLinker>::Instance();
|
|
void* ptr = rt->ApplicationHeapMemalign(alignment, size);
|
|
|
|
const auto index = log_count.fetch_add(1, std::memory_order_relaxed);
|
|
if (index < 8 || (index % 600) == 0) {
|
|
LOGF("\t KernelApplicationHeapGetMem alignment = 0x%016" PRIx64 ", size = 0x%016" PRIx64
|
|
", ptr = 0x%016" PRIx64 "\n",
|
|
alignment, size, reinterpret_cast<uint64_t>(ptr));
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI KernelDlsym(KernelModule handle, const char* symbol, void** addr) {
|
|
static std::atomic_uint32_t log_count = 0;
|
|
const auto index = log_count.fetch_add(1, std::memory_order_relaxed);
|
|
if (index < 32 || (index % 600) == 0) {
|
|
PRINT_NAME();
|
|
LOGF("\t call_count = %" PRIu32 ", handle = %" PRId32 ", symbol = %s\n", index + 1, handle,
|
|
symbol != nullptr ? symbol : "(null)");
|
|
}
|
|
|
|
if (addr == nullptr) {
|
|
return KERNEL_ERROR_EFAULT;
|
|
}
|
|
|
|
if (symbol == nullptr) {
|
|
*addr = nullptr;
|
|
return KERNEL_ERROR_EFAULT;
|
|
}
|
|
|
|
auto* rt = Common::Singleton<Loader::RuntimeLinker>::Instance();
|
|
auto* program = rt->FindProgramById(handle);
|
|
if (program == nullptr || program->export_symbols == nullptr) {
|
|
*addr = nullptr;
|
|
LOGF("\t unresolved dlsym: handle = %" PRId32 ", symbol = %s, reason = module not found\n",
|
|
handle, symbol);
|
|
return KERNEL_ERROR_ESRCH;
|
|
}
|
|
|
|
const auto* record = kernel_find_export_symbol(program->export_symbols.get(), symbol);
|
|
if (record != nullptr) {
|
|
*addr = reinterpret_cast<void*>(record->vaddr);
|
|
if (index < 32 || (index % 600) == 0) {
|
|
LOGF("\t resolved = 0x%016" PRIx64 " (%s)\n", record->vaddr, record->name.c_str());
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
if (handle == 0 && std::strcmp(symbol, "scriptingGetMem") == 0) {
|
|
*addr = reinterpret_cast<void*>(&KernelApplicationHeapGetMem);
|
|
if (index < 32 || (index % 600) == 0) {
|
|
LOGF("\t resolved = 0x%016" PRIx64 " (application heap scriptingGetMem)\n",
|
|
reinterpret_cast<uint64_t>(*addr));
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
*addr = nullptr;
|
|
LOGF("\t unresolved dlsym: handle = %" PRId32 ", module = %s, symbol = %s\n", handle,
|
|
Common::PathToString(program->file_name).c_str(), symbol);
|
|
return KERNEL_ERROR_ESRCH;
|
|
}
|
|
|
|
#pragma pack(1)
|
|
|
|
struct KernelLoadModuleOpt {
|
|
size_t size;
|
|
};
|
|
|
|
struct KernelUnloadModuleOpt {
|
|
size_t size;
|
|
};
|
|
|
|
struct TlsInfo {
|
|
Loader::Program* program;
|
|
uint64_t offset;
|
|
};
|
|
|
|
struct MallocReplace {
|
|
uint64_t size = sizeof(MallocReplace);
|
|
void* malloc_initialize = nullptr;
|
|
void* malloc_finalize = nullptr;
|
|
void* malloc = nullptr;
|
|
void* free = nullptr;
|
|
void* calloc = nullptr;
|
|
void* realloc = nullptr;
|
|
void* memalign = nullptr;
|
|
void* reallocalign = nullptr;
|
|
void* posix_memalign = nullptr;
|
|
void* malloc_stats = nullptr;
|
|
void* malloc_stats_fast = nullptr;
|
|
void* malloc_usable_size = nullptr;
|
|
void* aligned_alloc = nullptr;
|
|
};
|
|
|
|
struct NewReplace {
|
|
uint64_t size = sizeof(NewReplace);
|
|
void* new_p = nullptr;
|
|
void* new_nothrow = nullptr;
|
|
void* new_array = nullptr;
|
|
void* new_array_nothrow = nullptr;
|
|
void* delete_p = nullptr;
|
|
void* delete_nothrow = nullptr;
|
|
void* delete_array = nullptr;
|
|
void* delete_array_nothrow = nullptr;
|
|
void* delete_with_size = nullptr;
|
|
void* delete_with_size_nothrow = nullptr;
|
|
void* delete_array_with_size = nullptr;
|
|
void* delete_array_with_size_nothrow = nullptr;
|
|
};
|
|
|
|
struct ModuleInfo {
|
|
uint64_t size;
|
|
uint64_t info[32];
|
|
KernelModule handle;
|
|
uint8_t pad[156];
|
|
};
|
|
|
|
struct ModuleInfoForUnwind {
|
|
uint64_t st_size;
|
|
char name[256];
|
|
uint64_t eh_frame_hdr_addr;
|
|
uint64_t eh_frame_addr;
|
|
uint64_t eh_frame_size;
|
|
uint64_t seg0_addr;
|
|
uint64_t seg0_size;
|
|
};
|
|
|
|
static_assert(sizeof(ModuleInfoForUnwind) == 304);
|
|
|
|
#pragma pack()
|
|
|
|
constexpr size_t PROGNAME_MAX_SIZE = 511;
|
|
|
|
static uint64_t g_stack_chk_guard = 0xDeadBeef00000007;
|
|
static char g_progname_buf[PROGNAME_MAX_SIZE + 1] = {0};
|
|
static const char* g_progname = g_progname_buf;
|
|
static std::atomic_uint64_t g_gpo_state_bits {0};
|
|
|
|
static get_thread_atexit_count_func_t g_get_thread_atexit_count_func = nullptr;
|
|
static thread_atexit_report_func_t g_thread_atexit_report_func = nullptr;
|
|
|
|
void SetProgName(const std::string& name) {
|
|
strncpy(g_progname_buf, name.c_str(), PROGNAME_MAX_SIZE);
|
|
}
|
|
|
|
static KYTY_SYSV_ABI int* get_error_addr() {
|
|
return Posix::GetErrorAddr();
|
|
}
|
|
|
|
static KYTY_SYSV_ABI int getargc() {
|
|
PRINT_NAME();
|
|
|
|
return LibC::GetArgc();
|
|
}
|
|
|
|
static KYTY_SYSV_ABI const char** getargv() {
|
|
PRINT_NAME();
|
|
|
|
return LibC::GetArgv();
|
|
}
|
|
|
|
static void dump_stack_chk_fail_context(uint64_t caller, uint64_t rsp) {
|
|
LOGF("stack_chk_fail caller = 0x%016" PRIx64 "\n", caller);
|
|
LOGF("stack_chk_fail guard = 0x%016" PRIx64 " @ 0x%016" PRIx64 "\n", g_stack_chk_guard,
|
|
reinterpret_cast<uint64_t>(&g_stack_chk_guard));
|
|
|
|
auto* linker = Common::Singleton<Loader::RuntimeLinker>::Instance();
|
|
auto* program = linker->FindProgramByAddr(caller);
|
|
|
|
LOGF("stack_chk_fail rsp = 0x%016" PRIx64 "\n"
|
|
"stack_chk_fail caller/saved-guard scan:\n",
|
|
rsp);
|
|
for (int i = -32; i < 512; i++) {
|
|
auto addr = rsp + static_cast<int64_t>(i) * 8;
|
|
auto value = *reinterpret_cast<uint64_t*>(addr);
|
|
|
|
if (value == caller) {
|
|
auto saved_guard_addr = addr + 0x4a8;
|
|
auto saved_guard = *reinterpret_cast<uint64_t*>(saved_guard_addr);
|
|
LOGF(" return addr at 0x%016" PRIx64
|
|
"; saved guard candidate [ret+0x4a8] = 0x%016" PRIx64 " -> 0x%016" PRIx64 "\n",
|
|
addr, saved_guard_addr, saved_guard);
|
|
}
|
|
|
|
if (value == g_stack_chk_guard) {
|
|
LOGF(" guard value found at stack 0x%016" PRIx64 "\n", addr);
|
|
}
|
|
}
|
|
|
|
LOGF("stack_chk_fail stack qwords:\n");
|
|
for (int i = -4; i < 20; i++) {
|
|
auto addr = rsp + static_cast<int64_t>(i) * 8;
|
|
auto value = *reinterpret_cast<uint64_t*>(addr);
|
|
auto* p = linker->FindProgramByAddr(value);
|
|
auto module_name =
|
|
(p == nullptr ? std::string() : Common::PathToString(p->file_name.filename()));
|
|
LOGF(" [%+03d] 0x%016" PRIx64 " : 0x%016" PRIx64 " %s\n", i, addr, value,
|
|
module_name.c_str());
|
|
}
|
|
|
|
if (program == nullptr) {
|
|
LOGF("stack_chk_fail caller module = <unknown>\n");
|
|
return;
|
|
}
|
|
|
|
auto caller_module_name = Common::PathToString(program->file_name.filename());
|
|
LOGF("stack_chk_fail caller module = %s, base = 0x%016" PRIx64 ", offset = 0x%016" PRIx64 "\n",
|
|
caller_module_name.c_str(), program->base_vaddr, caller - program->base_vaddr);
|
|
|
|
auto read_byte = [program](uint64_t vaddr) {
|
|
auto aligned = vaddr & ~0x07ull;
|
|
uint64_t value = Loader::RuntimeLinker::ReadFromElf(program, aligned);
|
|
return static_cast<uint8_t>((value >> ((vaddr - aligned) * 8u)) & 0xffu);
|
|
};
|
|
|
|
if (caller >= 5 && read_byte(caller - 5) == 0xe8) {
|
|
uint32_t rel = 0;
|
|
for (uint32_t i = 0; i < 4; i++) {
|
|
rel |= static_cast<uint32_t>(read_byte(caller - 4 + i)) << (i * 8u);
|
|
}
|
|
auto target = caller + static_cast<int32_t>(rel);
|
|
LOGF("stack_chk_fail preceding call = rel32 at 0x%016" PRIx64 ", target = 0x%016" PRIx64
|
|
"\n",
|
|
caller - 5, target);
|
|
} else if (caller >= 6 && read_byte(caller - 6) == 0xff &&
|
|
(read_byte(caller - 5) & 0x38u) == 0x10) {
|
|
LOGF("stack_chk_fail preceding call = indirect at 0x%016" PRIx64 "\n", caller - 6);
|
|
}
|
|
|
|
auto dump_start = (caller >= 64 ? caller - 64 : 0);
|
|
auto dump_end = caller + 32;
|
|
|
|
LOGF("stack_chk_fail caller bytes from ELF:\n");
|
|
for (auto addr = dump_start; addr < dump_end; addr += 16) {
|
|
LOGF(" 0x%016" PRIx64 ":", addr);
|
|
for (uint32_t i = 0; i < 16; i++) {
|
|
LOGF("%c%02" PRIx8, (addr + i == caller ? '>' : ' '), read_byte(addr + i));
|
|
}
|
|
LOGF("\n");
|
|
}
|
|
}
|
|
|
|
static KYTY_SYSV_ABI void stack_chk_fail() {
|
|
PRINT_NAME();
|
|
|
|
uint64_t rsp = 0;
|
|
asm volatile("movq %%rsp, %0" : "=r"(rsp));
|
|
dump_stack_chk_fail_context(reinterpret_cast<uint64_t>(__builtin_return_address(0)), rsp);
|
|
|
|
EXIT("stack fail!!!");
|
|
}
|
|
|
|
static KYTY_SYSV_ABI int sigprocmask(int /*how*/, const void* /*set*/, void* /*oset*/) {
|
|
// PRINT_NAME();
|
|
|
|
// LOGF("\t how = %d\n", how);
|
|
// LOGF("\t set = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(set));
|
|
// LOGF("\t oset = %016" PRIx64 "\n", reinterpret_cast<uint64_t>(oset));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool IsAllowedExceptionSignal(int signum) {
|
|
constexpr int POSIX_SIGHUP = 1;
|
|
constexpr int POSIX_SIGILL = 4;
|
|
constexpr int POSIX_SIGFPE = 8;
|
|
constexpr int POSIX_SIGBUS = 10;
|
|
constexpr int POSIX_SIGSEGV = 11;
|
|
constexpr int POSIX_SIGUSR1 = 30;
|
|
|
|
return signum == POSIX_SIGHUP || signum == POSIX_SIGILL || signum == POSIX_SIGFPE ||
|
|
signum == POSIX_SIGBUS || signum == POSIX_SIGSEGV || signum == POSIX_SIGUSR1;
|
|
}
|
|
|
|
static void* g_exception_handlers[130] = {};
|
|
|
|
using exception_handler_func_t = KYTY_SYSV_ABI void (*)(int, void*);
|
|
|
|
static thread_local bool g_dispatching_signal_handler = false;
|
|
|
|
struct SignalDispatchScope {
|
|
SignalDispatchScope(): previous(g_dispatching_signal_handler) {
|
|
g_dispatching_signal_handler = true;
|
|
}
|
|
~SignalDispatchScope() { g_dispatching_signal_handler = previous; }
|
|
|
|
bool previous;
|
|
};
|
|
|
|
bool KernelIsDispatchingSignalOnCurrentThread() {
|
|
return g_dispatching_signal_handler;
|
|
}
|
|
|
|
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
|
static void WaitForSignalDispatch(Pthread thread, int signum) {
|
|
constexpr auto DISPATCH_WAIT_STEP = std::chrono::microseconds(1000);
|
|
constexpr auto DISPATCH_WAIT_MAX = std::chrono::milliseconds(2);
|
|
|
|
auto waited = std::chrono::microseconds(0);
|
|
while (PthreadHasPendingSignal(thread, signum) && waited < DISPATCH_WAIT_MAX) {
|
|
Common::Thread::SleepMicro(DISPATCH_WAIT_STEP.count());
|
|
waited += DISPATCH_WAIT_STEP;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
struct SignalMcontext {
|
|
uint64_t mc_onstack;
|
|
uint64_t mc_rdi;
|
|
uint64_t mc_rsi;
|
|
uint64_t mc_rdx;
|
|
uint64_t mc_rcx;
|
|
uint64_t mc_r8;
|
|
uint64_t mc_r9;
|
|
uint64_t mc_rax;
|
|
uint64_t mc_rbx;
|
|
uint64_t mc_rbp;
|
|
uint64_t mc_r10;
|
|
uint64_t mc_r11;
|
|
uint64_t mc_r12;
|
|
uint64_t mc_r13;
|
|
uint64_t mc_r14;
|
|
uint64_t mc_r15;
|
|
int mc_trapno;
|
|
uint16_t mc_fs;
|
|
uint16_t mc_gs;
|
|
uint64_t mc_addr;
|
|
int mc_flags;
|
|
uint16_t mc_es;
|
|
uint16_t mc_ds;
|
|
uint64_t mc_err;
|
|
uint64_t mc_rip;
|
|
uint64_t mc_cs;
|
|
uint64_t mc_rflags;
|
|
uint64_t mc_reserved_0xb8[8];
|
|
uint64_t mc_rsp;
|
|
uint64_t mc_ss;
|
|
uint64_t mc_len;
|
|
uint64_t mc_fpformat;
|
|
uint64_t mc_ownedfp;
|
|
uint64_t mc_lbrfrom;
|
|
uint64_t mc_lbrto;
|
|
uint64_t mc_aux1;
|
|
uint64_t mc_aux2;
|
|
uint64_t mc_fpstate[104];
|
|
uint64_t mc_fsbase;
|
|
uint64_t mc_gsbase;
|
|
uint64_t mc_spare[6];
|
|
};
|
|
|
|
struct SignalStack {
|
|
void* ss_sp;
|
|
uint64_t ss_size;
|
|
int ss_flags;
|
|
int _align;
|
|
};
|
|
|
|
struct SignalUcontext {
|
|
SignalMcontext uc_mcontext;
|
|
};
|
|
|
|
static_assert(offsetof(SignalUcontext, uc_mcontext) == 0x00);
|
|
static_assert(offsetof(SignalMcontext, mc_rip) == 0xa0);
|
|
static_assert(offsetof(SignalUcontext, uc_mcontext) + offsetof(SignalMcontext, mc_rsp) == 0xf8);
|
|
|
|
static SignalUcontext CreateSignalUcontext(
|
|
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
|
const CONTEXT* source_ctx = nullptr
|
|
#endif
|
|
) {
|
|
SignalUcontext ctx = {};
|
|
|
|
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
|
CONTEXT host_ctx = {};
|
|
if (source_ctx != nullptr) {
|
|
host_ctx = *source_ctx;
|
|
} else {
|
|
host_ctx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS;
|
|
RtlCaptureContext(&host_ctx);
|
|
}
|
|
|
|
ctx.uc_mcontext.mc_r8 = host_ctx.R8;
|
|
ctx.uc_mcontext.mc_r9 = host_ctx.R9;
|
|
ctx.uc_mcontext.mc_r10 = host_ctx.R10;
|
|
ctx.uc_mcontext.mc_r11 = host_ctx.R11;
|
|
ctx.uc_mcontext.mc_r12 = host_ctx.R12;
|
|
ctx.uc_mcontext.mc_r13 = host_ctx.R13;
|
|
ctx.uc_mcontext.mc_r14 = host_ctx.R14;
|
|
ctx.uc_mcontext.mc_r15 = host_ctx.R15;
|
|
ctx.uc_mcontext.mc_rdi = host_ctx.Rdi;
|
|
ctx.uc_mcontext.mc_rsi = host_ctx.Rsi;
|
|
ctx.uc_mcontext.mc_rbp = host_ctx.Rbp;
|
|
ctx.uc_mcontext.mc_rbx = host_ctx.Rbx;
|
|
ctx.uc_mcontext.mc_rdx = host_ctx.Rdx;
|
|
ctx.uc_mcontext.mc_rax = host_ctx.Rax;
|
|
ctx.uc_mcontext.mc_rcx = host_ctx.Rcx;
|
|
ctx.uc_mcontext.mc_rsp = host_ctx.Rsp;
|
|
ctx.uc_mcontext.mc_rip = host_ctx.Rip;
|
|
ctx.uc_mcontext.mc_cs = host_ctx.SegCs;
|
|
ctx.uc_mcontext.mc_fs = host_ctx.SegFs;
|
|
ctx.uc_mcontext.mc_gs = host_ctx.SegGs;
|
|
ctx.uc_mcontext.mc_ss = host_ctx.SegSs;
|
|
ctx.uc_mcontext.mc_ds = host_ctx.SegDs;
|
|
ctx.uc_mcontext.mc_es = host_ctx.SegEs;
|
|
ctx.uc_mcontext.mc_rflags = host_ctx.EFlags;
|
|
ctx.uc_mcontext.mc_len = sizeof(SignalMcontext);
|
|
#else
|
|
ctx.uc_mcontext.mc_len = sizeof(SignalMcontext);
|
|
uint64_t stack_marker = 0;
|
|
ctx.uc_mcontext.mc_rsp = reinterpret_cast<uint64_t>(&stack_marker);
|
|
ctx.uc_mcontext.mc_rbp = reinterpret_cast<uint64_t>(__builtin_frame_address(0));
|
|
ctx.uc_mcontext.mc_rip = reinterpret_cast<uint64_t>(__builtin_return_address(0));
|
|
|
|
#if defined(__x86_64__)
|
|
// Capture the remaining guest-visible registers.
|
|
uint64_t rax = 0;
|
|
uint64_t rbx = 0;
|
|
uint64_t rcx = 0;
|
|
uint64_t rdx = 0;
|
|
uint64_t rsi = 0;
|
|
uint64_t rdi = 0;
|
|
asm volatile("movq %%rax, %0\n\t"
|
|
"movq %%rbx, %1\n\t"
|
|
"movq %%rcx, %2\n\t"
|
|
"movq %%rdx, %3\n\t"
|
|
"movq %%rsi, %4\n\t"
|
|
"movq %%rdi, %5\n\t"
|
|
: "=m"(rax), "=m"(rbx), "=m"(rcx), "=m"(rdx), "=m"(rsi), "=m"(rdi)
|
|
:
|
|
: "memory");
|
|
|
|
uint64_t r8 = 0;
|
|
uint64_t r9 = 0;
|
|
uint64_t r10 = 0;
|
|
uint64_t r11 = 0;
|
|
uint64_t r12 = 0;
|
|
uint64_t r13 = 0;
|
|
uint64_t r14 = 0;
|
|
uint64_t r15 = 0;
|
|
asm volatile("movq %%r8, %0\n\t"
|
|
"movq %%r9, %1\n\t"
|
|
"movq %%r10, %2\n\t"
|
|
"movq %%r11, %3\n\t"
|
|
"movq %%r12, %4\n\t"
|
|
"movq %%r13, %5\n\t"
|
|
"movq %%r14, %6\n\t"
|
|
"movq %%r15, %7\n\t"
|
|
: "=m"(r8), "=m"(r9), "=m"(r10), "=m"(r11), "=m"(r12), "=m"(r13), "=m"(r14),
|
|
"=m"(r15)
|
|
:
|
|
: "memory");
|
|
|
|
uint64_t rflags = 0;
|
|
uint64_t cs = 0;
|
|
uint64_t ss = 0;
|
|
asm volatile("pushfq\n\t"
|
|
"popq %0\n\t"
|
|
"movq %%cs, %1\n\t"
|
|
"movq %%ss, %2\n\t"
|
|
: "=r"(rflags), "=r"(cs), "=r"(ss)
|
|
:
|
|
: "memory");
|
|
|
|
ctx.uc_mcontext.mc_rax = rax;
|
|
ctx.uc_mcontext.mc_rbx = rbx;
|
|
ctx.uc_mcontext.mc_rcx = rcx;
|
|
ctx.uc_mcontext.mc_rdx = rdx;
|
|
ctx.uc_mcontext.mc_rsi = rsi;
|
|
ctx.uc_mcontext.mc_rdi = rdi;
|
|
ctx.uc_mcontext.mc_r8 = r8;
|
|
ctx.uc_mcontext.mc_r9 = r9;
|
|
ctx.uc_mcontext.mc_r10 = r10;
|
|
ctx.uc_mcontext.mc_r11 = r11;
|
|
ctx.uc_mcontext.mc_r12 = r12;
|
|
ctx.uc_mcontext.mc_r13 = r13;
|
|
ctx.uc_mcontext.mc_r14 = r14;
|
|
ctx.uc_mcontext.mc_r15 = r15;
|
|
ctx.uc_mcontext.mc_rflags = rflags;
|
|
ctx.uc_mcontext.mc_cs = cs;
|
|
ctx.uc_mcontext.mc_ss = ss;
|
|
#endif
|
|
#endif
|
|
|
|
return ctx;
|
|
}
|
|
|
|
static SignalUcontext CreateCurrentGuestCallSignalUcontext(uint64_t rip) {
|
|
auto ctx = CreateSignalUcontext();
|
|
|
|
uint64_t rsp = 0;
|
|
uint64_t rbp = 0;
|
|
uint64_t rbx = 0;
|
|
uint64_t r12 = 0;
|
|
uint64_t r13 = 0;
|
|
uint64_t r14 = 0;
|
|
uint64_t r15 = 0;
|
|
asm volatile("movq %%rsp, %0\n\t"
|
|
"movq %%rbp, %1\n\t"
|
|
"movq %%rbx, %2\n\t"
|
|
"movq %%r12, %3\n\t"
|
|
"movq %%r13, %4\n\t"
|
|
"movq %%r14, %5\n\t"
|
|
"movq %%r15, %6\n\t"
|
|
: "=r"(rsp), "=r"(rbp), "=r"(rbx), "=r"(r12), "=r"(r13), "=r"(r14), "=r"(r15)
|
|
:
|
|
: "memory");
|
|
|
|
ctx.uc_mcontext.mc_rax = 0;
|
|
ctx.uc_mcontext.mc_rcx = 0;
|
|
ctx.uc_mcontext.mc_rdx = 0;
|
|
ctx.uc_mcontext.mc_rsi = 0;
|
|
ctx.uc_mcontext.mc_rdi = 0;
|
|
ctx.uc_mcontext.mc_r8 = 0;
|
|
ctx.uc_mcontext.mc_r9 = 0;
|
|
ctx.uc_mcontext.mc_r10 = 0;
|
|
ctx.uc_mcontext.mc_r11 = 0;
|
|
ctx.uc_mcontext.mc_rbx = rbx;
|
|
ctx.uc_mcontext.mc_rbp = rbp;
|
|
ctx.uc_mcontext.mc_rsp = rsp;
|
|
ctx.uc_mcontext.mc_r12 = r12;
|
|
ctx.uc_mcontext.mc_r13 = r13;
|
|
ctx.uc_mcontext.mc_r14 = r14;
|
|
ctx.uc_mcontext.mc_r15 = r15;
|
|
ctx.uc_mcontext.mc_rip = rip;
|
|
|
|
return ctx;
|
|
}
|
|
|
|
static bool IsGuestCodeAddress(uint64_t addr) {
|
|
return addr >= 0x0000000900000000ull && addr < 0x0000000980000000ull;
|
|
}
|
|
|
|
static bool IsAddressInRange(uint64_t addr, uint64_t base, uint64_t size) {
|
|
return size != 0 && addr >= base && addr < base + size;
|
|
}
|
|
|
|
static uint64_t GetFallbackStackPointer(Pthread thread) {
|
|
uint64_t stack_addr = 0;
|
|
uint64_t stack_size = 0;
|
|
if (!PthreadGetGuestStack(thread, &stack_addr, &stack_size)) {
|
|
return 0;
|
|
}
|
|
|
|
const auto stack_top = (stack_addr + stack_size) & ~0xfull;
|
|
if (stack_top <= stack_addr + 0x100) {
|
|
return stack_top;
|
|
}
|
|
return stack_top - 0x100;
|
|
}
|
|
|
|
static void SanitizeNonGuestSignalUcontext(SignalUcontext* ctx, Pthread thread) {
|
|
if (ctx == nullptr || IsGuestCodeAddress(ctx->uc_mcontext.mc_rip)) {
|
|
return;
|
|
}
|
|
|
|
uint64_t stack_addr = 0;
|
|
uint64_t stack_size = 0;
|
|
const bool has_stack = PthreadGetGuestStack(thread, &stack_addr, &stack_size);
|
|
const bool rsp_valid =
|
|
has_stack && IsAddressInRange(ctx->uc_mcontext.mc_rsp, stack_addr, stack_size);
|
|
const bool rbp_valid =
|
|
has_stack && IsAddressInRange(ctx->uc_mcontext.mc_rbp, stack_addr, stack_size);
|
|
const auto fallback_sp = GetFallbackStackPointer(thread);
|
|
|
|
ctx->uc_mcontext.mc_rax = 0;
|
|
ctx->uc_mcontext.mc_rbx = 0;
|
|
ctx->uc_mcontext.mc_rcx = 0;
|
|
ctx->uc_mcontext.mc_rdx = 0;
|
|
ctx->uc_mcontext.mc_rsi = 0;
|
|
ctx->uc_mcontext.mc_rdi = 0;
|
|
ctx->uc_mcontext.mc_rbp = (rbp_valid ? ctx->uc_mcontext.mc_rbp : fallback_sp);
|
|
ctx->uc_mcontext.mc_rsp = (rsp_valid ? ctx->uc_mcontext.mc_rsp : fallback_sp);
|
|
ctx->uc_mcontext.mc_r8 = 0;
|
|
ctx->uc_mcontext.mc_r9 = 0;
|
|
ctx->uc_mcontext.mc_r10 = 0;
|
|
ctx->uc_mcontext.mc_r11 = 0;
|
|
ctx->uc_mcontext.mc_r12 = 0;
|
|
ctx->uc_mcontext.mc_r13 = 0;
|
|
ctx->uc_mcontext.mc_r14 = 0;
|
|
ctx->uc_mcontext.mc_r15 = 0;
|
|
ctx->uc_mcontext.mc_rip = 0;
|
|
}
|
|
|
|
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
|
static void ApplySignalUcontext(CONTEXT* dst_ctx, const SignalUcontext& src_ctx) {
|
|
if (dst_ctx == nullptr) {
|
|
return;
|
|
}
|
|
|
|
dst_ctx->R8 = src_ctx.uc_mcontext.mc_r8;
|
|
dst_ctx->R9 = src_ctx.uc_mcontext.mc_r9;
|
|
dst_ctx->R10 = src_ctx.uc_mcontext.mc_r10;
|
|
dst_ctx->R11 = src_ctx.uc_mcontext.mc_r11;
|
|
dst_ctx->R12 = src_ctx.uc_mcontext.mc_r12;
|
|
dst_ctx->R13 = src_ctx.uc_mcontext.mc_r13;
|
|
dst_ctx->R14 = src_ctx.uc_mcontext.mc_r14;
|
|
dst_ctx->R15 = src_ctx.uc_mcontext.mc_r15;
|
|
dst_ctx->Rdi = src_ctx.uc_mcontext.mc_rdi;
|
|
dst_ctx->Rsi = src_ctx.uc_mcontext.mc_rsi;
|
|
dst_ctx->Rbp = src_ctx.uc_mcontext.mc_rbp;
|
|
dst_ctx->Rbx = src_ctx.uc_mcontext.mc_rbx;
|
|
dst_ctx->Rdx = src_ctx.uc_mcontext.mc_rdx;
|
|
dst_ctx->Rax = src_ctx.uc_mcontext.mc_rax;
|
|
dst_ctx->Rcx = src_ctx.uc_mcontext.mc_rcx;
|
|
dst_ctx->Rsp = src_ctx.uc_mcontext.mc_rsp;
|
|
dst_ctx->Rip = src_ctx.uc_mcontext.mc_rip;
|
|
dst_ctx->SegCs = static_cast<DWORD>(src_ctx.uc_mcontext.mc_cs);
|
|
dst_ctx->SegFs = src_ctx.uc_mcontext.mc_fs;
|
|
dst_ctx->SegGs = src_ctx.uc_mcontext.mc_gs;
|
|
dst_ctx->SegSs = static_cast<DWORD>(src_ctx.uc_mcontext.mc_ss);
|
|
dst_ctx->SegDs = src_ctx.uc_mcontext.mc_ds;
|
|
dst_ctx->SegEs = src_ctx.uc_mcontext.mc_es;
|
|
dst_ctx->EFlags = static_cast<DWORD>(src_ctx.uc_mcontext.mc_rflags);
|
|
}
|
|
#endif
|
|
|
|
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS && defined(__x86_64__)
|
|
|
|
static SignalUcontext CreateSignalUcontextFromHost(const ucontext_t* host_ctx) {
|
|
SignalUcontext ctx = {};
|
|
if (host_ctx == nullptr) {
|
|
return ctx;
|
|
}
|
|
|
|
#if defined(__APPLE__)
|
|
const auto& ss = host_ctx->uc_mcontext->__ss;
|
|
|
|
ctx.uc_mcontext.mc_rdi = ss.__rdi;
|
|
ctx.uc_mcontext.mc_rsi = ss.__rsi;
|
|
ctx.uc_mcontext.mc_rdx = ss.__rdx;
|
|
ctx.uc_mcontext.mc_rcx = ss.__rcx;
|
|
ctx.uc_mcontext.mc_r8 = ss.__r8;
|
|
ctx.uc_mcontext.mc_r9 = ss.__r9;
|
|
ctx.uc_mcontext.mc_rax = ss.__rax;
|
|
ctx.uc_mcontext.mc_rbx = ss.__rbx;
|
|
ctx.uc_mcontext.mc_rbp = ss.__rbp;
|
|
ctx.uc_mcontext.mc_r10 = ss.__r10;
|
|
ctx.uc_mcontext.mc_r11 = ss.__r11;
|
|
ctx.uc_mcontext.mc_r12 = ss.__r12;
|
|
ctx.uc_mcontext.mc_r13 = ss.__r13;
|
|
ctx.uc_mcontext.mc_r14 = ss.__r14;
|
|
ctx.uc_mcontext.mc_r15 = ss.__r15;
|
|
ctx.uc_mcontext.mc_rip = ss.__rip;
|
|
ctx.uc_mcontext.mc_rsp = ss.__rsp;
|
|
ctx.uc_mcontext.mc_rflags = ss.__rflags;
|
|
ctx.uc_mcontext.mc_cs = ss.__cs & 0xffffu;
|
|
ctx.uc_mcontext.mc_gs = static_cast<uint16_t>(ss.__gs & 0xffffu);
|
|
ctx.uc_mcontext.mc_fs = static_cast<uint16_t>(ss.__fs & 0xffffu);
|
|
ctx.uc_mcontext.mc_len = sizeof(SignalMcontext);
|
|
|
|
return ctx;
|
|
#else
|
|
const auto* gregs = host_ctx->uc_mcontext.gregs;
|
|
|
|
ctx.uc_mcontext.mc_rdi = static_cast<uint64_t>(gregs[REG_RDI]);
|
|
ctx.uc_mcontext.mc_rsi = static_cast<uint64_t>(gregs[REG_RSI]);
|
|
ctx.uc_mcontext.mc_rdx = static_cast<uint64_t>(gregs[REG_RDX]);
|
|
ctx.uc_mcontext.mc_rcx = static_cast<uint64_t>(gregs[REG_RCX]);
|
|
ctx.uc_mcontext.mc_r8 = static_cast<uint64_t>(gregs[REG_R8]);
|
|
ctx.uc_mcontext.mc_r9 = static_cast<uint64_t>(gregs[REG_R9]);
|
|
ctx.uc_mcontext.mc_rax = static_cast<uint64_t>(gregs[REG_RAX]);
|
|
ctx.uc_mcontext.mc_rbx = static_cast<uint64_t>(gregs[REG_RBX]);
|
|
ctx.uc_mcontext.mc_rbp = static_cast<uint64_t>(gregs[REG_RBP]);
|
|
ctx.uc_mcontext.mc_r10 = static_cast<uint64_t>(gregs[REG_R10]);
|
|
ctx.uc_mcontext.mc_r11 = static_cast<uint64_t>(gregs[REG_R11]);
|
|
ctx.uc_mcontext.mc_r12 = static_cast<uint64_t>(gregs[REG_R12]);
|
|
ctx.uc_mcontext.mc_r13 = static_cast<uint64_t>(gregs[REG_R13]);
|
|
ctx.uc_mcontext.mc_r14 = static_cast<uint64_t>(gregs[REG_R14]);
|
|
ctx.uc_mcontext.mc_r15 = static_cast<uint64_t>(gregs[REG_R15]);
|
|
ctx.uc_mcontext.mc_rip = static_cast<uint64_t>(gregs[REG_RIP]);
|
|
ctx.uc_mcontext.mc_rsp = static_cast<uint64_t>(gregs[REG_RSP]);
|
|
ctx.uc_mcontext.mc_rflags = static_cast<uint64_t>(gregs[REG_EFL]);
|
|
|
|
// Linux packs cs/gs/fs into one greg.
|
|
const auto csgsfs = static_cast<uint64_t>(gregs[REG_CSGSFS]);
|
|
ctx.uc_mcontext.mc_cs = csgsfs & 0xffffu;
|
|
ctx.uc_mcontext.mc_gs = static_cast<uint16_t>((csgsfs >> 16u) & 0xffffu);
|
|
ctx.uc_mcontext.mc_fs = static_cast<uint16_t>((csgsfs >> 32u) & 0xffffu);
|
|
ctx.uc_mcontext.mc_len = sizeof(SignalMcontext);
|
|
|
|
return ctx;
|
|
#endif
|
|
}
|
|
|
|
static void ApplySignalUcontextToHost(ucontext_t* dst_ctx, const SignalUcontext& src_ctx) {
|
|
if (dst_ctx == nullptr) {
|
|
return;
|
|
}
|
|
|
|
#if defined(__APPLE__)
|
|
auto& ss = dst_ctx->uc_mcontext->__ss;
|
|
|
|
ss.__rdi = src_ctx.uc_mcontext.mc_rdi;
|
|
ss.__rsi = src_ctx.uc_mcontext.mc_rsi;
|
|
ss.__rdx = src_ctx.uc_mcontext.mc_rdx;
|
|
ss.__rcx = src_ctx.uc_mcontext.mc_rcx;
|
|
ss.__r8 = src_ctx.uc_mcontext.mc_r8;
|
|
ss.__r9 = src_ctx.uc_mcontext.mc_r9;
|
|
ss.__rax = src_ctx.uc_mcontext.mc_rax;
|
|
ss.__rbx = src_ctx.uc_mcontext.mc_rbx;
|
|
ss.__rbp = src_ctx.uc_mcontext.mc_rbp;
|
|
ss.__r10 = src_ctx.uc_mcontext.mc_r10;
|
|
ss.__r11 = src_ctx.uc_mcontext.mc_r11;
|
|
ss.__r12 = src_ctx.uc_mcontext.mc_r12;
|
|
ss.__r13 = src_ctx.uc_mcontext.mc_r13;
|
|
ss.__r14 = src_ctx.uc_mcontext.mc_r14;
|
|
ss.__r15 = src_ctx.uc_mcontext.mc_r15;
|
|
ss.__rip = src_ctx.uc_mcontext.mc_rip;
|
|
ss.__rsp = src_ctx.uc_mcontext.mc_rsp;
|
|
ss.__rflags = src_ctx.uc_mcontext.mc_rflags;
|
|
// Segment selectors are left untouched; XNU validates them on sigreturn.
|
|
#else
|
|
auto* gregs = dst_ctx->uc_mcontext.gregs;
|
|
|
|
gregs[REG_RDI] = static_cast<greg_t>(src_ctx.uc_mcontext.mc_rdi);
|
|
gregs[REG_RSI] = static_cast<greg_t>(src_ctx.uc_mcontext.mc_rsi);
|
|
gregs[REG_RDX] = static_cast<greg_t>(src_ctx.uc_mcontext.mc_rdx);
|
|
gregs[REG_RCX] = static_cast<greg_t>(src_ctx.uc_mcontext.mc_rcx);
|
|
gregs[REG_R8] = static_cast<greg_t>(src_ctx.uc_mcontext.mc_r8);
|
|
gregs[REG_R9] = static_cast<greg_t>(src_ctx.uc_mcontext.mc_r9);
|
|
gregs[REG_RAX] = static_cast<greg_t>(src_ctx.uc_mcontext.mc_rax);
|
|
gregs[REG_RBX] = static_cast<greg_t>(src_ctx.uc_mcontext.mc_rbx);
|
|
gregs[REG_RBP] = static_cast<greg_t>(src_ctx.uc_mcontext.mc_rbp);
|
|
gregs[REG_R10] = static_cast<greg_t>(src_ctx.uc_mcontext.mc_r10);
|
|
gregs[REG_R11] = static_cast<greg_t>(src_ctx.uc_mcontext.mc_r11);
|
|
gregs[REG_R12] = static_cast<greg_t>(src_ctx.uc_mcontext.mc_r12);
|
|
gregs[REG_R13] = static_cast<greg_t>(src_ctx.uc_mcontext.mc_r13);
|
|
gregs[REG_R14] = static_cast<greg_t>(src_ctx.uc_mcontext.mc_r14);
|
|
gregs[REG_R15] = static_cast<greg_t>(src_ctx.uc_mcontext.mc_r15);
|
|
gregs[REG_RIP] = static_cast<greg_t>(src_ctx.uc_mcontext.mc_rip);
|
|
gregs[REG_RSP] = static_cast<greg_t>(src_ctx.uc_mcontext.mc_rsp);
|
|
gregs[REG_EFL] = static_cast<greg_t>(src_ctx.uc_mcontext.mc_rflags);
|
|
|
|
// The kernel validates packed segment selectors on sigreturn.
|
|
#endif
|
|
}
|
|
|
|
static int SignalDispatchHostSignal() {
|
|
#if defined(__APPLE__)
|
|
// macOS has no realtime signals; SIGUSR1 is otherwise unused on the host side (the
|
|
// guest's SIGUSR1 is an emulated signal number, not a host registration).
|
|
static const int host_signal = SIGUSR1;
|
|
#else
|
|
static const int host_signal = SIGRTMIN + 3;
|
|
#endif
|
|
return host_signal;
|
|
}
|
|
|
|
static void HostSignalDispatchHandler(int /*host_signal*/, siginfo_t* /*info*/,
|
|
void* native_context) {
|
|
Pthread current = PthreadSelfOrNull();
|
|
if (current == nullptr) {
|
|
return;
|
|
}
|
|
|
|
auto* host_ctx = static_cast<ucontext_t*>(native_context);
|
|
|
|
for (int signum = 0; signum < static_cast<int>(std::size(g_exception_handlers)); signum++) {
|
|
if (!PthreadTakePendingSignal(current, signum)) {
|
|
continue;
|
|
}
|
|
|
|
auto* handler = reinterpret_cast<exception_handler_func_t>(g_exception_handlers[signum]);
|
|
if (handler != nullptr) {
|
|
SignalDispatchScope scope;
|
|
auto ctx = CreateSignalUcontextFromHost(host_ctx);
|
|
if (IsGuestCodeAddress(ctx.uc_mcontext.mc_rip)) {
|
|
handler(signum, &ctx);
|
|
ApplySignalUcontextToHost(host_ctx, ctx);
|
|
} else {
|
|
// Do not expose or restore a host frame.
|
|
SanitizeNonGuestSignalUcontext(&ctx, current);
|
|
handler(signum, &ctx);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
static bool EnsureHostSignalDispatchInstalled() {
|
|
static const bool installed = [] {
|
|
struct sigaction action {};
|
|
action.sa_sigaction = HostSignalDispatchHandler;
|
|
sigemptyset(&action.sa_mask);
|
|
action.sa_flags = SA_SIGINFO | SA_RESTART;
|
|
return ::sigaction(SignalDispatchHostSignal(), &action, nullptr) == 0;
|
|
}();
|
|
return installed;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
|
enum KytyQueueUserApcFlags : ULONG_PTR {
|
|
KytyQueueUserApcFlagsNone = 0,
|
|
KytyQueueUserApcFlagsSpecialUserApc = 1,
|
|
KytyQueueUserApcFlagsCallbackContext = 0x00010000,
|
|
KytyQueueUserApcFlagsMaxValue = 2,
|
|
};
|
|
|
|
union KytyUserApcOption {
|
|
ULONG_PTR UserApcFlags;
|
|
HANDLE MemoryReserveHandle;
|
|
};
|
|
|
|
using KytyPsApcRoutine = void (*)(void* apc_arg1, void* apc_arg2, void* apc_arg3, PCONTEXT context);
|
|
using NtQueueApcThreadExFunc = uint64_t(WINAPI*)(HANDLE thread, KytyUserApcOption option,
|
|
KytyPsApcRoutine routine, void* arg1, void* arg2,
|
|
void* arg3);
|
|
|
|
static NtQueueApcThreadExFunc GetNtQueueApcThreadEx() {
|
|
static auto* nt_queue_apc_thread_ex = reinterpret_cast<NtQueueApcThreadExFunc>(
|
|
GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueueApcThreadEx"));
|
|
return nt_queue_apc_thread_ex;
|
|
}
|
|
|
|
static void SignalApcHandler(void* arg1, void* arg2, void* /*arg3*/, PCONTEXT context) {
|
|
auto* thread = static_cast<Pthread>(arg1);
|
|
const auto signum = static_cast<int>(reinterpret_cast<intptr_t>(arg2));
|
|
if (!PthreadTakePendingSignal(thread, signum)) {
|
|
return;
|
|
}
|
|
|
|
auto* handler = reinterpret_cast<exception_handler_func_t>(g_exception_handlers[signum]);
|
|
if (handler != nullptr) {
|
|
SignalDispatchScope scope;
|
|
auto ctx = CreateSignalUcontext(context);
|
|
auto guest_context = IsGuestCodeAddress(ctx.uc_mcontext.mc_rip);
|
|
if (!guest_context) {
|
|
SanitizeNonGuestSignalUcontext(&ctx, thread);
|
|
std::thread([thread, signum, handler, ctx]() mutable {
|
|
SignalDispatchScope helper_scope;
|
|
auto* previous_self = PthreadSwapSelfForSignal(thread);
|
|
handler(signum, &ctx);
|
|
PthreadSwapSelfForSignal(previous_self);
|
|
}).detach();
|
|
return;
|
|
}
|
|
handler(signum, &ctx);
|
|
if (guest_context) {
|
|
ApplySignalUcontext(context, ctx);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
void KernelDispatchPendingSignalForCurrentThread() {
|
|
Pthread current = PthreadSelfOrNull();
|
|
if (current == nullptr) {
|
|
return;
|
|
}
|
|
|
|
for (int signum = 0; signum < static_cast<int>(std::size(g_exception_handlers)); signum++) {
|
|
if (!PthreadTakePendingSignal(current, signum)) {
|
|
continue;
|
|
}
|
|
|
|
auto* handler = reinterpret_cast<exception_handler_func_t>(g_exception_handlers[signum]);
|
|
if (handler != nullptr) {
|
|
SignalDispatchScope scope;
|
|
auto ctx = CreateSignalUcontext();
|
|
SanitizeNonGuestSignalUcontext(&ctx, current);
|
|
handler(signum, &ctx);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
static int KYTY_SYSV_ABI KernelInstallExceptionHandler(int signum, void* handler) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t signum = %d\n"
|
|
"\t handler = 0x%016" PRIx64 "\n",
|
|
signum, reinterpret_cast<uint64_t>(handler));
|
|
|
|
if (!IsAllowedExceptionSignal(signum)) {
|
|
return KERNEL_ERROR_EINVAL;
|
|
}
|
|
if (g_exception_handlers[signum] != nullptr) {
|
|
return KERNEL_ERROR_EAGAIN;
|
|
}
|
|
|
|
g_exception_handlers[signum] = handler;
|
|
return OK;
|
|
}
|
|
|
|
static int KYTY_SYSV_ABI KernelRemoveExceptionHandler(int signum) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t signum = %d\n", signum);
|
|
|
|
if (!IsAllowedExceptionSignal(signum)) {
|
|
return KERNEL_ERROR_EINVAL;
|
|
}
|
|
|
|
g_exception_handlers[signum] = nullptr;
|
|
return OK;
|
|
}
|
|
|
|
static int KYTY_SYSV_ABI KernelRaiseException(Pthread thread, int signum) {
|
|
constexpr int POSIX_SIGUSR1 = 30;
|
|
|
|
if (signum != POSIX_SIGUSR1 || thread == nullptr) {
|
|
return KERNEL_ERROR_EINVAL;
|
|
}
|
|
|
|
auto* handler = reinterpret_cast<exception_handler_func_t>(g_exception_handlers[signum]);
|
|
if (handler != nullptr) {
|
|
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
|
|
const auto target_thread_id = PthreadGetHostThreadId(thread);
|
|
auto* nt_queue_apc_thread_ex = GetNtQueueApcThreadEx();
|
|
if (target_thread_id == 0) {
|
|
return KERNEL_ERROR_EINVAL;
|
|
}
|
|
if (target_thread_id == static_cast<uint64_t>(GetCurrentThreadId())) {
|
|
SignalDispatchScope scope;
|
|
auto ctx = CreateCurrentGuestCallSignalUcontext(
|
|
reinterpret_cast<uint64_t>(__builtin_return_address(0)));
|
|
handler(signum, &ctx);
|
|
return OK;
|
|
}
|
|
if (nt_queue_apc_thread_ex == nullptr) {
|
|
return KERNEL_ERROR_EINVAL;
|
|
}
|
|
|
|
HANDLE target_thread =
|
|
OpenThread(THREAD_SET_CONTEXT, FALSE, static_cast<DWORD>(target_thread_id));
|
|
if (target_thread == nullptr) {
|
|
return KERNEL_ERROR_EINVAL;
|
|
}
|
|
|
|
PthreadQueuePendingSignal(thread, signum);
|
|
KytyUserApcOption option {};
|
|
option.UserApcFlags = KytyQueueUserApcFlagsSpecialUserApc;
|
|
|
|
const auto status =
|
|
nt_queue_apc_thread_ex(target_thread, option, SignalApcHandler, thread,
|
|
reinterpret_cast<void*>(static_cast<intptr_t>(signum)), nullptr);
|
|
|
|
if (status != 0) {
|
|
PthreadTakePendingSignal(thread, signum);
|
|
CloseHandle(target_thread);
|
|
LOGF("\t NtQueueApcThreadEx failed: target_os_thread=%" PRIu64 ", status=0x%016" PRIx64
|
|
"\n",
|
|
target_thread_id, status);
|
|
return KERNEL_ERROR_EINVAL;
|
|
}
|
|
|
|
PthreadWakeForSignal(thread);
|
|
CloseHandle(target_thread);
|
|
return OK;
|
|
#elif defined(__x86_64__)
|
|
// Deliver on the target thread.
|
|
if (thread == PthreadSelfOrNull()) {
|
|
SignalDispatchScope scope;
|
|
auto ctx = CreateCurrentGuestCallSignalUcontext(
|
|
reinterpret_cast<uint64_t>(__builtin_return_address(0)));
|
|
handler(signum, &ctx);
|
|
return OK;
|
|
}
|
|
|
|
if (!EnsureHostSignalDispatchInstalled()) {
|
|
return KERNEL_ERROR_EINVAL;
|
|
}
|
|
|
|
PthreadQueuePendingSignal(thread, signum);
|
|
if (!PthreadKillHost(thread, SignalDispatchHostSignal())) {
|
|
PthreadTakePendingSignal(thread, signum);
|
|
LOGF("\t pthread_kill failed for target thread\n");
|
|
return KERNEL_ERROR_EINVAL;
|
|
}
|
|
|
|
PthreadWakeForSignal(thread);
|
|
WaitForSignalDispatch(thread, signum);
|
|
return OK;
|
|
#else
|
|
auto ctx = CreateSignalUcontext();
|
|
handler(signum, &ctx);
|
|
#endif
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
static KYTY_SYSV_ABI KernelModule KernelLoadStartModule(const char* module_file_name, size_t args,
|
|
const void* argp, uint32_t flags,
|
|
const KernelLoadModuleOpt* opt, int* res) {
|
|
PRINT_NAME();
|
|
|
|
// EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread());
|
|
|
|
LOGF("\tmodule_file_name = %s\n", module_file_name);
|
|
|
|
EXIT_NOT_IMPLEMENTED(flags != 0);
|
|
EXIT_NOT_IMPLEMENTED(opt != nullptr);
|
|
|
|
auto* rt = Common::Singleton<Loader::RuntimeLinker>::Instance();
|
|
|
|
auto module_path = FileSystem::GetRealFilename(std::string(module_file_name));
|
|
if (!Common::File::IsFileExisting(module_path)) {
|
|
LOGF("\tmodule real path missing = %s\n", Common::PathToString(module_path).c_str());
|
|
if (res != nullptr) {
|
|
*res = KERNEL_ERROR_ENOENT;
|
|
}
|
|
return KERNEL_ERROR_ENOENT;
|
|
}
|
|
|
|
auto* program = rt->FindProgramByFileName(module_path);
|
|
if (program != nullptr) {
|
|
if (res != nullptr) {
|
|
*res = OK;
|
|
}
|
|
return static_cast<KernelModule>(program->unique_id);
|
|
}
|
|
|
|
program = rt->LoadProgram(module_path);
|
|
|
|
auto handle = program->unique_id;
|
|
|
|
program->dbg_print_reloc = true;
|
|
|
|
rt->RelocateProgram(program);
|
|
|
|
int result = rt->StartModule(program, args, argp, nullptr);
|
|
|
|
LOGF("\tmodule_start() result = %d\n", result);
|
|
|
|
EXIT_NOT_IMPLEMENTED(result < 0);
|
|
|
|
if (res != nullptr) {
|
|
*res = result;
|
|
}
|
|
|
|
return static_cast<KernelModule>(handle);
|
|
}
|
|
|
|
static int KYTY_SYSV_ABI KernelStopUnloadModule(KernelModule handle, size_t args, const void* argp,
|
|
uint32_t flags, const KernelUnloadModuleOpt* opt,
|
|
int* res) {
|
|
PRINT_NAME();
|
|
|
|
// EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread());
|
|
|
|
auto* rt = Common::Singleton<Loader::RuntimeLinker>::Instance();
|
|
|
|
EXIT_NOT_IMPLEMENTED(flags != 0);
|
|
EXIT_NOT_IMPLEMENTED(opt != nullptr);
|
|
|
|
auto* program = rt->FindProgramById(handle);
|
|
|
|
if (program == nullptr) {
|
|
LOGF("\tinvalid module handle = %" PRId32 "\n", handle);
|
|
return OK;
|
|
}
|
|
|
|
if (g_get_thread_atexit_count_func != nullptr &&
|
|
g_get_thread_atexit_count_func(program->unique_id) > 0) {
|
|
LOGF("KernelStopUnloadModule: cannot unload %s\n",
|
|
Common::PathToString(program->file_name).c_str());
|
|
if (g_thread_atexit_report_func != nullptr) {
|
|
g_thread_atexit_report_func(program->unique_id);
|
|
}
|
|
return KERNEL_ERROR_EBUSY;
|
|
}
|
|
|
|
int result = rt->StopModule(program, args, argp, nullptr);
|
|
|
|
LOGF("\tmodule_stop() result = %d\n", result);
|
|
|
|
EXIT_NOT_IMPLEMENTED(result < 0);
|
|
|
|
if (res != nullptr) {
|
|
*res = result;
|
|
}
|
|
|
|
rt->UnloadProgram(program);
|
|
|
|
return OK;
|
|
}
|
|
|
|
static void* KYTY_SYSV_ABI tls_get_addr(TlsInfo* info) {
|
|
PRINT_NAME();
|
|
|
|
// EXIT_NOT_IMPLEMENTED(!Common::Thread::IsMainThread());
|
|
|
|
return Loader::RuntimeLinker::TlsGetAddr(info->program) + info->offset;
|
|
}
|
|
|
|
static void* KYTY_SYSV_ABI KernelGetProcParam() {
|
|
PRINT_NAME();
|
|
|
|
auto* rt = Common::Singleton<Loader::RuntimeLinker>::Instance(); // NOLINT
|
|
|
|
return reinterpret_cast<void*>(rt->GetProcParam());
|
|
}
|
|
|
|
static int KYTY_SYSV_ABI KernelIsTrinityMode() {
|
|
static std::atomic_uint32_t call_count {0};
|
|
|
|
auto count = call_count.fetch_add(1, std::memory_order_relaxed);
|
|
if (count < 8 || (count % 1024) == 0) {
|
|
PRINT_NAME();
|
|
LOGF("\tcall = %u, return = 0 (base PS5 mode)\n", count + 1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int KYTY_SYSV_ABI KernelGetOperationMode(int* mode, int* submode) {
|
|
PRINT_NAME();
|
|
|
|
*mode = 2; // PS5 Base
|
|
*submode = 0; // None
|
|
LOGF("\t mode = %d, submode = %d\n", *mode, *submode);
|
|
return OK;
|
|
}
|
|
|
|
static int KYTY_SYSV_ABI KernelFsync(int fd) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t fd = %d\n", fd);
|
|
|
|
return OK;
|
|
}
|
|
|
|
static void KYTY_SYSV_ABI KernelSync() {
|
|
PRINT_NAME();
|
|
}
|
|
|
|
static int KYTY_SYSV_ABI getpid() {
|
|
PRINT_NAME();
|
|
|
|
return 100;
|
|
}
|
|
|
|
static void KYTY_SYSV_ABI KernelRtldSetApplicationHeapAPI(void* api[]) {
|
|
PRINT_NAME();
|
|
|
|
auto* rt = Common::Singleton<Loader::RuntimeLinker>::Instance();
|
|
rt->SetApplicationHeapApi(api);
|
|
|
|
if (api == nullptr) {
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
LOGF("\tapi[%d] = 0x%016" PRIx64 "\n", i, reinterpret_cast<uint64_t>(api[i]));
|
|
}
|
|
|
|
[[maybe_unused]] auto* heap_malloc = api[0];
|
|
[[maybe_unused]] auto* heap_free = api[1];
|
|
[[maybe_unused]] auto* heap_posix_memalign = api[6];
|
|
}
|
|
|
|
static int64_t KYTY_SYSV_ABI write(int d, const char* str, int64_t size) {
|
|
// PRINT_NAME();
|
|
|
|
EXIT_NOT_IMPLEMENTED(d < 0);
|
|
|
|
if (Network::Net::IsSocket(d)) {
|
|
return Network::Net::Send(d, str, static_cast<uint64_t>(size), 0);
|
|
}
|
|
|
|
if (d > 2) {
|
|
return POSIX_N_CALL(FileSystem::KernelWrite(d, str, size));
|
|
}
|
|
|
|
int size_int = static_cast<int>(size);
|
|
|
|
return size_int;
|
|
}
|
|
|
|
static int KYTY_SYSV_ABI open(const char* path, int flags, int mode) {
|
|
return POSIX_N_CALL(FileSystem::KernelOpen(path, flags, mode));
|
|
}
|
|
|
|
static int KYTY_SYSV_ABI close(int d) {
|
|
if (d < 0) {
|
|
*Posix::GetErrorAddr() = Posix::POSIX_EBADF;
|
|
return -1;
|
|
}
|
|
|
|
if (d <= 2) {
|
|
return 0;
|
|
}
|
|
|
|
if (Network::Net::IsSocket(d)) {
|
|
return POSIX_NET_CALL(Network::Net::SocketClose(d));
|
|
}
|
|
|
|
return POSIX_CALL(FileSystem::KernelClose(d));
|
|
}
|
|
|
|
static int64_t KYTY_SYSV_ABI read(int d, void* buf, uint64_t nbytes) {
|
|
// PRINT_NAME();
|
|
|
|
if (buf == nullptr) {
|
|
*Posix::GetErrorAddr() = Posix::POSIX_EFAULT;
|
|
return -1;
|
|
}
|
|
|
|
if (d < 0 || d == 1 || d == 2) {
|
|
*Posix::GetErrorAddr() = Posix::POSIX_EBADF;
|
|
return -1;
|
|
}
|
|
|
|
if (Network::Net::IsSocket(d)) {
|
|
return Network::Net::Recv(d, buf, nbytes, 0);
|
|
}
|
|
|
|
if (d > 2) {
|
|
return POSIX_N_CALL(FileSystem::KernelRead(d, buf, nbytes));
|
|
}
|
|
|
|
if (nbytes == 0) {
|
|
return 0;
|
|
}
|
|
|
|
auto* str = std::fgets(static_cast<char*>(buf), static_cast<int>(nbytes), stdin);
|
|
|
|
return (str != nullptr ? static_cast<int64_t>(strlen(str)) : 0);
|
|
}
|
|
|
|
static bool DecodeEhFramePointer(const uint8_t* data, const uint8_t* end, uint8_t encoding,
|
|
uint64_t field_addr, uint64_t* value) {
|
|
EXIT_IF(value == nullptr);
|
|
|
|
if (encoding == 0xff) {
|
|
return false;
|
|
}
|
|
|
|
uint64_t out = 0;
|
|
auto format = encoding & 0x0f;
|
|
auto app = encoding & 0x70;
|
|
bool is_signed = false;
|
|
|
|
switch (format) {
|
|
case 0x00:
|
|
if (data + sizeof(uint64_t) > end) {
|
|
return false;
|
|
}
|
|
std::memcpy(&out, data, sizeof(uint64_t));
|
|
break;
|
|
case 0x03:
|
|
if (data + sizeof(uint32_t) > end) {
|
|
return false;
|
|
}
|
|
{
|
|
uint32_t tmp = 0;
|
|
std::memcpy(&tmp, data, sizeof(tmp));
|
|
out = tmp;
|
|
}
|
|
break;
|
|
case 0x04:
|
|
if (data + sizeof(uint64_t) > end) {
|
|
return false;
|
|
}
|
|
std::memcpy(&out, data, sizeof(uint64_t));
|
|
break;
|
|
case 0x0b:
|
|
if (data + sizeof(int32_t) > end) {
|
|
return false;
|
|
}
|
|
{
|
|
int32_t tmp = 0;
|
|
std::memcpy(&tmp, data, sizeof(tmp));
|
|
out = static_cast<uint64_t>(static_cast<int64_t>(tmp));
|
|
is_signed = true;
|
|
}
|
|
break;
|
|
case 0x0c:
|
|
if (data + sizeof(int64_t) > end) {
|
|
return false;
|
|
}
|
|
std::memcpy(&out, data, sizeof(int64_t));
|
|
is_signed = true;
|
|
break;
|
|
default: return false;
|
|
}
|
|
|
|
if (app == 0x10) {
|
|
out = (is_signed ? static_cast<uint64_t>(static_cast<int64_t>(field_addr) +
|
|
static_cast<int64_t>(out))
|
|
: field_addr + out);
|
|
} else if (app != 0) {
|
|
return false;
|
|
}
|
|
|
|
*value = out;
|
|
return true;
|
|
}
|
|
|
|
// Kyty does not create guest signal-return trampoline frames.
|
|
int KYTY_SYSV_ABI KernelIsSignalReturn(uint64_t /*pc*/) {
|
|
return 0;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI KernelGetModuleInfoForUnwind(uint64_t addr, int flags,
|
|
ModuleInfoForUnwind* info) {
|
|
if (flags >= 3) {
|
|
if (info != nullptr) {
|
|
std::memset(info, 0, sizeof(ModuleInfoForUnwind));
|
|
}
|
|
return KERNEL_ERROR_EINVAL;
|
|
}
|
|
|
|
if (info == nullptr) {
|
|
return KERNEL_ERROR_EFAULT;
|
|
}
|
|
|
|
if (info->st_size < sizeof(ModuleInfoForUnwind)) {
|
|
return KERNEL_ERROR_EINVAL;
|
|
}
|
|
|
|
auto* rt = Common::Singleton<Loader::RuntimeLinker>::Instance();
|
|
auto* program = rt->FindProgramByAddr(addr);
|
|
if (program == nullptr || program->elf == nullptr) {
|
|
if (addr < 0x800000000ull) {
|
|
// TODO(unwind): guest unwinding can reach a Kyty host return address below the guest VA
|
|
// range. Report a synthetic boundary with no unwind tables so libc stops cleanly
|
|
// instead of raising.
|
|
std::memset(info, 0, sizeof(ModuleInfoForUnwind));
|
|
info->st_size = sizeof(ModuleInfoForUnwind);
|
|
std::snprintf(info->name, sizeof(info->name), "%s", "KytyHostBoundary");
|
|
info->seg0_addr = addr & ~0xfffffull;
|
|
info->seg0_size = 0x100000;
|
|
return OK;
|
|
}
|
|
|
|
return KERNEL_ERROR_ESRCH;
|
|
}
|
|
|
|
const auto* ehdr = program->elf->GetEhdr();
|
|
const auto* phdr = program->elf->GetPhdr();
|
|
if (ehdr == nullptr || phdr == nullptr) {
|
|
return KERNEL_ERROR_ESRCH;
|
|
}
|
|
|
|
constexpr Loader::Elf64_Word PT_GNU_EH_FRAME = 0x6474e550;
|
|
|
|
uint64_t eh_frame_hdr_size = 0;
|
|
|
|
std::memset(info, 0, sizeof(ModuleInfoForUnwind));
|
|
info->st_size = sizeof(ModuleInfoForUnwind);
|
|
|
|
if (program->dynamic_info != nullptr && program->dynamic_info->so_name != nullptr &&
|
|
program->dynamic_info->so_name[0] != '\0') {
|
|
std::snprintf(info->name, sizeof(info->name), "%s", program->dynamic_info->so_name);
|
|
} else {
|
|
auto fallback_name = Common::PathToString(program->file_name.filename());
|
|
std::snprintf(info->name, sizeof(info->name), "%s", fallback_name.c_str());
|
|
}
|
|
|
|
uint64_t image_start = UINT64_MAX;
|
|
uint64_t image_end = 0;
|
|
|
|
for (int i = 0; i < ehdr->e_phnum; i++) {
|
|
if (phdr[i].p_memsz == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (phdr[i].p_type == Loader::PT_LOAD || phdr[i].p_type == Loader::PT_OS_RELRO) {
|
|
const auto start = program->base_vaddr + phdr[i].p_vaddr;
|
|
const auto end = start + phdr[i].p_memsz;
|
|
image_start = std::min(image_start, start);
|
|
image_end = std::max(image_end, end);
|
|
}
|
|
|
|
if (phdr[i].p_type == PT_GNU_EH_FRAME) {
|
|
info->eh_frame_hdr_addr = program->base_vaddr + phdr[i].p_vaddr;
|
|
eh_frame_hdr_size = phdr[i].p_memsz;
|
|
}
|
|
}
|
|
|
|
const auto* shdr = program->elf->GetShdr();
|
|
if (shdr != nullptr) {
|
|
for (int i = 0; i < ehdr->e_shnum; i++) {
|
|
const char* section_name = program->elf->GetSectionName(i);
|
|
if (section_name != nullptr && std::strcmp(section_name, ".eh_frame") == 0) {
|
|
info->eh_frame_addr = program->base_vaddr + shdr[i].sh_addr;
|
|
info->eh_frame_size = shdr[i].sh_size;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (info->eh_frame_hdr_addr != 0 && eh_frame_hdr_size >= 4) {
|
|
const auto* hdr = reinterpret_cast<const uint8_t*>(info->eh_frame_hdr_addr);
|
|
const auto* end = hdr + eh_frame_hdr_size;
|
|
if (hdr[0] == 1) {
|
|
uint64_t eh_frame_addr = 0;
|
|
if (DecodeEhFramePointer(hdr + 4, end, hdr[1], info->eh_frame_hdr_addr + 4,
|
|
&eh_frame_addr)) {
|
|
info->eh_frame_addr = eh_frame_addr;
|
|
if (info->eh_frame_size == 0 && info->eh_frame_hdr_addr > eh_frame_addr) {
|
|
info->eh_frame_size = info->eh_frame_hdr_addr - eh_frame_addr;
|
|
} else if (info->eh_frame_size == 0 &&
|
|
program->base_size_aligned >
|
|
info->eh_frame_hdr_addr - program->base_vaddr) {
|
|
info->eh_frame_size = program->base_size_aligned -
|
|
(info->eh_frame_hdr_addr - program->base_vaddr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (image_start != UINT64_MAX && image_end > image_start) {
|
|
info->seg0_addr = image_start;
|
|
info->seg0_size = image_end - image_start;
|
|
}
|
|
|
|
if (info->eh_frame_addr != 0 && info->eh_frame_size == 0) {
|
|
for (int i = 0; i < ehdr->e_phnum; i++) {
|
|
if (phdr[i].p_memsz == 0 ||
|
|
(phdr[i].p_type != Loader::PT_LOAD && phdr[i].p_type != Loader::PT_OS_RELRO)) {
|
|
continue;
|
|
}
|
|
|
|
const auto start = program->base_vaddr + phdr[i].p_vaddr;
|
|
const auto end = start + phdr[i].p_memsz;
|
|
if (info->eh_frame_addr >= start && info->eh_frame_addr < end) {
|
|
info->eh_frame_size = end - info->eh_frame_addr;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
static int KYTY_SYSV_ABI KernelGetModuleInfoFromAddr(uint64_t addr, int n, ModuleInfo* r) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\taddr = %016" PRIx64 "\n"
|
|
"\tn = %d\n",
|
|
addr, n);
|
|
|
|
EXIT_NOT_IMPLEMENTED(n != 2);
|
|
EXIT_NOT_IMPLEMENTED(r == nullptr);
|
|
|
|
auto* rt = Common::Singleton<Loader::RuntimeLinker>::Instance();
|
|
|
|
auto* p = rt->FindProgramByAddr(addr);
|
|
|
|
if (p == nullptr) {
|
|
LOGF("\thandle: not found\n");
|
|
r->handle = 0;
|
|
return -1;
|
|
}
|
|
|
|
r->handle = p->unique_id;
|
|
|
|
LOGF("\thandle: %d\n", r->handle);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void KYTY_SYSV_ABI KernelDebugRaiseExceptionOnReleaseMode(int /*c1*/, int /*c2*/) {
|
|
PRINT_NAME();
|
|
}
|
|
|
|
static void KYTY_SYSV_ABI KernelDebugRaiseException(int /*c1*/, int /*c2*/) {
|
|
PRINT_NAME();
|
|
}
|
|
|
|
static void KYTY_SYSV_ABI exit(int code) {
|
|
PRINT_NAME();
|
|
|
|
::exit(code);
|
|
}
|
|
|
|
static KYTY_SYSV_ABI MallocReplace* KernelGetSanitizerMallocReplaceExternal() {
|
|
PRINT_NAME();
|
|
|
|
static MallocReplace ret;
|
|
|
|
return &ret;
|
|
}
|
|
|
|
static KYTY_SYSV_ABI NewReplace* KernelGetSanitizerNewReplaceExternal() {
|
|
PRINT_NAME();
|
|
|
|
static NewReplace ret;
|
|
|
|
return &ret;
|
|
}
|
|
|
|
static KYTY_SYSV_ABI int elf_phdr_match_addr(ModuleInfo* m, uint64_t dtor_vaddr) {
|
|
PRINT_NAME();
|
|
|
|
EXIT_NOT_IMPLEMENTED(m == nullptr);
|
|
|
|
auto* rt = Common::Singleton<Loader::RuntimeLinker>::Instance();
|
|
auto* p = rt->FindProgramByAddr(dtor_vaddr);
|
|
int result = (p != nullptr && p->unique_id == m->handle) ? 1 : 0;
|
|
|
|
LOGF("\thandle = %" PRId32 "\n"
|
|
"\tdtor_vaddr = %016" PRIx64 "\n"
|
|
"\tmatch = %s\n",
|
|
m->handle, dtor_vaddr, result == 1 ? "true" : "false");
|
|
|
|
return result;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI KernelUuidCreate(uint32_t* uuid) {
|
|
PRINT_NAME();
|
|
|
|
if (uuid == nullptr) {
|
|
return KERNEL_ERROR_EINVAL;
|
|
}
|
|
|
|
static thread_local std::mt19937 rng(std::random_device {}());
|
|
uuid[0] = rng();
|
|
uuid[1] = rng();
|
|
uuid[2] = rng();
|
|
uuid[3] = rng();
|
|
|
|
return OK;
|
|
}
|
|
|
|
static KYTY_SYSV_ABI int KernelGetOpenPsId(void* open_ps_id) {
|
|
PRINT_NAME();
|
|
|
|
if (open_ps_id == nullptr) {
|
|
return KERNEL_ERROR_EINVAL;
|
|
}
|
|
|
|
static constexpr uint8_t id[16] = {
|
|
0x4b, 0x79, 0x74, 0x79, 0x4f, 0x70, 0x65, 0x6e,
|
|
0x50, 0x73, 0x49, 0x64, 0x00, 0x00, 0x00, 0x01,
|
|
};
|
|
|
|
std::memcpy(open_ps_id, id, sizeof(id));
|
|
|
|
return OK;
|
|
}
|
|
|
|
static KYTY_SYSV_ABI void pthread_cxa_finalize(void* /*p*/) {
|
|
PRINT_NAME();
|
|
}
|
|
|
|
void KYTY_SYSV_ABI KernelSetThreadAtexitCount(get_thread_atexit_count_func_t func) {
|
|
PRINT_NAME();
|
|
|
|
EXIT_NOT_IMPLEMENTED(g_get_thread_atexit_count_func != nullptr);
|
|
|
|
g_get_thread_atexit_count_func = func;
|
|
}
|
|
|
|
void KYTY_SYSV_ABI KernelSetThreadAtexitReport(thread_atexit_report_func_t func) {
|
|
PRINT_NAME();
|
|
|
|
EXIT_NOT_IMPLEMENTED(g_thread_atexit_report_func != nullptr);
|
|
|
|
g_thread_atexit_report_func = func;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI KernelRtldThreadAtexitIncrement(uint64_t* /*c*/) {
|
|
PRINT_NAME();
|
|
|
|
//__sync_fetch_and_add(c, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI KernelRtldThreadAtexitDecrement(uint64_t* /*c*/) {
|
|
PRINT_NAME();
|
|
|
|
//__sync_fetch_and_sub(c, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static KYTY_SYSV_ABI int KernelGetCurrentCpu() {
|
|
PRINT_NAME();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI clock_gettime(int clock_id, LibKernel::KernelTimespec* time) {
|
|
PRINT_NAME();
|
|
|
|
return POSIX_CALL(LibKernel::KernelClockGettime(clock_id, time));
|
|
}
|
|
|
|
int KYTY_SYSV_ABI clock_getres(int clock_id, LibKernel::KernelTimespec* res) {
|
|
PRINT_NAME();
|
|
|
|
return POSIX_CALL(LibKernel::KernelClockGetres(clock_id, res));
|
|
}
|
|
|
|
void KYTY_SYSV_ABI KernelSetGPO(uint32_t bits) {
|
|
g_gpo_state_bits.store(bits, std::memory_order_relaxed);
|
|
|
|
static std::atomic_uint32_t call_count {0};
|
|
auto count = call_count.fetch_add(1, std::memory_order_relaxed);
|
|
if (count < 16 || (count % 1024) == 0) {
|
|
PRINT_NAME();
|
|
LOGF("\tcall = %u, bits = 0x%08" PRIx32 "\n", count + 1, bits);
|
|
}
|
|
}
|
|
|
|
uint64_t KYTY_SYSV_ABI KernelGetGPI() {
|
|
auto bits = g_gpo_state_bits.load(std::memory_order_relaxed);
|
|
|
|
static std::atomic_uint32_t call_count {0};
|
|
auto count = call_count.fetch_add(1, std::memory_order_relaxed);
|
|
if (count < 16 || (count % 1024) == 0) {
|
|
PRINT_NAME();
|
|
LOGF("\tcall = %u, return = 0x%016" PRIx64 "\n", count + 1, bits);
|
|
}
|
|
|
|
return bits;
|
|
}
|
|
|
|
} // namespace LibKernel
|
|
|
|
namespace LibKernelWriteThrottling {
|
|
|
|
LIB_VERSION("libkernel_write_throttling", 1, "libkernel", 1, 1);
|
|
|
|
static uint64_t KYTY_SYSV_ABI WriteThrottlingStub() {
|
|
return 0;
|
|
}
|
|
|
|
LIB_DEFINE(InitLibKernelWriteThrottling) {
|
|
LIB_FUNC("YFC3dBBipj8", WriteThrottlingStub);
|
|
}
|
|
|
|
} // namespace LibKernelWriteThrottling
|
|
|
|
namespace Posix {
|
|
|
|
LIB_VERSION("Posix", 1, "libkernel", 1, 1);
|
|
|
|
int KYTY_SYSV_ABI getpagesize() {
|
|
PRINT_NAME();
|
|
|
|
return 0x4000;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI clock_gettime(int clock_id, LibKernel::KernelTimespec* time) {
|
|
PRINT_NAME();
|
|
|
|
return POSIX_CALL(LibKernel::KernelClockGettime(clock_id, time));
|
|
}
|
|
|
|
int KYTY_SYSV_ABI clock_getres(int clock_id, LibKernel::KernelTimespec* res) {
|
|
PRINT_NAME();
|
|
|
|
return POSIX_CALL(LibKernel::KernelClockGetres(clock_id, res));
|
|
}
|
|
|
|
struct KernelTimezone {
|
|
int32_t tz_minuteswest;
|
|
int32_t tz_dsttime;
|
|
};
|
|
|
|
int KYTY_SYSV_ABI gettimeofday(LibKernel::KernelTimeval* time, KernelTimezone* timezone) {
|
|
PRINT_NAME();
|
|
|
|
if (time != nullptr) {
|
|
int result = LibKernel::KernelGettimeofday(time);
|
|
if (result != OK) {
|
|
*GetErrorAddr() = LibKernel::KernelToPosix(result);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (timezone != nullptr) {
|
|
timezone->tz_minuteswest = 0;
|
|
timezone->tz_dsttime = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI nanosleep(const LibKernel::KernelTimespec* rqtp,
|
|
LibKernel::KernelTimespec* rmtp) {
|
|
PRINT_NAME();
|
|
|
|
return POSIX_CALL(LibKernel::KernelNanosleep(rqtp, rmtp));
|
|
}
|
|
|
|
int KYTY_SYSV_ABI stat(const char* path, LibKernel::FileSystem::FileStat* sb) {
|
|
PRINT_NAME();
|
|
|
|
return POSIX_CALL(LibKernel::FileSystem::KernelStat(path, sb));
|
|
}
|
|
|
|
int KYTY_SYSV_ABI mkdir(const char* path, uint16_t mode) {
|
|
PRINT_NAME();
|
|
|
|
return POSIX_CALL(LibKernel::FileSystem::KernelMkdir(path, mode));
|
|
}
|
|
|
|
int64_t KYTY_SYSV_ABI lseek(int d, int64_t offset, int whence) {
|
|
PRINT_NAME();
|
|
|
|
return POSIX_N_CALL(LibKernel::FileSystem::KernelLseek(d, offset, whence));
|
|
}
|
|
|
|
int64_t KYTY_SYSV_ABI pread(int d, void* buf, size_t nbytes, int64_t offset) {
|
|
PRINT_NAME();
|
|
|
|
return POSIX_N_CALL(LibKernel::FileSystem::KernelPread(d, buf, nbytes, offset));
|
|
}
|
|
|
|
int64_t KYTY_SYSV_ABI pwrite(int d, const void* buf, size_t nbytes, int64_t offset) {
|
|
PRINT_NAME();
|
|
|
|
return POSIX_N_CALL(LibKernel::FileSystem::KernelPwrite(d, buf, nbytes, offset));
|
|
}
|
|
|
|
int KYTY_SYSV_ABI flock(int d, int operation) {
|
|
PRINT_NAME();
|
|
|
|
constexpr int lock_sh = 0x01;
|
|
constexpr int lock_ex = 0x02;
|
|
constexpr int lock_nb = 0x04;
|
|
constexpr int lock_un = 0x08;
|
|
|
|
auto lock_mode = operation & ~(lock_nb);
|
|
if (lock_mode != lock_sh && lock_mode != lock_ex && lock_mode != lock_un) {
|
|
*GetErrorAddr() = POSIX_EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
LibKernel::FileSystem::FileStat sb {};
|
|
auto result = LibKernel::FileSystem::KernelFstat(d, &sb);
|
|
if (result != OK) {
|
|
*GetErrorAddr() = LibKernel::KernelToPosix(result);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int64_t KYTY_SYSV_ABI fstat(int d, LibKernel::FileSystem::FileStat* sb) {
|
|
PRINT_NAME();
|
|
|
|
return POSIX_N_CALL(LibKernel::FileSystem::KernelFstat(d, sb));
|
|
}
|
|
|
|
int KYTY_SYSV_ABI ftruncate(int d, int64_t length) {
|
|
PRINT_NAME();
|
|
|
|
return POSIX_CALL(LibKernel::FileSystem::KernelFtruncate(d, length));
|
|
}
|
|
|
|
int KYTY_SYSV_ABI socket(int family, int type, int protocol) {
|
|
PRINT_NAME();
|
|
return Network::Net::Socket(family, type, protocol);
|
|
}
|
|
|
|
int KYTY_SYSV_ABI bind(int s, const void* addr, uint32_t addrlen) {
|
|
PRINT_NAME();
|
|
return Network::Net::Bind(s, addr, addrlen);
|
|
}
|
|
|
|
int KYTY_SYSV_ABI connect(int s, const void* addr, uint32_t addrlen) {
|
|
PRINT_NAME();
|
|
return Network::Net::Connect(s, addr, addrlen);
|
|
}
|
|
|
|
int KYTY_SYSV_ABI listen(int s, int backlog) {
|
|
PRINT_NAME();
|
|
return Network::Net::Listen(s, backlog);
|
|
}
|
|
|
|
int KYTY_SYSV_ABI accept(int s, void* addr, uint32_t* addrlen) {
|
|
PRINT_NAME();
|
|
return Network::Net::Accept(s, addr, addrlen);
|
|
}
|
|
|
|
int KYTY_SYSV_ABI getsockname(int s, void* addr, uint32_t* addrlen) {
|
|
PRINT_NAME();
|
|
return Network::Net::Getsockname(s, addr, addrlen);
|
|
}
|
|
|
|
int KYTY_SYSV_ABI getsockopt(int s, int level, int optname, void* optval, uint32_t* optlen) {
|
|
PRINT_NAME();
|
|
return Network::Net::Getsockopt(s, level, optname, optval, optlen);
|
|
}
|
|
|
|
int KYTY_SYSV_ABI setsockopt(int s, int level, int optname, const void* optval, uint32_t optlen) {
|
|
PRINT_NAME();
|
|
return Network::Net::Setsockopt(s, level, optname, optval, optlen);
|
|
}
|
|
|
|
int KYTY_SYSV_ABI select(int nfds, void* readfds, void* writefds, void* exceptfds,
|
|
const void* timeout) {
|
|
PRINT_NAME();
|
|
return Network::Net::Select(nfds, readfds, writefds, exceptfds, timeout);
|
|
}
|
|
|
|
int64_t KYTY_SYSV_ABI send(int s, const void* buf, uint64_t len, int flags) {
|
|
PRINT_NAME();
|
|
return Network::Net::Send(s, buf, len, flags);
|
|
}
|
|
|
|
int64_t KYTY_SYSV_ABI sendto(int s, const void* buf, uint64_t len, int flags, const void* addr,
|
|
uint32_t addrlen) {
|
|
PRINT_NAME();
|
|
return Network::Net::Sendto(s, buf, len, flags, addr, addrlen);
|
|
}
|
|
|
|
int64_t KYTY_SYSV_ABI recv(int s, void* buf, uint64_t len, int flags) {
|
|
PRINT_NAME();
|
|
return Network::Net::Recv(s, buf, len, flags);
|
|
}
|
|
|
|
int64_t KYTY_SYSV_ABI recvfrom(int s, void* buf, uint64_t len, int flags, void* addr,
|
|
uint32_t* addrlen) {
|
|
PRINT_NAME();
|
|
return Network::Net::Recvfrom(s, buf, len, flags, addr, addrlen);
|
|
}
|
|
|
|
int KYTY_SYSV_ABI inet_pton(int af, const char* src, void* dst) {
|
|
PRINT_NAME();
|
|
|
|
const int result = Network::Net::NetInetPton(af, src, dst);
|
|
if (result < 0) {
|
|
*GetErrorAddr() = Network::NetToPosix(result);
|
|
return -1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
const char* KYTY_SYSV_ABI inet_ntop(int af, const void* src, char* dst, uint32_t size) {
|
|
PRINT_NAME();
|
|
|
|
const char* result = Network::Net::NetInetNtop(af, src, dst, size);
|
|
if (result == nullptr) {
|
|
*GetErrorAddr() = (af == 2 ? POSIX_ENOSPC : POSIX_EAFNOSUPPORT);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
uint64_t KYTY_SYSV_ABI cfwBSQyr5Ys(uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5,
|
|
uint64_t a6) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t return = 0x%016" PRIx64 "\n"
|
|
"\t a1 = 0x%016" PRIx64 "\n"
|
|
"\t a2 = 0x%016" PRIx64 "\n"
|
|
"\t a3 = 0x%016" PRIx64 "\n"
|
|
"\t a4 = 0x%016" PRIx64 "\n"
|
|
"\t a5 = 0x%016" PRIx64 "\n"
|
|
"\t a6 = 0x%016" PRIx64 "\n"
|
|
"\t result = 0\n",
|
|
reinterpret_cast<uint64_t>(__builtin_return_address(0)), a1, a2, a3, a4, a5, a6);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void LogExperimentalSyncOnAddress(std::atomic_bool& logged, const char* function_name) {
|
|
if (!logged.exchange(true, std::memory_order_relaxed)) {
|
|
::printf("WARNING: %s is experimental\n", function_name);
|
|
LOGF("WARNING: %s is experimental\n", function_name);
|
|
}
|
|
}
|
|
|
|
int KYTY_SYSV_ABI KernelSyncOnAddressWait(volatile uint32_t* address, uint32_t expected,
|
|
const uint32_t* timeout_micros) {
|
|
return LibKernel::SyncOnAddress::Wait32(address, expected, timeout_micros,
|
|
LibKernel::KernelDispatchPendingSignalForCurrentThread);
|
|
}
|
|
|
|
int KYTY_SYSV_ABI KernelSyncOnAddressWait32(volatile uint32_t* address, uint32_t expected,
|
|
const uint32_t* timeout_micros) {
|
|
static std::atomic_bool logged {false};
|
|
LogExperimentalSyncOnAddress(logged, "sceKernelSyncOnAddressWait32");
|
|
return LibKernel::SyncOnAddress::Wait32(address, expected, timeout_micros,
|
|
LibKernel::KernelDispatchPendingSignalForCurrentThread);
|
|
}
|
|
|
|
int KYTY_SYSV_ABI KernelSyncOnAddressWait64(volatile uint64_t* address, uint64_t expected,
|
|
const uint32_t* timeout_micros) {
|
|
static std::atomic_bool logged {false};
|
|
LogExperimentalSyncOnAddress(logged, "sceKernelSyncOnAddressWait64");
|
|
return LibKernel::SyncOnAddress::Wait64(address, expected, timeout_micros,
|
|
LibKernel::KernelDispatchPendingSignalForCurrentThread);
|
|
}
|
|
|
|
int KYTY_SYSV_ABI KernelSyncOnAddressWake(volatile void* address, int32_t count) {
|
|
return LibKernel::SyncOnAddress::Wake(address, count);
|
|
}
|
|
|
|
int KYTY_SYSV_ABI UmtxOp(volatile void* address, int operation, uint64_t value,
|
|
void* uaddr, const void* timeout) {
|
|
constexpr int UMTX_OP_WAIT = 2;
|
|
constexpr int UMTX_OP_WAKE = 3;
|
|
|
|
EXIT_NOT_IMPLEMENTED(uaddr != nullptr);
|
|
|
|
switch (operation) {
|
|
case UMTX_OP_WAIT:
|
|
if (timeout != nullptr) {
|
|
LibKernel::KernelTimespec duration {};
|
|
std::memcpy(&duration, timeout, sizeof(duration));
|
|
if (duration.tv_sec < 0 || duration.tv_nsec < 0 || duration.tv_nsec >= 1000000000) {
|
|
*GetErrorAddr() = POSIX_EINVAL;
|
|
return -1;
|
|
}
|
|
const auto max_ns = std::chrono::nanoseconds::max().count();
|
|
const auto timeout_ns = duration.tv_sec > (max_ns - duration.tv_nsec) / 1000000000
|
|
? max_ns
|
|
: duration.tv_sec * 1000000000 + duration.tv_nsec;
|
|
return POSIX_CALL(LibKernel::SyncOnAddress::Wait64(
|
|
static_cast<volatile uint64_t*>(address), value, std::chrono::nanoseconds(timeout_ns),
|
|
LibKernel::KernelDispatchPendingSignalForCurrentThread));
|
|
}
|
|
return POSIX_CALL(LibKernel::SyncOnAddress::Wait64(
|
|
static_cast<volatile uint64_t*>(address), value, nullptr,
|
|
LibKernel::KernelDispatchPendingSignalForCurrentThread));
|
|
case UMTX_OP_WAKE:
|
|
return POSIX_CALL(LibKernel::SyncOnAddress::Wake(address, static_cast<int32_t>(value)));
|
|
default: EXIT("Unsupported _umtx_op operation: %d\n", operation);
|
|
}
|
|
}
|
|
|
|
LIB_DEFINE(InitLibKernel_1_Posix) {
|
|
LIB_FUNC("k+AXqu2-eBc", getpagesize);
|
|
LIB_FUNC("lLMT9vJAck0", clock_gettime);
|
|
LIB_FUNC("smIj7eqzZE8", clock_getres);
|
|
LIB_FUNC("n88vx3C5nW8", gettimeofday);
|
|
LIB_FUNC("NhpspxdjEKU", nanosleep);
|
|
LIB_FUNC("yS8U2TGCe1A", nanosleep);
|
|
LIB_FUNC("E6ao34wPw+U", stat);
|
|
LIB_FUNC("JGMio+21L4c", mkdir);
|
|
LIB_FUNC("ih4CD9-gghM", Posix::ftruncate);
|
|
LIB_FUNC("pDuPEf3m4fI", Posix::sem_init);
|
|
LIB_FUNC("cDW233RAwWo", Posix::sem_destroy);
|
|
LIB_FUNC("YCV5dGGBcCo", Posix::sem_wait);
|
|
LIB_FUNC("WBWzsRifCEA", Posix::sem_trywait);
|
|
LIB_FUNC("4SbrhCozqQU", Posix::sem_reltimedwait_np);
|
|
LIB_FUNC("w5IHyvahg-o", Posix::sem_timedwait);
|
|
LIB_FUNC("IKP8typ0QUk", Posix::sem_post);
|
|
LIB_FUNC("Bq+LRV-N6Hk", Posix::sem_getvalue);
|
|
|
|
LIB_FUNC("OxhIB8LB-PQ", Posix::pthread_create);
|
|
LIB_FUNC("Jmi+9w9u0E4", Posix::pthread_create_name_np);
|
|
LIB_FUNC("+U1R4WtXvoc", Posix::pthread_detach);
|
|
LIB_FUNC("FJrT5LuUBAU", Posix::pthread_exit);
|
|
LIB_FUNC("EotR8a3ASf4", Posix::pthread_self);
|
|
LIB_FUNC("9vyP6Z7bqzc", Posix::pthread_rename_np);
|
|
LIB_FUNC("lZzFeSxPl08", Posix::pthread_setcancelstate);
|
|
LIB_FUNC("a2P9wYGeZvc", Posix::pthread_setprio);
|
|
LIB_FUNC("FIs3-UQT9sg", Posix::pthread_getschedparam);
|
|
LIB_FUNC("P41kTWUS3EI", Posix::pthread_getschedparam);
|
|
LIB_FUNC("Xs9hdiD7sAA", Posix::pthread_setschedparam);
|
|
LIB_FUNC("oIRFTjoILbg", Posix::pthread_setschedparam);
|
|
LIB_FUNC("B5GmVDKwpn0", Posix::pthread_yield);
|
|
LIB_FUNC("h9CcP3J0oVM", Posix::pthread_join);
|
|
LIB_FUNC("wtkt-teR1so", Posix::pthread_attr_init);
|
|
LIB_FUNC("zHchY8ft5pk", Posix::pthread_attr_destroy);
|
|
LIB_FUNC("vQm4fDEsWi8", Posix::pthread_attr_getstack);
|
|
LIB_FUNC("2Q0z6rnBrTE", Posix::pthread_attr_setstacksize);
|
|
LIB_FUNC("Ucsu-OK+els", Posix::pthread_attr_get_np);
|
|
LIB_FUNC("RtLRV-pBTTY", Posix::pthread_attr_getschedpolicy);
|
|
LIB_FUNC("JarMIy8kKEY", Posix::pthread_attr_setschedpolicy);
|
|
LIB_FUNC("E+tyo3lp5Lw", Posix::pthread_attr_setdetachstate);
|
|
LIB_FUNC("euKRgm0Vn2M", Posix::pthread_attr_setschedparam);
|
|
LIB_FUNC("7ZlAakEf0Qg", Posix::pthread_attr_setinheritsched);
|
|
LIB_FUNC("0qOtCR-ZHck", Posix::pthread_attr_getstacksize);
|
|
LIB_FUNC("VUT1ZSrHT0I", Posix::pthread_attr_getdetachstate);
|
|
LIB_FUNC("JKyG3SWyA10", Posix::pthread_attr_setguardsize);
|
|
LIB_FUNC("JNkVVsVDmOk", Posix::pthread_attr_getguardsize);
|
|
LIB_FUNC("qlk9pSLsUmM", Posix::pthread_attr_getschedparam);
|
|
LIB_FUNC("FXPWHNk8Of0", Posix::pthread_attr_getschedparam);
|
|
LIB_FUNC("7Xl257M4VNI", LibKernel::PthreadEqual);
|
|
LIB_FUNC("7H0iTOciTLo", Posix::pthread_mutex_lock);
|
|
LIB_FUNC("K-jXhbt2gn4", Posix::pthread_mutex_trylock);
|
|
LIB_FUNC("2Z+PpY6CaJg", Posix::pthread_mutex_unlock);
|
|
LIB_FUNC("ttHNfU+qDBU", Posix::pthread_mutex_init);
|
|
LIB_FUNC("dQHWEsJtoE4", Posix::pthread_mutexattr_init);
|
|
LIB_FUNC("mDmgMOGVUqg", Posix::pthread_mutexattr_settype);
|
|
LIB_FUNC("5txKfcMUAok", Posix::pthread_mutexattr_setprotocol);
|
|
LIB_FUNC("HF7lK46xzjY", Posix::pthread_mutexattr_destroy);
|
|
LIB_FUNC("ltCfaGr2JGE", Posix::pthread_mutex_destroy);
|
|
LIB_FUNC("mkx2fVhNMsg", Posix::pthread_cond_broadcast);
|
|
LIB_FUNC("2MOy+rUfuhQ", Posix::pthread_cond_signal);
|
|
LIB_FUNC("0TyVk4MSLt0", Posix::pthread_cond_init);
|
|
LIB_FUNC("Op8TBGY5KHg", Posix::pthread_cond_wait);
|
|
LIB_FUNC("Z4QosVuAsA0", Posix::pthread_once);
|
|
LIB_FUNC("1471ajPzxh0", Posix::pthread_rwlock_destroy);
|
|
LIB_FUNC("ytQULN-nhL4", Posix::pthread_rwlock_init);
|
|
LIB_FUNC("sIlRvQqsN2Y", Posix::pthread_rwlock_wrlock);
|
|
LIB_FUNC("SFxTMOfuCkE", LibKernel::PthreadRwlockTryrdlock);
|
|
LIB_FUNC("XhWHn6P5R7U", LibKernel::PthreadRwlockTrywrlock);
|
|
LIB_FUNC("mqULNdimTn0", Posix::pthread_key_create);
|
|
LIB_FUNC("6BpEZuDT7YI", Posix::pthread_key_delete);
|
|
LIB_FUNC("WrOLvHU0yQM", Posix::pthread_setspecific);
|
|
LIB_FUNC("0-KXaS70xy4", Posix::pthread_getspecific);
|
|
LIB_FUNC("-quPa4SEJUw", Posix::pthread_getstack);
|
|
LIB_FUNC("YQOfxL4QfeU", LibKernel::Memory::KernelMprotect);
|
|
LIB_FUNC("UqDGjXA5yUM", LibKernel::Memory::KernelMunmap);
|
|
LIB_FUNC("AqBioC2vF3I", LibKernel::read);
|
|
LIB_FUNC("TU-d9PfIHPM", Posix::socket);
|
|
LIB_FUNC("KuOmgKoqCdY", Posix::bind);
|
|
LIB_FUNC("XVL8So3QJUk", Posix::connect);
|
|
LIB_FUNC("pxnCmagrtao", Posix::listen);
|
|
LIB_FUNC("3e+4Iv7IJ8U", Posix::accept);
|
|
LIB_FUNC("RenI1lL1WFk", Posix::getsockname);
|
|
LIB_FUNC("6O8EwYOgH9Y", Posix::getsockopt);
|
|
LIB_FUNC("fFxGkxF2bVo", Posix::setsockopt);
|
|
LIB_FUNC("T8fER+tIGgk", Posix::select);
|
|
LIB_FUNC("fZOeZIOEmLw", Posix::send);
|
|
LIB_FUNC("oBr313PppNE", Posix::sendto);
|
|
LIB_FUNC("Ez8xjo9UF4E", Posix::recv);
|
|
LIB_FUNC("lUk6wrGXyMw", Posix::recvfrom);
|
|
LIB_FUNC("4n51s0zEf0c", Posix::inet_pton);
|
|
LIB_FUNC("5jRCs2axtr4", Posix::inet_ntop);
|
|
LIB_FUNC("cfwBSQyr5Ys", cfwBSQyr5Ys);
|
|
}
|
|
|
|
} // namespace Posix
|
|
|
|
namespace Coredump {
|
|
|
|
LIB_VERSION("Coredump", 1, "Coredump", 1, 1);
|
|
|
|
static uint64_t g_coredump_handler = 0;
|
|
static uint64_t g_coredump_handler_context = 0;
|
|
|
|
int KYTY_SYSV_ABI sceCoredumpRegisterCoredumpHandler(uint64_t handler, size_t stack_size,
|
|
uint64_t context) {
|
|
PRINT_NAME();
|
|
|
|
g_coredump_handler = handler;
|
|
g_coredump_handler_context = context;
|
|
|
|
LOGF("\t handler = 0x%016" PRIx64 "\n"
|
|
"\t stack = 0x%016" PRIx64 "\n"
|
|
"\t context = 0x%016" PRIx64 "\n",
|
|
handler, static_cast<uint64_t>(stack_size), context);
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI sceCoredumpUnregisterCoredumpHandler() {
|
|
PRINT_NAME();
|
|
|
|
g_coredump_handler = 0;
|
|
g_coredump_handler_context = 0;
|
|
|
|
return OK;
|
|
}
|
|
|
|
} // namespace Coredump
|
|
|
|
namespace Fiber {
|
|
|
|
LIB_VERSION("Fiber", 1, "Fiber", 1, 1);
|
|
|
|
constexpr int32_t FIBER_ERROR_NULL = -2141650943; /* 0x80590001 */
|
|
constexpr int32_t FIBER_ERROR_ALIGNMENT = -2141650942; /* 0x80590002 */
|
|
constexpr int32_t FIBER_ERROR_RANGE = -2141650941; /* 0x80590003 */
|
|
constexpr int32_t FIBER_ERROR_INVALID = -2141650940; /* 0x80590004 */
|
|
constexpr int32_t FIBER_ERROR_PERMISSION = -2141650939; /* 0x80590005 */
|
|
constexpr int32_t FIBER_ERROR_STATE = -2141650938; /* 0x80590006 */
|
|
|
|
constexpr uint32_t FIBER_MAGIC_START = 0xdef1649c;
|
|
constexpr uint32_t FIBER_MAGIC_END = 0xb37592a0;
|
|
constexpr uint32_t FIBER_OPT_MAGIC = 0xbb40e64d;
|
|
constexpr uint64_t FIBER_STACK_MAGIC = 0x7149f2ca7149f2ca;
|
|
constexpr uint64_t FIBER_CONTEXT_MIN_SIZE = 512;
|
|
constexpr size_t FIBER_MAX_NAME_LENGTH = 31;
|
|
constexpr uint32_t FIBER_STATE_RUNNING = 1;
|
|
constexpr uint32_t FIBER_STATE_IDLE = 2;
|
|
constexpr uint32_t FIBER_STATE_TERMINATED = 3;
|
|
constexpr uint32_t FIBER_FLAG_SET_FPU_REGS = 0x100;
|
|
|
|
using FiberEntry = KYTY_SYSV_ABI void (*)(uint64_t arg_on_initialize, uint64_t arg_on_run);
|
|
|
|
struct FiberOptParam {
|
|
uint32_t magic;
|
|
};
|
|
|
|
struct FiberCpuContext {
|
|
uint64_t rbx;
|
|
uint64_t rbp;
|
|
uint64_t rdi;
|
|
uint64_t rsi;
|
|
uint64_t r12;
|
|
uint64_t r13;
|
|
uint64_t r14;
|
|
uint64_t r15;
|
|
uint64_t rsp;
|
|
uint64_t rip;
|
|
};
|
|
|
|
static_assert(sizeof(FiberCpuContext) == 80);
|
|
|
|
struct FiberObject {
|
|
uint32_t magic_start;
|
|
uint32_t state;
|
|
FiberEntry entry;
|
|
uint64_t arg_on_initialize;
|
|
void* addr_context;
|
|
uint64_t size_context;
|
|
char name[FIBER_MAX_NAME_LENGTH + 1];
|
|
void* context;
|
|
uint32_t flags;
|
|
uint32_t padding;
|
|
void* context_start;
|
|
void* context_end;
|
|
FiberCpuContext saved_context;
|
|
uint64_t arg_on_run;
|
|
uint64_t arg_on_return;
|
|
bool context_valid;
|
|
uint32_t magic_end;
|
|
};
|
|
|
|
static_assert(sizeof(FiberObject) <= 256);
|
|
|
|
struct FiberInfo {
|
|
uint64_t size;
|
|
FiberEntry entry;
|
|
uint64_t arg_on_initialize;
|
|
void* addr_context;
|
|
uint64_t size_context;
|
|
char name[FIBER_MAX_NAME_LENGTH + 1];
|
|
uint64_t size_context_margin;
|
|
uint8_t padding[48];
|
|
};
|
|
|
|
static_assert(sizeof(FiberInfo) == 128);
|
|
|
|
static bool FiberIsValid(const FiberObject* fiber) {
|
|
return fiber != nullptr && fiber->magic_start == FIBER_MAGIC_START &&
|
|
fiber->magic_end == FIBER_MAGIC_END;
|
|
}
|
|
|
|
static thread_local FiberObject* g_current_fiber = nullptr;
|
|
static thread_local FiberObject* g_thread_return_fiber = nullptr;
|
|
static thread_local FiberObject* g_pending_idle_fiber = nullptr;
|
|
static thread_local FiberCpuContext g_thread_fiber_context {};
|
|
|
|
static uint32_t FiberLoadState(const FiberObject* fiber) {
|
|
auto& state = const_cast<uint32_t&>(fiber->state);
|
|
return std::atomic_ref<uint32_t>(state).load(std::memory_order_acquire);
|
|
}
|
|
|
|
static void FiberStoreState(FiberObject* fiber, uint32_t state) {
|
|
std::atomic_ref<uint32_t>(fiber->state).store(state, std::memory_order_release);
|
|
}
|
|
|
|
// A fiber may resume on another host thread. Do not cache TLS across the switch.
|
|
[[gnu::noinline]] static void FiberCompleteSwitch(FiberObject* current) {
|
|
g_current_fiber = current;
|
|
// Only publish IDLE after switching away from the departing fiber's stack.
|
|
auto* fiber = g_pending_idle_fiber;
|
|
g_pending_idle_fiber = nullptr;
|
|
if (fiber != nullptr) {
|
|
FiberStoreState(fiber, FIBER_STATE_IDLE);
|
|
}
|
|
}
|
|
|
|
static bool FiberCompareExchangeState(FiberObject* fiber, uint32_t expected, uint32_t desired) {
|
|
return std::atomic_ref<uint32_t>(fiber->state)
|
|
.compare_exchange_strong(expected, desired, std::memory_order_acq_rel,
|
|
std::memory_order_acquire);
|
|
}
|
|
|
|
static void FiberSetContextValid(FiberObject* fiber, bool valid) {
|
|
fiber->context_valid = valid;
|
|
fiber->context = valid ? &fiber->saved_context : nullptr;
|
|
}
|
|
|
|
#if defined(__x86_64__) || defined(_M_X64)
|
|
[[gnu::naked,
|
|
gnu::returns_twice]] static KYTY_SYSV_ABI int FiberSaveContext(FiberCpuContext* /*ctx*/) {
|
|
asm volatile("movq %rdi, %r10\n\t"
|
|
"movq %rbx, 0(%r10)\n\t"
|
|
"movq %rbp, 8(%r10)\n\t"
|
|
"movq %rdi, 16(%r10)\n\t"
|
|
"movq %rsi, 24(%r10)\n\t"
|
|
"movq %r12, 32(%r10)\n\t"
|
|
"movq %r13, 40(%r10)\n\t"
|
|
"movq %r14, 48(%r10)\n\t"
|
|
"movq %r15, 56(%r10)\n\t"
|
|
"leaq 8(%rsp), %r11\n\t"
|
|
"movq %r11, 64(%r10)\n\t"
|
|
"movq (%rsp), %r11\n\t"
|
|
"movq %r11, 72(%r10)\n\t"
|
|
"xorl %eax, %eax\n\t"
|
|
"retq\n");
|
|
}
|
|
|
|
[[gnu::naked, gnu::noreturn]] static KYTY_SYSV_ABI void
|
|
FiberRestoreContext(FiberCpuContext* /*ctx*/, uint64_t /*ret*/) {
|
|
asm volatile("movq %rdi, %r10\n\t"
|
|
"movq %rsi, %rax\n\t"
|
|
"movq 72(%r10), %r11\n\t"
|
|
"movq 0(%r10), %rbx\n\t"
|
|
"movq 8(%r10), %rbp\n\t"
|
|
"movq 16(%r10), %rdi\n\t"
|
|
"movq 24(%r10), %rsi\n\t"
|
|
"movq 32(%r10), %r12\n\t"
|
|
"movq 40(%r10), %r13\n\t"
|
|
"movq 48(%r10), %r14\n\t"
|
|
"movq 56(%r10), %r15\n\t"
|
|
"movq 64(%r10), %rsp\n\t"
|
|
"jmp *%r11\n");
|
|
}
|
|
#else
|
|
static int FiberSaveContext(FiberCpuContext* ctx) {
|
|
(void)ctx;
|
|
return 0;
|
|
}
|
|
|
|
static void FiberRestoreContext(FiberCpuContext* ctx, uint64_t ret) {
|
|
(void)ctx;
|
|
(void)ret;
|
|
EXIT("Fiber context switching is only implemented on x86_64\n");
|
|
}
|
|
#endif
|
|
|
|
[[noreturn]] static KYTY_SYSV_ABI void FiberStartTrampoline(FiberObject* fiber);
|
|
|
|
[[noreturn]] static KYTY_SYSV_ABI void FiberStartOnGuestStack(FiberObject* fiber) {
|
|
FiberCpuContext ctx {};
|
|
const auto stack_top = reinterpret_cast<uintptr_t>(fiber->addr_context) +
|
|
static_cast<uintptr_t>(fiber->size_context);
|
|
auto rsp = (stack_top & ~static_cast<uintptr_t>(0x0f));
|
|
rsp -= sizeof(uint64_t);
|
|
*reinterpret_cast<uint64_t*>(rsp) = 0;
|
|
|
|
ctx.rsp = rsp;
|
|
ctx.rip = reinterpret_cast<uint64_t>(&FiberStartTrampoline);
|
|
ctx.rdi = reinterpret_cast<uint64_t>(fiber);
|
|
|
|
FiberRestoreContext(&ctx, 1);
|
|
}
|
|
|
|
[[noreturn]] static KYTY_SYSV_ABI void FiberStartTrampoline(FiberObject* fiber) {
|
|
FiberCompleteSwitch(fiber);
|
|
|
|
fiber->entry(fiber->arg_on_initialize, fiber->arg_on_run);
|
|
|
|
FiberStoreState(fiber, FIBER_STATE_TERMINATED);
|
|
FiberSetContextValid(fiber, false);
|
|
fiber->arg_on_return = 0;
|
|
g_thread_return_fiber = fiber;
|
|
g_current_fiber = nullptr;
|
|
|
|
FiberRestoreContext(&g_thread_fiber_context, 1);
|
|
}
|
|
|
|
int32_t KYTY_SYSV_ABI FiberInitialize(FiberObject* fiber, const char* name, FiberEntry entry,
|
|
uint64_t arg_on_initialize, void* addr_context,
|
|
uint64_t size_context, const FiberOptParam* opt_param,
|
|
uint32_t build_version) {
|
|
PRINT_NAME();
|
|
|
|
if (fiber == nullptr || name == nullptr || entry == nullptr) {
|
|
return FIBER_ERROR_NULL;
|
|
}
|
|
|
|
if ((reinterpret_cast<uint64_t>(fiber) & 7u) != 0 ||
|
|
(reinterpret_cast<uint64_t>(addr_context) & 15u) != 0 ||
|
|
(opt_param != nullptr && (reinterpret_cast<uint64_t>(opt_param) & 7u) != 0)) {
|
|
return FIBER_ERROR_ALIGNMENT;
|
|
}
|
|
|
|
if (size_context != 0 && size_context < FIBER_CONTEXT_MIN_SIZE) {
|
|
return FIBER_ERROR_RANGE;
|
|
}
|
|
|
|
if ((size_context & 15u) != 0 || (addr_context == nullptr && size_context != 0) ||
|
|
(addr_context != nullptr && size_context == 0) ||
|
|
(opt_param != nullptr && opt_param->magic != FIBER_OPT_MAGIC)) {
|
|
return FIBER_ERROR_INVALID;
|
|
}
|
|
|
|
std::memset(fiber, 0, sizeof(*fiber));
|
|
std::strncpy(fiber->name, name, FIBER_MAX_NAME_LENGTH);
|
|
fiber->name[FIBER_MAX_NAME_LENGTH] = '\0';
|
|
|
|
fiber->magic_start = FIBER_MAGIC_START;
|
|
fiber->state = FIBER_STATE_IDLE;
|
|
fiber->entry = entry;
|
|
fiber->arg_on_initialize = arg_on_initialize;
|
|
fiber->addr_context = addr_context;
|
|
fiber->size_context = size_context;
|
|
fiber->flags = (build_version >= 0x03500000 ? FIBER_FLAG_SET_FPU_REGS : 0);
|
|
fiber->context_start = addr_context;
|
|
fiber->context_end =
|
|
(addr_context != nullptr ? static_cast<uint8_t*>(addr_context) + size_context : nullptr);
|
|
std::memset(&fiber->saved_context, 0, sizeof(fiber->saved_context));
|
|
FiberSetContextValid(fiber, false);
|
|
fiber->magic_end = FIBER_MAGIC_END;
|
|
|
|
if (addr_context != nullptr) {
|
|
*static_cast<uint64_t*>(addr_context) = FIBER_STACK_MAGIC;
|
|
}
|
|
|
|
LOGF("\t fiber init: %s, entry = 0x%016" PRIx64 ", context = 0x%016" PRIx64 ", size = %" PRIu64
|
|
"\n",
|
|
fiber->name, reinterpret_cast<uint64_t>(entry), reinterpret_cast<uint64_t>(addr_context),
|
|
size_context);
|
|
|
|
return OK;
|
|
}
|
|
|
|
int32_t KYTY_SYSV_ABI FiberInitializeInternal(FiberObject* fiber, const char* name,
|
|
FiberEntry entry, uint64_t arg_on_initialize,
|
|
void* addr_context, uint64_t size_context,
|
|
const FiberOptParam* opt_param, uint32_t flags,
|
|
uint32_t build_version) {
|
|
auto ret = FiberInitialize(fiber, name, entry, arg_on_initialize, addr_context, size_context,
|
|
opt_param, build_version);
|
|
if (ret == OK && fiber != nullptr) {
|
|
fiber->flags |= flags;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int32_t KYTY_SYSV_ABI FiberOptParamInitialize(FiberOptParam* opt_param) {
|
|
PRINT_NAME();
|
|
|
|
if (opt_param == nullptr) {
|
|
return FIBER_ERROR_NULL;
|
|
}
|
|
if ((reinterpret_cast<uint64_t>(opt_param) & 7u) != 0) {
|
|
return FIBER_ERROR_ALIGNMENT;
|
|
}
|
|
|
|
std::memset(opt_param, 0, 128);
|
|
opt_param->magic = FIBER_OPT_MAGIC;
|
|
|
|
return OK;
|
|
}
|
|
|
|
int32_t KYTY_SYSV_ABI FiberFinalize(FiberObject* fiber) {
|
|
PRINT_NAME();
|
|
|
|
if (fiber == nullptr) {
|
|
return FIBER_ERROR_NULL;
|
|
}
|
|
if (!FiberIsValid(fiber)) {
|
|
return FIBER_ERROR_INVALID;
|
|
}
|
|
if (!FiberCompareExchangeState(fiber, FIBER_STATE_IDLE, FIBER_STATE_TERMINATED)) {
|
|
return FIBER_ERROR_STATE;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int32_t KYTY_SYSV_ABI FiberRun(FiberObject* fiber, uint64_t arg_on_run, uint64_t* arg_on_return) {
|
|
PRINT_NAME();
|
|
|
|
if (!FiberIsValid(fiber)) {
|
|
return FIBER_ERROR_INVALID;
|
|
}
|
|
if (g_current_fiber != nullptr) {
|
|
return FIBER_ERROR_PERMISSION;
|
|
}
|
|
if (!FiberCompareExchangeState(fiber, FIBER_STATE_IDLE, FIBER_STATE_RUNNING)) {
|
|
return FIBER_ERROR_STATE;
|
|
}
|
|
|
|
fiber->arg_on_run = arg_on_run;
|
|
fiber->arg_on_return = 0;
|
|
g_thread_return_fiber = nullptr;
|
|
|
|
if (FiberSaveContext(&g_thread_fiber_context) == 0) {
|
|
if (fiber->context_valid) {
|
|
FiberRestoreContext(&fiber->saved_context, 1);
|
|
}
|
|
FiberStartOnGuestStack(fiber);
|
|
}
|
|
|
|
auto* returned_fiber = (g_thread_return_fiber != nullptr ? g_thread_return_fiber : fiber);
|
|
const auto return_value = returned_fiber->arg_on_return;
|
|
const auto return_code =
|
|
FiberLoadState(returned_fiber) == FIBER_STATE_TERMINATED ? FIBER_ERROR_STATE : OK;
|
|
// Copy results before another thread can acquire and resume the fiber.
|
|
g_thread_return_fiber = nullptr;
|
|
FiberCompleteSwitch(nullptr);
|
|
|
|
if (arg_on_return != nullptr) {
|
|
*arg_on_return = return_value;
|
|
}
|
|
|
|
return return_code;
|
|
}
|
|
|
|
int32_t KYTY_SYSV_ABI FiberSwitch(FiberObject* fiber, uint64_t arg_on_run,
|
|
uint64_t* arg_on_return) {
|
|
PRINT_NAME();
|
|
|
|
if (!FiberIsValid(fiber)) {
|
|
return FIBER_ERROR_INVALID;
|
|
}
|
|
if (g_current_fiber == nullptr) {
|
|
return FIBER_ERROR_PERMISSION;
|
|
}
|
|
|
|
if (!FiberCompareExchangeState(fiber, FIBER_STATE_IDLE, FIBER_STATE_RUNNING)) {
|
|
return FIBER_ERROR_STATE;
|
|
}
|
|
|
|
auto* caller = g_current_fiber;
|
|
|
|
fiber->arg_on_run = arg_on_run;
|
|
fiber->arg_on_return = 0;
|
|
|
|
if (FiberSaveContext(&caller->saved_context) == 0) {
|
|
FiberSetContextValid(caller, true);
|
|
g_pending_idle_fiber = caller;
|
|
if (fiber->context_valid) {
|
|
FiberRestoreContext(&fiber->saved_context, 1);
|
|
}
|
|
FiberStartOnGuestStack(fiber);
|
|
}
|
|
|
|
FiberCompleteSwitch(caller);
|
|
|
|
if (arg_on_return != nullptr) {
|
|
*arg_on_return = caller->arg_on_run;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int32_t KYTY_SYSV_ABI FiberGetSelf(FiberObject** fiber) {
|
|
PRINT_NAME();
|
|
|
|
if (fiber == nullptr) {
|
|
return FIBER_ERROR_NULL;
|
|
}
|
|
|
|
*fiber = g_current_fiber;
|
|
|
|
return OK;
|
|
}
|
|
|
|
int32_t KYTY_SYSV_ABI FiberReturnToThread(uint64_t arg_on_return, uint64_t* arg_on_run) {
|
|
PRINT_NAME();
|
|
|
|
if (g_current_fiber == nullptr) {
|
|
return FIBER_ERROR_PERMISSION;
|
|
}
|
|
|
|
auto* fiber = g_current_fiber;
|
|
fiber->arg_on_return = arg_on_return;
|
|
|
|
if (FiberSaveContext(&fiber->saved_context) == 0) {
|
|
FiberSetContextValid(fiber, true);
|
|
g_thread_return_fiber = fiber;
|
|
g_pending_idle_fiber = fiber;
|
|
FiberRestoreContext(&g_thread_fiber_context, 1);
|
|
}
|
|
|
|
FiberCompleteSwitch(fiber);
|
|
if (arg_on_run != nullptr) {
|
|
*arg_on_run = fiber->arg_on_run;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
int32_t KYTY_SYSV_ABI FiberGetInfo(FiberObject* fiber, FiberInfo* fiber_info) {
|
|
PRINT_NAME();
|
|
|
|
if (fiber == nullptr || fiber_info == nullptr) {
|
|
return FIBER_ERROR_NULL;
|
|
}
|
|
if (!FiberIsValid(fiber)) {
|
|
return FIBER_ERROR_INVALID;
|
|
}
|
|
|
|
std::memset(fiber_info, 0, sizeof(*fiber_info));
|
|
fiber_info->size = sizeof(*fiber_info);
|
|
fiber_info->entry = fiber->entry;
|
|
fiber_info->arg_on_initialize = fiber->arg_on_initialize;
|
|
fiber_info->addr_context = fiber->addr_context;
|
|
fiber_info->size_context = fiber->size_context;
|
|
std::strncpy(fiber_info->name, fiber->name, FIBER_MAX_NAME_LENGTH);
|
|
|
|
return OK;
|
|
}
|
|
|
|
int32_t KYTY_SYSV_ABI FiberStartContextSizeCheck(uint32_t flags) {
|
|
PRINT_NAME();
|
|
LOGF("\t flags = 0x%08" PRIx32 "\n", flags);
|
|
return OK;
|
|
}
|
|
|
|
int32_t KYTY_SYSV_ABI FiberStopContextSizeCheck() {
|
|
PRINT_NAME();
|
|
return OK;
|
|
}
|
|
|
|
int32_t KYTY_SYSV_ABI FiberRename(FiberObject* fiber, const char* name) {
|
|
PRINT_NAME();
|
|
|
|
if (fiber == nullptr || name == nullptr) {
|
|
return FIBER_ERROR_NULL;
|
|
}
|
|
if (!FiberIsValid(fiber)) {
|
|
return FIBER_ERROR_INVALID;
|
|
}
|
|
|
|
std::strncpy(fiber->name, name, FIBER_MAX_NAME_LENGTH);
|
|
fiber->name[FIBER_MAX_NAME_LENGTH] = '\0';
|
|
|
|
return OK;
|
|
}
|
|
|
|
int32_t KYTY_SYSV_ABI FiberGetThreadFramePointerAddress(uint64_t* addr_frame_pointer) {
|
|
PRINT_NAME();
|
|
|
|
if (addr_frame_pointer == nullptr) {
|
|
return FIBER_ERROR_NULL;
|
|
}
|
|
|
|
*addr_frame_pointer = 0;
|
|
|
|
return OK;
|
|
}
|
|
|
|
} // namespace Fiber
|
|
|
|
int chmod(const char* path, int mode) {
|
|
PRINT_NAME();
|
|
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI KernelAioInitializeImpl(void* param, int32_t size) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t param = 0x%016" PRIx64 "\n"
|
|
"\t size = 0x%08" PRIx32 "\n",
|
|
reinterpret_cast<uint64_t>(param), static_cast<uint32_t>(size));
|
|
|
|
return OK;
|
|
}
|
|
|
|
void KYTY_SYSV_ABI KernelAioInitializeParam(void* param) {
|
|
PRINT_NAME();
|
|
|
|
LOGF("\t param = 0x%016" PRIx64 "\n", reinterpret_cast<uint64_t>(param));
|
|
}
|
|
|
|
constexpr int32_t KERNEL_AIO_STATE_SUBMITTED = 1;
|
|
constexpr int32_t KERNEL_AIO_STATE_PROCESSING = 2;
|
|
constexpr int32_t KERNEL_AIO_STATE_COMPLETED = 3;
|
|
constexpr int32_t KERNEL_AIO_STATE_ABORTED = 4;
|
|
constexpr int32_t KERNEL_AIO_MAX_QUEUE = 512;
|
|
constexpr int32_t KERNEL_AIO_MAX_REQUESTS = 128;
|
|
|
|
struct KernelAioResult {
|
|
int64_t return_value;
|
|
uint32_t state;
|
|
};
|
|
|
|
struct KernelAioRwRequest {
|
|
int64_t offset;
|
|
size_t nbyte;
|
|
void* buf;
|
|
KernelAioResult* result;
|
|
int32_t fd;
|
|
};
|
|
|
|
static std::array<std::atomic_int32_t, KERNEL_AIO_MAX_QUEUE> g_kernel_aio_state = {};
|
|
static std::atomic_int32_t g_kernel_aio_next_id {1};
|
|
|
|
static int32_t kernel_aio_next_id() {
|
|
const auto sequence = g_kernel_aio_next_id.fetch_add(1, std::memory_order_relaxed);
|
|
return ((sequence - 1) % (KERNEL_AIO_MAX_QUEUE - 1)) + 1;
|
|
}
|
|
|
|
static bool kernel_aio_is_valid_id(int32_t id) {
|
|
return id > 0 && id < KERNEL_AIO_MAX_QUEUE;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI KernelAioSubmitReadCommands(KernelAioRwRequest* req, int32_t size, int32_t prio,
|
|
int32_t* id) {
|
|
PRINT_NAME();
|
|
|
|
if (req == nullptr || id == nullptr) {
|
|
return LibKernel::KERNEL_ERROR_EFAULT;
|
|
}
|
|
|
|
if (size <= 0 || size > KERNEL_AIO_MAX_REQUESTS) {
|
|
return LibKernel::KERNEL_ERROR_EINVAL;
|
|
}
|
|
|
|
const auto submit_id = kernel_aio_next_id();
|
|
g_kernel_aio_state[submit_id].store(KERNEL_AIO_STATE_PROCESSING, std::memory_order_release);
|
|
|
|
for (int32_t i = 0; i < size; i++) {
|
|
if (req[i].result == nullptr) {
|
|
g_kernel_aio_state[submit_id].store(KERNEL_AIO_STATE_ABORTED,
|
|
std::memory_order_release);
|
|
return LibKernel::KERNEL_ERROR_EFAULT;
|
|
}
|
|
|
|
req[i].result->state = KERNEL_AIO_STATE_SUBMITTED;
|
|
|
|
const auto result =
|
|
LibKernel::FileSystem::KernelPread(req[i].fd, req[i].buf, req[i].nbyte, req[i].offset);
|
|
|
|
req[i].result->return_value = result;
|
|
req[i].result->state = (result < 0 ? KERNEL_AIO_STATE_ABORTED : KERNEL_AIO_STATE_COMPLETED);
|
|
}
|
|
|
|
g_kernel_aio_state[submit_id].store(KERNEL_AIO_STATE_COMPLETED, std::memory_order_release);
|
|
*id = submit_id;
|
|
|
|
(void)prio;
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI KernelAioSubmitWriteCommands(KernelAioRwRequest* req, int32_t size, int32_t prio,
|
|
int32_t* id) {
|
|
PRINT_NAME();
|
|
|
|
if (req == nullptr || id == nullptr) {
|
|
return LibKernel::KERNEL_ERROR_EFAULT;
|
|
}
|
|
|
|
if (size <= 0 || size > KERNEL_AIO_MAX_REQUESTS) {
|
|
return LibKernel::KERNEL_ERROR_EINVAL;
|
|
}
|
|
|
|
const auto submit_id = kernel_aio_next_id();
|
|
g_kernel_aio_state[submit_id].store(KERNEL_AIO_STATE_PROCESSING, std::memory_order_release);
|
|
|
|
for (int32_t i = 0; i < size; i++) {
|
|
if (req[i].result == nullptr) {
|
|
g_kernel_aio_state[submit_id].store(KERNEL_AIO_STATE_ABORTED,
|
|
std::memory_order_release);
|
|
return LibKernel::KERNEL_ERROR_EFAULT;
|
|
}
|
|
|
|
req[i].result->state = KERNEL_AIO_STATE_SUBMITTED;
|
|
|
|
const auto result =
|
|
LibKernel::FileSystem::KernelPwrite(req[i].fd, req[i].buf, req[i].nbyte, req[i].offset);
|
|
|
|
req[i].result->return_value = result;
|
|
req[i].result->state = (result < 0 ? KERNEL_AIO_STATE_ABORTED : KERNEL_AIO_STATE_COMPLETED);
|
|
}
|
|
|
|
g_kernel_aio_state[submit_id].store(KERNEL_AIO_STATE_COMPLETED, std::memory_order_release);
|
|
*id = submit_id;
|
|
|
|
(void)prio;
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI KernelAioPollRequest(int32_t id, int32_t* state) {
|
|
PRINT_NAME();
|
|
|
|
if (state == nullptr) {
|
|
return LibKernel::KERNEL_ERROR_EFAULT;
|
|
}
|
|
|
|
if (!kernel_aio_is_valid_id(id)) {
|
|
return LibKernel::KERNEL_ERROR_EINVAL;
|
|
}
|
|
|
|
*state = g_kernel_aio_state[id].load(std::memory_order_acquire);
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI KernelAioWaitRequest(int32_t id, int32_t* state, uint32_t* usec) {
|
|
PRINT_NAME();
|
|
|
|
if (state == nullptr) {
|
|
return LibKernel::KERNEL_ERROR_EFAULT;
|
|
}
|
|
|
|
if (!kernel_aio_is_valid_id(id)) {
|
|
return LibKernel::KERNEL_ERROR_EINVAL;
|
|
}
|
|
|
|
uint32_t waited = 0;
|
|
auto current = g_kernel_aio_state[id].load(std::memory_order_acquire);
|
|
while (current == KERNEL_AIO_STATE_PROCESSING) {
|
|
if (usec != nullptr && *usec != 0 && waited >= *usec) {
|
|
*state = current;
|
|
return LibKernel::KERNEL_ERROR_ETIMEDOUT;
|
|
}
|
|
|
|
Common::Thread::SleepMicro(10);
|
|
waited += 10;
|
|
current = g_kernel_aio_state[id].load(std::memory_order_acquire);
|
|
}
|
|
|
|
*state = current;
|
|
return OK;
|
|
}
|
|
|
|
int KYTY_SYSV_ABI KernelAioDeleteRequest(int32_t id, int32_t* ret) {
|
|
PRINT_NAME();
|
|
|
|
if (ret == nullptr) {
|
|
return LibKernel::KERNEL_ERROR_EFAULT;
|
|
}
|
|
|
|
if (!kernel_aio_is_valid_id(id)) {
|
|
return LibKernel::KERNEL_ERROR_EINVAL;
|
|
}
|
|
|
|
g_kernel_aio_state[id].store(KERNEL_AIO_STATE_ABORTED, std::memory_order_release);
|
|
*ret = OK;
|
|
|
|
return OK;
|
|
}
|
|
|
|
namespace FileSystem = LibKernel::FileSystem;
|
|
namespace Memory = LibKernel::Memory;
|
|
namespace EventQueue = LibKernel::EventQueue;
|
|
namespace EventFlag = LibKernel::EventFlag;
|
|
namespace Semaphore = LibKernel::Semaphore;
|
|
|
|
namespace Fiber {
|
|
|
|
LIB_DEFINE(InitFiber_1) {
|
|
LIB_FUNC("hVYD7Ou2pCQ", Fiber::FiberInitialize);
|
|
LIB_FUNC("7+OJIpko9RY", Fiber::FiberInitializeInternal);
|
|
LIB_FUNC("asjUJJ+aa8s", Fiber::FiberOptParamInitialize);
|
|
LIB_FUNC("JeNX5F-NzQU", Fiber::FiberFinalize);
|
|
LIB_FUNC("a0LLrZWac0M", Fiber::FiberRun);
|
|
LIB_FUNC("PFT2S-tJ7Uk", Fiber::FiberSwitch);
|
|
LIB_FUNC("p+zLIOg27zU", Fiber::FiberGetSelf);
|
|
LIB_FUNC("B0ZX2hx9DMw", Fiber::FiberReturnToThread);
|
|
LIB_FUNC("avfGJ94g36Q", Fiber::FiberRun);
|
|
LIB_FUNC("ZqhZFuzKT6U", Fiber::FiberSwitch);
|
|
LIB_FUNC("uq2Y5BFz0PE", Fiber::FiberGetInfo);
|
|
LIB_FUNC("Lcqty+QNWFc", Fiber::FiberStartContextSizeCheck);
|
|
LIB_FUNC("Kj4nXMpnM8Y", Fiber::FiberStopContextSizeCheck);
|
|
LIB_FUNC("JzyT91ucGDc", Fiber::FiberRename);
|
|
LIB_FUNC("0dy4JtMUcMQ", Fiber::FiberGetThreadFramePointerAddress);
|
|
}
|
|
|
|
} // namespace Fiber
|
|
|
|
namespace Coredump {
|
|
|
|
LIB_DEFINE(InitCoredump_1) {
|
|
LIB_FUNC("8zLSfEfW5AU", Coredump::sceCoredumpRegisterCoredumpHandler);
|
|
LIB_FUNC("fFkhOgztiCA", Coredump::sceCoredumpUnregisterCoredumpHandler);
|
|
}
|
|
|
|
} // namespace Coredump
|
|
|
|
LIB_DEFINE(InitLibKernel_1_FS) {
|
|
LIB_FUNC("1G3lF1Gg1k8", FileSystem::KernelOpen);
|
|
LIB_FUNC("UK2Tl2DWUns", FileSystem::KernelClose);
|
|
LIB_FUNC("Cg4srZ6TKbU", FileSystem::KernelRead);
|
|
LIB_FUNC("4wSze92BhLI", FileSystem::KernelWrite);
|
|
LIB_FUNC("+r3rMFwItV4", FileSystem::KernelPread);
|
|
LIB_FUNC("nKWi-N2HBV4", FileSystem::KernelPwrite);
|
|
LIB_FUNC("eV9wAD2riIA", FileSystem::KernelStat);
|
|
LIB_FUNC("kBwCPsYX-m4", FileSystem::KernelFstat);
|
|
LIB_FUNC("AUXVxWeJU-A", FileSystem::KernelUnlink);
|
|
LIB_FUNC("52NcYU9+lEo", FileSystem::KernelRename);
|
|
LIB_FUNC("taRWhTJFTgE", FileSystem::KernelGetdirentries);
|
|
LIB_FUNC("oib76F-12fk", FileSystem::KernelLseek);
|
|
LIB_FUNC("j2AIqSqJP0w", FileSystem::KernelGetdents);
|
|
LIB_FUNC("1-LFLmRFxxM", FileSystem::KernelMkdir);
|
|
LIB_FUNC("naInUjYt3so", FileSystem::KernelRmdir);
|
|
LIB_FUNC("uWyW3v98sU4", FileSystem::KernelCheckReachability);
|
|
}
|
|
|
|
LIB_DEFINE(InitLibKernel_1_Mem) {
|
|
LIB_FUNC("mL8NDH86iQI", Memory::KernelMapNamedFlexibleMemory);
|
|
LIB_FUNC("IWIBBdTHit4", Memory::KernelMapFlexibleMemory);
|
|
LIB_FUNC("DGMG3JshrZU", Memory::KernelSetVirtualRangeName);
|
|
LIB_FUNC("mkgXxsoxWHg", Memory::KernelClearVirtualRangeName);
|
|
// 6xx
|
|
LIB_FUNC("4h6F1LLbTiw", Memory::KernelMapFlexibleMemory);
|
|
LIB_FUNC("cQke9UuBQOk", Memory::KernelMunmap);
|
|
LIB_FUNC("pO96TwzOm5E", Memory::KernelGetDirectMemorySize);
|
|
LIB_FUNC("C0f7TJcbfac", Memory::KernelAvailableDirectMemorySize);
|
|
LIB_FUNC("tZ2yplY8MBY", Memory::KernelGetPageTableStats);
|
|
LIB_FUNC("rTXw65xmLIA", Memory::KernelAllocateDirectMemory);
|
|
LIB_FUNC("B+vc2AO2Zrc", Memory::KernelAllocateMainDirectMemory);
|
|
LIB_FUNC("rVjRvHJ0X6c", Memory::KernelVirtualQuery);
|
|
LIB_FUNC("yDBwVAolDgg", Memory::KernelIsStack);
|
|
LIB_FUNC("7oxv3PPCumo", Memory::KernelReserveVirtualRange);
|
|
LIB_FUNC("L-Q3LEjIbgA", Memory::KernelMapDirectMemory);
|
|
LIB_FUNC("BQQniolj9tQ", Memory::KernelMapDirectMemory2);
|
|
LIB_FUNC("NcaWUxfMNIQ", Memory::KernelMapNamedDirectMemory);
|
|
LIB_FUNC("BohYr-F7-is", Memory::KernelSetPrtAperture);
|
|
LIB_FUNC("L0v2Go5jOuM", Memory::KernelGetPrtAperture);
|
|
LIB_FUNC("hwVSPCmp5tM", Memory::KernelCheckedReleaseDirectMemory);
|
|
LIB_FUNC("MBuItvba6z8", Memory::KernelReleaseDirectMemory);
|
|
LIB_FUNC("jh+8XiK4LeE", Memory::KernelIsAddressSanitizerEnabled);
|
|
LIB_FUNC("WFcfL2lzido", Memory::KernelQueryMemoryProtection);
|
|
LIB_FUNC("BHouLQzh0X0", Memory::KernelDirectMemoryQuery);
|
|
LIB_FUNC("aNz11fnnzi4", Memory::KernelAvailableFlexibleMemorySize);
|
|
LIB_FUNC("n1-v6FgU7MQ", Memory::KernelConfiguredFlexibleMemorySize);
|
|
LIB_FUNC("vSMAm3cxYTY", Memory::KernelMprotect);
|
|
LIB_FUNC("9bfdLIyuwCY", Memory::KernelMtypeprotect);
|
|
LIB_FUNC("2SKEx6bSq-4", Memory::KernelBatchMap);
|
|
LIB_FUNC("kBJzF8x4SyE", Memory::KernelBatchMap2);
|
|
LIB_FUNC("pU-QydtGcGY", Memory::KernelMemoryPoolReserve);
|
|
LIB_FUNC("qCSfqDILlns", Memory::KernelMemoryPoolExpand);
|
|
LIB_FUNC("Vzl66WmfLvk", Memory::KernelMemoryPoolCommit);
|
|
LIB_FUNC("LXo1tpFqJGs", Memory::KernelMemoryPoolDecommit);
|
|
LIB_FUNC("YN878uKRBbE", Memory::KernelMemoryPoolBatch);
|
|
LIB_FUNC("bvD+95Q6asU", Memory::KernelMemoryPoolGetBlockStats);
|
|
}
|
|
|
|
LIB_DEFINE(InitLibKernel_1_Equeue) {
|
|
LIB_FUNC("D0OdFMjp46I", EventQueue::KernelCreateEqueue);
|
|
LIB_FUNC("jpFjmgAC5AE", EventQueue::KernelDeleteEqueue);
|
|
LIB_FUNC("fzyMKs9kim0", EventQueue::KernelWaitEqueue);
|
|
LIB_FUNC("vz+pg2zdopI", EventQueue::KernelGetEventUserData);
|
|
LIB_FUNC("mJ7aghmgvfc", EventQueue::KernelGetEventId);
|
|
LIB_FUNC("23CPPI1tyBY", EventQueue::KernelGetEventFilter);
|
|
LIB_FUNC("kwGyyjohI50", EventQueue::KernelGetEventData);
|
|
LIB_FUNC("Q0qr9AyqJSk", EventQueue::KernelGetEventFflags);
|
|
LIB_FUNC("Uu-iDFC9aUc", EventQueue::KernelGetEventError);
|
|
LIB_FUNC("4R6-OvI2cEA", EventQueue::KernelAddUserEvent);
|
|
LIB_FUNC("WDszmSbWuDk", EventQueue::KernelAddUserEventEdge);
|
|
LIB_FUNC("F6e0kwo4cnk", EventQueue::KernelTriggerUserEvent);
|
|
LIB_FUNC("LJDwdSNTnDg", EventQueue::KernelDeleteUserEvent);
|
|
LIB_FUNC("R74tt43xP6k", EventQueue::KernelAddHRTimerEvent);
|
|
LIB_FUNC("J+LF6LwObXU", EventQueue::KernelDeleteHRTimerEvent);
|
|
LIB_FUNC("bBfz7kMF2Ho", EventQueue::KernelAddAmprEvent);
|
|
LIB_FUNC("vuae5JPNt9A", EventQueue::KernelAddAmprSystemEvent);
|
|
LIB_FUNC("bMmid3pfyjo", EventQueue::KernelDeleteAmprEvent);
|
|
LIB_FUNC("Ij+ryuEClXQ", EventQueue::KernelDeleteAmprSystemEvent);
|
|
}
|
|
|
|
LIB_DEFINE(InitLibKernel_1_EventFlag) {
|
|
LIB_FUNC("PZku4ZrXJqg", EventFlag::KernelCancelEventFlag);
|
|
LIB_FUNC("7uhBFWRAS60", EventFlag::KernelClearEventFlag);
|
|
LIB_FUNC("BpFoboUJoZU", EventFlag::KernelCreateEventFlag);
|
|
LIB_FUNC("8mql9OcQnd4", EventFlag::KernelDeleteEventFlag);
|
|
LIB_FUNC("9lvj5DjHZiA", EventFlag::KernelPollEventFlag);
|
|
LIB_FUNC("JTvBflhYazQ", EventFlag::KernelWaitEventFlag);
|
|
LIB_FUNC("IOnSvHzqu6A", EventFlag::KernelSetEventFlag);
|
|
}
|
|
|
|
LIB_DEFINE(InitLibKernel_1_Semaphore) {
|
|
LIB_FUNC("188x57JYp0g", Semaphore::KernelCreateSema);
|
|
LIB_FUNC("R1Jvn8bSCW8", Semaphore::KernelDeleteSema);
|
|
LIB_FUNC("Zxa0VhQVTsk", Semaphore::KernelWaitSema);
|
|
LIB_FUNC("12wOHk8ywb0", Semaphore::KernelPollSema);
|
|
LIB_FUNC("4czppHBiriw", Semaphore::KernelSignalSema);
|
|
LIB_FUNC("4DM06U2BNEY", Semaphore::KernelCancelSema);
|
|
LIB_FUNC("GEnUkDZoUwY", Semaphore::PthreadSemInit);
|
|
LIB_FUNC("Vwc+L05e6oE", Semaphore::PthreadSemDestroy);
|
|
LIB_FUNC("C36iRE0F5sE", Semaphore::PthreadSemWait);
|
|
LIB_FUNC("H2a+IN9TP0E", Semaphore::PthreadSemTrywait);
|
|
LIB_FUNC("fjN6NQHhK8k", Semaphore::PthreadSemTimedwait);
|
|
LIB_FUNC("aishVAiFaYM", Semaphore::PthreadSemPost);
|
|
LIB_FUNC("DjpBvGlaWbQ", Semaphore::PthreadSemGetvalue);
|
|
}
|
|
|
|
LIB_DEFINE(InitLibKernel_1_Pthread) {
|
|
LIB_FUNC("9UK1vLZQft4", LibKernel::PthreadMutexLock);
|
|
LIB_FUNC("tn3VlD0hG60", LibKernel::PthreadMutexUnlock);
|
|
LIB_FUNC("2Of0f+3mhhE", LibKernel::PthreadMutexDestroy);
|
|
LIB_FUNC("cmo1RIYva9o", LibKernel::PthreadMutexInit);
|
|
LIB_FUNC("upoVrzMHFeE", LibKernel::PthreadMutexTrylock);
|
|
LIB_FUNC("smWEktiyyG0", LibKernel::PthreadMutexattrDestroy);
|
|
LIB_FUNC("F8bUHwAG284", LibKernel::PthreadMutexattrInit);
|
|
LIB_FUNC("iMp8QpE+XO4", LibKernel::PthreadMutexattrSettype);
|
|
LIB_FUNC("1FGvU0i9saQ", LibKernel::PthreadMutexattrSetprotocol);
|
|
|
|
LIB_FUNC("aI+OeCz8xrQ", LibKernel::PthreadSelf);
|
|
LIB_FUNC("EotR8a3ASf4", LibKernel::PthreadSelf);
|
|
LIB_FUNC("6UgtwV+0zb4", LibKernel::PthreadCreate);
|
|
LIB_FUNC("3PtV6p3QNX4", LibKernel::PthreadEqual);
|
|
LIB_FUNC("onNY9Byn-W8", LibKernel::PthreadJoin);
|
|
LIB_FUNC("4qGrR6eoP9Y", LibKernel::PthreadDetach);
|
|
LIB_FUNC("3kg7rT0NQIs", LibKernel::PthreadExit);
|
|
LIB_FUNC("qBDmpCyGssE", LibKernel::PthreadCancel);
|
|
LIB_FUNC("OAmWq+OHSjw", LibKernel::PthreadSetcancelstate);
|
|
LIB_FUNC("sCJd99Phct0", LibKernel::PthreadSetcanceltype);
|
|
LIB_FUNC("How7B8Oet6k", LibKernel::PthreadGetname);
|
|
LIB_FUNC("GBUY7ywdULE", LibKernel::PthreadRename);
|
|
LIB_FUNC("bt3CTBKmGyI", LibKernel::PthreadSetaffinity);
|
|
LIB_FUNC("rcrVFJsQWRY", LibKernel::PthreadGetaffinity);
|
|
LIB_FUNC("1tKyG7RlMJo", LibKernel::PthreadGetprio);
|
|
LIB_FUNC("W0Hpm2X0uPE", LibKernel::PthreadSetprio);
|
|
LIB_FUNC("T72hz6ffq08", LibKernel::PthreadYield);
|
|
LIB_FUNC("6XG4B33N09g", LibKernel::PthreadYield);
|
|
LIB_FUNC("CBNtXOoef-E", Posix::sched_get_priority_max);
|
|
LIB_FUNC("m0iS6jNsXds", Posix::sched_get_priority_min);
|
|
LIB_FUNC("EI-5-jlq2dE", LibKernel::PthreadGetthreadid);
|
|
LIB_FUNC("geDaqgH9lTg", LibKernel::PthreadKeyCreate);
|
|
LIB_FUNC("PrdHuuDekhY", LibKernel::PthreadKeyDelete);
|
|
LIB_FUNC("+BzXYkqYeLE", LibKernel::PthreadSetspecific);
|
|
LIB_FUNC("eoht7mQOCmo", LibKernel::PthreadGetspecific);
|
|
|
|
LIB_FUNC("62KCwEMmzcM", LibKernel::PthreadAttrDestroy);
|
|
LIB_FUNC("x1X76arYMxU", LibKernel::PthreadAttrGet);
|
|
LIB_FUNC("8+s5BzZjxSg", LibKernel::PthreadAttrGetaffinity);
|
|
LIB_FUNC("nsYoNRywwNg", LibKernel::PthreadAttrInit);
|
|
LIB_FUNC("JaRMy+QcpeU", LibKernel::PthreadAttrGetdetachstate);
|
|
LIB_FUNC("-quPa4SEJUw", LibKernel::PthreadAttrGetstack);
|
|
LIB_FUNC("Ru36fiTtJzA", LibKernel::PthreadAttrGetstackaddr);
|
|
LIB_FUNC("-fA+7ZlGDQs", LibKernel::PthreadAttrGetstacksize);
|
|
LIB_FUNC("txHtngJ+eyc", LibKernel::PthreadAttrGetguardsize);
|
|
LIB_FUNC("9RnL-m0+diQ", LibKernel::PthreadAttrGetsolosched);
|
|
LIB_FUNC("Bvn74vj6oLo", LibKernel::PthreadAttrSetstack);
|
|
LIB_FUNC("F+yfmduIBB8", LibKernel::PthreadAttrSetstackaddr);
|
|
LIB_FUNC("UTXzJbWhhTE", LibKernel::PthreadAttrSetstacksize);
|
|
LIB_FUNC("2Q0z6rnBrTE", LibKernel::PthreadAttrSetstacksize);
|
|
LIB_FUNC("-Wreprtu0Qs", LibKernel::PthreadAttrSetdetachstate);
|
|
LIB_FUNC("El+cQ20DynU", LibKernel::PthreadAttrSetguardsize);
|
|
LIB_FUNC("eXbUSpEaTsA", LibKernel::PthreadAttrSetinheritsched);
|
|
LIB_FUNC("DzES9hQF4f4", LibKernel::PthreadAttrSetschedparam);
|
|
LIB_FUNC("4+h9EzwKF4I", LibKernel::PthreadAttrSetschedpolicy);
|
|
LIB_FUNC("Dk6FC-TI+7Q", LibKernel::PthreadAttrSetsolosched);
|
|
LIB_FUNC("3qxgM4ezETA", LibKernel::PthreadAttrSetaffinity);
|
|
LIB_FUNC("FXPWHNk8Of0", LibKernel::PthreadAttrGetschedparam);
|
|
|
|
LIB_FUNC("6ULAa0fq4jA", LibKernel::PthreadRwlockInit);
|
|
LIB_FUNC("BB+kb08Tl9A", LibKernel::PthreadRwlockDestroy);
|
|
LIB_FUNC("Ox9i0c7L5w0", LibKernel::PthreadRwlockRdlock);
|
|
LIB_FUNC("iGjsr1WAtI0", LibKernel::PthreadRwlockRdlock);
|
|
LIB_FUNC("+L98PIbGttk", LibKernel::PthreadRwlockUnlock);
|
|
LIB_FUNC("EgmLo6EWgso", LibKernel::PthreadRwlockUnlock);
|
|
LIB_FUNC("mqdNorrB+gI", LibKernel::PthreadRwlockWrlock);
|
|
LIB_FUNC("sIlRvQqsN2Y", LibKernel::PthreadRwlockWrlock);
|
|
LIB_FUNC("bIHoZCTomsI", LibKernel::PthreadRwlockTrywrlock);
|
|
LIB_FUNC("XD3mDeybCnk", LibKernel::PthreadRwlockTryrdlock);
|
|
LIB_FUNC("iPtZRWICjrM", LibKernel::PthreadRwlockTimedrdlock);
|
|
LIB_FUNC("adh--6nIqTk", LibKernel::PthreadRwlockTimedwrlock);
|
|
LIB_FUNC("i2ifZ3fS2fo", LibKernel::PthreadRwlockattrDestroy);
|
|
LIB_FUNC("yOfGg-I1ZII", LibKernel::PthreadRwlockattrInit);
|
|
LIB_FUNC("qsdmgXjqSgk", LibKernel::PthreadRwlockattrDestroy);
|
|
LIB_FUNC("xFebsA4YsFI", LibKernel::PthreadRwlockattrInit);
|
|
LIB_FUNC("h-OifiouBd8", LibKernel::PthreadRwlockattrSettype);
|
|
|
|
LIB_FUNC("2Tb92quprl0", LibKernel::PthreadCondInit);
|
|
LIB_FUNC("g+PZd2hiacg", LibKernel::PthreadCondDestroy);
|
|
LIB_FUNC("RXXqi4CtF8w", LibKernel::PthreadCondDestroy);
|
|
LIB_FUNC("WKAXJ4XBPQ4", LibKernel::PthreadCondWait);
|
|
LIB_FUNC("JGgj7Uvrl+A", LibKernel::PthreadCondBroadcast);
|
|
LIB_FUNC("kDh-NfxgMtE", LibKernel::PthreadCondSignal);
|
|
LIB_FUNC("2MOy+rUfuhQ", LibKernel::PthreadCondSignal);
|
|
LIB_FUNC("o69RpYO-Mu0", LibKernel::PthreadCondSignalto);
|
|
LIB_FUNC("BmMjYxmew1w", LibKernel::PthreadCondTimedwait);
|
|
LIB_FUNC("27bAgiJmOh0", Posix::pthread_cond_timedwait);
|
|
LIB_FUNC("m5-2bsNfv7s", LibKernel::PthreadCondattrInit);
|
|
LIB_FUNC("mKoTx03HRWA", Posix::pthread_condattr_init);
|
|
LIB_FUNC("waPcxYiR3WA", LibKernel::PthreadCondattrDestroy);
|
|
LIB_FUNC("dJcuQVn6-Iw", Posix::pthread_condattr_destroy);
|
|
LIB_FUNC("EjllaAqAPZo", Posix::pthread_condattr_setclock);
|
|
|
|
LIB_FUNC("wRYVA5Zolso", LibKernel::KernelClockGetres);
|
|
LIB_FUNC("QBi7HCK03hw", LibKernel::KernelClockGettime);
|
|
LIB_FUNC("ejekcaNQNq0", LibKernel::KernelGettimeofday);
|
|
LIB_FUNC("kOcnerypnQA", LibKernel::KernelGettimezone);
|
|
LIB_FUNC("0NTHN1NKONI", LibKernel::KernelConvertLocaltimeToUtc);
|
|
LIB_FUNC("-o5uEDpN+oY", LibKernel::KernelConvertUtcToLocaltime);
|
|
LIB_FUNC("n88vx3C5nW8", Posix::gettimeofday);
|
|
LIB_FUNC("-2IRUCO--PM", LibKernel::KernelReadTsc);
|
|
LIB_FUNC("1j3S3n-tTW4", LibKernel::KernelGetTscFrequency);
|
|
LIB_FUNC("4J2sUJmuHZQ", LibKernel::KernelGetProcessTime);
|
|
LIB_FUNC("BNowx2l588E", LibKernel::KernelGetProcessTimeCounterFrequency);
|
|
LIB_FUNC("fgxnMeTNUtY", LibKernel::KernelGetProcessTimeCounter);
|
|
|
|
LIB_FUNC("7H0iTOciTLo", Posix::pthread_mutex_lock);
|
|
LIB_FUNC("2Z+PpY6CaJg", Posix::pthread_mutex_unlock);
|
|
LIB_FUNC("IafI2PxcPnQ", LibKernel::PthreadMutexTimedlock);
|
|
LIB_FUNC("Io9+nTKXZtA", Posix::pthread_mutex_timedlock);
|
|
LIB_FUNC("mkx2fVhNMsg", Posix::pthread_cond_broadcast);
|
|
LIB_FUNC("Op8TBGY5KHg", Posix::pthread_cond_wait);
|
|
LIB_FUNC("14bOACANTBo", Posix::pthread_once);
|
|
LIB_FUNC("Z4QosVuAsA0", Posix::pthread_once);
|
|
LIB_FUNC("Oy6IpwgtYOk", Posix::lseek);
|
|
LIB_FUNC("ezv-RSBNKqI", Posix::pread);
|
|
LIB_FUNC("C2kJ-byS5rM", Posix::pwrite);
|
|
LIB_FUNC("9eMlfusH4sU", Posix::flock);
|
|
LIB_FUNC("mqQMh1zPPT8", Posix::fstat);
|
|
|
|
LIB_FUNC("z0dtnPxYgtg", chmod);
|
|
LIB_FUNC("VAzswvTOCzI", FileSystem::KernelUnlink);
|
|
LIB_FUNC("JGMio+21L4c", Posix::mkdir);
|
|
LIB_FUNC("wuCroIGjt2g", FileSystem::KernelOpen);
|
|
LIB_FUNC("bY-PO6JhzhQ", FileSystem::KernelClose);
|
|
LIB_FUNC("FN4gaPmuFV8", FileSystem::KernelWrite);
|
|
}
|
|
|
|
static void AddLibkernelUnityFunc(Loader::SymbolDatabase* s, const char* nid, uint64_t func,
|
|
const std::string& dbg_name) {
|
|
Loader::SymbolResolve sr {};
|
|
sr.name = nid;
|
|
sr.library = "libkernel_unity";
|
|
sr.library_version = 1;
|
|
sr.module = "libkernel";
|
|
sr.module_version_major = 1;
|
|
sr.module_version_minor = 1;
|
|
sr.type = Loader::SymbolType::Func;
|
|
s->Add(sr, func, dbg_name);
|
|
}
|
|
|
|
LIB_DEFINE(InitLibKernel_1) {
|
|
LIB_FUNC("04AjkP0jO9U", Posix::UmtxOp);
|
|
InitLibKernel_1_FS(s);
|
|
InitLibKernel_1_Mem(s);
|
|
InitLibKernel_1_Equeue(s);
|
|
InitLibKernel_1_EventFlag(s);
|
|
InitLibKernel_1_Semaphore(s);
|
|
InitLibKernel_1_Pthread(s);
|
|
LibKernelApr::InitLibKernel_1_Apr(s);
|
|
Posix::InitLibKernel_1_Posix(s);
|
|
LibKernelWriteThrottling::InitLibKernelWriteThrottling(s);
|
|
|
|
LIB_OBJECT("f7uOxY9mM1U", &LibKernel::g_stack_chk_guard);
|
|
LIB_OBJECT("djxxOmW6-aw", &LibKernel::g_progname);
|
|
|
|
LIB_FUNC("1jfXLRVzisc", LibKernel::KernelUsleep);
|
|
LIB_FUNC("QcteRwbsnV0", LibKernel::KernelUsleep);
|
|
LIB_FUNC("QvsZxomvUHs", LibKernel::KernelNanosleep);
|
|
LIB_FUNC("LwG8g3niqwA", LibKernel::KernelDlsym);
|
|
LIB_FUNC("-ZR+hG7aDHw", LibKernel::KernelSleep);
|
|
LIB_FUNC("6c3rCVE-fTU", LibKernel::open);
|
|
LIB_FUNC("6xVpy0Fdq+I", LibKernel::sigprocmask);
|
|
LIB_FUNC("6Z83sYWFlA8", LibKernel::exit);
|
|
LIB_FUNC("8OnWXlgQlvo", LibKernel::KernelRtldThreadAtexitDecrement);
|
|
LIB_FUNC("959qrazPIrg", LibKernel::KernelGetProcParam);
|
|
LIB_FUNC("tU5e3f9gSiU", LibKernel::KernelIsTrinityMode);
|
|
LIB_FUNC("NH6xARDOVv8", LibKernel::KernelGetOperationMode);
|
|
LIB_FUNC("fTx66l5iWIA", LibKernel::KernelFsync);
|
|
LIB_FUNC("uvT2iYBBnkY", LibKernel::KernelSync);
|
|
LIB_FUNC("HoLVWNanBBc", LibKernel::getpid);
|
|
LIB_FUNC("9BcDykPmo1I", LibKernel::get_error_addr);
|
|
LIB_FUNC("k+AXqu2-eBc", Posix::getpagesize);
|
|
LIB_FUNC("bnZxYgAFeA0", LibKernel::KernelGetSanitizerNewReplaceExternal);
|
|
LIB_FUNC("ca7v6Cxulzs", LibKernel::KernelSetGPO);
|
|
LIB_FUNC("4oXYe9Xmk0Q", LibKernel::KernelGetGPI);
|
|
LIB_FUNC("DRuBt2pvICk", LibKernel::read);
|
|
LIB_FUNC("AqBioC2vF3I", LibKernel::read);
|
|
LIB_FUNC("f7KBOafysXo", LibKernel::KernelGetModuleInfoFromAddr);
|
|
LIB_FUNC("RpQJJVKTiFM", LibKernel::KernelGetModuleInfoForUnwind);
|
|
LIB_FUNC("crb5j7mkk1c", LibKernel::KernelIsSignalReturn); // _is_signal_return
|
|
LIB_FUNC("Fjc4-n1+y2g", LibKernel::elf_phdr_match_addr);
|
|
LIB_FUNC("FxVZqBAA7ks", LibKernel::write);
|
|
LIB_FUNC("FJmglmTMdr4", LibKernel::getargv);
|
|
LIB_FUNC("kbw4UHHSYy0", LibKernel::pthread_cxa_finalize);
|
|
LIB_FUNC("lLMT9vJAck0", LibKernel::clock_gettime);
|
|
LIB_FUNC("5TgME6AYty4", KernelAioDeleteRequest);
|
|
LIB_FUNC("HgX7+AORI58", KernelAioSubmitReadCommands);
|
|
LIB_FUNC("2pOuoWoCxdk", KernelAioPollRequest);
|
|
LIB_FUNC("KOF-oJbQVvc", KernelAioWaitRequest);
|
|
LIB_FUNC("XQ8C8y+de+E", KernelAioSubmitWriteCommands);
|
|
LIB_FUNC("nu4a0-arQis", KernelAioInitializeParam);
|
|
LIB_FUNC("il03nluKfMk", LibKernel::KernelRaiseException);
|
|
LIB_FUNC("iKJMWrAumPE", LibKernel::getargc);
|
|
LIB_FUNC("NNtFaKJbPt0", LibKernel::close);
|
|
LIB_FUNC("OMDRKKAZ8I4", LibKernel::KernelDebugRaiseException);
|
|
LIB_FUNC("Ou3iL1abvng", LibKernel::stack_chk_fail);
|
|
LIB_FUNC("p5EcQeEeJAE", LibKernel::KernelRtldSetApplicationHeapAPI);
|
|
LIB_FUNC("pB-yGZ2nQ9o", LibKernel::KernelSetThreadAtexitCount);
|
|
LIB_FUNC("py6L8jiVAN8", LibKernel::KernelGetSanitizerMallocReplaceExternal);
|
|
LIB_FUNC("Qhv5ARAoOEc", LibKernel::KernelRemoveExceptionHandler);
|
|
LIB_FUNC("QKd0qM58Qes", LibKernel::KernelStopUnloadModule);
|
|
LIB_FUNC("rNhWz+lvOMU", LibKernel::KernelSetThreadDtors);
|
|
LIB_FUNC("Tz4RNUCBbGI", LibKernel::KernelRtldThreadAtexitIncrement);
|
|
LIB_FUNC("vNe1w4diLCs", LibKernel::tls_get_addr);
|
|
LIB_FUNC("vYU8P9Td2Zo", KernelAioInitializeImpl);
|
|
LIB_FUNC("WhCc1w3EhSI", LibKernel::KernelSetThreadAtexitReport);
|
|
LIB_FUNC("WkwEd3N7w0Y", LibKernel::KernelInstallExceptionHandler);
|
|
LIB_FUNC("g0VTBxfJyu0", LibKernel::KernelGetCurrentCpu);
|
|
LIB_FUNC("wzvqT4UqKX8", LibKernel::KernelLoadStartModule);
|
|
LIB_FUNC("Xjoosiw+XPI", LibKernel::KernelUuidCreate);
|
|
LIB_FUNC("DLORcroUqbc", LibKernel::KernelGetOpenPsId);
|
|
LIB_FUNC("zE-wXIZjLoM", LibKernel::KernelDebugRaiseExceptionOnReleaseMode);
|
|
LIB_FUNC("Hc4CaR6JBL0", Posix::KernelSyncOnAddressWait);
|
|
LIB_FUNC("B2n8aDorSH4", Posix::KernelSyncOnAddressWait32);
|
|
LIB_FUNC("PZQhiiLXRFs", Posix::KernelSyncOnAddressWait64);
|
|
LIB_FUNC("q2y-wDIVWZA", Posix::KernelSyncOnAddressWake);
|
|
|
|
AddLibkernelUnityFunc(s, "Qhv5ARAoOEc",
|
|
reinterpret_cast<uint64_t>(LibKernel::KernelRemoveExceptionHandler),
|
|
"LibKernel::KernelRemoveExceptionHandler");
|
|
AddLibkernelUnityFunc(s, "il03nluKfMk",
|
|
reinterpret_cast<uint64_t>(LibKernel::KernelRaiseException),
|
|
"LibKernel::KernelRaiseException");
|
|
AddLibkernelUnityFunc(s, "WkwEd3N7w0Y",
|
|
reinterpret_cast<uint64_t>(LibKernel::KernelInstallExceptionHandler),
|
|
"LibKernel::KernelInstallExceptionHandler");
|
|
}
|
|
|
|
} // namespace Libs
|