From 2bbe1349c2039b1c2a80712c81f0f8656915f6bb Mon Sep 17 00:00:00 2001 From: raphaelthegreat <47210458+raphaelthegreat@users.noreply.github.com> Date: Mon, 3 Jun 2024 03:54:12 +0300 Subject: [PATCH] linker: Proper TLS implementation --- CMakeLists.txt | 2 + src/common/alignment.h | 6 +- src/core/libraries/kernel/file_system.cpp | 2 +- src/core/libraries/kernel/libkernel.cpp | 5 +- .../libraries/kernel/memory_management.cpp | 24 +- src/core/libraries/kernel/memory_management.h | 5 +- src/core/libraries/kernel/physical_memory.cpp | 2 +- .../libraries/kernel/thread_management.cpp | 23 +- src/core/libraries/kernel/time_management.cpp | 27 +- src/core/libraries/kernel/time_management.h | 2 +- src/core/linker.cpp | 708 ++++-------------- src/core/linker.h | 145 +--- src/core/loader/elf.cpp | 6 +- src/core/loader/elf.h | 8 +- src/core/loader/symbols_resolver.cpp | 18 +- src/core/memory.cpp | 4 +- src/core/module.cpp | 419 +++++++++++ src/core/module.h | 172 +++++ src/core/tls.cpp | 15 +- src/core/tls.h | 5 +- src/main.cpp | 6 +- .../frontend/translate/scalar_alu.cpp | 22 +- .../frontend/translate/translate.cpp | 6 +- .../frontend/translate/vector_alu.cpp | 2 +- src/shader_recompiler/ir/ir_emitter.cpp | 1 + .../renderer_vulkan/vk_graphics_pipeline.cpp | 4 +- .../renderer_vulkan/vk_rasterizer.cpp | 5 +- .../renderer_vulkan/vk_stream_buffer.cpp | 6 +- .../texture_cache/texture_cache.cpp | 2 +- 29 files changed, 905 insertions(+), 747 deletions(-) create mode 100644 src/core/module.cpp create mode 100644 src/core/module.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1237432d..b5367338 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -287,6 +287,8 @@ set(CORE src/core/aerolib/stubs.cpp src/core/linker.h src/core/memory.cpp src/core/memory.h + src/core/module.cpp + src/core/module.h src/core/platform.h src/core/memory.h src/core/tls.cpp diff --git a/src/common/alignment.h b/src/common/alignment.h index b55a54cf..367efb6b 100644 --- a/src/common/alignment.h +++ b/src/common/alignment.h @@ -9,7 +9,7 @@ namespace Common { template -[[nodiscard]] constexpr T alignUp(T value, std::size_t size) { +[[nodiscard]] constexpr T AlignUp(T value, std::size_t size) { static_assert(std::is_unsigned_v, "T must be an unsigned value."); auto mod{static_cast(value % size)}; value -= mod; @@ -17,14 +17,14 @@ template } template -[[nodiscard]] constexpr T alignDown(T value, std::size_t size) { +[[nodiscard]] constexpr T AlignDown(T value, std::size_t size) { static_assert(std::is_unsigned_v, "T must be an unsigned value."); return static_cast(value - value % size); } template requires std::is_integral_v -[[nodiscard]] constexpr bool is16KBAligned(T value) { +[[nodiscard]] constexpr bool Is16KBAligned(T value) { return (value & 0x3FFF) == 0; } diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index f0fec319..999251bf 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -204,7 +204,7 @@ int PS4_SYSV_ABI posix_stat(const char* path, OrbisKernelStat* sb) { return ORBIS_OK; } -int PS4_SYSV_ABI sceKernelCheckReachability(const char *path) { +int PS4_SYSV_ABI sceKernelCheckReachability(const char* path) { LOG_INFO(Lib_Kernel, "path = {}", path); auto* mnt = Common::Singleton::Instance(); std::string path_name = mnt->GetHostFile(path); diff --git a/src/core/libraries/kernel/libkernel.cpp b/src/core/libraries/kernel/libkernel.cpp index 406ae626..b52e6b4f 100644 --- a/src/core/libraries/kernel/libkernel.cpp +++ b/src/core/libraries/kernel/libkernel.cpp @@ -204,7 +204,8 @@ void LibKernel_Register(Core::Loader::SymbolsResolver* sym) { LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &g_stack_chk_guard); // memory LIB_FUNCTION("rTXw65xmLIA", "libkernel", 1, "libkernel", 1, 1, sceKernelAllocateDirectMemory); - LIB_FUNCTION("B+vc2AO2Zrc", "libkernel", 1, "libkernel", 1, 1, sceKernelAllocateMainDirectMemory); + LIB_FUNCTION("B+vc2AO2Zrc", "libkernel", 1, "libkernel", 1, 1, + sceKernelAllocateMainDirectMemory); LIB_FUNCTION("pO96TwzOm5E", "libkernel", 1, "libkernel", 1, 1, sceKernelGetDirectMemorySize); LIB_FUNCTION("L-Q3LEjIbgA", "libkernel", 1, "libkernel", 1, 1, sceKernelMapDirectMemory); LIB_FUNCTION("WFcfL2lzido", "libkernel", 1, "libkernel", 1, 1, sceKernelQueryMemoryProtection); @@ -213,6 +214,8 @@ void LibKernel_Register(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("cQke9UuBQOk", "libkernel", 1, "libkernel", 1, 1, sceKernelMunmap); LIB_FUNCTION("mL8NDH86iQI", "libkernel", 1, "libkernel", 1, 1, sceKernelMapNamedFlexibleMemory); LIB_FUNCTION("IWIBBdTHit4", "libkernel", 1, "libkernel", 1, 1, sceKernelMapFlexibleMemory); + LIB_FUNCTION("p5EcQeEeJAE", "libkernel", 1, "libkernel", 1, 1, _sceKernelRtldSetApplicationHeapAPI); + // equeue LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateEqueue); LIB_FUNCTION("jpFjmgAC5AE", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteEqueue); diff --git a/src/core/libraries/kernel/memory_management.cpp b/src/core/libraries/kernel/memory_management.cpp index 8d839c5a..250920cd 100644 --- a/src/core/libraries/kernel/memory_management.cpp +++ b/src/core/libraries/kernel/memory_management.cpp @@ -8,6 +8,7 @@ #include "core/libraries/error_codes.h" #include "core/libraries/kernel/memory_management.h" #include "core/memory.h" +#include "core/linker.h" namespace Libraries::Kernel { @@ -23,11 +24,11 @@ int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u return SCE_KERNEL_ERROR_EINVAL; } const bool is_in_range = searchEnd - searchStart >= len; - if (len <= 0 || !Common::is16KBAligned(len) || !is_in_range) { + if (len <= 0 || !Common::Is16KBAligned(len) || !is_in_range) { LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!"); return SCE_KERNEL_ERROR_EINVAL; } - if ((alignment != 0 || Common::is16KBAligned(alignment)) && !std::has_single_bit(alignment)) { + if ((alignment != 0 || Common::Is16KBAligned(alignment)) && !std::has_single_bit(alignment)) { LOG_ERROR(Kernel_Vmm, "Alignment value is invalid!"); return SCE_KERNEL_ERROR_EINVAL; } @@ -48,8 +49,10 @@ int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u return SCE_OK; } -s32 PS4_SYSV_ABI sceKernelAllocateMainDirectMemory(size_t len, size_t alignment, int memoryType, s64 *physAddrOut) { - return sceKernelAllocateDirectMemory(0, SCE_KERNEL_MAIN_DMEM_SIZE, len, alignment, memoryType, physAddrOut); +s32 PS4_SYSV_ABI sceKernelAllocateMainDirectMemory(size_t len, size_t alignment, int memoryType, + s64* physAddrOut) { + return sceKernelAllocateDirectMemory(0, SCE_KERNEL_MAIN_DMEM_SIZE, len, alignment, memoryType, + physAddrOut); } int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int flags, @@ -59,16 +62,16 @@ int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int fl "len = {:#x}, prot = {:#x}, flags = {:#x}, directMemoryStart = {:#x}, alignment = {:#x}", len, prot, flags, directMemoryStart, alignment); - if (len == 0 || !Common::is16KBAligned(len)) { + if (len == 0 || !Common::Is16KBAligned(len)) { LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 16KB aligned!"); return SCE_KERNEL_ERROR_EINVAL; } - if (!Common::is16KBAligned(directMemoryStart)) { + if (!Common::Is16KBAligned(directMemoryStart)) { LOG_ERROR(Kernel_Vmm, "Start address is not 16KB aligned!"); return SCE_KERNEL_ERROR_EINVAL; } if (alignment != 0) { - if ((!std::has_single_bit(alignment) && !Common::is16KBAligned(alignment))) { + if ((!std::has_single_bit(alignment) && !Common::Is16KBAligned(alignment))) { LOG_ERROR(Kernel_Vmm, "Alignment value is invalid!"); return SCE_KERNEL_ERROR_EINVAL; } @@ -85,7 +88,7 @@ int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int fl s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t len, int prot, int flags, const char* name) { - if (len == 0 || !Common::is16KBAligned(len)) { + if (len == 0 || !Common::Is16KBAligned(len)) { LOG_ERROR(Kernel_Vmm, "len is 0 or not 16kb multiple"); return ORBIS_KERNEL_ERROR_EINVAL; } @@ -131,4 +134,9 @@ int PS4_SYSV_ABI sceKernelDirectMemoryQuery(u64 offset, int flags, OrbisQueryInf return memory->DirectMemoryQuery(offset, flags == 1, query_info); } +void PS4_SYSV_ABI _sceKernelRtldSetApplicationHeapAPI(void* func) { + auto* linker = Common::Singleton::Instance(); + linker->SetHeapApiFunc(func); +} + } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/memory_management.h b/src/core/libraries/kernel/memory_management.h index 8cb83986..7108b7b3 100644 --- a/src/core/libraries/kernel/memory_management.h +++ b/src/core/libraries/kernel/memory_management.h @@ -39,7 +39,8 @@ struct OrbisQueryInfo { u64 PS4_SYSV_ABI sceKernelGetDirectMemorySize(); int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u64 len, u64 alignment, int memoryType, s64* physAddrOut); -s32 PS4_SYSV_ABI sceKernelAllocateMainDirectMemory(size_t len, size_t alignment, int memoryType, s64 *physAddrOut); +s32 PS4_SYSV_ABI sceKernelAllocateMainDirectMemory(size_t len, size_t alignment, int memoryType, + s64* physAddrOut); int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int flags, s64 directMemoryStart, u64 alignment); s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addrInOut, std::size_t len, int prot, @@ -51,4 +52,6 @@ int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** int PS4_SYSV_ABI sceKernelDirectMemoryQuery(u64 offset, int flags, OrbisQueryInfo* query_info, size_t infoSize); +void PS4_SYSV_ABI _sceKernelRtldSetApplicationHeapAPI(void* func); + } // namespace Libraries::Kernel diff --git a/src/core/libraries/kernel/physical_memory.cpp b/src/core/libraries/kernel/physical_memory.cpp index 82884a63..d81bbe5f 100644 --- a/src/core/libraries/kernel/physical_memory.cpp +++ b/src/core/libraries/kernel/physical_memory.cpp @@ -20,7 +20,7 @@ bool PhysicalMemory::Alloc(u64 searchStart, u64 searchEnd, u64 len, u64 alignmen } // Align free position - find_free_pos = Common::alignUp(find_free_pos, alignment); + find_free_pos = Common::AlignUp(find_free_pos, alignment); // If the new position is between searchStart - searchEnd , allocate a new block if (find_free_pos >= searchStart && find_free_pos + len <= searchEnd) { diff --git a/src/core/libraries/kernel/thread_management.cpp b/src/core/libraries/kernel/thread_management.cpp index bdeee180..745293ae 100644 --- a/src/core/libraries/kernel/thread_management.cpp +++ b/src/core/libraries/kernel/thread_management.cpp @@ -6,10 +6,12 @@ #include "common/assert.h" #include "common/logging/log.h" #include "common/thread.h" +#include "common/singleton.h" #include "core/libraries/error_codes.h" #include "core/libraries/kernel/thread_management.h" #include "core/libraries/libs.h" #include "core/tls.h" +#include "core/linker.h" #ifdef _WIN64 #include #endif @@ -701,7 +703,7 @@ int PS4_SYSV_ABI pthread_attr_setstacksize(ScePthreadAttr* attr, size_t stacksiz return result; } -int PS4_SYSV_ABI pthread_attr_setdetachstate(ScePthreadAttr *attr, int detachstate) { +int PS4_SYSV_ABI pthread_attr_setdetachstate(ScePthreadAttr* attr, int detachstate) { // LOG_INFO(Kernel_Pthread, "posix pthread_mutexattr_init redirect to scePthreadMutexattrInit"); int result = scePthreadAttrSetdetachstate(attr, detachstate); if (result < 0) { @@ -713,7 +715,7 @@ int PS4_SYSV_ABI pthread_attr_setdetachstate(ScePthreadAttr *attr, int detachsta return result; } -int PS4_SYSV_ABI pthread_mutexattr_init(ScePthreadMutexattr *attr) { +int PS4_SYSV_ABI pthread_mutexattr_init(ScePthreadMutexattr* attr) { // LOG_INFO(Kernel_Pthread, "posix pthread_mutexattr_init redirect to scePthreadMutexattrInit"); int result = scePthreadMutexattrInit(attr); if (result < 0) { @@ -725,7 +727,7 @@ int PS4_SYSV_ABI pthread_mutexattr_init(ScePthreadMutexattr *attr) { return result; } -int PS4_SYSV_ABI pthread_mutexattr_settype(ScePthreadMutexattr *attr, int type) { +int PS4_SYSV_ABI pthread_mutexattr_settype(ScePthreadMutexattr* attr, int type) { // LOG_INFO(Kernel_Pthread, "posix pthread_mutex_init redirect to scePthreadMutexInit"); int result = scePthreadMutexattrSettype(attr, type); if (result < 0) { @@ -902,7 +904,8 @@ static void cleanup_thread(void* arg) { static void* run_thread(void* arg) { auto* thread = static_cast(arg); Common::SetCurrentThreadName(thread->name.c_str()); - Core::SetTLSStorage(0, 0); + auto* linker = Common::Singleton::Instance(); + linker->InitTlsForThread(); void* ret = nullptr; g_pthread_self = thread; pthread_cleanup_push(cleanup_thread, thread); @@ -1109,6 +1112,16 @@ int PS4_SYSV_ABI scePthreadEqual(ScePthread thread1, ScePthread thread2) { return (thread1 == thread2 ? 1 : 0); } +struct TlsIndex { + u64 ti_module; + u64 ti_offset; +}; + +void* PS4_SYSV_ABI __tls_get_addr(TlsIndex* index) { + auto* linker = Common::Singleton::Instance(); + return linker->TlsGetAddr(index->ti_module, index->ti_offset); +} + void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetschedpolicy); LIB_FUNCTION("-Wreprtu0Qs", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetdetachstate); @@ -1121,10 +1134,12 @@ void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("aI+OeCz8xrQ", "libkernel", 1, "libkernel", 1, 1, scePthreadSelf); LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, pthread_self); + LIB_FUNCTION("EotR8a3ASf4", "libScePosix", 1, "libkernel", 1, 1, pthread_self); LIB_FUNCTION("3qxgM4ezETA", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetaffinity); LIB_FUNCTION("8+s5BzZjxSg", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetaffinity); LIB_FUNCTION("x1X76arYMxU", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGet); LIB_FUNCTION("UTXzJbWhhTE", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetstacksize); + LIB_FUNCTION("vNe1w4diLCs", "libkernel", 1, "libkernel", 1, 1, __tls_get_addr); LIB_FUNCTION("bt3CTBKmGyI", "libkernel", 1, "libkernel", 1, 1, scePthreadSetaffinity); LIB_FUNCTION("6UgtwV+0zb4", "libkernel", 1, "libkernel", 1, 1, scePthreadCreate); diff --git a/src/core/libraries/kernel/time_management.cpp b/src/core/libraries/kernel/time_management.cpp index 462cea17..74b5b94c 100644 --- a/src/core/libraries/kernel/time_management.cpp +++ b/src/core/libraries/kernel/time_management.cpp @@ -1,9 +1,9 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include #include +#include +#include #include "common/native_clock.h" #include "core/libraries/kernel/time_management.h" #include "core/libraries/libs.h" @@ -37,18 +37,15 @@ u64 PS4_SYSV_ABI sceKernelReadTsc() { return clock->GetUptime(); } -int PS4_SYSV_ABI sceKernelGettimeofday(SceKernelTimeval *tp) { - -} +int PS4_SYSV_ABI sceKernelGettimeofday(SceKernelTimeval* tp) {} #if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) -#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 +#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 #else -#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL +#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL #endif -struct timezone -{ +struct timezone { int tz_minuteswest; /* minutes W of Greenwich */ int tz_dsttime; /* type of dst correction */ }; @@ -65,20 +62,20 @@ static const unsigned __int64 epoch = 116444736000000000ULL; * FILETIME represents the number of 100-nanosecond intervals since * January 1, 1601 (UTC). */ -#define FILETIME_UNITS_PER_SEC 10000000L +#define FILETIME_UNITS_PER_SEC 10000000L #define FILETIME_UNITS_PER_USEC 10 -int PS4_SYSV_ABI gettimeofday(struct timeval * tp, struct timezone * tzp) { - FILETIME file_time; +int PS4_SYSV_ABI gettimeofday(struct timeval* tp, struct timezone* tzp) { + FILETIME file_time; ULARGE_INTEGER ularge; GetSystemTimePreciseAsFileTime(&file_time); ularge.LowPart = file_time.dwLowDateTime; ularge.HighPart = file_time.dwHighDateTime; - tp->tv_sec = (long) ((ularge.QuadPart - epoch) / FILETIME_UNITS_PER_SEC); - tp->tv_usec = (long) (((ularge.QuadPart - epoch) % FILETIME_UNITS_PER_SEC) - / FILETIME_UNITS_PER_USEC); + tp->tv_sec = (long)((ularge.QuadPart - epoch) / FILETIME_UNITS_PER_SEC); + tp->tv_usec = + (long)(((ularge.QuadPart - epoch) % FILETIME_UNITS_PER_SEC) / FILETIME_UNITS_PER_USEC); return 0; } diff --git a/src/core/libraries/kernel/time_management.h b/src/core/libraries/kernel/time_management.h index f754921a..808ba638 100644 --- a/src/core/libraries/kernel/time_management.h +++ b/src/core/libraries/kernel/time_management.h @@ -21,7 +21,7 @@ u64 PS4_SYSV_ABI sceKernelGetProcessTime(); u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounter(); u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounterFrequency(); u64 PS4_SYSV_ABI sceKernelReadTsc(); -int PS4_SYSV_ABI sceKernelGettimeofday(SceKernelTimeval *tp); +int PS4_SYSV_ABI sceKernelGettimeofday(SceKernelTimeval* tp); void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym); diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 444c0f8e..7065a792 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -1,9 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include -#include +#include "common/assert.h" +#include "common/alignment.h" #include "common/config.h" #include "common/logging/log.h" #include "common/path_util.h" @@ -12,468 +11,138 @@ #include "core/aerolib/aerolib.h" #include "core/aerolib/stubs.h" #include "core/libraries/kernel/thread_management.h" +#include "core/libraries/kernel/memory_management.h" #include "core/linker.h" #include "core/tls.h" #include "core/virtual_memory.h" +#include namespace Core { -static u64 LoadAddress = SYSTEM_RESERVED + CODE_BASE_OFFSET; -static constexpr u64 CODE_BASE_INCR = 0x010000000u; +using ExitFunc = PS4_SYSV_ABI void (*)(); -static u64 GetAlignedSize(const elf_program_header& phdr) { - return (phdr.p_align != 0 ? (phdr.p_memsz + (phdr.p_align - 1)) & ~(phdr.p_align - 1) - : phdr.p_memsz); +static PS4_SYSV_ABI void ProgramExitFunc() { + fmt::print("exit function called\n"); } -static u64 CalculateBaseSize(const elf_header& ehdr, std::span phdr) { - u64 base_size = 0; - for (u16 i = 0; i < ehdr.e_phnum; i++) { - if (phdr[i].p_memsz != 0 && (phdr[i].p_type == PT_LOAD || phdr[i].p_type == PT_SCE_RELRO)) { - u64 last_addr = phdr[i].p_vaddr + GetAlignedSize(phdr[i]); - if (last_addr > base_size) { - base_size = last_addr; - } - } - } - return base_size; -} +static void RunMainEntry(VAddr addr, EntryParams* params, ExitFunc exit_func) { + // reinterpret_cast(addr)(params, exit_func); // can't be used, stack has to have + // a specific layout + asm volatile("andq $-16, %%rsp\n" // Align to 16 bytes + "subq $8, %%rsp\n" // videoout_basic expects the stack to be misaligned -static std::string EncodeId(u64 nVal) { - std::string enc; - const char pCodes[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-"; - if (nVal < 0x40u) { - enc += pCodes[nVal]; - } else { - if (nVal < 0x1000u) { - enc += pCodes[static_cast(nVal >> 6u) & 0x3fu]; - enc += pCodes[nVal & 0x3fu]; - } else { - enc += pCodes[static_cast(nVal >> 12u) & 0x3fu]; - enc += pCodes[static_cast(nVal >> 6u) & 0x3fu]; - enc += pCodes[nVal & 0x3fu]; - } - } - return enc; + // Kernel also pushes some more things here during process init + // at least: environment, auxv, possibly other things + + "pushq 8(%1)\n" // copy EntryParams to top of stack like the kernel does + "pushq 0(%1)\n" // OpenOrbis expects to find it there + + "movq %1, %%rdi\n" // also pass params and exit func + "movq %2, %%rsi\n" // as before + + "jmp *%0\n" // can't use call here, as that would mangle the prepared stack. + // there's no coming back + : + : "r"(addr), "r"(params), "r"(exit_func) + : "rax", "rsi", "rdi"); } Linker::Linker() = default; Linker::~Linker() = default; -Module* Linker::LoadModule(const std::filesystem::path& elf_name) { - std::scoped_lock lock{m_mutex}; +void Linker::Execute() { + if (Config::debugDump()) { + DebugDump(); + } + // Calculate static TLS size. + static constexpr size_t StOff = 0x80; // TODO: What is this offset? + static_tls_size = std::ranges::fold_left(m_modules, StOff, [&](u32 size, auto& module) { + const size_t new_size = size + module->tls.image_size; + module->tls.distance_from_fs = new_size; + return new_size; + }); + + Common::SetCurrentThreadName("GAME_MainThread"); + Libraries::Kernel::pthreadInitSelfMainThread(); + + // Init primary thread TLS. + InitTlsForThread(true); + + // Relocate all modules + for (const auto& m : m_modules) { + Relocate(m.get()); + } + + // Start shared library modules + for (auto& m : m_modules) { + if (m->IsSharedLib()) { + m->Start(0, nullptr, nullptr); + } + } + + // Start main module. + EntryParams p{}; + p.argc = 1; + p.argv[0] = "eboot.bin"; + + for (auto& m : m_modules) { + if (!m->IsSharedLib()) { + RunMainEntry(m->GetEntryAddress(), &p, ProgramExitFunc); + } + } +} + +Module* Linker::LoadModule(const std::filesystem::path& elf_name) { if (!std::filesystem::exists(elf_name)) { - LOG_ERROR(Core_Linker, "Provided module {} does not exist", elf_name.string()); + LOG_ERROR(Core_Linker, "Provided file {} does not exist", elf_name.string()); return nullptr; } - auto& m = m_modules.emplace_back(); - m = std::make_unique(); - m->elf.Open(elf_name); - m->file_name = std::filesystem::path(elf_name).filename().string(); - - if (m->elf.IsElfFile()) { - LoadModuleToMemory(m.get()); - LoadDynamicInfo(m.get()); - LoadSymbols(m.get()); - } else { - m_modules.pop_back(); - return nullptr; // It is not a valid elf file //TODO check it why! + auto module = std::make_unique(elf_name); + if (!module->IsValid()) { + LOG_ERROR(Core_Linker, "Provided file {} is not valid ELF file", elf_name.string()); + return nullptr; } - return m.get(); + if (!module->IsSharedLib()) { + main_proc_param_addr = module->GetProcParam(); + } + + return m_modules.emplace_back(std::move(module)).get(); } -void Linker::LoadModuleToMemory(Module* m) { - // Retrieve elf header and program header - const auto elf_header = m->elf.GetElfHeader(); - const auto elf_pheader = m->elf.GetProgramHeader(); - - u64 base_size = CalculateBaseSize(elf_header, elf_pheader); - m->aligned_base_size = (base_size & ~(static_cast(0x1000) - 1)) + - 0x1000; // align base size to 0x1000 block size (TODO is that the default - // block size or it can be changed? - - static constexpr u64 TrampolineSize = 8_MB; - m->base_virtual_addr = - VirtualMemory::memory_alloc(LoadAddress, m->aligned_base_size + TrampolineSize, - VirtualMemory::MemoryMode::ExecuteReadWrite); - - LoadAddress += CODE_BASE_INCR * (1 + m->aligned_base_size / CODE_BASE_INCR); - - void* trampoline_addr = reinterpret_cast(m->base_virtual_addr + m->aligned_base_size); - Xbyak::CodeGenerator c(TrampolineSize, trampoline_addr); - - LOG_INFO(Core_Linker, "======== Load Module to Memory ========"); - LOG_INFO(Core_Linker, "base_virtual_addr ......: {:#018x}", m->base_virtual_addr); - LOG_INFO(Core_Linker, "base_size ..............: {:#018x}", base_size); - LOG_INFO(Core_Linker, "aligned_base_size ......: {:#018x}", m->aligned_base_size); - - for (u16 i = 0; i < elf_header.e_phnum; i++) { - switch (elf_pheader[i].p_type) { - case PT_LOAD: - case PT_SCE_RELRO: - if (elf_pheader[i].p_memsz != 0) { - u64 segment_addr = elf_pheader[i].p_vaddr + m->base_virtual_addr; - u64 segment_file_size = elf_pheader[i].p_filesz; - u64 segment_memory_size = GetAlignedSize(elf_pheader[i]); - auto segment_mode = m->elf.ElfPheaderFlagsStr(elf_pheader[i].p_flags); - LOG_INFO(Core_Linker, "program header = [{}] type = {}", i, - m->elf.ElfPheaderTypeStr(elf_pheader[i].p_type)); - LOG_INFO(Core_Linker, "segment_addr ..........: {:#018x}", segment_addr); - LOG_INFO(Core_Linker, "segment_file_size .....: {}", segment_file_size); - LOG_INFO(Core_Linker, "segment_memory_size ...: {}", segment_memory_size); - LOG_INFO(Core_Linker, "segment_mode ..........: {}", segment_mode); - - m->elf.LoadSegment(segment_addr, elf_pheader[i].p_offset, segment_file_size); - - if (elf_pheader[i].p_flags & PF_EXEC) { - PatchTLS(segment_addr, segment_file_size, c); - } - } else { - LOG_ERROR(Core_Linker, "p_memsz==0 in type {}", - m->elf.ElfPheaderTypeStr(elf_pheader[i].p_type)); - } - break; - case PT_DYNAMIC: - if (elf_pheader[i].p_filesz != 0) { - m->m_dynamic.resize(elf_pheader[i].p_filesz); - m->elf.LoadSegment(reinterpret_cast(m->m_dynamic.data()), - elf_pheader[i].p_offset, elf_pheader[i].p_filesz); - } else { - LOG_ERROR(Core_Linker, "p_filesz==0 in type {}", - m->elf.ElfPheaderTypeStr(elf_pheader[i].p_type)); - } - break; - case PT_SCE_DYNLIBDATA: - if (elf_pheader[i].p_filesz != 0) { - m->m_dynamic_data.resize(elf_pheader[i].p_filesz); - m->elf.LoadSegment(reinterpret_cast(m->m_dynamic_data.data()), - elf_pheader[i].p_offset, elf_pheader[i].p_filesz); - } else { - LOG_ERROR(Core_Linker, "p_filesz==0 in type {}", - m->elf.ElfPheaderTypeStr(elf_pheader[i].p_type)); - } - break; - case PT_TLS: - m->tls.image_virtual_addr = elf_pheader[i].p_vaddr + m->base_virtual_addr; - m->tls.image_size = GetAlignedSize(elf_pheader[i]); - LOG_INFO(Core_Linker, "TLS virtual address = {:#x}", m->tls.image_virtual_addr); - LOG_INFO(Core_Linker, "TLS image size = {}", m->tls.image_size); - break; - case PT_SCE_PROCPARAM: - m->proc_param_virtual_addr = elf_pheader[i].p_vaddr + m->base_virtual_addr; - break; - default: - LOG_ERROR(Core_Linker, "Unimplemented type {}", - m->elf.ElfPheaderTypeStr(elf_pheader[i].p_type)); - } - } - LOG_INFO(Core_Linker, "program entry addr ..........: {:#018x}", - m->elf.GetElfEntry() + m->base_virtual_addr); -} - -void Linker::LoadDynamicInfo(Module* m) { - for (const auto* dyn = reinterpret_cast(m->m_dynamic.data()); - dyn->d_tag != DT_NULL; dyn++) { - switch (dyn->d_tag) { - case DT_SCE_HASH: // Offset of the hash table. - m->dynamic_info.hash_table = - reinterpret_cast(m->m_dynamic_data.data() + dyn->d_un.d_ptr); - break; - case DT_SCE_HASHSZ: // Size of the hash table - m->dynamic_info.hash_table_size = dyn->d_un.d_val; - break; - case DT_SCE_STRTAB: // Offset of the string table. - m->dynamic_info.str_table = - reinterpret_cast(m->m_dynamic_data.data() + dyn->d_un.d_ptr); - break; - case DT_SCE_STRSZ: // Size of the string table. - m->dynamic_info.str_table_size = dyn->d_un.d_val; - break; - case DT_SCE_SYMTAB: // Offset of the symbol table. - m->dynamic_info.symbol_table = - reinterpret_cast(m->m_dynamic_data.data() + dyn->d_un.d_ptr); - break; - case DT_SCE_SYMTABSZ: // Size of the symbol table. - m->dynamic_info.symbol_table_total_size = dyn->d_un.d_val; - break; - case DT_INIT: - m->dynamic_info.init_virtual_addr = dyn->d_un.d_ptr; - break; - case DT_FINI: - m->dynamic_info.fini_virtual_addr = dyn->d_un.d_ptr; - break; - case DT_SCE_PLTGOT: // Offset of the global offset table. - m->dynamic_info.pltgot_virtual_addr = dyn->d_un.d_ptr; - break; - case DT_SCE_JMPREL: // Offset of the table containing jump slots. - m->dynamic_info.jmp_relocation_table = - reinterpret_cast(m->m_dynamic_data.data() + dyn->d_un.d_ptr); - break; - case DT_SCE_PLTRELSZ: // Size of the global offset table. - m->dynamic_info.jmp_relocation_table_size = dyn->d_un.d_val; - break; - case DT_SCE_PLTREL: // The type of relocations in the relocation table. Should be DT_RELA - m->dynamic_info.jmp_relocation_type = dyn->d_un.d_val; - if (m->dynamic_info.jmp_relocation_type != DT_RELA) { - LOG_WARNING(Core_Linker, "DT_SCE_PLTREL is NOT DT_RELA should check!"); - } - break; - case DT_SCE_RELA: // Offset of the relocation table. - m->dynamic_info.relocation_table = - reinterpret_cast(m->m_dynamic_data.data() + dyn->d_un.d_ptr); - break; - case DT_SCE_RELASZ: // Size of the relocation table. - m->dynamic_info.relocation_table_size = dyn->d_un.d_val; - break; - case DT_SCE_RELAENT: // The size of relocation table entries. - m->dynamic_info.relocation_table_entries_size = dyn->d_un.d_val; - if (m->dynamic_info.relocation_table_entries_size != - 0x18) // this value should always be 0x18 - { - LOG_WARNING(Core_Linker, "DT_SCE_RELAENT is NOT 0x18 should check!"); - } - break; - case DT_INIT_ARRAY: // Address of the array of pointers to initialization functions - m->dynamic_info.init_array_virtual_addr = dyn->d_un.d_ptr; - break; - case DT_FINI_ARRAY: // Address of the array of pointers to termination functions - m->dynamic_info.fini_array_virtual_addr = dyn->d_un.d_ptr; - break; - case DT_INIT_ARRAYSZ: // Size in bytes of the array of initialization functions - m->dynamic_info.init_array_size = dyn->d_un.d_val; - break; - case DT_FINI_ARRAYSZ: // Size in bytes of the array of terminationfunctions - m->dynamic_info.fini_array_size = dyn->d_un.d_val; - break; - case DT_PREINIT_ARRAY: // Address of the array of pointers to pre - initialization functions - m->dynamic_info.preinit_array_virtual_addr = dyn->d_un.d_ptr; - break; - case DT_PREINIT_ARRAYSZ: // Size in bytes of the array of pre - initialization functions - m->dynamic_info.preinit_array_size = dyn->d_un.d_val; - break; - case DT_SCE_SYMENT: // The size of symbol table entries - m->dynamic_info.symbol_table_entries_size = dyn->d_un.d_val; - if (m->dynamic_info.symbol_table_entries_size != - 0x18) // this value should always be 0x18 - { - LOG_WARNING(Core_Linker, "DT_SCE_SYMENT is NOT 0x18 should check!"); - } - break; - case DT_DEBUG: - m->dynamic_info.debug = dyn->d_un.d_val; - break; - case DT_TEXTREL: - m->dynamic_info.textrel = dyn->d_un.d_val; - break; - case DT_FLAGS: - m->dynamic_info.flags = dyn->d_un.d_val; - if (m->dynamic_info.flags != 0x04) // this value should always be DF_TEXTREL (0x04) - { - LOG_WARNING(Core_Linker, "DT_FLAGS is NOT 0x04 should check!"); - } - break; - case DT_NEEDED: // Offset of the library string in the string table to be linked in. - if (m->dynamic_info.str_table != - nullptr) // in theory this should already be filled from about just make a test case - { - m->dynamic_info.needed.push_back(m->dynamic_info.str_table + dyn->d_un.d_val); - } else { - LOG_ERROR(Core_Linker, "DT_NEEDED str table is not loaded should check!"); - } - break; - case DT_SCE_NEEDED_MODULE: { - ModuleInfo info{}; - info.value = dyn->d_un.d_val; - info.name = m->dynamic_info.str_table + info.name_offset; - info.enc_id = EncodeId(info.id); - m->dynamic_info.import_modules.push_back(info); - } break; - case DT_SCE_IMPORT_LIB: { - LibraryInfo info{}; - info.value = dyn->d_un.d_val; - info.name = m->dynamic_info.str_table + info.name_offset; - info.enc_id = EncodeId(info.id); - m->dynamic_info.import_libs.push_back(info); - } break; - case DT_SCE_FINGERPRINT: - // The fingerprint is a 24 byte (0x18) size buffer that contains a unique identifier for - // the given app. How exactly this is generated isn't known, however it is not necessary - // to have a valid fingerprint. While an invalid fingerprint will cause a warning to be - // printed to the kernel log, the ELF will still load and run. - LOG_INFO(Core_Linker, "unsupported DT_SCE_FINGERPRINT value = ..........: {:#018x}", - dyn->d_un.d_val); - break; - case DT_SCE_IMPORT_LIB_ATTR: - // The upper 32-bits should contain the module index multiplied by 0x10000. The lower - // 32-bits should be a constant 0x9. - LOG_INFO(Core_Linker, "unsupported DT_SCE_IMPORT_LIB_ATTR value = ......: {:#018x}", - dyn->d_un.d_val); - break; - case DT_SCE_ORIGINAL_FILENAME: - m->dynamic_info.filename = m->dynamic_info.str_table + dyn->d_un.d_val; - break; - case DT_SCE_MODULE_INFO: // probably only useable in shared modules - { - ModuleInfo info{}; - info.value = dyn->d_un.d_val; - info.name = m->dynamic_info.str_table + info.name_offset; - info.enc_id = EncodeId(info.id); - m->dynamic_info.export_modules.push_back(info); - } break; - case DT_SCE_MODULE_ATTR: - // TODO? - LOG_INFO(Core_Linker, "unsupported DT_SCE_MODULE_ATTR value = ..........: {:#018x}", - dyn->d_un.d_val); - break; - case DT_SCE_EXPORT_LIB: { - LibraryInfo info{}; - info.value = dyn->d_un.d_val; - info.name = m->dynamic_info.str_table + info.name_offset; - info.enc_id = EncodeId(info.id); - m->dynamic_info.export_libs.push_back(info); - } break; - default: - LOG_INFO(Core_Linker, "unsupported dynamic tag ..........: {:#018x}", dyn->d_tag); - } - } -} - -const ModuleInfo* Linker::FindModule(const Module& m, const std::string& id) { - const auto& import_modules = m.dynamic_info.import_modules; - int index = 0; - for (const auto& mod : import_modules) { - if (mod.enc_id.compare(id) == 0) { - return &import_modules.at(index); - } - index++; - } - const auto& export_modules = m.dynamic_info.export_modules; - index = 0; - for (const auto& mod : export_modules) { - if (mod.enc_id.compare(id) == 0) { - return &export_modules.at(index); - } - index++; - } - return nullptr; -} - -const LibraryInfo* Linker::FindLibrary(const Module& m, const std::string& id) { - const auto& import_libs = m.dynamic_info.import_libs; - int index = 0; - for (const auto& lib : import_libs) { - if (lib.enc_id.compare(id) == 0) { - return &import_libs.at(index); - } - index++; - } - const auto& export_libs = m.dynamic_info.export_libs; - index = 0; - for (const auto& lib : export_libs) { - if (lib.enc_id.compare(id) == 0) { - return &export_libs.at(index); - } - index++; - } - return nullptr; -} - -void Linker::LoadSymbols(Module* m) { - const auto symbol_database = [this, m](Loader::SymbolsResolver* symbol, bool export_func) { - if (m->dynamic_info.symbol_table == nullptr || m->dynamic_info.str_table == nullptr || - m->dynamic_info.symbol_table_total_size == 0) { - LOG_INFO(Core_Linker, "Symbol table not found!"); - return; - } - for (auto* sym = m->dynamic_info.symbol_table; - reinterpret_cast(sym) < reinterpret_cast(m->dynamic_info.symbol_table) + - m->dynamic_info.symbol_table_total_size; - sym++) { - std::string id = std::string(m->dynamic_info.str_table + sym->st_name); - auto bind = sym->GetBind(); - auto type = sym->GetType(); - auto visibility = sym->GetVisibility(); - const auto ids = Common::SplitString(id, '#'); - if (ids.size() == 3) { - const auto* library = FindLibrary(*m, ids.at(1)); - const auto* module = FindModule(*m, ids.at(2)); - ASSERT_MSG(library && module, "Unable to find library and module"); - if ((bind == STB_GLOBAL || bind == STB_WEAK) && - (type == STT_FUN || type == STT_OBJECT) && - export_func == (sym->st_value != 0)) { - std::string nidName = ""; - auto aeronid = AeroLib::FindByNid(ids.at(0).c_str()); - if (aeronid != nullptr) { - nidName = aeronid->name; - } else { - nidName = "UNK"; - } - Loader::SymbolResolver sym_r{}; - sym_r.name = ids.at(0); - sym_r.nidName = nidName; - sym_r.library = library->name; - sym_r.library_version = library->version; - sym_r.module = module->name; - sym_r.module_version_major = module->version_major; - sym_r.module_version_minor = module->version_minor; - switch (type) { - case STT_NOTYPE: - sym_r.type = Loader::SymbolType::NoType; - break; - case STT_FUN: - sym_r.type = Loader::SymbolType::Function; - break; - case STT_OBJECT: - sym_r.type = Loader::SymbolType::Object; - break; - default: - sym_r.type = Loader::SymbolType::Unknown; - break; - } - symbol->AddSymbol(sym_r, - (export_func ? sym->st_value + m->base_virtual_addr : 0)); - } - } - } - }; - symbol_database(&m->export_sym, true); - symbol_database(&m->import_sym, false); -} - -void Linker::Relocate(Module* m) { - const auto relocate = [this](u32 idx, elf_relocation* rel, Module* m, bool isJmpRel) { +void Linker::Relocate(Module* module) { + module->ForEachRelocation([&](elf_relocation* rel, bool isJmpRel) { auto type = rel->GetType(); auto symbol = rel->GetSymbol(); auto addend = rel->rel_addend; - auto* symbolsTlb = m->dynamic_info.symbol_table; - auto* namesTlb = m->dynamic_info.str_table; + auto* symbol_table = module->dynamic_info.symbol_table; + auto* namesTlb = module->dynamic_info.str_table; + const VAddr rel_base_virtual_addr = module->GetBaseAddress(); + const VAddr rel_virtual_addr = rel_base_virtual_addr + rel->rel_offset; + bool rel_is_resolved = false; u64 rel_value = 0; - u64 rel_base_virtual_addr = m->base_virtual_addr; - u64 rel_virtual_addr = m->base_virtual_addr + rel->rel_offset; - bool rel_isResolved = false; Loader::SymbolType rel_sym_type = Loader::SymbolType::Unknown; std::string rel_name; switch (type) { case R_X86_64_RELATIVE: rel_value = rel_base_virtual_addr + addend; - rel_isResolved = true; + rel_is_resolved = true; break; case R_X86_64_DTPMOD64: - rel_value = reinterpret_cast(m); - rel_isResolved = true; + rel_value = reinterpret_cast(module); + rel_is_resolved = true; rel_sym_type = Loader::SymbolType::Tls; break; case R_X86_64_GLOB_DAT: case R_X86_64_JUMP_SLOT: addend = 0; case R_X86_64_64: { - auto sym = symbolsTlb[symbol]; + auto sym = symbol_table[symbol]; auto sym_bind = sym.GetBind(); auto sym_type = sym.GetType(); auto sym_visibility = sym.GetVisibility(); @@ -492,10 +161,11 @@ void Linker::Relocate(Module* m) { default: ASSERT_MSG(0, "unknown symbol type {}", sym_type); } - if (sym_visibility != 0) // should be zero log if else - { + + if (sym_visibility != 0) { LOG_INFO(Core_Linker, "symbol visilibity !=0"); } + switch (sym_bind) { case STB_LOCAL: symbol_vitrual_addr = rel_base_virtual_addr + sym.st_value; @@ -503,60 +173,36 @@ void Linker::Relocate(Module* m) { case STB_GLOBAL: case STB_WEAK: { rel_name = namesTlb + sym.st_name; - Resolve(rel_name, rel_sym_type, m, &symrec); + Resolve(rel_name, rel_sym_type, module, &symrec); symbol_vitrual_addr = symrec.virtual_address; - } break; + break; + } default: ASSERT_MSG(0, "unknown bind type {}", sym_bind); } - rel_isResolved = (symbol_vitrual_addr != 0); - rel_value = (rel_isResolved ? symbol_vitrual_addr + addend : 0); + rel_is_resolved = (symbol_vitrual_addr != 0); + rel_value = (rel_is_resolved ? symbol_vitrual_addr + addend : 0); rel_name = symrec.name; - } break; + break; + } default: LOG_INFO(Core_Linker, "UNK type {:#010x} rel symbol : {:#010x}", type, symbol); } - if (rel_isResolved) { + if (rel_is_resolved) { VirtualMemory::memory_patch(rel_virtual_addr, rel_value); } else { LOG_INFO(Core_Linker, "function not patched! {}", rel_name); } - }; - - u32 idx = 0; - for (auto* rel = m->dynamic_info.relocation_table; - reinterpret_cast(rel) < reinterpret_cast(m->dynamic_info.relocation_table) + - m->dynamic_info.relocation_table_size; - rel++, idx++) { - relocate(idx, rel, m, false); - } - idx = 0; - for (auto* rel = m->dynamic_info.jmp_relocation_table; - reinterpret_cast(rel) < reinterpret_cast(m->dynamic_info.jmp_relocation_table) + - m->dynamic_info.jmp_relocation_table_size; - rel++, idx++) { - relocate(idx, rel, m, true); - } + }); } -template -bool contains(const std::vector& vecObj, const T& element) { - auto it = std::find(vecObj.begin(), vecObj.end(), element); - return it != vecObj.end(); -} - -Module* Linker::FindExportedModule(const ModuleInfo& module, const LibraryInfo& library) { - // std::scoped_lock lock{m_mutex}; - for (auto& m : m_modules) { - const auto& export_libs = m->dynamic_info.export_libs; - const auto& export_modules = m->dynamic_info.export_modules; - - if (contains(export_libs, library) && contains(export_modules, module)) { - return m.get(); - } - } - return nullptr; +const Module* Linker::FindExportedModule(const ModuleInfo& module, const LibraryInfo& library) { + const auto it = std::ranges::find_if(m_modules, [&](const auto& m) { + return std::ranges::contains(m->GetExportLibs(), library) && + std::ranges::contains(m->GetExportModules(), module); + }); + return it == m_modules.end() ? nullptr : it->get(); } void Linker::Resolve(const std::string& name, Loader::SymbolType sym_type, Module* m, @@ -569,8 +215,8 @@ void Linker::Resolve(const std::string& name, Loader::SymbolType sym_type, Modul return; } - const auto* library = FindLibrary(*m, ids.at(1)); - const auto* module = FindModule(*m, ids.at(2)); + const LibraryInfo* library = m->FindLibrary(ids[1]); + const ModuleInfo* module = m->FindModule(ids[2]); ASSERT_MSG(library && module, "Unable to find library and module"); Loader::SymbolResolver sr{}; @@ -585,8 +231,8 @@ void Linker::Resolve(const std::string& name, Loader::SymbolType sym_type, Modul const auto* record = m_hle_symbols.FindSymbol(sr); if (!record) { // Check if it an export function - if (auto* p = FindExportedModule(*module, *library); - p && p->export_sym.GetSize() > 0) { + const auto* p = FindExportedModule(*module, *library); + if (p && p->export_sym.GetSize() > 0) { record = p->export_sym.FindSymbol(sr); } } @@ -607,102 +253,72 @@ void Linker::Resolve(const std::string& name, Loader::SymbolType sym_type, Modul return_info->name, library->name, module->name); } -u64 Linker::GetProcParam() { - for (auto& m : m_modules) { - if (!m->elf.IsSharedLib()) { - return m->proc_param_virtual_addr; +void* Linker::TlsGetAddr(u64 module_index, u64 offset) { + ASSERT_MSG(dtv_table[0].counter == dtv_generation_counter, + "Reallocation of DTV table is not supported"); + + void* module = dtv_table[module_index - 1].pointer; + ASSERT_MSG(module, "DTV allocation is not supported"); + return module; +} + +void Linker::InitTlsForThread(bool is_primary) { + static constexpr size_t TcbSize = 0x40; + static constexpr size_t TlsAllocAlign = 0x20; + const size_t total_tls_size = Common::AlignUp(static_tls_size, TlsAllocAlign) + TcbSize; + + // The kernel module has a few different paths for TLS allocation. + // For SDK < 1.7 it allocates both main and secondary thread blocks using libc mspace/malloc. + // In games compiled with newer SDK, the main thread gets mapped from flexible memory, + // with addr = 0, so system managed area. Here we will only implement the latter. + void* addr_out{}; + if (is_primary) { + const size_t tls_aligned = Common::AlignUp(total_tls_size, 16_KB); + const int ret = Libraries::Kernel::sceKernelMapNamedFlexibleMemory(&addr_out, tls_aligned, + 3, 0, "SceKernelPrimaryTcbTls"); + ASSERT_MSG(ret == 0, "Unable to allocate TLS+TCB for the primary thread"); + } else { + if (heap_api_func) { + addr_out = heap_api_func(total_tls_size); + } else { + addr_out = std::malloc(total_tls_size); } } - return 0; -} -using exit_func_t = PS4_SYSV_ABI void (*)(); -using entry_func_t = PS4_SYSV_ABI void (*)(EntryParams* params, exit_func_t atexit_func); -using module_ini_func_t = PS4_SYSV_ABI int (*)(size_t args, const void* argp, ModuleFunc func); + // Initialize allocated memory and allocate DTV table. + const u32 num_dtvs = m_modules.size() - 1; + std::memset(addr_out, 0, total_tls_size); + dtv_table.resize(num_dtvs + 2); -static PS4_SYSV_ABI int RunModule(uint64_t addr, size_t args, const void* argp, - ModuleFunc func) { - return reinterpret_cast(addr)(args, argp, func); -} + // Initialize thread control block + u8* addr = reinterpret_cast(addr_out); + Tcb* tcb = reinterpret_cast(addr + static_tls_size); + tcb->tcb_self = tcb; + tcb->tcb_dtv = dtv_table.data(); -int Linker::StartModule(Module* m, size_t args, const void* argp, ModuleFunc func) { - LOG_INFO(Core_Linker, "Module started : {}", m->file_name); - return RunModule(m->dynamic_info.init_virtual_addr + m->base_virtual_addr, args, argp, func); -} + // Dtv[0] is the generation counter. libkernel puts their number into dtv[1] (why?) + dtv_table[0].counter = dtv_generation_counter; + dtv_table[1].counter = num_dtvs; -void Linker::StartAllModules() { - std::scoped_lock lock{m_mutex}; - - for (auto& m : m_modules) { - if (m->elf.IsSharedLib()) { - StartModule(m.get(), 0, nullptr, nullptr); - } - } -} - -static PS4_SYSV_ABI void ProgramExitFunc() { - fmt::print("exit function called\n"); -} - -static void RunMainEntry(u64 addr, EntryParams* params, exit_func_t exit_func) { - // reinterpret_cast(addr)(params, exit_func); // can't be used, stack has to have - // a specific layout - - asm volatile("andq $-16, %%rsp\n" // Align to 16 bytes - "subq $8, %%rsp\n" // videoout_basic expects the stack to be misaligned - - // Kernel also pushes some more things here during process init - // at least: environment, auxv, possibly other things - - "pushq 8(%1)\n" // copy EntryParams to top of stack like the kernel does - "pushq 0(%1)\n" // OpenOrbis expects to find it there - - "movq %1, %%rdi\n" // also pass params and exit func - "movq %2, %%rsi\n" // as before - - "jmp *%0\n" // can't use call here, as that would mangle the prepared stack. - // there's no coming back - : - : "r"(addr), "r"(params), "r"(exit_func) - : "rax", "rsi", "rdi"); -} - -void Linker::Execute() { - if (Config::debugDump()) { - DebugDump(); + // Copy init images to TLS thread blocks and map them to DTV slots. + for (u32 i = 2; const auto& module : m_modules) { + u8* dest = reinterpret_cast(addr + static_tls_size - module->tls.distance_from_fs); + const u8* src = reinterpret_cast(module->tls.image_virtual_addr); + std::memcpy(dest, src, module->tls.init_image_size); + tcb->tcb_dtv[i++].pointer = dest; } - Common::SetCurrentThreadName("GAME_MainThread"); - - Libraries::Kernel::pthreadInitSelfMainThread(); - // Relocate all modules - for (const auto& m : m_modules) { - Relocate(m.get()); - } - StartAllModules(); - EntryParams p{}; - p.argc = 1; - p.argv[0] = "eboot.bin"; // hmm should be ok? - - for (auto& m : m_modules) { - if (m->elf.IsSharedLib()) { - continue; - } - if (m->tls.image_virtual_addr != 0) { - SetTLSStorage(m->tls.image_virtual_addr, m->tls.image_size); - } - RunMainEntry(m->elf.GetElfEntry() + m->base_virtual_addr, &p, ProgramExitFunc); - } + // Set pointer to FS base + SetTcbBase(tcb); } void Linker::DebugDump() { - std::scoped_lock lock{m_mutex}; const auto& log_dir = Common::FS::GetUserPath(Common::FS::PathType::LogDir); const std::filesystem::path debug(log_dir / "debugdump"); std::filesystem::create_directory(debug); for (const auto& m : m_modules) { // TODO make a folder with game id for being more unique? - const std::filesystem::path filepath(debug / m.get()->file_name); + const std::filesystem::path filepath(debug / m.get()->file.stem()); std::filesystem::create_directory(filepath); m.get()->import_sym.DebugDump(filepath / "imports.txt"); m.get()->export_sym.DebugDump(filepath / "exports.txt"); diff --git a/src/core/linker.h b/src/core/linker.h index cd6065de..385ac118 100644 --- a/src/core/linker.h +++ b/src/core/linker.h @@ -3,10 +3,8 @@ #pragma once -#include #include -#include "core/loader/elf.h" -#include "core/loader/symbols_resolver.h" +#include "core/module.h" namespace Core { @@ -19,133 +17,60 @@ struct EntryParams { const char* argv[3]; }; -struct ModuleInfo { - bool operator==(const ModuleInfo& other) const { - return version_major == other.version_major && version_minor == other.version_minor && - name == other.name; - } - std::string name; - union { - u64 value; - struct { - u32 name_offset; - u8 version_minor; - u8 version_major; - u16 id; - }; +union DtvEntry { + struct { + size_t counter; }; - std::string enc_id; + void* pointer; }; -struct LibraryInfo { - bool operator==(const LibraryInfo& other) const { - return version == other.version && name == other.name; - } - std::string name; - union { - u64 value; - struct { - u32 name_offset; - u16 version; - u16 id; - }; - }; - std::string enc_id; +struct Tcb { + Tcb* tcb_self; + DtvEntry* tcb_dtv; + void* tcb_thread; }; -struct PS4ThreadLocal { - u64 image_virtual_addr = 0; - u64 image_size = 0; -}; - -struct DynamicModuleInfo { - void* hash_table = nullptr; - u64 hash_table_size = 0; - - char* str_table = nullptr; - u64 str_table_size = 0; - - elf_symbol* symbol_table = nullptr; - u64 symbol_table_total_size = 0; - u64 symbol_table_entries_size = 0; - - u64 init_virtual_addr = 0; - u64 fini_virtual_addr = 0; - u64 pltgot_virtual_addr = 0; - u64 init_array_virtual_addr = 0; - u64 fini_array_virtual_addr = 0; - u64 preinit_array_virtual_addr = 0; - u64 init_array_size = 0; - u64 fini_array_size = 0; - u64 preinit_array_size = 0; - - elf_relocation* jmp_relocation_table = nullptr; - u64 jmp_relocation_table_size = 0; - s64 jmp_relocation_type = 0; - - elf_relocation* relocation_table = nullptr; - u64 relocation_table_size = 0; - u64 relocation_table_entries_size = 0; - - u64 debug = 0; - u64 textrel = 0; - u64 flags = 0; - - std::vector needed; - std::vector import_modules; - std::vector export_modules; - std::vector import_libs; - std::vector export_libs; - - std::string filename; // Filename with absolute path -}; - -// This struct keeps neccesary info about loaded modules. Main executeable is included too as well -struct Module { - u64 aligned_base_size = 0; - VAddr base_virtual_addr = 0; - VAddr proc_param_virtual_addr = 0; - std::string file_name; - Loader::Elf elf; - std::vector m_dynamic; - std::vector m_dynamic_data; - DynamicModuleInfo dynamic_info{}; - Loader::SymbolsResolver export_sym; - Loader::SymbolsResolver import_sym; - PS4ThreadLocal tls; -}; - -using ModuleFunc = int (*)(size_t, const void*); +using HeapApiFunc = PS4_SYSV_ABI void*(*)(size_t); class Linker { public: explicit Linker(); ~Linker(); - Module* LoadModule(const std::filesystem::path& elf_name); - void LoadModuleToMemory(Module* m); - void LoadDynamicInfo(Module* m); - void LoadSymbols(Module* m); - Loader::SymbolsResolver& getHLESymbols() { + Loader::SymbolsResolver& GetHLESymbols() { return m_hle_symbols; } - void Relocate(Module* m); - void Resolve(const std::string& name, Loader::SymbolType Symtype, Module* m, - Loader::SymbolRecord* return_info); + + VAddr GetProcParam() const { + return main_proc_param_addr; + } + + void SetHeapApiFunc(void* func) { + heap_api_func = *reinterpret_cast(func); + } + + void* TlsGetAddr(u64 module_index, u64 offset); + void InitTlsForThread(bool is_primary = false); + + Module* LoadModule(const std::filesystem::path& elf_name); + + void Relocate(Module* module); + void Resolve(const std::string& name, Loader::SymbolType type, + Module* module, Loader::SymbolRecord* return_info); void Execute(); void DebugDump(); - u64 GetProcParam(); private: - const ModuleInfo* FindModule(const Module& m, const std::string& id); - const LibraryInfo* FindLibrary(const Module& program, const std::string& id); - Module* FindExportedModule(const ModuleInfo& m, const LibraryInfo& l); - int StartModule(Module* m, size_t args, const void* argp, ModuleFunc func); - void StartAllModules(); + const Module* FindExportedModule(const ModuleInfo& m, const LibraryInfo& l); + void InitTls(); + std::vector dtv_table; + u32 dtv_generation_counter{1}; + size_t static_tls_size{}; + HeapApiFunc heap_api_func{}; std::vector> m_modules; Loader::SymbolsResolver m_hle_symbols{}; - std::mutex m_mutex; + VAddr main_proc_param_addr{}; }; } // namespace Core diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 0b58880a..d31793f6 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -445,7 +445,7 @@ std::string Elf::ElfHeaderStr() { return header; } -std::string Elf::ElfPheaderTypeStr(u32 type) { +std::string_view Elf::ElfPheaderTypeStr(u32 type) { switch (type) { case PT_NULL: return "Null"; @@ -538,8 +538,4 @@ void Elf::LoadSegment(u64 virtual_addr, u64 file_offset, u64 size) { UNREACHABLE(); } -bool Elf::IsSharedLib() { - return m_elf_header.e_type == ET_SCE_DYNAMIC; -} - } // namespace Core::Loader diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h index b2d2ea59..9476a798 100644 --- a/src/core/loader/elf.h +++ b/src/core/loader/elf.h @@ -3,7 +3,6 @@ #pragma once -#include #include #include #include @@ -482,15 +481,18 @@ public: return m_elf_header.e_entry; } + [[nodiscard]] bool IsSharedLib() const { + return m_elf_header.e_type == ET_SCE_DYNAMIC; + } + std::string SElfHeaderStr(); std::string SELFSegHeader(u16 no); std::string ElfHeaderStr(); std::string ElfPHeaderStr(u16 no); - std::string ElfPheaderTypeStr(u32 type); + std::string_view ElfPheaderTypeStr(u32 type); std::string ElfPheaderFlagsStr(u32 flags); void LoadSegment(u64 virtual_addr, u64 file_offset, u64 size); - bool IsSharedLib(); private: Common::FS::IOFile m_f{}; diff --git a/src/core/loader/symbols_resolver.cpp b/src/core/loader/symbols_resolver.cpp index 86537208..129f23e4 100644 --- a/src/core/loader/symbols_resolver.cpp +++ b/src/core/loader/symbols_resolver.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include "common/io_file.h" #include "common/string_util.h" #include "common/types.h" @@ -10,9 +11,7 @@ namespace Core::Loader { void SymbolsResolver::AddSymbol(const SymbolResolver& s, u64 virtual_addr) { - SymbolRecord& r = m_symbols.emplace_back(); - r.name = GenerateName(s); - r.virtual_address = virtual_addr; + m_symbols.emplace_back(GenerateName(s), virtual_addr); } std::string SymbolsResolver::GenerateName(const SymbolResolver& s) { @@ -37,17 +36,12 @@ void SymbolsResolver::DebugDump(const std::filesystem::path& file_name) { Common::FS::FileType::TextFile}; for (const auto& symbol : m_symbols) { const auto ids = Common::SplitString(symbol.name, '#'); - std::string nidName = ""; - auto aeronid = AeroLib::FindByNid(ids.at(0).c_str()); - if (aeronid != nullptr) { - nidName = aeronid->name; - } else { - nidName = "UNK"; - } + const auto aeronid = AeroLib::FindByNid(ids.at(0).c_str()); + const auto nid_name = aeronid ? aeronid->name : "UNK"; f.WriteString( fmt::format("0x{:<20x} {:<16} {:<60} {:<30} {:<2} {:<30} {:<2} {:<2} {:<10}\n", - symbol.virtual_address, ids.at(0), nidName, ids.at(1), ids.at(2), ids.at(3), - ids.at(4), ids.at(5), ids.at(6))); + symbol.virtual_address, ids.at(0), nid_name, ids.at(1), ids.at(2), + ids.at(3), ids.at(4), ids.at(5), ids.at(6))); } } diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 18d8f1a3..c109dfbe 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -35,7 +35,7 @@ PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size, } // Align free position - free_addr = Common::alignUp(free_addr, alignment); + free_addr = Common::AlignUp(free_addr, alignment); ASSERT(free_addr >= search_start && free_addr + size <= search_end); // Add the allocated region to the list and commit its pages. @@ -56,7 +56,7 @@ void MemoryManager::Free(PAddr phys_addr, size_t size) { int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot, MemoryMapFlags flags, VMAType type, std::string_view name, PAddr phys_addr, u64 alignment) { - VAddr mapped_addr = alignment > 0 ? Common::alignUp(virtual_addr, alignment) : virtual_addr; + VAddr mapped_addr = alignment > 0 ? Common::AlignUp(virtual_addr, alignment) : virtual_addr; SCOPE_EXIT { auto& new_vma = AddMapping(mapped_addr, size); new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce); diff --git a/src/core/module.cpp b/src/core/module.cpp new file mode 100644 index 00000000..cb266a2f --- /dev/null +++ b/src/core/module.cpp @@ -0,0 +1,419 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "common/alignment.h" +#include "common/assert.h" +#include "common/logging/log.h" +#include "common/string_util.h" +#include "core/aerolib/aerolib.h" +#include "core/module.h" +#include "core/tls.h" +#include "core/virtual_memory.h" + +namespace Core { + +using EntryFunc = PS4_SYSV_ABI int (*)(size_t args, const void* argp, ModuleFunc func); + +static u64 LoadAddress = SYSTEM_RESERVED + CODE_BASE_OFFSET; +static constexpr u64 CODE_BASE_INCR = 0x010000000u; + +static u64 GetAlignedSize(const elf_program_header& phdr) { + return (phdr.p_align != 0 ? (phdr.p_memsz + (phdr.p_align - 1)) & ~(phdr.p_align - 1) + : phdr.p_memsz); +} + +static u64 CalculateBaseSize(const elf_header& ehdr, std::span phdr) { + u64 base_size = 0; + for (u16 i = 0; i < ehdr.e_phnum; i++) { + if (phdr[i].p_memsz != 0 && (phdr[i].p_type == PT_LOAD || phdr[i].p_type == PT_SCE_RELRO)) { + const u64 last_addr = phdr[i].p_vaddr + GetAlignedSize(phdr[i]); + base_size = std::max(last_addr, base_size); + } + } + return base_size; +} + +static std::string EncodeId(u64 nVal) { + std::string enc; + static constexpr std::string_view codes = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-"; + if (nVal < 0x40u) { + enc += codes[nVal]; + } else { + if (nVal < 0x1000u) { + enc += codes[static_cast(nVal >> 6u) & 0x3fu]; + enc += codes[nVal & 0x3fu]; + } else { + enc += codes[static_cast(nVal >> 12u) & 0x3fu]; + enc += codes[static_cast(nVal >> 6u) & 0x3fu]; + enc += codes[nVal & 0x3fu]; + } + } + return enc; +} + +Module::Module(const std::filesystem::path& file_) : file{file_} { + elf.Open(file); + if (elf.IsElfFile()) { + LoadModuleToMemory(); + LoadDynamicInfo(); + LoadSymbols(); + } +} + +Module::~Module() = default; + +void Module::Start(size_t args, const void* argp, ModuleFunc func) { + LOG_INFO(Core_Linker, "Module started : {}", file.filename().string()); + const VAddr addr = dynamic_info.init_virtual_addr + GetBaseAddress(); + reinterpret_cast(addr)(args, argp, func); +} + +void Module::LoadModuleToMemory() { + static constexpr size_t BlockAlign = 0x1000; + static constexpr u64 TrampolineSize = 8_MB; + + // Retrieve elf header and program header + const auto elf_header = elf.GetElfHeader(); + const auto elf_pheader = elf.GetProgramHeader(); + const u64 base_size = CalculateBaseSize(elf_header, elf_pheader); + aligned_base_size = Common::AlignUp(base_size, BlockAlign); + + // Map module segments (and possible TLS trampolines) + base_virtual_addr = VirtualMemory::memory_alloc(LoadAddress, aligned_base_size + TrampolineSize, + VirtualMemory::MemoryMode::ExecuteReadWrite); + LoadAddress += CODE_BASE_INCR * (1 + aligned_base_size / CODE_BASE_INCR); + + // Initialize trampoline generator. + void* trampoline_addr = std::bit_cast(base_virtual_addr + aligned_base_size); + Xbyak::CodeGenerator c(TrampolineSize, trampoline_addr); + + LOG_INFO(Core_Linker, "======== Load Module to Memory ========"); + LOG_INFO(Core_Linker, "base_virtual_addr ......: {:#018x}", base_virtual_addr); + LOG_INFO(Core_Linker, "base_size ..............: {:#018x}", base_size); + LOG_INFO(Core_Linker, "aligned_base_size ......: {:#018x}", aligned_base_size); + + for (u16 i = 0; i < elf_header.e_phnum; i++) { + const auto header_type = elf.ElfPheaderTypeStr(elf_pheader[i].p_type); + switch (elf_pheader[i].p_type) { + case PT_LOAD: + case PT_SCE_RELRO: { + if (elf_pheader[i].p_memsz == 0) { + LOG_ERROR(Core_Linker, "p_memsz==0 in type {}", header_type); + continue; + } + + const u64 segment_addr = elf_pheader[i].p_vaddr + base_virtual_addr; + const u64 segment_file_size = elf_pheader[i].p_filesz; + const u64 segment_memory_size = GetAlignedSize(elf_pheader[i]); + const auto segment_mode = elf.ElfPheaderFlagsStr(elf_pheader[i].p_flags); + LOG_INFO(Core_Linker, "program header = [{}] type = {}", i, header_type); + LOG_INFO(Core_Linker, "segment_addr ..........: {:#018x}", segment_addr); + LOG_INFO(Core_Linker, "segment_file_size .....: {}", segment_file_size); + LOG_INFO(Core_Linker, "segment_memory_size ...: {}", segment_memory_size); + LOG_INFO(Core_Linker, "segment_mode ..........: {}", segment_mode); + + elf.LoadSegment(segment_addr, elf_pheader[i].p_offset, segment_file_size); + if (elf_pheader[i].p_flags & PF_EXEC) { + PatchTLS(segment_addr, segment_memory_size, c); + } + break; + } + case PT_DYNAMIC: + if (elf_pheader[i].p_filesz != 0) { + m_dynamic.resize(elf_pheader[i].p_filesz); + const VAddr segment_addr = std::bit_cast(m_dynamic.data()); + elf.LoadSegment(segment_addr, elf_pheader[i].p_offset, elf_pheader[i].p_filesz); + } else { + LOG_ERROR(Core_Linker, "p_filesz==0 in type {}", header_type); + } + break; + case PT_SCE_DYNLIBDATA: + if (elf_pheader[i].p_filesz != 0) { + m_dynamic_data.resize(elf_pheader[i].p_filesz); + const VAddr segment_addr = std::bit_cast(m_dynamic_data.data()); + elf.LoadSegment(segment_addr, elf_pheader[i].p_offset, elf_pheader[i].p_filesz); + } else { + LOG_ERROR(Core_Linker, "p_filesz==0 in type {}", header_type); + } + break; + case PT_TLS: + tls.init_image_size = elf_pheader[i].p_filesz; + tls.align = elf_pheader[i].p_align; + tls.image_virtual_addr = elf_pheader[i].p_vaddr + base_virtual_addr; + tls.image_size = GetAlignedSize(elf_pheader[i]); + LOG_INFO(Core_Linker, "TLS virtual address = {:#x}", tls.image_virtual_addr); + LOG_INFO(Core_Linker, "TLS image size = {}", tls.image_size); + break; + case PT_SCE_PROCPARAM: + proc_param_virtual_addr = elf_pheader[i].p_vaddr + base_virtual_addr; + break; + default: + LOG_ERROR(Core_Linker, "Unimplemented type {}", header_type); + } + } + + const VAddr entry_addr = base_virtual_addr + elf.GetElfEntry(); + LOG_INFO(Core_Linker, "program entry addr ..........: {:#018x}", entry_addr); +} + +void Module::LoadDynamicInfo() { + for (const auto* dyn = reinterpret_cast(m_dynamic.data()); dyn->d_tag != DT_NULL; + dyn++) { + switch (dyn->d_tag) { + case DT_SCE_HASH: // Offset of the hash table. + dynamic_info.hash_table = + reinterpret_cast(m_dynamic_data.data() + dyn->d_un.d_ptr); + break; + case DT_SCE_HASHSZ: // Size of the hash table + dynamic_info.hash_table_size = dyn->d_un.d_val; + break; + case DT_SCE_STRTAB: // Offset of the string table. + dynamic_info.str_table = + reinterpret_cast(m_dynamic_data.data() + dyn->d_un.d_ptr); + break; + case DT_SCE_STRSZ: // Size of the string table. + dynamic_info.str_table_size = dyn->d_un.d_val; + break; + case DT_SCE_SYMTAB: // Offset of the symbol table. + dynamic_info.symbol_table = + reinterpret_cast(m_dynamic_data.data() + dyn->d_un.d_ptr); + break; + case DT_SCE_SYMTABSZ: // Size of the symbol table. + dynamic_info.symbol_table_total_size = dyn->d_un.d_val; + break; + case DT_INIT: + dynamic_info.init_virtual_addr = dyn->d_un.d_ptr; + break; + case DT_FINI: + dynamic_info.fini_virtual_addr = dyn->d_un.d_ptr; + break; + case DT_SCE_PLTGOT: // Offset of the global offset table. + dynamic_info.pltgot_virtual_addr = dyn->d_un.d_ptr; + break; + case DT_SCE_JMPREL: // Offset of the table containing jump slots. + dynamic_info.jmp_relocation_table = + reinterpret_cast(m_dynamic_data.data() + dyn->d_un.d_ptr); + break; + case DT_SCE_PLTRELSZ: // Size of the global offset table. + dynamic_info.jmp_relocation_table_size = dyn->d_un.d_val; + break; + case DT_SCE_PLTREL: // The type of relocations in the relocation table. Should be DT_RELA + dynamic_info.jmp_relocation_type = dyn->d_un.d_val; + if (dynamic_info.jmp_relocation_type != DT_RELA) { + LOG_WARNING(Core_Linker, "DT_SCE_PLTREL is NOT DT_RELA should check!"); + } + break; + case DT_SCE_RELA: // Offset of the relocation table. + dynamic_info.relocation_table = + reinterpret_cast(m_dynamic_data.data() + dyn->d_un.d_ptr); + break; + case DT_SCE_RELASZ: // Size of the relocation table. + dynamic_info.relocation_table_size = dyn->d_un.d_val; + break; + case DT_SCE_RELAENT: // The size of relocation table entries. + dynamic_info.relocation_table_entries_size = dyn->d_un.d_val; + if (dynamic_info.relocation_table_entries_size != 0x18) { + LOG_WARNING(Core_Linker, "DT_SCE_RELAENT is NOT 0x18 should check!"); + } + break; + case DT_INIT_ARRAY: // Address of the array of pointers to initialization functions + dynamic_info.init_array_virtual_addr = dyn->d_un.d_ptr; + break; + case DT_FINI_ARRAY: // Address of the array of pointers to termination functions + dynamic_info.fini_array_virtual_addr = dyn->d_un.d_ptr; + break; + case DT_INIT_ARRAYSZ: // Size in bytes of the array of initialization functions + dynamic_info.init_array_size = dyn->d_un.d_val; + break; + case DT_FINI_ARRAYSZ: // Size in bytes of the array of terminationfunctions + dynamic_info.fini_array_size = dyn->d_un.d_val; + break; + case DT_PREINIT_ARRAY: // Address of the array of pointers to pre - initialization functions + dynamic_info.preinit_array_virtual_addr = dyn->d_un.d_ptr; + break; + case DT_PREINIT_ARRAYSZ: // Size in bytes of the array of pre - initialization functions + dynamic_info.preinit_array_size = dyn->d_un.d_val; + break; + case DT_SCE_SYMENT: // The size of symbol table entries + dynamic_info.symbol_table_entries_size = dyn->d_un.d_val; + if (dynamic_info.symbol_table_entries_size != 0x18) { + LOG_WARNING(Core_Linker, "DT_SCE_SYMENT is NOT 0x18 should check!"); + } + break; + case DT_DEBUG: + dynamic_info.debug = dyn->d_un.d_val; + break; + case DT_TEXTREL: + dynamic_info.textrel = dyn->d_un.d_val; + break; + case DT_FLAGS: + dynamic_info.flags = dyn->d_un.d_val; + // This value should always be DF_TEXTREL (0x04) + if (dynamic_info.flags != 0x04) { + LOG_WARNING(Core_Linker, "DT_FLAGS is NOT 0x04 should check!"); + } + break; + case DT_NEEDED: + // Offset of the library string in the string table to be linked in. + // In theory this should already be filled from about just make a test case + if (dynamic_info.str_table) { + dynamic_info.needed.push_back(dynamic_info.str_table + dyn->d_un.d_val); + } else { + LOG_ERROR(Core_Linker, "DT_NEEDED str table is not loaded should check!"); + } + break; + case DT_SCE_NEEDED_MODULE: { + ModuleInfo& info = dynamic_info.import_modules.emplace_back(); + info.value = dyn->d_un.d_val; + info.name = dynamic_info.str_table + info.name_offset; + info.enc_id = EncodeId(info.id); + break; + } + case DT_SCE_IMPORT_LIB: { + LibraryInfo& info = dynamic_info.import_libs.emplace_back(); + info.value = dyn->d_un.d_val; + info.name = dynamic_info.str_table + info.name_offset; + info.enc_id = EncodeId(info.id); + break; + } + case DT_SCE_FINGERPRINT: + // The fingerprint is a 24 byte (0x18) size buffer that contains a unique identifier for + // the given app. How exactly this is generated isn't known, however it is not necessary + // to have a valid fingerprint. While an invalid fingerprint will cause a warning to be + // printed to the kernel log, the ELF will still load and run. + LOG_INFO(Core_Linker, "unsupported DT_SCE_FINGERPRINT value = ..........: {:#018x}", + dyn->d_un.d_val); + break; + case DT_SCE_IMPORT_LIB_ATTR: + // The upper 32-bits should contain the module index multiplied by 0x10000. The lower + // 32-bits should be a constant 0x9. + LOG_INFO(Core_Linker, "unsupported DT_SCE_IMPORT_LIB_ATTR value = ......: {:#018x}", + dyn->d_un.d_val); + break; + case DT_SCE_ORIGINAL_FILENAME: + dynamic_info.filename = dynamic_info.str_table + dyn->d_un.d_val; + break; + case DT_SCE_MODULE_INFO: { + ModuleInfo& info = dynamic_info.export_modules.emplace_back(); + info.value = dyn->d_un.d_val; + info.name = dynamic_info.str_table + info.name_offset; + info.enc_id = EncodeId(info.id); + break; + }; + case DT_SCE_MODULE_ATTR: + LOG_INFO(Core_Linker, "unsupported DT_SCE_MODULE_ATTR value = ..........: {:#018x}", + dyn->d_un.d_val); + break; + case DT_SCE_EXPORT_LIB: { + LibraryInfo& info = dynamic_info.export_libs.emplace_back(); + info.value = dyn->d_un.d_val; + info.name = dynamic_info.str_table + info.name_offset; + info.enc_id = EncodeId(info.id); + break; + } + default: + LOG_INFO(Core_Linker, "unsupported dynamic tag ..........: {:#018x}", dyn->d_tag); + } + } +} + +void Module::LoadSymbols() { + const auto symbol_database = [this](Loader::SymbolsResolver& symbol, bool export_func) { + if (!dynamic_info.symbol_table || !dynamic_info.str_table || + dynamic_info.symbol_table_total_size == 0) { + LOG_INFO(Core_Linker, "Symbol table not found!"); + return; + } + for (auto* sym = dynamic_info.symbol_table; + reinterpret_cast(sym) < reinterpret_cast(dynamic_info.symbol_table) + + dynamic_info.symbol_table_total_size; + sym++) { + const u8 bind = sym->GetBind(); + const u8 type = sym->GetType(); + const u8 visibility = sym->GetVisibility(); + const auto id = std::string(dynamic_info.str_table + sym->st_name); + const auto ids = Common::SplitString(id, '#'); + if (ids.size() != 3) { + continue; + } + + const auto* library = FindLibrary(ids[1]); + const auto* module = FindModule(ids[2]); + ASSERT_MSG(library && module, "Unable to find library and module"); + if ((bind != STB_GLOBAL && bind != STB_WEAK) || + (type != STT_FUN && type != STT_OBJECT) || export_func != (sym->st_value != 0)) { + continue; + } + + const auto aeronid = AeroLib::FindByNid(ids.at(0).c_str()); + const auto nid_name = aeronid ? aeronid->name : "UNK"; + + Loader::SymbolResolver sym_r{}; + sym_r.name = ids.at(0); + sym_r.nidName = nid_name; + sym_r.library = library->name; + sym_r.library_version = library->version; + sym_r.module = module->name; + sym_r.module_version_major = module->version_major; + sym_r.module_version_minor = module->version_minor; + switch (type) { + case STT_NOTYPE: + sym_r.type = Loader::SymbolType::NoType; + break; + case STT_FUN: + sym_r.type = Loader::SymbolType::Function; + break; + case STT_OBJECT: + sym_r.type = Loader::SymbolType::Object; + break; + default: + sym_r.type = Loader::SymbolType::Unknown; + break; + } + const VAddr sym_addr = export_func ? sym->st_value + base_virtual_addr : 0; + symbol.AddSymbol(sym_r, sym_addr); + } + }; + symbol_database(export_sym, true); + symbol_database(import_sym, false); +} + +const ModuleInfo* Module::FindModule(std::string_view id) { + const auto& import_modules = dynamic_info.import_modules; + for (u32 i = 0; const auto& mod : import_modules) { + if (mod.enc_id == id) { + return &import_modules[i]; + } + i++; + } + const auto& export_modules = dynamic_info.export_modules; + for (u32 i = 0; const auto& mod : export_modules) { + if (mod.enc_id == id) { + return &export_modules[i]; + } + i++; + } + return nullptr; +} + +const LibraryInfo* Module::FindLibrary(std::string_view id) { + const auto& import_libs = dynamic_info.import_libs; + for (u32 i = 0; const auto& lib : import_libs) { + if (lib.enc_id == id) { + return &import_libs[i]; + } + i++; + } + const auto& export_libs = dynamic_info.export_libs; + for (u32 i = 0; const auto& lib : export_libs) { + if (lib.enc_id == id) { + return &export_libs[i]; + } + i++; + } + return nullptr; +} + +} // namespace Core diff --git a/src/core/module.h b/src/core/module.h new file mode 100644 index 00000000..ccc3f6b3 --- /dev/null +++ b/src/core/module.h @@ -0,0 +1,172 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include "common/types.h" +#include "core/loader/elf.h" +#include "core/loader/symbols_resolver.h" + +namespace Core { + +struct ModuleInfo { + bool operator==(const ModuleInfo& other) const { + return version_major == other.version_major && version_minor == other.version_minor && + name == other.name; + } + std::string name; + union { + u64 value; + struct { + u32 name_offset; + u8 version_minor; + u8 version_major; + u16 id; + }; + }; + std::string enc_id; +}; + +struct LibraryInfo { + bool operator==(const LibraryInfo& other) const { + return version == other.version && name == other.name; + } + std::string name; + union { + u64 value; + struct { + u32 name_offset; + u16 version; + u16 id; + }; + }; + std::string enc_id; +}; + +struct ThreadLocalImage { + u64 align; + VAddr image_virtual_addr; + u64 init_image_size; + u64 image_size; + u64 distance_from_fs; +}; + +struct DynamicModuleInfo { + void* hash_table = nullptr; + u64 hash_table_size = 0; + + char* str_table = nullptr; + u64 str_table_size = 0; + + elf_symbol* symbol_table = nullptr; + u64 symbol_table_total_size = 0; + u64 symbol_table_entries_size = 0; + + u64 init_virtual_addr = 0; + u64 fini_virtual_addr = 0; + u64 pltgot_virtual_addr = 0; + u64 init_array_virtual_addr = 0; + u64 fini_array_virtual_addr = 0; + u64 preinit_array_virtual_addr = 0; + u64 init_array_size = 0; + u64 fini_array_size = 0; + u64 preinit_array_size = 0; + + elf_relocation* jmp_relocation_table = nullptr; + u64 jmp_relocation_table_size = 0; + s64 jmp_relocation_type = 0; + + elf_relocation* relocation_table = nullptr; + u64 relocation_table_size = 0; + u64 relocation_table_entries_size = 0; + + u64 debug = 0; + u64 textrel = 0; + u64 flags = 0; + + std::vector needed; + std::vector import_modules; + std::vector export_modules; + std::vector import_libs; + std::vector export_libs; + + std::string filename; +}; + +using ModuleFunc = int (*)(size_t, const void*); + +class Module { +public: + explicit Module(const std::filesystem::path& file); + ~Module(); + + VAddr GetBaseAddress() const noexcept { + return base_virtual_addr; + } + + VAddr GetEntryAddress() const noexcept { + return base_virtual_addr + elf.GetElfEntry(); + } + + bool IsValid() const noexcept { + return base_virtual_addr != 0; + } + + bool IsSharedLib() const noexcept { + return elf.IsSharedLib(); + } + + VAddr GetProcParam() const noexcept { + return proc_param_virtual_addr; + } + + std::span GetImportModules() const { + return dynamic_info.import_modules; + } + + std::span GetExportModules() const { + return dynamic_info.export_modules; + } + + std::span GetImportLibs() const { + return dynamic_info.import_libs; + } + + std::span GetExportLibs() const { + return dynamic_info.export_libs; + } + + void ForEachRelocation(auto&& func) { + for (u32 i = 0; i < dynamic_info.relocation_table_size / sizeof(elf_relocation); i++) { + func(&dynamic_info.relocation_table[i], false); + } + for (u32 i = 0; i < dynamic_info.jmp_relocation_table_size / sizeof(elf_relocation); i++) { + func(&dynamic_info.jmp_relocation_table[i], true); + } + } + + void Start(size_t args, const void* argp, ModuleFunc func); + void LoadModuleToMemory(); + void LoadDynamicInfo(); + void LoadSymbols(); + + const ModuleInfo* FindModule(std::string_view id); + const LibraryInfo* FindLibrary(std::string_view id); + +public: + std::filesystem::path file; + Loader::Elf elf; + u64 aligned_base_size{}; + VAddr base_virtual_addr{}; + VAddr proc_param_virtual_addr{}; + DynamicModuleInfo dynamic_info{}; + std::vector m_dynamic; + std::vector m_dynamic_data; + Loader::SymbolsResolver export_sym; + Loader::SymbolsResolver import_sym; + ThreadLocalImage tls; +}; + +} // namespace Core diff --git a/src/core/tls.cpp b/src/core/tls.cpp index a524970f..3cebed4a 100644 --- a/src/core/tls.cpp +++ b/src/core/tls.cpp @@ -43,20 +43,9 @@ constexpr static TLSPattern TlsPatterns[] = { #ifdef _WIN32 static DWORD slot = 0; -u8* tls_memory{}; -u64 tls_image{}; -u32 tls_image_size{}; -void SetTLSStorage(u64 image_address, u32 image_size) { - // Guest apps will use both positive and negative offsets to the TLS pointer. - // User data at probably in negative offsets, while pthread data at positive offset. - if (tls_image == 0) { - tls_image = image_address; - tls_image_size = image_size; - } - tls_memory = (u8*)std::malloc(tls_image_size + 8192); - std::memcpy(tls_memory, reinterpret_cast(tls_image), tls_image_size); - const BOOL result = TlsSetValue(slot, tls_memory + tls_image_size); +void SetTcbBase(void* image_address) { + const BOOL result = TlsSetValue(slot, image_address); ASSERT(result != 0); } diff --git a/src/core/tls.h b/src/core/tls.h index 5b2487fc..311d85f0 100644 --- a/src/core/tls.h +++ b/src/core/tls.h @@ -11,15 +11,14 @@ class CodeGenerator; namespace Core { -/// Sets the data pointer that contains the TLS image. -void SetTLSStorage(u64 image_address, u32 image_size); +/// Sets the data pointer to the TCB block. +void SetTcbBase(void* image_address); /// Patches any instructions that access guest TLS to use provided storage. void PatchTLS(u64 segment_addr, u64 segment_size, Xbyak::CodeGenerator& c); class ThreadLocalStorage { public: - }; } // namespace Core diff --git a/src/main.cpp b/src/main.cpp index 43196046..6f43d941 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -76,10 +76,10 @@ int main(int argc, char* argv[]) { } auto linker = Common::Singleton::Instance(); - Libraries::InitHLELibs(&linker->getHLESymbols()); + Libraries::InitHLELibs(&linker->GetHLESymbols()); linker->LoadModule(path); - // check if we have system modules to load + // Check if we have system modules to load const auto& sys_module_path = Common::FS::GetUserPath(Common::FS::PathType::SysModuleDir); for (const auto& entry : std::filesystem::directory_iterator(sys_module_path)) { if (entry.path().filename() == "libSceNgs2.sprx") { @@ -103,7 +103,7 @@ int main(int argc, char* argv[]) { } } if (!found) { - Libraries::LibC::libcSymbolsRegister(&linker->getHLESymbols()); + Libraries::LibC::libcSymbolsRegister(&linker->GetHLESymbols()); } std::thread mainthread([linker]() { linker->Execute(); }); Discord::RPC discordRPC; diff --git a/src/shader_recompiler/frontend/translate/scalar_alu.cpp b/src/shader_recompiler/frontend/translate/scalar_alu.cpp index ac915734..0fb945e3 100644 --- a/src/shader_recompiler/frontend/translate/scalar_alu.cpp +++ b/src/shader_recompiler/frontend/translate/scalar_alu.cpp @@ -63,18 +63,28 @@ void Translator::S_AND_SAVEEXEC_B64(const GcnInst& inst) { // This instruction normally operates on 64-bit data (EXEC, VCC, SGPRs) // However here we flatten it to 1-bit EXEC and 1-bit VCC. For the destination // SGPR we have a special IR opcode for SPGRs that act as thread masks. + ASSERT(inst.src[0].field == OperandField::VccLo); const IR::U1 exec{ir.GetExec()}; + const IR::U1 vcc{ir.GetVcc()}; // Mark destination SPGR as an EXEC context. This means we will use 1-bit // IR instruction whenever it's loaded. - ASSERT(inst.dst[0].field == OperandField::ScalarGPR); - const u32 reg = inst.dst[0].code; - exec_contexts[reg] = true; - ir.SetThreadBitScalarReg(IR::ScalarReg(reg), exec); + switch (inst.dst[0].field) { + case OperandField::ScalarGPR: { + const u32 reg = inst.dst[0].code; + exec_contexts[reg] = true; + ir.SetThreadBitScalarReg(IR::ScalarReg(reg), exec); + break; + } + case OperandField::VccLo: + ir.SetVcc(exec); + break; + default: + UNREACHABLE(); + } // Update EXEC. - ASSERT(inst.src[0].field == OperandField::VccLo); - ir.SetExec(ir.LogicalAnd(exec, ir.GetVcc())); + ir.SetExec(ir.LogicalAnd(exec, vcc)); } void Translator::S_MOV_B64(const GcnInst& inst) { diff --git a/src/shader_recompiler/frontend/translate/translate.cpp b/src/shader_recompiler/frontend/translate/translate.cpp index d5ea8c48..77b4d742 100644 --- a/src/shader_recompiler/frontend/translate/translate.cpp +++ b/src/shader_recompiler/frontend/translate/translate.cpp @@ -128,7 +128,11 @@ IR::U1U32F32 Translator::GetSrc(const InstOperand& operand, bool force_flt) { value = ir.GetExec(); break; case OperandField::VccLo: - value = ir.GetVccLo(); + if (force_flt) { + value = ir.BitCast(ir.GetVccLo()); + } else { + value = ir.GetVccLo(); + } break; case OperandField::VccHi: value = ir.GetVccHi(); diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index 085d8694..56f4c82f 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -33,7 +33,7 @@ void Translator::V_CNDMASK_B32(const GcnInst& inst) { const IR::VectorReg dst_reg{inst.dst[0].code}; const IR::ScalarReg flag_reg{inst.src[2].code}; const IR::U1 flag = inst.src[2].field == OperandField::ScalarGPR - ? ir.INotEqual(ir.GetScalarReg(flag_reg), ir.Imm32(0U)) + ? ir.GetThreadBitScalarReg(flag_reg) : ir.GetVcc(); // We can treat the instruction as integer most of the time, but when a source is diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index bd41d587..b5c067cf 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -774,6 +774,7 @@ U1 IREmitter::FPLessThanEqual(const F32F64& lhs, const F32F64& rhs, bool ordered } U1 IREmitter::FPGreaterThanEqual(const F32F64& lhs, const F32F64& rhs, bool ordered) { + ASSERT(lhs.Type() == rhs.Type()); if (lhs.Type() != rhs.Type()) { throw InvalidArgument("Mismatching types {} and {}", lhs.Type(), rhs.Type()); } diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 8c78a857..b83caa4e 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -63,8 +63,8 @@ GraphicsPipeline::GraphicsPipeline(const Instance& instance_, Scheduler& schedul .pVertexAttributeDescriptions = attributes.data(), }; - ASSERT_MSG(key.prim_type != Liverpool::PrimitiveType::RectList || IsEmbeddedVs(), - "Rectangle List primitive type is only supported for embedded VS"); + // ASSERT_MSG(key.prim_type != Liverpool::PrimitiveType::RectList || IsEmbeddedVs(), + // "Rectangle List primitive type is only supported for embedded VS"); const vk::PipelineInputAssemblyStateCreateInfo input_assembly = { .topology = LiverpoolToVK::PrimitiveType(key.prim_type), diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index e3b38ca4..3b953138 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -41,6 +41,7 @@ void Rasterizer::Draw(bool is_indexed) { boost::container::static_vector color_attachments{}; + const std::array color = {0.5f, 0.5f, 0.5f, 1.f}; for (auto col_buf_id = 0u; col_buf_id < Liverpool::NumColorBuffers; ++col_buf_id) { const auto& col_buf = regs.color_buffers[col_buf_id]; if (!col_buf) { @@ -53,10 +54,12 @@ void Rasterizer::Draw(bool is_indexed) { color_attachments.push_back({ .imageView = *image_view.image_view, .imageLayout = vk::ImageLayout::eGeneral, - .loadOp = vk::AttachmentLoadOp::eLoad, + .loadOp = compute_done ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, .storeOp = vk::AttachmentStoreOp::eStore, + .clearValue = vk::ClearValue{color}, }); } + compute_done = false; // TODO: Don't restart renderpass every draw const auto& scissor = regs.screen_scissor; diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp index 0d9bedfe..cad4daea 100644 --- a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp +++ b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp @@ -92,14 +92,14 @@ StreamBuffer::~StreamBuffer() { std::tuple StreamBuffer::Map(u64 size, u64 alignment) { if (!is_coherent && type == BufferType::Stream) { - size = Common::alignUp(size, instance.NonCoherentAtomSize()); + size = Common::AlignUp(size, instance.NonCoherentAtomSize()); } ASSERT(size <= stream_buffer_size); mapped_size = size; if (alignment > 0) { - offset = Common::alignUp(offset, alignment); + offset = Common::AlignUp(offset, alignment); } bool invalidate{false}; @@ -124,7 +124,7 @@ std::tuple StreamBuffer::Map(u64 size, u64 alignment) { void StreamBuffer::Commit(u64 size) { if (!is_coherent && type == BufferType::Stream) { - size = Common::alignUp(size, instance.NonCoherentAtomSize()); + size = Common::AlignUp(size, instance.NonCoherentAtomSize()); } ASSERT_MSG(size <= mapped_size, "Reserved size {} is too small compared to {}", mapped_size, diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index 3e2a7dea..85b44018 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -338,7 +338,7 @@ void TextureCache::UpdatePagesCachedCount(VAddr addr, u64 size, s32 delta) { const u32 interval_size = interval_end_addr - interval_start_addr; void* addr = reinterpret_cast(interval_start_addr); if (delta > 0 && count == delta) { - mprotect(addr, interval_size, PAGE_READONLY); + // mprotect(addr, interval_size, PAGE_READONLY); } else if (delta < 0 && count == -delta) { mprotect(addr, interval_size, PAGE_READWRITE); } else {