mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-04-26 22:38:22 +00:00
kernel: Add basic exceptions for linux
This commit is contained in:
parent
b2fe34bf45
commit
f0ac8ebfb1
20 changed files with 351 additions and 38 deletions
|
@ -196,6 +196,8 @@ set(GNM_LIB src/core/libraries/gnmdriver/gnmdriver.cpp
|
||||||
|
|
||||||
set(KERNEL_LIB src/core/libraries/kernel/threads/condvar.cpp
|
set(KERNEL_LIB src/core/libraries/kernel/threads/condvar.cpp
|
||||||
src/core/libraries/kernel/threads/event_flag.cpp
|
src/core/libraries/kernel/threads/event_flag.cpp
|
||||||
|
src/core/libraries/kernel/threads/exception.cpp
|
||||||
|
src/core/libraries/kernel/threads/exception.h
|
||||||
src/core/libraries/kernel/threads/mutex.cpp
|
src/core/libraries/kernel/threads/mutex.cpp
|
||||||
src/core/libraries/kernel/threads/pthread_attr.cpp
|
src/core/libraries/kernel/threads/pthread_attr.cpp
|
||||||
src/core/libraries/kernel/threads/pthread_clean.cpp
|
src/core/libraries/kernel/threads/pthread_clean.cpp
|
||||||
|
|
|
@ -374,9 +374,12 @@ int PS4_SYSV_ABI sceGnmAreSubmitsAllowed() {
|
||||||
return submission_lock == 0;
|
return submission_lock == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceGnmBeginWorkload() {
|
int PS4_SYSV_ABI sceGnmBeginWorkload(uint param_1, ulong* param_2) {
|
||||||
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called");
|
if (param_2 != (ulong*)0x0) {
|
||||||
return ORBIS_OK;
|
*param_2 = (ulong)(-(uint)(param_1 < 0x10) & 1);
|
||||||
|
return 0xf < param_1;
|
||||||
|
}
|
||||||
|
return (bool)3;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceGnmComputeWaitOnAddress(u32* cmdbuf, u32 size, uintptr_t addr, u32 mask,
|
s32 PS4_SYSV_ABI sceGnmComputeWaitOnAddress(u32* cmdbuf, u32 size, uintptr_t addr, u32 mask,
|
||||||
|
@ -916,9 +919,11 @@ int PS4_SYSV_ABI sceGnmDriverTriggerCapture() {
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceGnmEndWorkload() {
|
int PS4_SYSV_ABI sceGnmEndWorkload(long param_1) {
|
||||||
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called");
|
if (param_1 != 0) {
|
||||||
return ORBIS_OK;
|
return (0xf < (u8)((ulong)param_1 >> 0x38)) * 2;
|
||||||
|
}
|
||||||
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceGnmFindResourcesPublic() {
|
s32 PS4_SYSV_ABI sceGnmFindResourcesPublic() {
|
||||||
|
|
|
@ -16,7 +16,7 @@ using namespace Kernel;
|
||||||
|
|
||||||
s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata);
|
s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata);
|
||||||
int PS4_SYSV_ABI sceGnmAreSubmitsAllowed();
|
int PS4_SYSV_ABI sceGnmAreSubmitsAllowed();
|
||||||
int PS4_SYSV_ABI sceGnmBeginWorkload();
|
int PS4_SYSV_ABI sceGnmBeginWorkload(uint param_1, ulong* param_2);
|
||||||
s32 PS4_SYSV_ABI sceGnmComputeWaitOnAddress(u32* cmdbuf, u32 size, uintptr_t addr, u32 mask,
|
s32 PS4_SYSV_ABI sceGnmComputeWaitOnAddress(u32* cmdbuf, u32 size, uintptr_t addr, u32 mask,
|
||||||
u32 cmp_func, u32 ref);
|
u32 cmp_func, u32 ref);
|
||||||
int PS4_SYSV_ABI sceGnmComputeWaitSemaphore();
|
int PS4_SYSV_ABI sceGnmComputeWaitSemaphore();
|
||||||
|
@ -74,7 +74,7 @@ int PS4_SYSV_ABI sceGnmDriverInternalRetrieveGnmInterfaceForValidation();
|
||||||
int PS4_SYSV_ABI sceGnmDriverInternalVirtualQuery();
|
int PS4_SYSV_ABI sceGnmDriverInternalVirtualQuery();
|
||||||
int PS4_SYSV_ABI sceGnmDriverTraceInProgress();
|
int PS4_SYSV_ABI sceGnmDriverTraceInProgress();
|
||||||
int PS4_SYSV_ABI sceGnmDriverTriggerCapture();
|
int PS4_SYSV_ABI sceGnmDriverTriggerCapture();
|
||||||
int PS4_SYSV_ABI sceGnmEndWorkload();
|
int PS4_SYSV_ABI sceGnmEndWorkload(long param_1);
|
||||||
s32 PS4_SYSV_ABI sceGnmFindResourcesPublic();
|
s32 PS4_SYSV_ABI sceGnmFindResourcesPublic();
|
||||||
void PS4_SYSV_ABI sceGnmFlushGarlic();
|
void PS4_SYSV_ABI sceGnmFlushGarlic();
|
||||||
int PS4_SYSV_ABI sceGnmGetCoredumpAddress();
|
int PS4_SYSV_ABI sceGnmGetCoredumpAddress();
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "core/libraries/kernel/memory.h"
|
#include "core/libraries/kernel/memory.h"
|
||||||
#include "core/libraries/kernel/process.h"
|
#include "core/libraries/kernel/process.h"
|
||||||
#include "core/libraries/kernel/threads.h"
|
#include "core/libraries/kernel/threads.h"
|
||||||
|
#include "core/libraries/kernel/threads/exception.h"
|
||||||
#include "core/libraries/kernel/time.h"
|
#include "core/libraries/kernel/time.h"
|
||||||
#include "core/libraries/libs.h"
|
#include "core/libraries/libs.h"
|
||||||
#include "core/linker.h"
|
#include "core/linker.h"
|
||||||
|
@ -153,17 +154,17 @@ void PS4_SYSV_ABI sceLibcHeapGetTraceInfo(HeapInfoInfo* info) {
|
||||||
info->getSegmentInfo = 0;
|
info->getSegmentInfo = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
s64 PS4_SYSV_ABI ps4__write(int d, const void* buf, std::size_t nbytes) {
|
s64 PS4_SYSV_ABI ps4__write(int d, const char* buf, std::size_t nbytes) {
|
||||||
if (d <= 2) { // stdin,stdout,stderr
|
if (d <= 2) { // stdin,stdout,stderr
|
||||||
char* str = strdup((const char*)buf);
|
std::string_view str{buf};
|
||||||
if (str[nbytes - 1] == '\n')
|
if (str[nbytes - 1] == '\n') {
|
||||||
str[nbytes - 1] = 0;
|
str = str.substr(0, nbytes - 1);
|
||||||
|
}
|
||||||
LOG_INFO(Tty, "{}", str);
|
LOG_INFO(Tty, "{}", str);
|
||||||
free(str);
|
|
||||||
return nbytes;
|
return nbytes;
|
||||||
}
|
}
|
||||||
LOG_ERROR(Kernel, "(STUBBED) called d = {} nbytes = {} ", d, nbytes);
|
LOG_ERROR(Kernel, "(STUBBED) called d = {} nbytes = {} ", d, nbytes);
|
||||||
UNREACHABLE(); // normal write , is it a posix call??
|
UNREACHABLE();
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,6 +331,7 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) {
|
||||||
Libraries::Kernel::RegisterMemory(sym);
|
Libraries::Kernel::RegisterMemory(sym);
|
||||||
Libraries::Kernel::RegisterEventQueue(sym);
|
Libraries::Kernel::RegisterEventQueue(sym);
|
||||||
Libraries::Kernel::RegisterProcess(sym);
|
Libraries::Kernel::RegisterProcess(sym);
|
||||||
|
Libraries::Kernel::RegisterException(sym);
|
||||||
|
|
||||||
LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &g_stack_chk_guard);
|
LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &g_stack_chk_guard);
|
||||||
LIB_FUNCTION("JGfTMBOdUJo", "libkernel", 1, "libkernel", 1, 1, sceKernelGetFsSandboxRandomWord);
|
LIB_FUNCTION("JGfTMBOdUJo", "libkernel", 1, "libkernel", 1, 1, sceKernelGetFsSandboxRandomWord);
|
||||||
|
|
|
@ -37,6 +37,7 @@ struct WrapperImpl<name, PS4_SYSV_ABI R (*)(Args...), f> {
|
||||||
static R PS4_SYSV_ABI wrap(Args... args) {
|
static R PS4_SYSV_ABI wrap(Args... args) {
|
||||||
u32 ret = f(args...);
|
u32 ret = f(args...);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
|
LOG_ERROR(Lib_Kernel, "Function {} returned {}", std::string_view{name.value}, ret);
|
||||||
ret += SCE_KERNEL_ERROR_UNKNOWN;
|
ret += SCE_KERNEL_ERROR_UNKNOWN;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -219,6 +219,7 @@ int PS4_SYSV_ABI posix_pthread_condattr_setpshared(PthreadCondAttrT* attr, int p
|
||||||
|
|
||||||
void RegisterCond(Core::Loader::SymbolsResolver* sym) {
|
void RegisterCond(Core::Loader::SymbolsResolver* sym) {
|
||||||
// Posix
|
// Posix
|
||||||
|
LIB_FUNCTION("mKoTx03HRWA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_condattr_init);
|
||||||
LIB_FUNCTION("0TyVk4MSLt0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_init);
|
LIB_FUNCTION("0TyVk4MSLt0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_init);
|
||||||
LIB_FUNCTION("2MOy+rUfuhQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_signal);
|
LIB_FUNCTION("2MOy+rUfuhQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_signal);
|
||||||
LIB_FUNCTION("RXXqi4CtF8w", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_destroy);
|
LIB_FUNCTION("RXXqi4CtF8w", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_destroy);
|
||||||
|
|
102
src/core/libraries/kernel/threads/exception.cpp
Normal file
102
src/core/libraries/kernel/threads/exception.cpp
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "core/libraries/kernel/threads/exception.h"
|
||||||
|
#include "core/libraries/kernel/threads/pthread.h"
|
||||||
|
#include "core/libraries/libs.h"
|
||||||
|
|
||||||
|
#ifdef _WIN64
|
||||||
|
#else
|
||||||
|
#include <signal.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace Libraries::Kernel {
|
||||||
|
|
||||||
|
static std::array<SceKernelExceptionHandler, 32> Handlers{};
|
||||||
|
|
||||||
|
void SigactionHandler(int signum, siginfo_t* inf, ucontext_t* raw_context) {
|
||||||
|
const auto handler = Handlers[POSIX_SIGUSR1];
|
||||||
|
if (handler) {
|
||||||
|
auto ctx = Ucontext{};
|
||||||
|
auto& regs = raw_context->uc_mcontext.gregs;
|
||||||
|
ctx.uc_mcontext.mc_r8 = regs[REG_R8];
|
||||||
|
ctx.uc_mcontext.mc_r9 = regs[REG_R9];
|
||||||
|
ctx.uc_mcontext.mc_r10 = regs[REG_R10];
|
||||||
|
ctx.uc_mcontext.mc_r11 = regs[REG_R11];
|
||||||
|
ctx.uc_mcontext.mc_r12 = regs[REG_R12];
|
||||||
|
ctx.uc_mcontext.mc_r13 = regs[REG_R13];
|
||||||
|
ctx.uc_mcontext.mc_r14 = regs[REG_R14];
|
||||||
|
ctx.uc_mcontext.mc_r15 = regs[REG_R15];
|
||||||
|
ctx.uc_mcontext.mc_rdi = regs[REG_RDI];
|
||||||
|
ctx.uc_mcontext.mc_rsi = regs[REG_RSI];
|
||||||
|
ctx.uc_mcontext.mc_rbp = regs[REG_RBP];
|
||||||
|
ctx.uc_mcontext.mc_rbx = regs[REG_RBX];
|
||||||
|
ctx.uc_mcontext.mc_rdx = regs[REG_RDX];
|
||||||
|
ctx.uc_mcontext.mc_rax = regs[REG_RAX];
|
||||||
|
ctx.uc_mcontext.mc_rcx = regs[REG_RCX];
|
||||||
|
ctx.uc_mcontext.mc_rsp = regs[REG_RSP];
|
||||||
|
ctx.uc_mcontext.mc_fs = (regs[REG_CSGSFS] >> 32) & 0xFFFF;
|
||||||
|
ctx.uc_mcontext.mc_gs = (regs[REG_CSGSFS] >> 16) & 0xFFFF;
|
||||||
|
handler(POSIX_SIGUSR1, &ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int PS4_SYSV_ABI sceKernelInstallExceptionHandler(s32 signum, SceKernelExceptionHandler handler) {
|
||||||
|
if (signum == POSIX_SIGSEGV) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ASSERT_MSG(signum == POSIX_SIGUSR1 && !Handlers[POSIX_SIGUSR1], "Invalid parameters");
|
||||||
|
Handlers[POSIX_SIGUSR1] = handler;
|
||||||
|
#ifdef _WIN64
|
||||||
|
UNREACHABLE_MSG("Missing exception implementation");
|
||||||
|
#else
|
||||||
|
struct sigaction act = {};
|
||||||
|
act.sa_flags = SA_SIGINFO | SA_RESTART;
|
||||||
|
act.sa_sigaction = reinterpret_cast<decltype(act.sa_sigaction)>(SigactionHandler);
|
||||||
|
sigaction(SIGUSR2, &act, nullptr);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int PS4_SYSV_ABI sceKernelRemoveExceptionHandler(s32 signum) {
|
||||||
|
if (signum == 8) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ASSERT_MSG(signum == POSIX_SIGUSR1 && Handlers[POSIX_SIGUSR1], "Invalid parameters");
|
||||||
|
Handlers[POSIX_SIGUSR1] = nullptr;
|
||||||
|
#ifdef _WIN64
|
||||||
|
UNREACHABLE_MSG("Missing exception implementation");
|
||||||
|
#else
|
||||||
|
struct sigaction act = {};
|
||||||
|
act.sa_flags = SA_SIGINFO | SA_RESTART;
|
||||||
|
act.sa_sigaction = nullptr;
|
||||||
|
sigaction(SIGUSR2, &act, nullptr);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::mutex mtx;
|
||||||
|
|
||||||
|
int PS4_SYSV_ABI sceKernelRaiseException(PthreadT thread, int signum) {
|
||||||
|
std::scoped_lock lk{mtx};
|
||||||
|
LOG_ERROR(Lib_Kernel, "Raising exception");
|
||||||
|
ASSERT_MSG(signum == POSIX_SIGUSR1, "Attempting to raise non user defined signal!");
|
||||||
|
#ifdef _WIN64
|
||||||
|
UNREACHABLE("Missing exception implementation");
|
||||||
|
#else
|
||||||
|
pthread_t pthr = *reinterpret_cast<pthread_t*>(thread->native_handle);
|
||||||
|
pthread_kill(pthr, SIGUSR2);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegisterException(Core::Loader::SymbolsResolver* sym) {
|
||||||
|
LIB_FUNCTION("il03nluKfMk", "libkernel_unity", 1, "libkernel", 1, 1, sceKernelRaiseException);
|
||||||
|
LIB_FUNCTION("WkwEd3N7w0Y", "libkernel_unity", 1, "libkernel", 1, 1,
|
||||||
|
sceKernelInstallExceptionHandler);
|
||||||
|
LIB_FUNCTION("Qhv5ARAoOEc", "libkernel_unity", 1, "libkernel", 1, 1,
|
||||||
|
sceKernelRemoveExceptionHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Libraries::Kernel
|
86
src/core/libraries/kernel/threads/exception.h
Normal file
86
src/core/libraries/kernel/threads/exception.h
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/types.h"
|
||||||
|
|
||||||
|
namespace Core::Loader {
|
||||||
|
class SymbolsResolver;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Libraries::Kernel {
|
||||||
|
|
||||||
|
using SceKernelExceptionHandler = PS4_SYSV_ABI void (*)(int, void*);
|
||||||
|
|
||||||
|
constexpr int POSIX_SIGSEGV = 11;
|
||||||
|
constexpr int POSIX_SIGUSR1 = 30;
|
||||||
|
|
||||||
|
struct Mcontext {
|
||||||
|
u64 mc_onstack;
|
||||||
|
u64 mc_rdi;
|
||||||
|
u64 mc_rsi;
|
||||||
|
u64 mc_rdx;
|
||||||
|
u64 mc_rcx;
|
||||||
|
u64 mc_r8;
|
||||||
|
u64 mc_r9;
|
||||||
|
u64 mc_rax;
|
||||||
|
u64 mc_rbx;
|
||||||
|
u64 mc_rbp;
|
||||||
|
u64 mc_r10;
|
||||||
|
u64 mc_r11;
|
||||||
|
u64 mc_r12;
|
||||||
|
u64 mc_r13;
|
||||||
|
u64 mc_r14;
|
||||||
|
u64 mc_r15;
|
||||||
|
int mc_trapno;
|
||||||
|
u16 mc_fs;
|
||||||
|
u16 mc_gs;
|
||||||
|
u64 mc_addr;
|
||||||
|
int mc_flags;
|
||||||
|
u16 mc_es;
|
||||||
|
u16 mc_ds;
|
||||||
|
u64 mc_err;
|
||||||
|
u64 mc_rip;
|
||||||
|
u64 mc_cs;
|
||||||
|
u64 mc_rflags;
|
||||||
|
u64 mc_rsp;
|
||||||
|
u64 mc_ss;
|
||||||
|
u64 mc_len;
|
||||||
|
u64 mc_fpformat;
|
||||||
|
u64 mc_ownedfp;
|
||||||
|
u64 mc_lbrfrom;
|
||||||
|
u64 mc_lbrto;
|
||||||
|
u64 mc_aux1;
|
||||||
|
u64 mc_aux2;
|
||||||
|
u64 mc_fpstate[104];
|
||||||
|
u64 mc_fsbase;
|
||||||
|
u64 mc_gsbase;
|
||||||
|
u64 mc_spare[6];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Stack {
|
||||||
|
void* ss_sp;
|
||||||
|
std::size_t ss_size;
|
||||||
|
int ss_flags;
|
||||||
|
int _align;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Sigset {
|
||||||
|
u64 bits[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Ucontext {
|
||||||
|
struct Sigset uc_sigmask;
|
||||||
|
int field1_0x10[12];
|
||||||
|
struct Mcontext uc_mcontext;
|
||||||
|
struct Ucontext* uc_link;
|
||||||
|
struct Stack uc_stack;
|
||||||
|
int uc_flags;
|
||||||
|
int __spare[4];
|
||||||
|
int field7_0x4f4[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
void RegisterException(Core::Loader::SymbolsResolver* sym);
|
||||||
|
|
||||||
|
} // namespace Libraries::Kernel
|
|
@ -281,12 +281,11 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(PthreadT* thread, const PthreadAtt
|
||||||
(*thread) = new_thread;
|
(*thread) = new_thread;
|
||||||
|
|
||||||
/* Create thread */
|
/* Create thread */
|
||||||
pthread_t pthr;
|
pthread_t* pthr = reinterpret_cast<pthread_t*>(&new_thread->native_handle);
|
||||||
pthread_attr_t pattr;
|
pthread_attr_t pattr;
|
||||||
pthread_attr_init(&pattr);
|
pthread_attr_init(&pattr);
|
||||||
// pthread_attr_setstack(&pattr, new_thread->attr.stackaddr_attr,
|
pthread_attr_setstack(&pattr, new_thread->attr.stackaddr_attr, new_thread->attr.stacksize_attr);
|
||||||
// new_thread->attr.stacksize_attr);
|
int ret = pthread_create(pthr, &pattr, (PthreadEntryFunc)RunThread, new_thread);
|
||||||
int ret = pthread_create(&pthr, &pattr, (PthreadEntryFunc)RunThread, new_thread);
|
|
||||||
ASSERT_MSG(ret == 0, "Failed to create thread with error {}", ret);
|
ASSERT_MSG(ret == 0, "Failed to create thread with error {}", ret);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
*thread = nullptr;
|
*thread = nullptr;
|
||||||
|
@ -303,6 +302,11 @@ int PS4_SYSV_ABI posix_pthread_getthreadid_np() {
|
||||||
return g_curthread->tid;
|
return g_curthread->tid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int PS4_SYSV_ABI posix_pthread_getname_np(PthreadT thread, char* name) {
|
||||||
|
std::memcpy(name, thread->name.data(), std::min(thread->name.size(), 32UL));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI posix_pthread_equal(PthreadT thread1, PthreadT thread2) {
|
int PS4_SYSV_ABI posix_pthread_equal(PthreadT thread1, PthreadT thread2) {
|
||||||
return (thread1 == thread2 ? 1 : 0);
|
return (thread1 == thread2 ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
@ -417,13 +421,42 @@ int PS4_SYSV_ABI scePthreadGetprio(PthreadT thread, int* priority) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sceNpWebApiTerminate() {
|
enum class PthreadCancelState : u32 {
|
||||||
|
Enable = 0,
|
||||||
|
Disable = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define POSIX_PTHREAD_CANCELED ((void*)1)
|
||||||
|
|
||||||
|
static inline void TestCancel(Pthread* curthread) {
|
||||||
|
if (curthread->ShouldCancel() && !curthread->InCritical()) [[unlikely]] {
|
||||||
|
posix_pthread_exit(POSIX_PTHREAD_CANCELED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int PS4_SYSV_ABI posix_pthread_setcancelstate(PthreadCancelState state,
|
||||||
|
PthreadCancelState* oldstate) {
|
||||||
|
Pthread* curthread = g_curthread;
|
||||||
|
int oldval = curthread->cancel_enable;
|
||||||
|
switch (state) {
|
||||||
|
case PthreadCancelState::Disable:
|
||||||
|
curthread->cancel_enable = 0;
|
||||||
|
break;
|
||||||
|
case PthreadCancelState::Enable:
|
||||||
|
curthread->cancel_enable = 1;
|
||||||
|
TestCancel(curthread);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return POSIX_EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldstate) {
|
||||||
|
*oldstate = oldval ? PthreadCancelState::Enable : PthreadCancelState::Disable;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegisterThread(Core::Loader::SymbolsResolver* sym) {
|
void RegisterThread(Core::Loader::SymbolsResolver* sym) {
|
||||||
LIB_FUNCTION("asz3TtIqGF8", "libSceNpWebApi", 1, "libSceNpWebApi", 1, 1, sceNpWebApiTerminate);
|
|
||||||
|
|
||||||
// Posix
|
// Posix
|
||||||
LIB_FUNCTION("Z4QosVuAsA0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_once);
|
LIB_FUNCTION("Z4QosVuAsA0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_once);
|
||||||
LIB_FUNCTION("7Xl257M4VNI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_equal);
|
LIB_FUNCTION("7Xl257M4VNI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_equal);
|
||||||
|
@ -436,6 +469,7 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) {
|
||||||
LIB_FUNCTION("h9CcP3J0oVM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_join);
|
LIB_FUNCTION("h9CcP3J0oVM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_join);
|
||||||
LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create);
|
LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create);
|
||||||
LIB_FUNCTION("Jmi+9w9u0E4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create_name_np);
|
LIB_FUNCTION("Jmi+9w9u0E4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create_name_np);
|
||||||
|
LIB_FUNCTION("lZzFeSxPl08", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setcancelstate);
|
||||||
|
|
||||||
// Posix-Kernel
|
// Posix-Kernel
|
||||||
LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self);
|
LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self);
|
||||||
|
@ -448,6 +482,9 @@ void RegisterThread(Core::Loader::SymbolsResolver* sym) {
|
||||||
ORBIS(posix_pthread_create_name_np));
|
ORBIS(posix_pthread_create_name_np));
|
||||||
LIB_FUNCTION("4qGrR6eoP9Y", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_detach));
|
LIB_FUNCTION("4qGrR6eoP9Y", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_detach));
|
||||||
LIB_FUNCTION("onNY9Byn-W8", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_join));
|
LIB_FUNCTION("onNY9Byn-W8", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_join));
|
||||||
|
LIB_FUNCTION("P41kTWUS3EI", "libkernel", 1, "libkernel", 1, 1,
|
||||||
|
ORBIS(posix_pthread_getschedparam));
|
||||||
|
LIB_FUNCTION("How7B8Oet6k", "libkernel", 1, "libkernel", 1, 1, ORBIS(posix_pthread_getname_np));
|
||||||
LIB_FUNCTION("3kg7rT0NQIs", "libkernel", 1, "libkernel", 1, 1, posix_pthread_exit);
|
LIB_FUNCTION("3kg7rT0NQIs", "libkernel", 1, "libkernel", 1, 1, posix_pthread_exit);
|
||||||
LIB_FUNCTION("aI+OeCz8xrQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self);
|
LIB_FUNCTION("aI+OeCz8xrQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self);
|
||||||
LIB_FUNCTION("3PtV6p3QNX4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_equal);
|
LIB_FUNCTION("3PtV6p3QNX4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_equal);
|
||||||
|
|
|
@ -255,6 +255,7 @@ struct Pthread {
|
||||||
int refcount;
|
int refcount;
|
||||||
void* PS4_SYSV_ABI (*start_routine)(void*);
|
void* PS4_SYSV_ABI (*start_routine)(void*);
|
||||||
void* arg;
|
void* arg;
|
||||||
|
uintptr_t native_handle;
|
||||||
PthreadAttr attr;
|
PthreadAttr attr;
|
||||||
bool cancel_enable;
|
bool cancel_enable;
|
||||||
bool cancel_pending;
|
bool cancel_pending;
|
||||||
|
|
|
@ -298,6 +298,8 @@ void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym) {
|
||||||
posix_pthread_attr_setinheritsched);
|
posix_pthread_attr_setinheritsched);
|
||||||
LIB_FUNCTION("0qOtCR-ZHck", "libScePosix", 1, "libkernel", 1, 1,
|
LIB_FUNCTION("0qOtCR-ZHck", "libScePosix", 1, "libkernel", 1, 1,
|
||||||
posix_pthread_attr_getstacksize);
|
posix_pthread_attr_getstacksize);
|
||||||
|
LIB_FUNCTION("VUT1ZSrHT0I", "libScePosix", 1, "libkernel", 1, 1,
|
||||||
|
posix_pthread_attr_getdetachstate);
|
||||||
|
|
||||||
// Orbis
|
// Orbis
|
||||||
LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1,
|
LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1,
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
|
|
||||||
namespace Libraries::Kernel {
|
namespace Libraries::Kernel {
|
||||||
|
|
||||||
void PS4_SYSV_ABI __pthread_cleanup_push_imp(PthreadCleanupFunc routine, void* arg, PthreadCleanup* newbuf) {
|
void PS4_SYSV_ABI __pthread_cleanup_push_imp(PthreadCleanupFunc routine, void* arg,
|
||||||
|
PthreadCleanup* newbuf) {
|
||||||
newbuf->routine = routine;
|
newbuf->routine = routine;
|
||||||
newbuf->routine_arg = arg;
|
newbuf->routine_arg = arg;
|
||||||
newbuf->onheap = 0;
|
newbuf->onheap = 0;
|
||||||
|
|
|
@ -142,6 +142,7 @@ const void* PS4_SYSV_ABI posix_pthread_getspecific(PthreadKeyT key) {
|
||||||
void RegisterSpec(Core::Loader::SymbolsResolver* sym) {
|
void RegisterSpec(Core::Loader::SymbolsResolver* sym) {
|
||||||
// Posix
|
// Posix
|
||||||
LIB_FUNCTION("mqULNdimTn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_key_create);
|
LIB_FUNCTION("mqULNdimTn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_key_create);
|
||||||
|
LIB_FUNCTION("6BpEZuDT7YI", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_key_delete);
|
||||||
LIB_FUNCTION("0-KXaS70xy4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_getspecific);
|
LIB_FUNCTION("0-KXaS70xy4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_getspecific);
|
||||||
LIB_FUNCTION("WrOLvHU0yQM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setspecific);
|
LIB_FUNCTION("WrOLvHU0yQM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setspecific);
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,46 @@
|
||||||
#include "core/loader/elf.h"
|
#include "core/loader/elf.h"
|
||||||
#include "core/loader/symbols_resolver.h"
|
#include "core/loader/symbols_resolver.h"
|
||||||
|
|
||||||
#define LIB_FUNCTION(nid, lib, libversion, mod, moduleVersionMajor, moduleVersionMinor, function) \
|
template <size_t N>
|
||||||
|
struct StringLiteral {
|
||||||
|
constexpr StringLiteral(const char (&str)[N]) {
|
||||||
|
std::copy_n(str, N, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
char value[N];
|
||||||
|
};
|
||||||
|
|
||||||
|
template <StringLiteral name, class F, F f>
|
||||||
|
struct wrapper_impl;
|
||||||
|
|
||||||
|
template <StringLiteral name, class R, class... Args, PS4_SYSV_ABI R (*f)(Args...)>
|
||||||
|
struct wrapper_impl<name, PS4_SYSV_ABI R (*)(Args...), f> {
|
||||||
|
static R PS4_SYSV_ABI wrap(Args... args) {
|
||||||
|
if (std::string_view(name.value) != "scePthreadEqual" &&
|
||||||
|
std::string_view(name.value) != "sceUserServiceGetEvent" &&
|
||||||
|
!std::string_view(name.value).contains("scePthreadMutex") &&
|
||||||
|
!std::string_view(name.value).contains("pthread_mutex")) {
|
||||||
|
// LOG_WARNING(Core_Linker, "Function {} called", name.value);
|
||||||
|
}
|
||||||
|
if constexpr (std::is_same_v<R, s32> || std::is_same_v<R, u32>) {
|
||||||
|
const u32 ret = f(args...);
|
||||||
|
if (ret != 0 && !std::string_view(name.value).contains("pthread_equal")) {
|
||||||
|
LOG_WARNING(Core_Linker, "Function {} returned {:#x}", name.value, ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
// stuff
|
||||||
|
return f(args...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <StringLiteral name, class F, F f>
|
||||||
|
constexpr auto wrapper = wrapper_impl<name, F, f>::wrap;
|
||||||
|
|
||||||
|
#define W(foo) wrapper<#foo, decltype(&foo), foo>
|
||||||
|
// #define W(foo) foo
|
||||||
|
|
||||||
|
#define LIB_FUNCTION(nid, lib, libversion, mod, moduleVersionMajor, moduleVersionMinor, f) \
|
||||||
{ \
|
{ \
|
||||||
Core::Loader::SymbolResolver sr{}; \
|
Core::Loader::SymbolResolver sr{}; \
|
||||||
sr.name = nid; \
|
sr.name = nid; \
|
||||||
|
@ -19,8 +58,10 @@
|
||||||
sr.module_version_major = moduleVersionMajor; \
|
sr.module_version_major = moduleVersionMajor; \
|
||||||
sr.module_version_minor = moduleVersionMinor; \
|
sr.module_version_minor = moduleVersionMinor; \
|
||||||
sr.type = Core::Loader::SymbolType::Function; \
|
sr.type = Core::Loader::SymbolType::Function; \
|
||||||
auto func = reinterpret_cast<u64>(function); \
|
{ \
|
||||||
|
auto func = reinterpret_cast<u64>(wrapper<#f, decltype(&f), f>); \
|
||||||
sym->AddSymbol(sr, func); \
|
sym->AddSymbol(sr, func); \
|
||||||
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define LIB_OBJ(nid, lib, libversion, mod, moduleVersionMajor, moduleVersionMinor, function) \
|
#define LIB_OBJ(nid, lib, libversion, mod, moduleVersionMajor, moduleVersionMinor, function) \
|
||||||
|
|
|
@ -25,6 +25,7 @@ int PS4_SYSV_ABI scePadConnectPort() {
|
||||||
int PS4_SYSV_ABI scePadDeviceClassGetExtendedInformation(
|
int PS4_SYSV_ABI scePadDeviceClassGetExtendedInformation(
|
||||||
s32 handle, OrbisPadDeviceClassExtendedInformation* pExtInfo) {
|
s32 handle, OrbisPadDeviceClassExtendedInformation* pExtInfo) {
|
||||||
LOG_ERROR(Lib_Pad, "(STUBBED) called");
|
LOG_ERROR(Lib_Pad, "(STUBBED) called");
|
||||||
|
std::memset(pExtInfo, 0, sizeof(OrbisPadDeviceClassExtendedInformation));
|
||||||
if (Config::getUseSpecialPad()) {
|
if (Config::getUseSpecialPad()) {
|
||||||
pExtInfo->deviceClass = (OrbisPadDeviceClass)Config::getSpecialPadClass();
|
pExtInfo->deviceClass = (OrbisPadDeviceClass)Config::getSpecialPadClass();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
#pragma clang optimize off
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/config.h"
|
#include "common/config.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
@ -95,7 +95,7 @@ s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* co
|
||||||
LOG_ERROR(Lib_VideoOut, "Addresses are null");
|
LOG_ERROR(Lib_VideoOut, "Addresses are null");
|
||||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS;
|
return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS;
|
||||||
}
|
}
|
||||||
VAddr ret_addr = (VAddr)__builtin_return_address(0);
|
|
||||||
auto* port = driver->GetPort(handle);
|
auto* port = driver->GetPort(handle);
|
||||||
if (!port || !port->is_open) {
|
if (!port || !port->is_open) {
|
||||||
LOG_ERROR(Lib_VideoOut, "Invalid handle = {}", handle);
|
LOG_ERROR(Lib_VideoOut, "Invalid handle = {}", handle);
|
||||||
|
|
|
@ -284,7 +284,7 @@ void* Linker::TlsGetAddr(u64 module_index, u64 offset) {
|
||||||
const u32 old_num_dtvs = dtv_table[1].counter;
|
const u32 old_num_dtvs = dtv_table[1].counter;
|
||||||
ASSERT_MSG(max_tls_index > old_num_dtvs, "Module unloading unsupported");
|
ASSERT_MSG(max_tls_index > old_num_dtvs, "Module unloading unsupported");
|
||||||
// Module was loaded, increase DTV table size.
|
// Module was loaded, increase DTV table size.
|
||||||
DtvEntry* new_dtv_table = new DtvEntry[max_tls_index + 2];
|
DtvEntry* new_dtv_table = new DtvEntry[max_tls_index + 2]{};
|
||||||
std::memcpy(new_dtv_table + 2, dtv_table + 2, old_num_dtvs * sizeof(DtvEntry));
|
std::memcpy(new_dtv_table + 2, dtv_table + 2, old_num_dtvs * sizeof(DtvEntry));
|
||||||
new_dtv_table[0].counter = dtv_generation_counter;
|
new_dtv_table[0].counter = dtv_generation_counter;
|
||||||
new_dtv_table[1].counter = max_tls_index;
|
new_dtv_table[1].counter = max_tls_index;
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma clang optimize off
|
||||||
|
#include <cryptopp/sha.h>
|
||||||
|
|
||||||
#include "common/alignment.h"
|
#include "common/alignment.h"
|
||||||
#include "common/arch.h"
|
#include "common/arch.h"
|
||||||
|
@ -56,6 +58,30 @@ static std::string EncodeId(u64 nVal) {
|
||||||
return enc;
|
return enc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string StringToNid(std::string_view symbol) {
|
||||||
|
static constexpr std::array<u8, 16> Salt = {0x51, 0x8D, 0x64, 0xA6, 0x35, 0xDE, 0xD8, 0xC1,
|
||||||
|
0xE6, 0xB0, 0x39, 0xB1, 0xC3, 0xE5, 0x52, 0x30};
|
||||||
|
std::vector<u8> input(symbol.size() + Salt.size());
|
||||||
|
std::memcpy(input.data(), symbol.data(), symbol.size());
|
||||||
|
std::memcpy(input.data() + symbol.size(), Salt.data(), Salt.size());
|
||||||
|
|
||||||
|
std::array<u8, CryptoPP::SHA1::DIGESTSIZE> hash;
|
||||||
|
CryptoPP::SHA1().CalculateDigest(hash.data(), input.data(), input.size());
|
||||||
|
|
||||||
|
u64 digest;
|
||||||
|
std::memcpy(&digest, hash.data(), sizeof(digest));
|
||||||
|
|
||||||
|
static constexpr std::string_view codes =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-";
|
||||||
|
std::string dst(11, '\0');
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
dst[i] = codes[(digest >> (58 - i * 6)) & 0x3f];
|
||||||
|
}
|
||||||
|
dst[10] = codes[(digest & 0xf) * 4];
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
Module::Module(Core::MemoryManager* memory_, const std::filesystem::path& file_, u32& max_tls_index)
|
Module::Module(Core::MemoryManager* memory_, const std::filesystem::path& file_, u32& max_tls_index)
|
||||||
: memory{memory_}, file{file_}, name{file.stem().string()} {
|
: memory{memory_}, file{file_}, name{file.stem().string()} {
|
||||||
elf.Open(file);
|
elf.Open(file);
|
||||||
|
@ -489,4 +515,15 @@ const LibraryInfo* Module::FindLibrary(std::string_view id) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* Module::FindByName(std::string_view name) {
|
||||||
|
const auto nid_str = StringToNid(name);
|
||||||
|
const auto symbols = export_sym.GetSymbols();
|
||||||
|
const auto it = std::ranges::find_if(
|
||||||
|
symbols, [&](const Loader::SymbolRecord& record) { return record.name.contains(nid_str); });
|
||||||
|
if (it != symbols.end()) {
|
||||||
|
return reinterpret_cast<void*>(it->virtual_address);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
|
|
@ -165,15 +165,6 @@ public:
|
||||||
return elf.IsSharedLib();
|
return elf.IsSharedLib();
|
||||||
}
|
}
|
||||||
|
|
||||||
void* FindByName(std::string_view name) {
|
|
||||||
const auto symbols = export_sym.GetSymbols();
|
|
||||||
const auto it = std::ranges::find(symbols, name, &Loader::SymbolRecord::nid_name);
|
|
||||||
if (it != symbols.end()) {
|
|
||||||
return reinterpret_cast<void*>(it->virtual_address);
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T = VAddr>
|
template <typename T = VAddr>
|
||||||
T GetProcParam() const noexcept {
|
T GetProcParam() const noexcept {
|
||||||
return reinterpret_cast<T>(proc_param_virtual_addr);
|
return reinterpret_cast<T>(proc_param_virtual_addr);
|
||||||
|
@ -217,6 +208,8 @@ public:
|
||||||
void LoadDynamicInfo();
|
void LoadDynamicInfo();
|
||||||
void LoadSymbols();
|
void LoadSymbols();
|
||||||
|
|
||||||
|
void* FindByName(std::string_view name);
|
||||||
|
|
||||||
OrbisKernelModuleInfoEx GetModuleInfoEx() const;
|
OrbisKernelModuleInfoEx GetModuleInfoEx() const;
|
||||||
const ModuleInfo* FindModule(std::string_view id);
|
const ModuleInfo* FindModule(std::string_view id);
|
||||||
const LibraryInfo* FindLibrary(std::string_view id);
|
const LibraryInfo* FindLibrary(std::string_view id);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
#pragma clang optimize off
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/config.h"
|
#include "common/config.h"
|
||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue