KytyPS5/src/libs/libKernel.cpp

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