core: Properly implement TLS (#164)
* core: Split module code from linker * linker: Properly implement thread local storage * kernel: Fix a few memory functions * kernel: Implement module loading * Now it's easy to do anyway with new module rework
This commit is contained in:
parent
7d61b7ab9b
commit
728249f58d
|
@ -287,8 +287,9 @@ 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
|
||||
src/core/tls.h
|
||||
src/core/virtual_memory.cpp
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
namespace Common {
|
||||
|
||||
template <typename T>
|
||||
[[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>, "T must be an unsigned value.");
|
||||
auto mod{static_cast<T>(value % size)};
|
||||
value -= mod;
|
||||
|
@ -17,14 +17,14 @@ template <typename T>
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
[[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>, "T must be an unsigned value.");
|
||||
return static_cast<T>(value - value % size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T>
|
||||
[[nodiscard]] constexpr bool is16KBAligned(T value) {
|
||||
[[nodiscard]] constexpr bool Is16KBAligned(T value) {
|
||||
return (value & 0x3FFF) == 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -102,14 +102,14 @@ struct AddressSpace::Impl {
|
|||
|
||||
// Perform the map.
|
||||
void* ptr = nullptr;
|
||||
if (phys_addr) {
|
||||
if (phys_addr != -1) {
|
||||
ptr = MapViewOfFile3(backing_handle, process, reinterpret_cast<PVOID>(virtual_addr),
|
||||
phys_addr, size, MEM_REPLACE_PLACEHOLDER, prot, nullptr, 0);
|
||||
} else {
|
||||
ptr = VirtualAlloc2(process, reinterpret_cast<PVOID>(virtual_addr), size,
|
||||
MEM_REPLACE_PLACEHOLDER, prot, nullptr, 0);
|
||||
}
|
||||
ASSERT(ptr);
|
||||
ASSERT_MSG(ptr, "{}", Common::GetLastErrorMsg());
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
@ -121,7 +121,7 @@ struct AddressSpace::Impl {
|
|||
// (virtual_addr == 0 ? reinterpret_cast<PVOID>(SYSTEM_MANAGED_MIN)
|
||||
// : reinterpret_cast<PVOID>(virtual_addr));
|
||||
req.HighestEndingAddress = reinterpret_cast<PVOID>(SYSTEM_MANAGED_MAX);
|
||||
req.Alignment = alignment;
|
||||
req.Alignment = alignment < 64_KB ? 0 : alignment;
|
||||
param.Type = MemExtendedParameterAddressRequirements;
|
||||
param.Pointer = &req;
|
||||
ULONG alloc_type = MEM_COMMIT | MEM_RESERVE | (alignment > 2_MB ? MEM_LARGE_PAGES : 0);
|
||||
|
|
|
@ -42,7 +42,7 @@ public:
|
|||
* If zero is provided the mapping is considered as private.
|
||||
* @return A pointer to the mapped memory.
|
||||
*/
|
||||
void* Map(VAddr virtual_addr, size_t size, u64 alignment = 0, PAddr phys_addr = 0);
|
||||
void* Map(VAddr virtual_addr, size_t size, u64 alignment = 0, PAddr phys_addr = -1);
|
||||
|
||||
/// Unmaps specified virtual memory area.
|
||||
void Unmap(VAddr virtual_addr, size_t size);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/cpu_management.h"
|
||||
#include "core/libraries/kernel/event_flag/event_flag.h"
|
||||
|
@ -199,11 +200,48 @@ s64 PS4_SYSV_ABI ps4__read(int d, void* buf, u64 nbytes) {
|
|||
strlen(std::fgets(static_cast<char*>(buf), static_cast<int>(nbytes), stdin)));
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, size_t args, const void* argp,
|
||||
u32 flags, const void* pOpt, int* pRes) {
|
||||
LOG_INFO(Lib_Kernel, "called filename = {}, args = {}", moduleFileName, args);
|
||||
|
||||
if (flags != 0) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||
const auto path = mnt->GetHostFile(moduleFileName);
|
||||
|
||||
// Load PRX module.
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
u32 handle = linker->LoadModule(path);
|
||||
auto* module = linker->GetModule(handle);
|
||||
linker->Relocate(module);
|
||||
|
||||
// Retrieve and verify proc param according to libkernel.
|
||||
u64* param = module->GetProcParam<u64*>();
|
||||
ASSERT_MSG(!param || param[0] >= 0x18, "Invalid module param size: {}", param[0]);
|
||||
module->Start(args, argp, param);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelDlsym(s32 handle, const char* symbol, void** addrp) {
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
auto* module = linker->GetModule(handle);
|
||||
*addrp = module->FindByName(symbol);
|
||||
if (*addrp == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_ESRCH;
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void LibKernel_Register(Core::Loader::SymbolsResolver* sym) {
|
||||
// obj
|
||||
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("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);
|
||||
|
@ -212,6 +250,11 @@ 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);
|
||||
LIB_FUNCTION("wzvqT4UqKX8", "libkernel", 1, "libkernel", 1, 1, sceKernelLoadStartModule);
|
||||
LIB_FUNCTION("LwG8g3niqwA", "libkernel", 1, "libkernel", 1, 1, sceKernelDlsym);
|
||||
|
||||
// equeue
|
||||
LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateEqueue);
|
||||
LIB_FUNCTION("jpFjmgAC5AE", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteEqueue);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "common/singleton.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/memory_management.h"
|
||||
#include "core/linker.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
@ -22,12 +23,12 @@ int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u
|
|||
LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!");
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
const bool is_in_range = (searchStart < len && searchEnd > len);
|
||||
if (len <= 0 || !Common::is16KBAligned(len) || !is_in_range) {
|
||||
const bool is_in_range = searchEnd - searchStart >= len;
|
||||
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)) {
|
||||
LOG_ERROR(Kernel_Vmm, "Alignment value is invalid!");
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
@ -48,6 +49,12 @@ 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);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int flags,
|
||||
s64 directMemoryStart, u64 alignment) {
|
||||
LOG_INFO(
|
||||
|
@ -55,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;
|
||||
}
|
||||
|
@ -81,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;
|
||||
}
|
||||
|
@ -127,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<Core::Linker>::Instance();
|
||||
linker->SetHeapApiFunc(func);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
||||
|
|
|
@ -41,6 +41,8 @@ int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u
|
|||
u64 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 sceKernelAllocateMainDirectMemory(size_t len, size_t alignment, int memoryType,
|
||||
s64* physAddrOut);
|
||||
s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addrInOut, std::size_t len, int prot,
|
||||
int flags, const char* name);
|
||||
s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, std::size_t len, int prot,
|
||||
|
@ -49,5 +51,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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
#include <thread>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/singleton.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/thread_management.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/linker.h"
|
||||
#ifdef _WIN64
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
@ -829,6 +831,8 @@ static void cleanup_thread(void* arg) {
|
|||
static void* run_thread(void* arg) {
|
||||
auto* thread = static_cast<ScePthread>(arg);
|
||||
Common::SetCurrentThreadName(thread->name.c_str());
|
||||
auto* linker = Common::Singleton<Core::Linker>::Instance();
|
||||
linker->InitTlsForThread(false);
|
||||
void* ret = nullptr;
|
||||
g_pthread_self = thread;
|
||||
pthread_cleanup_push(cleanup_thread, thread);
|
||||
|
@ -1022,6 +1026,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<Core::Linker>::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);
|
||||
|
@ -1038,6 +1052,7 @@ void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
|||
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);
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <Zydis/Zydis.h>
|
||||
#include <common/assert.h>
|
||||
#include <xbyak/xbyak.h>
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/path_util.h"
|
||||
|
@ -11,6 +10,7 @@
|
|||
#include "common/thread.h"
|
||||
#include "core/aerolib/aerolib.h"
|
||||
#include "core/aerolib/stubs.h"
|
||||
#include "core/libraries/kernel/memory_management.h"
|
||||
#include "core/libraries/kernel/thread_management.h"
|
||||
#include "core/linker.h"
|
||||
#include "core/tls.h"
|
||||
|
@ -18,641 +18,15 @@
|
|||
|
||||
namespace Core {
|
||||
|
||||
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<const elf_program_header> 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 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<u16>(nVal >> 6u) & 0x3fu];
|
||||
enc += pCodes[nVal & 0x3fu];
|
||||
} else {
|
||||
enc += pCodes[static_cast<u16>(nVal >> 12u) & 0x3fu];
|
||||
enc += pCodes[static_cast<u16>(nVal >> 6u) & 0x3fu];
|
||||
enc += pCodes[nVal & 0x3fu];
|
||||
}
|
||||
}
|
||||
return enc;
|
||||
}
|
||||
|
||||
Linker::Linker() = default;
|
||||
|
||||
Linker::~Linker() = default;
|
||||
|
||||
Module* Linker::LoadModule(const std::filesystem::path& elf_name) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
if (!std::filesystem::exists(elf_name)) {
|
||||
LOG_ERROR(Core_Linker, "Provided module {} does not exist", elf_name.string());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto& m = m_modules.emplace_back();
|
||||
m = std::make_unique<Module>();
|
||||
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!
|
||||
}
|
||||
|
||||
return m.get();
|
||||
}
|
||||
|
||||
void Linker::LoadModuleToMemory(Module* m) {
|
||||
// get elf header, 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<u64>(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<void*>(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<u64>(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<u64>(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<elf_dynamic*>(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<void*>(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<char*>(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<elf_symbol*>(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<elf_relocation*>(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<elf_relocation*>(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](Module* 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<u8*>(sym) < reinterpret_cast<u8*>(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, &m->export_sym, true);
|
||||
symbol_database(m, &m->import_sym, false);
|
||||
}
|
||||
|
||||
void Linker::Relocate(Module* m) {
|
||||
const auto relocate = [this](u32 idx, elf_relocation* rel, Module* m, 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;
|
||||
|
||||
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;
|
||||
break;
|
||||
case R_X86_64_DTPMOD64:
|
||||
rel_value = reinterpret_cast<uint64_t>(m);
|
||||
rel_isResolved = 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_bind = sym.GetBind();
|
||||
auto sym_type = sym.GetType();
|
||||
auto sym_visibility = sym.GetVisibility();
|
||||
u64 symbol_vitrual_addr = 0;
|
||||
Loader::SymbolRecord symrec{};
|
||||
switch (sym_type) {
|
||||
case STT_FUN:
|
||||
rel_sym_type = Loader::SymbolType::Function;
|
||||
break;
|
||||
case STT_OBJECT:
|
||||
rel_sym_type = Loader::SymbolType::Object;
|
||||
break;
|
||||
case STT_NOTYPE:
|
||||
rel_sym_type = Loader::SymbolType::NoType;
|
||||
break;
|
||||
default:
|
||||
ASSERT_MSG(0, "unknown symbol type {}", sym_type);
|
||||
}
|
||||
if (sym_visibility != 0) // should be zero log if else
|
||||
{
|
||||
LOG_INFO(Core_Linker, "symbol visilibity !=0");
|
||||
}
|
||||
switch (sym_bind) {
|
||||
case STB_LOCAL:
|
||||
symbol_vitrual_addr = rel_base_virtual_addr + sym.st_value;
|
||||
break;
|
||||
case STB_GLOBAL:
|
||||
case STB_WEAK: {
|
||||
rel_name = namesTlb + sym.st_name;
|
||||
Resolve(rel_name, rel_sym_type, m, &symrec);
|
||||
symbol_vitrual_addr = symrec.virtual_address;
|
||||
} 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_name = symrec.name;
|
||||
} break;
|
||||
default:
|
||||
LOG_INFO(Core_Linker, "UNK type {:#010x} rel symbol : {:#010x}", type, symbol);
|
||||
}
|
||||
|
||||
if (rel_isResolved) {
|
||||
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<u8*>(rel) < reinterpret_cast<u8*>(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<u8*>(rel) < reinterpret_cast<u8*>(m->dynamic_info.jmp_relocation_table) +
|
||||
m->dynamic_info.jmp_relocation_table_size;
|
||||
rel++, idx++) {
|
||||
relocate(idx, rel, m, true);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool contains(const std::vector<T>& 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;
|
||||
}
|
||||
|
||||
void Linker::Resolve(const std::string& name, Loader::SymbolType sym_type, Module* m,
|
||||
Loader::SymbolRecord* return_info) {
|
||||
// std::scoped_lock lock{m_mutex};
|
||||
const auto ids = Common::SplitString(name, '#');
|
||||
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");
|
||||
|
||||
Loader::SymbolResolver sr{};
|
||||
sr.name = ids.at(0);
|
||||
sr.library = library->name;
|
||||
sr.library_version = library->version;
|
||||
sr.module = module->name;
|
||||
sr.module_version_major = module->version_major;
|
||||
sr.module_version_minor = module->version_minor;
|
||||
sr.type = sym_type;
|
||||
|
||||
const Loader::SymbolRecord* rec = nullptr;
|
||||
|
||||
rec = m_hle_symbols.FindSymbol(sr);
|
||||
if (rec == nullptr) {
|
||||
// check if it an export function
|
||||
if (auto* p = FindExportedModule(*module, *library);
|
||||
p != nullptr && p->export_sym.GetSize() > 0) {
|
||||
rec = p->export_sym.FindSymbol(sr);
|
||||
}
|
||||
}
|
||||
if (rec != nullptr) {
|
||||
*return_info = *rec;
|
||||
} else {
|
||||
auto aeronid = AeroLib::FindByNid(sr.name.c_str());
|
||||
if (aeronid) {
|
||||
return_info->name = aeronid->name;
|
||||
return_info->virtual_address = AeroLib::GetStub(aeronid->nid);
|
||||
} else {
|
||||
return_info->virtual_address = AeroLib::GetStub(sr.name.c_str());
|
||||
return_info->name = "Unknown !!!";
|
||||
}
|
||||
LOG_ERROR(Core_Linker, "Linker: Stub resolved {} as {} (lib: {}, mod: {})", sr.name,
|
||||
return_info->name, library->name, module->name);
|
||||
}
|
||||
} else {
|
||||
return_info->virtual_address = 0;
|
||||
return_info->name = name;
|
||||
LOG_ERROR(Core_Linker, "Not Resolved {}", name);
|
||||
}
|
||||
}
|
||||
|
||||
u64 Linker::GetProcParam() {
|
||||
// std::scoped_lock lock{m_mutex};
|
||||
|
||||
for (auto& m : m_modules) {
|
||||
if (!m->elf.IsSharedLib()) {
|
||||
return m->proc_param_virtual_addr;
|
||||
}
|
||||
}
|
||||
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, module_func_t func);
|
||||
|
||||
static PS4_SYSV_ABI int run_module(uint64_t addr, size_t args, const void* argp,
|
||||
module_func_t func) {
|
||||
return reinterpret_cast<module_ini_func_t>(addr)(args, argp, func);
|
||||
}
|
||||
|
||||
int Linker::StartModule(Module* m, size_t args, const void* argp, module_func_t func) {
|
||||
LOG_INFO(Core_Linker, "Module started : {}", m->file_name);
|
||||
return run_module(m->dynamic_info.init_virtual_addr + m->base_virtual_addr, args, argp, func);
|
||||
}
|
||||
|
||||
void Linker::StartAllModules() {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
for (auto& m : m_modules) {
|
||||
if (m->elf.IsSharedLib()) {
|
||||
StartModule(m.get(), 0, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
using ExitFunc = PS4_SYSV_ABI void (*)();
|
||||
|
||||
static PS4_SYSV_ABI void ProgramExitFunc() {
|
||||
fmt::print("exit function called\n");
|
||||
}
|
||||
|
||||
static void RunMainEntry(u64 addr, EntryParams* params, exit_func_t exit_func) {
|
||||
static void RunMainEntry(VAddr addr, EntryParams* params, ExitFunc exit_func) {
|
||||
// reinterpret_cast<entry_func_t>(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
|
||||
|
||||
|
@ -672,42 +46,283 @@ static void RunMainEntry(u64 addr, EntryParams* params, exit_func_t exit_func) {
|
|||
: "rax", "rsi", "rdi");
|
||||
}
|
||||
|
||||
Linker::Linker() = default;
|
||||
|
||||
Linker::~Linker() = default;
|
||||
|
||||
void Linker::Execute() {
|
||||
if (Config::debugDump()) {
|
||||
DebugDump();
|
||||
}
|
||||
|
||||
Common::SetCurrentThreadName("GAME_MainThread");
|
||||
// Calculate static TLS size.
|
||||
for (const auto& module : m_modules) {
|
||||
if (module->tls.image_size != 0) {
|
||||
module->tls.modid = ++max_tls_index;
|
||||
}
|
||||
static_tls_size += module->tls.image_size;
|
||||
module->tls.offset = static_tls_size;
|
||||
}
|
||||
|
||||
Libraries::Kernel::pthreadInitSelfMainThread();
|
||||
// Relocate all modules
|
||||
for (const auto& m : m_modules) {
|
||||
Relocate(m.get());
|
||||
}
|
||||
StartAllModules();
|
||||
|
||||
// Init primary thread.
|
||||
Common::SetCurrentThreadName("GAME_MainThread");
|
||||
Libraries::Kernel::pthreadInitSelfMainThread();
|
||||
InitTlsForThread(true);
|
||||
|
||||
// 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"; // hmm should be ok?
|
||||
p.argv[0] = "eboot.bin";
|
||||
|
||||
for (auto& m : m_modules) {
|
||||
if (m->elf.IsSharedLib()) {
|
||||
if (!m->IsSharedLib()) {
|
||||
RunMainEntry(m->GetEntryAddress(), &p, ProgramExitFunc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s32 Linker::LoadModule(const std::filesystem::path& elf_name) {
|
||||
std::scoped_lock lk{mutex};
|
||||
|
||||
if (!std::filesystem::exists(elf_name)) {
|
||||
LOG_ERROR(Core_Linker, "Provided file {} does not exist", elf_name.string());
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto module = std::make_unique<Module>(elf_name);
|
||||
if (!module->IsValid()) {
|
||||
LOG_ERROR(Core_Linker, "Provided file {} is not valid ELF file", elf_name.string());
|
||||
return -1;
|
||||
}
|
||||
|
||||
m_modules.emplace_back(std::move(module));
|
||||
return m_modules.size() - 1;
|
||||
}
|
||||
|
||||
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* 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;
|
||||
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_is_resolved = true;
|
||||
break;
|
||||
case R_X86_64_DTPMOD64:
|
||||
rel_value = static_cast<u64>(module->tls.modid);
|
||||
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 = symbol_table[symbol];
|
||||
auto sym_bind = sym.GetBind();
|
||||
auto sym_type = sym.GetType();
|
||||
auto sym_visibility = sym.GetVisibility();
|
||||
u64 symbol_vitrual_addr = 0;
|
||||
Loader::SymbolRecord symrec{};
|
||||
switch (sym_type) {
|
||||
case STT_FUN:
|
||||
rel_sym_type = Loader::SymbolType::Function;
|
||||
break;
|
||||
case STT_OBJECT:
|
||||
rel_sym_type = Loader::SymbolType::Object;
|
||||
break;
|
||||
case STT_NOTYPE:
|
||||
rel_sym_type = Loader::SymbolType::NoType;
|
||||
break;
|
||||
default:
|
||||
ASSERT_MSG(0, "unknown symbol type {}", sym_type);
|
||||
}
|
||||
|
||||
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;
|
||||
break;
|
||||
case STB_GLOBAL:
|
||||
case STB_WEAK: {
|
||||
rel_name = namesTlb + sym.st_name;
|
||||
Resolve(rel_name, rel_sym_type, module, &symrec);
|
||||
symbol_vitrual_addr = symrec.virtual_address;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT_MSG(0, "unknown bind type {}", sym_bind);
|
||||
}
|
||||
rel_is_resolved = (symbol_vitrual_addr != 0);
|
||||
rel_value = (rel_is_resolved ? symbol_vitrual_addr + addend : 0);
|
||||
rel_name = symrec.name;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_INFO(Core_Linker, "UNK type {:#010x} rel symbol : {:#010x}", type, symbol);
|
||||
}
|
||||
|
||||
if (rel_is_resolved) {
|
||||
VirtualMemory::memory_patch(rel_virtual_addr, rel_value);
|
||||
} else {
|
||||
LOG_INFO(Core_Linker, "function not patched! {}", rel_name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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,
|
||||
Loader::SymbolRecord* return_info) {
|
||||
const auto ids = Common::SplitString(name, '#');
|
||||
if (ids.size() != 3) {
|
||||
return_info->virtual_address = 0;
|
||||
return_info->name = name;
|
||||
LOG_ERROR(Core_Linker, "Not Resolved {}", name);
|
||||
return;
|
||||
}
|
||||
|
||||
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{};
|
||||
sr.name = ids.at(0);
|
||||
sr.library = library->name;
|
||||
sr.library_version = library->version;
|
||||
sr.module = module->name;
|
||||
sr.module_version_major = module->version_major;
|
||||
sr.module_version_minor = module->version_minor;
|
||||
sr.type = sym_type;
|
||||
|
||||
const auto* record = m_hle_symbols.FindSymbol(sr);
|
||||
if (!record) {
|
||||
// Check if it an export function
|
||||
const auto* p = FindExportedModule(*module, *library);
|
||||
if (p && p->export_sym.GetSize() > 0) {
|
||||
record = p->export_sym.FindSymbol(sr);
|
||||
}
|
||||
}
|
||||
if (record) {
|
||||
*return_info = *record;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto aeronid = AeroLib::FindByNid(sr.name.c_str());
|
||||
if (aeronid) {
|
||||
return_info->name = aeronid->name;
|
||||
return_info->virtual_address = AeroLib::GetStub(aeronid->nid);
|
||||
} else {
|
||||
return_info->virtual_address = AeroLib::GetStub(sr.name.c_str());
|
||||
return_info->name = "Unknown !!!";
|
||||
}
|
||||
LOG_ERROR(Core_Linker, "Linker: Stub resolved {} as {} (lib: {}, mod: {})", sr.name,
|
||||
return_info->name, library->name, module->name);
|
||||
}
|
||||
|
||||
void* Linker::TlsGetAddr(u64 module_index, u64 offset) {
|
||||
std::scoped_lock lk{mutex};
|
||||
|
||||
DtvEntry* dtv_table = GetTcbBase()->tcb_dtv;
|
||||
ASSERT_MSG(dtv_table[0].counter == dtv_generation_counter,
|
||||
"Reallocation of DTV table is not supported");
|
||||
|
||||
void* module = (u8*)dtv_table[module_index + 1].pointer + offset;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize allocated memory and allocate DTV table.
|
||||
const u32 num_dtvs = max_tls_index;
|
||||
std::memset(addr_out, 0, total_tls_size);
|
||||
DtvEntry* dtv_table = new DtvEntry[num_dtvs + 2];
|
||||
|
||||
// Initialize thread control block
|
||||
u8* addr = reinterpret_cast<u8*>(addr_out);
|
||||
Tcb* tcb = reinterpret_cast<Tcb*>(addr + static_tls_size);
|
||||
tcb->tcb_self = tcb;
|
||||
tcb->tcb_dtv = dtv_table;
|
||||
|
||||
// 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;
|
||||
|
||||
// Copy init images to TLS thread blocks and map them to DTV slots.
|
||||
for (const auto& module : m_modules) {
|
||||
if (module->tls.image_size == 0) {
|
||||
continue;
|
||||
}
|
||||
if (m->tls.image_virtual_addr != 0) {
|
||||
SetTLSStorage(m->tls.image_virtual_addr);
|
||||
}
|
||||
RunMainEntry(m->elf.GetElfEntry() + m->base_virtual_addr, &p, ProgramExitFunc);
|
||||
u8* dest = reinterpret_cast<u8*>(addr + static_tls_size - module->tls.offset);
|
||||
const u8* src = reinterpret_cast<const u8*>(module->tls.image_virtual_addr);
|
||||
std::memcpy(dest, src, module->tls.init_image_size);
|
||||
tcb->tcb_dtv[module->tls.modid + 1].pointer = dest;
|
||||
}
|
||||
|
||||
// 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");
|
||||
|
|
|
@ -5,11 +5,10 @@
|
|||
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include "core/loader/elf.h"
|
||||
#include "core/loader/symbols_resolver.h"
|
||||
#include "core/module.h"
|
||||
|
||||
namespace Core {
|
||||
using module_func_t = int (*)(size_t args, const void* argp);
|
||||
|
||||
struct DynamicModuleInfo;
|
||||
class Linker;
|
||||
|
||||
|
@ -19,136 +18,50 @@ 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;
|
||||
};
|
||||
};
|
||||
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 PS4ThreadLocal {
|
||||
u64 image_virtual_addr = 0;
|
||||
u64 image_size = 0;
|
||||
u64 handler_virtual_addr = 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<const char*> needed;
|
||||
std::vector<ModuleInfo> import_modules;
|
||||
std::vector<ModuleInfo> export_modules;
|
||||
std::vector<LibraryInfo> import_libs;
|
||||
std::vector<LibraryInfo> 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 {
|
||||
Loader::Elf elf;
|
||||
u64 aligned_base_size = 0;
|
||||
u64 base_virtual_addr = 0;
|
||||
u64 proc_param_virtual_addr = 0;
|
||||
|
||||
std::string file_name;
|
||||
|
||||
std::vector<u8> m_dynamic;
|
||||
std::vector<u8> m_dynamic_data;
|
||||
DynamicModuleInfo dynamic_info{};
|
||||
|
||||
Loader::SymbolsResolver export_sym;
|
||||
Loader::SymbolsResolver import_sym;
|
||||
|
||||
PS4ThreadLocal tls;
|
||||
};
|
||||
using HeapApiFunc = PS4_SYSV_ABI void* (*)(size_t);
|
||||
|
||||
class Linker {
|
||||
public:
|
||||
Linker();
|
||||
virtual ~Linker();
|
||||
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,
|
||||
|
||||
VAddr GetProcParam() const {
|
||||
return m_modules[0]->GetProcParam();
|
||||
}
|
||||
|
||||
Module* GetModule(s32 index) const {
|
||||
return m_modules.at(index).get();
|
||||
}
|
||||
|
||||
void SetHeapApiFunc(void* func) {
|
||||
heap_api_func = *reinterpret_cast<HeapApiFunc*>(func);
|
||||
}
|
||||
|
||||
void* TlsGetAddr(u64 module_index, u64 offset);
|
||||
void InitTlsForThread(bool is_primary = false);
|
||||
|
||||
s32 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, module_func_t func);
|
||||
void StartAllModules();
|
||||
const Module* FindExportedModule(const ModuleInfo& m, const LibraryInfo& l);
|
||||
|
||||
std::mutex mutex;
|
||||
u32 dtv_generation_counter{1};
|
||||
size_t static_tls_size{};
|
||||
size_t max_tls_index{};
|
||||
HeapApiFunc heap_api_func{};
|
||||
std::vector<std::unique_ptr<Module>> m_modules;
|
||||
Loader::SymbolsResolver m_hle_symbols{};
|
||||
std::mutex m_mutex;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <cinttypes>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -482,11 +481,15 @@ 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);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include "common/io_file.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/types.h"
|
||||
#include "core/aerolib/aerolib.h"
|
||||
|
@ -11,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), s.nidName, virtual_addr);
|
||||
}
|
||||
|
||||
std::string SymbolsResolver::GenerateName(const SymbolResolver& s) {
|
||||
|
@ -38,22 +36,13 @@ 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)));
|
||||
}
|
||||
}
|
||||
|
||||
int SymbolsResolver::GetSize() {
|
||||
return m_symbols.size();
|
||||
}
|
||||
|
||||
} // namespace Core::Loader
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "common/types.h"
|
||||
|
||||
|
@ -20,6 +21,7 @@ enum class SymbolType {
|
|||
|
||||
struct SymbolRecord {
|
||||
std::string name;
|
||||
std::string nid_name;
|
||||
u64 virtual_address;
|
||||
};
|
||||
|
||||
|
@ -42,6 +44,16 @@ public:
|
|||
void AddSymbol(const SymbolResolver& s, u64 virtual_addr);
|
||||
const SymbolRecord* FindSymbol(const SymbolResolver& s) const;
|
||||
|
||||
void DebugDump(const std::filesystem::path& file_name);
|
||||
|
||||
std::span<const SymbolRecord> GetSymbols() const {
|
||||
return m_symbols;
|
||||
}
|
||||
|
||||
size_t GetSize() const noexcept {
|
||||
return m_symbols.size();
|
||||
}
|
||||
|
||||
static std::string GenerateName(const SymbolResolver& s);
|
||||
|
||||
static std::string_view SymbolTypeToS(SymbolType sym_type) {
|
||||
|
@ -59,9 +71,6 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void DebugDump(const std::filesystem::path& file_name);
|
||||
int GetSize();
|
||||
|
||||
private:
|
||||
std::vector<SymbolRecord> m_symbols;
|
||||
};
|
||||
|
|
|
@ -26,7 +26,7 @@ MemoryManager::~MemoryManager() = default;
|
|||
|
||||
PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size, u64 alignment,
|
||||
int memory_type) {
|
||||
PAddr free_addr = 0;
|
||||
PAddr free_addr = search_start;
|
||||
|
||||
// Iterate through allocated blocked and find the next free position
|
||||
for (const auto& block : allocations) {
|
||||
|
@ -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);
|
||||
|
@ -99,7 +99,7 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M
|
|||
}
|
||||
|
||||
// Perform the mapping.
|
||||
*out_addr = impl.Map(mapped_addr, size);
|
||||
*out_addr = impl.Map(mapped_addr, size, alignment, phys_addr);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -107,7 +107,7 @@ public:
|
|||
|
||||
int MapMemory(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot,
|
||||
MemoryMapFlags flags, VMAType type, std::string_view name = "",
|
||||
PAddr phys_addr = 0, u64 alignment = 0);
|
||||
PAddr phys_addr = -1, u64 alignment = 0);
|
||||
|
||||
void UnmapMemory(VAddr virtual_addr, size_t size);
|
||||
|
||||
|
@ -121,7 +121,7 @@ private:
|
|||
VMAHandle FindVMA(VAddr target) {
|
||||
// Return first the VMA with base >= target.
|
||||
const auto it = vma_map.lower_bound(target);
|
||||
if (it->first == target) {
|
||||
if (it != vma_map.end() && it->first == target) {
|
||||
return it;
|
||||
}
|
||||
return std::prev(it);
|
||||
|
|
|
@ -0,0 +1,419 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <xbyak/xbyak.h>
|
||||
#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, void* param);
|
||||
|
||||
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<const elf_program_header> 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<u16>(nVal >> 6u) & 0x3fu];
|
||||
enc += codes[nVal & 0x3fu];
|
||||
} else {
|
||||
enc += codes[static_cast<u16>(nVal >> 12u) & 0x3fu];
|
||||
enc += codes[static_cast<u16>(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, void* param) {
|
||||
LOG_INFO(Core_Linker, "Module started : {}", file.filename().string());
|
||||
const VAddr addr = dynamic_info.init_virtual_addr + GetBaseAddress();
|
||||
reinterpret_cast<EntryFunc>(addr)(args, argp, param);
|
||||
}
|
||||
|
||||
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<void*>(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<VAddr>(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<VAddr>(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<elf_dynamic*>(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<void*>(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<char*>(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<elf_symbol*>(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<elf_relocation*>(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<elf_relocation*>(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<u8*>(sym) < reinterpret_cast<u8*>(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
|
|
@ -0,0 +1,183 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#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;
|
||||
u64 image_size;
|
||||
u64 offset;
|
||||
u32 modid;
|
||||
VAddr image_virtual_addr;
|
||||
u64 init_image_size;
|
||||
};
|
||||
|
||||
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<const char*> needed;
|
||||
std::vector<ModuleInfo> import_modules;
|
||||
std::vector<ModuleInfo> export_modules;
|
||||
std::vector<LibraryInfo> import_libs;
|
||||
std::vector<LibraryInfo> 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();
|
||||
}
|
||||
|
||||
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>
|
||||
T GetProcParam() const noexcept {
|
||||
return reinterpret_cast<T>(proc_param_virtual_addr);
|
||||
}
|
||||
|
||||
std::span<const ModuleInfo> GetImportModules() const {
|
||||
return dynamic_info.import_modules;
|
||||
}
|
||||
|
||||
std::span<const ModuleInfo> GetExportModules() const {
|
||||
return dynamic_info.export_modules;
|
||||
}
|
||||
|
||||
std::span<const LibraryInfo> GetImportLibs() const {
|
||||
return dynamic_info.import_libs;
|
||||
}
|
||||
|
||||
std::span<const LibraryInfo> 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, void* param);
|
||||
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<u8> m_dynamic;
|
||||
std::vector<u8> m_dynamic_data;
|
||||
Loader::SymbolsResolver export_sym;
|
||||
Loader::SymbolsResolver import_sym;
|
||||
ThreadLocalImage tls{};
|
||||
};
|
||||
|
||||
} // namespace Core
|
|
@ -44,13 +44,15 @@ constexpr static TLSPattern TlsPatterns[] = {
|
|||
#ifdef _WIN32
|
||||
static DWORD slot = 0;
|
||||
|
||||
void SetTLSStorage(u64 image_address) {
|
||||
// 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.
|
||||
const BOOL result = TlsSetValue(slot, reinterpret_cast<LPVOID>(image_address));
|
||||
void SetTcbBase(void* image_address) {
|
||||
const BOOL result = TlsSetValue(slot, image_address);
|
||||
ASSERT(result != 0);
|
||||
}
|
||||
|
||||
Tcb* GetTcbBase() {
|
||||
return reinterpret_cast<Tcb*>(TlsGetValue(slot));
|
||||
}
|
||||
|
||||
void PatchTLS(u64 segment_addr, u64 segment_size, Xbyak::CodeGenerator& c) {
|
||||
using namespace Xbyak::util;
|
||||
|
||||
|
@ -81,6 +83,7 @@ void PatchTLS(u64 segment_addr, u64 segment_size, Xbyak::CodeGenerator& c) {
|
|||
std::memcpy(&offset, code + tls_pattern.pattern_size, sizeof(u64));
|
||||
LOG_INFO(Core_Linker, "PATTERN64 FOUND at {}, reg: {} offset: {:#x}",
|
||||
fmt::ptr(code), tls_pattern.target_reg, offset);
|
||||
continue;
|
||||
}
|
||||
ASSERT(offset == 0);
|
||||
|
||||
|
@ -132,7 +135,11 @@ void PatchTLS(u64 segment_addr, u64 segment_size, Xbyak::CodeGenerator& c) {
|
|||
|
||||
#else
|
||||
|
||||
void SetTLSStorage(u64 image_address) {
|
||||
void SetTcbBase(void* image_address) {
|
||||
UNREACHABLE_MSG("Thread local storage is unimplemented on posix platforms!");
|
||||
}
|
||||
|
||||
Tcb* GetTcbBase() {
|
||||
UNREACHABLE_MSG("Thread local storage is unimplemented on posix platforms!");
|
||||
}
|
||||
|
||||
|
|
|
@ -11,8 +11,22 @@ class CodeGenerator;
|
|||
|
||||
namespace Core {
|
||||
|
||||
/// Sets the data pointer that contains the TLS image.
|
||||
void SetTLSStorage(u64 image_address);
|
||||
union DtvEntry {
|
||||
size_t counter;
|
||||
void* pointer;
|
||||
};
|
||||
|
||||
struct Tcb {
|
||||
Tcb* tcb_self;
|
||||
DtvEntry* tcb_dtv;
|
||||
void* tcb_thread;
|
||||
};
|
||||
|
||||
/// Sets the data pointer to the TCB block.
|
||||
void SetTcbBase(void* image_address);
|
||||
|
||||
/// Retrieves Tcb structure for the calling thread.
|
||||
Tcb* GetTcbBase();
|
||||
|
||||
/// Patches any instructions that access guest TLS to use provided storage.
|
||||
void PatchTLS(u64 segment_addr, u64 segment_size, Xbyak::CodeGenerator& c);
|
||||
|
|
|
@ -76,7 +76,7 @@ int main(int argc, char* argv[]) {
|
|||
}
|
||||
|
||||
auto linker = Common::Singleton<Core::Linker>::Instance();
|
||||
Libraries::InitHLELibs(&linker->getHLESymbols());
|
||||
Libraries::InitHLELibs(&linker->GetHLESymbols());
|
||||
linker->LoadModule(path);
|
||||
|
||||
// check if we have system modules to load
|
||||
|
@ -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;
|
||||
|
|
|
@ -85,7 +85,6 @@ void Rasterizer::Draw(bool is_indexed) {
|
|||
}
|
||||
|
||||
void Rasterizer::DispatchDirect() {
|
||||
compute_done = true;
|
||||
return;
|
||||
const auto cmdbuf = scheduler.CommandBuffer();
|
||||
const auto& cs_program = liverpool->regs.cs_program;
|
||||
|
|
|
@ -49,7 +49,6 @@ private:
|
|||
Core::MemoryManager* memory;
|
||||
PipelineCache pipeline_cache;
|
||||
StreamBuffer vertex_index_buffer;
|
||||
bool compute_done{};
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
|
|
@ -92,14 +92,14 @@ StreamBuffer::~StreamBuffer() {
|
|||
|
||||
std::tuple<u8*, u64, bool> 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<u8*, u64, bool> 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,
|
||||
|
|
|
@ -59,7 +59,7 @@ LONG WINAPI GuestFaultSignalHandler(EXCEPTION_POINTERS* pExp) noexcept {
|
|||
}
|
||||
#endif
|
||||
|
||||
static constexpr u64 StreamBufferSize = 128_MB;
|
||||
static constexpr u64 StreamBufferSize = 512_MB;
|
||||
static constexpr u64 PageShift = 12;
|
||||
|
||||
TextureCache::TextureCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_)
|
||||
|
|
Loading…
Reference in New Issue