Cuphead work

This commit is contained in:
raphaelthegreat 2024-06-12 12:59:28 +03:00
parent 1666b9d199
commit 24446bcc32
35 changed files with 709 additions and 75 deletions

View file

@ -105,8 +105,9 @@ set(KERNEL_LIB
src/core/libraries/kernel/event_flag/event_flag.h
src/core/libraries/kernel/event_flag/event_flag_obj.cpp
src/core/libraries/kernel/event_flag/event_flag_obj.h
src/core/libraries/kernel/threads/kernel_threads.h
src/core/libraries/kernel/threads/kernel_threads_rwlock.cpp
src/core/libraries/kernel/threads/rwlock.cpp
src/core/libraries/kernel/threads/semaphore.cpp
src/core/libraries/kernel/threads/threads.h
src/core/libraries/kernel/cpu_management.cpp
src/core/libraries/kernel/cpu_management.h
src/core/libraries/kernel/event_queue.cpp
@ -491,6 +492,8 @@ else()
src/emulator.h
src/sdl_window.h
src/sdl_window.cpp
src/core/loader/dwarf.h
src/core/loader/dwarf.cpp
)
endif()

View file

@ -3,7 +3,10 @@
#include <vector>
#include "common/assert.h"
#include "common/alignment.h"
#include "common/io_file.h"
#include "common/error.h"
#include "common/logging/log.h"
#include "common/path_util.h"
@ -221,7 +224,8 @@ void* IOFile::GetFileMapping() {
}
const int fd = fileno(file);
HANDLE hfile = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
file_mapping = CreateFileMapping(hfile, NULL, PAGE_READWRITE, 0, 0, NULL);
file_mapping = CreateFileMapping2(hfile, NULL, FILE_MAP_READ, PAGE_READONLY, SEC_COMMIT, 0, NULL, NULL, 0);
ASSERT_MSG(file_mapping, "{}", Common::GetLastErrorMsg());
return file_mapping;
#endif
}

View file

@ -253,7 +253,7 @@ void* AddressSpace::Map(VAddr virtual_addr, size_t size, u64 alignment, PAddr ph
}
void* AddressSpace::MapFile(VAddr virtual_addr, size_t size, size_t offset, void* fd) {
return impl->Map(virtual_addr, offset, size, PAGE_READWRITE, fd);
return impl->Map(virtual_addr, offset, size, fd ? PAGE_READONLY : PAGE_READWRITE, fd);
}
void AddressSpace::Unmap(VAddr virtual_addr, size_t size, bool has_backing) {

View file

@ -19,7 +19,7 @@ namespace Core::AeroLib {
// and to longer compile / CI times
//
// Must match STUBS_LIST define
constexpr u32 MAX_STUBS = 512;
constexpr u32 MAX_STUBS = 1024;
u64 UnresolvedStub() {
LOG_ERROR(Core, "Returning zero to {}", __builtin_return_address(0));
@ -60,8 +60,9 @@ static u32 UsedStubEntries;
#define XREP_128(x) XREP_64(x) XREP_64(x + 64)
#define XREP_256(x) XREP_128(x) XREP_128(x + 128)
#define XREP_512(x) XREP_256(x) XREP_256(x + 256)
#define XREP_1024(x) XREP_512(x) XREP_512(x + 512)
#define STUBS_LIST XREP_512(0)
#define STUBS_LIST XREP_1024(0)
static u64 (*stub_handlers[MAX_STUBS])() = {STUBS_LIST};

View file

@ -66,6 +66,9 @@ static inline u32* WriteTrailingNop(u32* cmdbuf) {
s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata) {
LOG_TRACE(Lib_GnmDriver, "called");
if (id != SceKernelEvent::Type::GfxEop) {
return ORBIS_OK;
}
ASSERT_MSG(id == SceKernelEvent::Type::GfxEop);
if (!eq) {

View file

@ -73,7 +73,8 @@ int PS4_SYSV_ABI sceKernelCloseEventFlag() {
return ORBIS_OK;
}
int PS4_SYSV_ABI sceKernelClearEventFlag(OrbisKernelEventFlag ef, u64 bitPattern) {
LOG_ERROR(Kernel_Event, "(STUBBED) called");
LOG_ERROR(Kernel_Event, "called");
ef->Clear(bitPattern);
return ORBIS_OK;
}
int PS4_SYSV_ABI sceKernelCancelEventFlag(OrbisKernelEventFlag ef, u64 setPattern,
@ -195,4 +196,4 @@ void RegisterKernelEventFlag(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("IOnSvHzqu6A", "libkernel", 1, "libkernel", 1, 1, sceKernelSetEventFlag);
LIB_FUNCTION("JTvBflhYazQ", "libkernel", 1, "libkernel", 1, 1, sceKernelWaitEventFlag);
}
} // namespace Libraries::Kernel
} // namespace Libraries::Kernel

View file

@ -90,4 +90,15 @@ void EventFlagInternal::Set(u64 bits) {
m_cond_var.notify_all();
}
} // namespace Libraries::Kernel
void EventFlagInternal::Clear(u64 bits) {
std::unique_lock lock{m_mutex};
while (m_status != Status::Set) {
m_mutex.unlock();
std::this_thread::sleep_for(std::chrono::microseconds(10));
m_mutex.lock();
}
m_bits &= bits;
}
} // namespace Libraries::Kernel

View file

@ -25,6 +25,7 @@ public:
int Wait(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result, u32* ptr_micros);
int Poll(u64 bits, WaitMode wait_mode, ClearMode clear_mode, u64* result);
void Set(u64 bits);
void Clear(u64 bits);
private:
enum class Status { Set, Canceled, Deleted };
@ -38,4 +39,4 @@ private:
QueueMode m_queue_mode = QueueMode::Fifo;
u64 m_bits = 0;
};
} // namespace Libraries::Kernel
} // namespace Libraries::Kernel

View file

@ -34,6 +34,13 @@ constexpr s16 EVFILT_GPU_SYSTEM_EXCEPTION = -21;
constexpr s16 EVFILT_GPU_DBGGC_EV = -22;
constexpr s16 EVFILT_SYSCOUNT = 22;
constexpr u16 EV_ONESHOT = 0x10; // only report one occurrence
constexpr u16 EV_CLEAR = 0x20; // clear event state after reporting
constexpr u16 EV_RECEIPT = 0x40; // force EV_ERROR on success, data=0
constexpr u16 EV_DISPATCH = 0x80; // disable event after reporting
constexpr u16 EV_SYSFLAGS = 0xF000; // reserved by system
constexpr u16 EV_FLAG1 = 0x2000; // filter-specific flag
class EqueueInternal;
struct EqueueEvent;

View file

@ -88,6 +88,24 @@ int PS4_SYSV_ABI sceKernelAddUserEvent(SceKernelEqueue eq, int id) {
event.event.ident = id;
event.event.filter = Kernel::EVFILT_USER;
event.event.udata = 0;
event.event.flags = 1;
event.event.fflags = 0;
event.event.data = 0;
return eq->addEvent(event);
}
int PS4_SYSV_ABI sceKernelAddUserEventEdge(SceKernelEqueue eq, int id) {
if (eq == nullptr) {
return ORBIS_KERNEL_ERROR_EBADF;
}
Kernel::EqueueEvent event{};
event.isTriggered = false;
event.event.ident = id;
event.event.filter = Kernel::EVFILT_USER;
event.event.udata = 0;
event.event.flags = 0x21;
event.event.fflags = 0;
event.event.data = 0;
@ -111,4 +129,5 @@ int PS4_SYSV_ABI sceKernelDeleteUserEvent(SceKernelEqueue eq, int id) {
eq->removeEvent(id);
return ORBIS_OK;
}
} // namespace Libraries::Kernel

View file

@ -18,5 +18,6 @@ void* PS4_SYSV_ABI sceKernelGetEventUserData(const SceKernelEvent* ev);
int PS4_SYSV_ABI sceKernelTriggerUserEvent(SceKernelEqueue eq, int id, void* udata);
int PS4_SYSV_ABI sceKernelDeleteUserEvent(SceKernelEqueue eq, int id);
int PS4_SYSV_ABI sceKernelAddUserEvent(SceKernelEqueue eq, int id);
int PS4_SYSV_ABI sceKernelAddUserEventEdge(SceKernelEqueue eq, int id);
} // namespace Libraries::Kernel

View file

@ -89,6 +89,11 @@ int PS4_SYSV_ABI sceKernelClose(int d) {
return SCE_OK;
}
int PS4_SYSV_ABI posix_close(int d) {
ASSERT(sceKernelClose(d) == 0);
return ORBIS_OK;
}
size_t PS4_SYSV_ABI sceKernelWrite(int d, void* buf, size_t nbytes) {
if (buf == nullptr) {
return SCE_KERNEL_ERROR_EFAULT;
@ -282,6 +287,10 @@ int PS4_SYSV_ABI sceKernelFStat(int fd, OrbisKernelStat* sb) {
return ORBIS_OK;
}
int PS4_SYSV_ABI posix_fstat(int fd, OrbisKernelStat* sb) {
return sceKernelFStat(fd, sb);
}
s32 PS4_SYSV_ABI sceKernelFsync(int fd) {
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto* file = h->GetFile(fd);
@ -293,6 +302,7 @@ void fileSystemSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("1G3lF1Gg1k8", "libkernel", 1, "libkernel", 1, 1, sceKernelOpen);
LIB_FUNCTION("wuCroIGjt2g", "libScePosix", 1, "libkernel", 1, 1, posix_open);
LIB_FUNCTION("UK2Tl2DWUns", "libkernel", 1, "libkernel", 1, 1, sceKernelClose);
LIB_FUNCTION("bY-PO6JhzhQ", "libScePosix", 1, "libkernel", 1, 1, posix_close);
LIB_FUNCTION("4wSze92BhLI", "libkernel", 1, "libkernel", 1, 1, sceKernelWrite);
LIB_FUNCTION("+WRlkKjZvag", "libkernel", 1, "libkernel", 1, 1, _readv);
@ -302,6 +312,7 @@ void fileSystemSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("1-LFLmRFxxM", "libkernel", 1, "libkernel", 1, 1, sceKernelMkdir);
LIB_FUNCTION("eV9wAD2riIA", "libkernel", 1, "libkernel", 1, 1, sceKernelStat);
LIB_FUNCTION("kBwCPsYX-m4", "libkernel", 1, "libkernel", 1, 1, sceKernelFStat);
LIB_FUNCTION("mqQMh1zPPT8", "libScePosix", 1, "libkernel", 1, 1, posix_fstat);
LIB_FUNCTION("E6ao34wPw+U", "libScePosix", 1, "libkernel", 1, 1, posix_stat);
LIB_FUNCTION("+r3rMFwItV4", "libkernel", 1, "libkernel", 1, 1, sceKernelPread);

View file

@ -62,7 +62,7 @@ size_t PS4_SYSV_ABI _writev(int fd, const struct iovec* iov, int iovcn) {
return total_written;
}
static thread_local int libc_error;
static thread_local int libc_error{};
int* PS4_SYSV_ABI __Error() {
return &libc_error;
}
@ -77,18 +77,19 @@ int PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, int prot, int flags, int fd,
if (fd == -1) {
handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, len + offset, NULL);
} else {
void* handle = h->GetFile(fd)->f.GetFileMapping();
handle = h->GetFile(fd)->f.GetFileMapping();
}
const auto mem_prot = static_cast<Core::MemoryProt>(prot);
const auto mem_flags = static_cast<Core::MemoryMapFlags>(flags);
return memory->MapFile(res, std::bit_cast<VAddr>(addr), len, mem_prot, mem_flags, handle, offset);
}
PS4_SYSV_ABI void* posix_mmap(void* addr, u64 len, int prot, int flags, int fd, u64 offset) {
void* PS4_SYSV_ABI posix_mmap(void* addr, u64 len, int prot, int flags, int fd, u64 offset) {
void* ptr;
LOG_INFO(Kernel_Vmm, "posix mmap redirect to sceKernelMmap\n");
// posix call the difference is that there is a different behaviour when it doesn't return 0 or
// SCE_OK
const VAddr ret_addr = (VAddr)__builtin_return_address(0);
int result = sceKernelMmap(addr, len, prot, flags, fd, offset, &ptr);
ASSERT(result == 0);
return ptr;
@ -172,11 +173,16 @@ s32 PS4_SYSV_ABI sceKernelLoadStartModule(const char* moduleFileName, size_t arg
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
const auto path = mnt->GetHostFile(moduleFileName);
// Load PRX module.
// Load PRX module and relocate any modules that import it.
auto* linker = Common::Singleton<Core::Linker>::Instance();
u32 handle = linker->LoadModule(path);
auto* module = linker->GetModule(handle);
linker->Relocate(module);
linker->RelocateAnyImports(module);
// If the new module has a TLS image, trigger its load when TlsGetAddr is called.
if (module->tls.image_size != 0) {
linker->AdvanceGenerationCounter();
}
// Retrieve and verify proc param according to libkernel.
u64* param = module->GetProcParam<u64*>();
@ -196,11 +202,65 @@ s32 PS4_SYSV_ABI sceKernelDlsym(s32 handle, const char* symbol, void** addrp) {
return ORBIS_OK;
}
static constexpr size_t ORBIS_DBG_MAX_NAME_LENGTH = 256;
struct OrbisModuleInfoForUnwind {
u64 st_size;
std::array<char, ORBIS_DBG_MAX_NAME_LENGTH> name;
VAddr eh_frame_hdr_addr;
VAddr eh_frame_addr;
u64 eh_frame_size;
VAddr seg0_addr;
u64 seg0_size;
};
s32 PS4_SYSV_ABI sceKernelGetModuleInfoForUnwind(VAddr addr, int flags, OrbisModuleInfoForUnwind* info) {
if (flags >= 3) {
std::memset(info, 0, sizeof(OrbisModuleInfoForUnwind));
return SCE_KERNEL_ERROR_EINVAL;
}
if (!info) {
return ORBIS_KERNEL_ERROR_EFAULT;
}
if (info->st_size <= sizeof(OrbisModuleInfoForUnwind)) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
// Find module that contains specified address.
LOG_INFO(Lib_Kernel, "called addr = {:#x}, flags = {:#x}", addr, flags);
auto* linker = Common::Singleton<Core::Linker>::Instance();
auto* module = linker->FindByAddress(addr);
const auto mod_info = module->GetModuleInfoEx();
// Fill in module info.
info->name = mod_info.name;
info->eh_frame_hdr_addr = mod_info.eh_frame_hdr_addr;
info->eh_frame_addr = mod_info.eh_frame_addr;
info->eh_frame_size = mod_info.eh_frame_size;
info->seg0_addr = mod_info.segments[0].address;
info->seg0_size = mod_info.segments[0].size;
return ORBIS_OK;
}
int PS4_SYSV_ABI sceKernelDebugRaiseException() {
UNREACHABLE();
return 0;
}
char PS4_SYSV_ABI _is_signal_return(s64 *param_1) {
char cVar1;
if (((*param_1 != 0x48006a40247c8d48ULL) || (param_1[1] != 0x50f000001a1c0c7ULL)) ||
(cVar1 = '\x01', (param_1[2] & 0xffffffU) != 0xfdebf4)) {
cVar1 = ((*(u64*)((s64)param_1 + -5) & 0xffffffffff) == 0x50fca8949) * '\x02';
}
return cVar1;
}
int PS4_SYSV_ABI sceKernelGetCpumode() {
return 5;
}
void LibKernel_Register(Core::Loader::SymbolsResolver* sym) {
// obj
LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &g_stack_chk_guard);
@ -225,6 +285,8 @@ void LibKernel_Register(Core::Loader::SymbolsResolver* sym) {
_sceKernelRtldSetApplicationHeapAPI);
LIB_FUNCTION("wzvqT4UqKX8", "libkernel", 1, "libkernel", 1, 1, sceKernelLoadStartModule);
LIB_FUNCTION("LwG8g3niqwA", "libkernel", 1, "libkernel", 1, 1, sceKernelDlsym);
LIB_FUNCTION("RpQJJVKTiFM", "libkernel", 1, "libkernel", 1, 1, sceKernelGetModuleInfoForUnwind);
LIB_FUNCTION("VOx8NGmHXTs", "libkernel", 1, "libkernel", 1, 1, sceKernelGetCpumode);
// equeue
LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateEqueue);
@ -232,6 +294,7 @@ void LibKernel_Register(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("fzyMKs9kim0", "libkernel", 1, "libkernel", 1, 1, sceKernelWaitEqueue);
LIB_FUNCTION("vz+pg2zdopI", "libkernel", 1, "libkernel", 1, 1, sceKernelGetEventUserData);
LIB_FUNCTION("4R6-OvI2cEA", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEvent);
LIB_FUNCTION("WDszmSbWuDk", "libkernel", 1, "libkernel", 1, 1, sceKernelAddUserEventEdge);
LIB_FUNCTION("F6e0kwo4cnk", "libkernel", 1, "libkernel", 1, 1, sceKernelTriggerUserEvent);
LIB_FUNCTION("LJDwdSNTnDg", "libkernel", 1, "libkernel", 1, 1, sceKernelDeleteUserEvent);
@ -240,11 +303,13 @@ void LibKernel_Register(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("Ou3iL1abvng", "libkernel", 1, "libkernel", 1, 1, stack_chk_fail);
LIB_FUNCTION("9BcDykPmo1I", "libkernel", 1, "libkernel", 1, 1, __Error);
LIB_FUNCTION("BPE9s9vQQXo", "libkernel", 1, "libkernel", 1, 1, posix_mmap);
LIB_FUNCTION("BPE9s9vQQXo", "libScePosix", 1, "libkernel", 1, 1, posix_mmap);
LIB_FUNCTION("YSHRBRLn2pI", "libkernel", 1, "libkernel", 1, 1, _writev);
LIB_FUNCTION("959qrazPIrg", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcParam);
LIB_FUNCTION("-o5uEDpN+oY", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertUtcToLocaltime);
LIB_FUNCTION("WB66evu8bsU", "libkernel", 1, "libkernel", 1, 1, sceKernelGetCompiledSdkVersion);
LIB_FUNCTION("DRuBt2pvICk", "libkernel", 1, "libkernel", 1, 1, ps4__read);
LIB_FUNCTION("crb5j7mkk1c", "libkernel", 1, "libkernel", 1, 1, _is_signal_return);
Libraries::Kernel::fileSystemSymbolsRegister(sym);
Libraries::Kernel::timeSymbolsRegister(sym);

View file

@ -80,6 +80,9 @@ s32 PS4_SYSV_ABI sceKernelAvailableDirectMemorySize(u64 searchStart, u64 searchE
s32 PS4_SYSV_ABI sceKernelVirtualQuery(const void* addr, int flags, OrbisVirtualQueryInfo* info,
size_t infoSize) {
LOG_INFO(Kernel_Vmm, "called addr = {}, flags = {:#x}", fmt::ptr(addr), flags);
if (!addr) {
return SCE_KERNEL_ERROR_EACCES;
}
auto* memory = Core::Memory::Instance();
return memory->VirtualQuery(std::bit_cast<VAddr>(addr), flags, info);
}
@ -169,7 +172,7 @@ 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) {
LOG_WARNING(Kernel_Vmm, "called");
LOG_WARNING(Kernel_Vmm, "called offset = {:#x}, flags = {:#x}", offset, flags);
auto* memory = Core::Memory::Instance();
return memory->DirectMemoryQuery(offset, flags == 1, query_info);
}

View file

@ -3,6 +3,7 @@
#include <mutex>
#include <thread>
#include <semaphore.h>
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/singleton.h"
@ -1070,7 +1071,7 @@ int PS4_SYSV_ABI scePthreadCondattrDestroy(ScePthreadCondattr* attr) {
}
int PS4_SYSV_ABI scePthreadMutexTrylock(ScePthreadMutex* mutex) {
mutex = reinterpret_cast<ScePthreadMutex*>(createMutex(mutex));
if (mutex == nullptr) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
@ -1121,7 +1122,7 @@ int PS4_SYSV_ABI posix_sched_get_priority_min() {
int PS4_SYSV_ABI posix_pthread_mutex_trylock(ScePthreadMutex* mutex) {
int result = scePthreadMutexTrylock(mutex);
if (result < 0) {
UNREACHABLE();
//UNREACHABLE();
}
return result;
}
@ -1164,6 +1165,11 @@ int PS4_SYSV_ABI posix_pthread_create_name_np(ScePthread* thread, const ScePthre
return result;
}
int PS4_SYSV_ABI posix_pthread_create(ScePthread* thread, const ScePthreadAttr* attr,
pthreadEntryFunc start_routine, void* arg) {
return posix_pthread_create_name_np(thread, attr, start_routine, arg, "NoName");
}
using Destructor = void(*)(void*);
int PS4_SYSV_ABI posix_pthread_key_create(u32* key, Destructor func) {
@ -1174,8 +1180,56 @@ int PS4_SYSV_ABI posix_pthread_setspecific(int key, const void *value) {
return pthread_setspecific(key, value);
}
void* PS4_SYSV_ABI posix_pthread_getspecific(int key) {
return pthread_getspecific(key);
}
int PS4_SYSV_ABI posix_pthread_cond_init(ScePthreadCond* cond, const ScePthreadCondattr* attr,
const char* name) {
// LOG_INFO(Kernel_Pthread, "posix pthread_mutex_init redirect to scePthreadMutexInit");
int result = scePthreadCondInit(cond, attr, name);
if (result < 0) {
int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP
? result + -SCE_KERNEL_ERROR_UNKNOWN
: POSIX_EOTHER;
return rt;
}
return result;
}
int PS4_SYSV_ABI posix_pthread_setcancelstate(int state, int* oldstate) {
return pthread_setcancelstate(state, oldstate);
}
int PS4_SYSV_ABI posix_pthread_detach(ScePthread thread) {
return pthread_detach(thread->pth);
}
int PS4_SYSV_ABI posix_sem_init(sem_t* sem, int pshared, unsigned int value) {
return sem_init(sem, pshared, value);
}
int PS4_SYSV_ABI posix_sem_wait(sem_t* sem) {
return sem_wait(sem);
}
int PS4_SYSV_ABI posix_sem_post(sem_t* sem) {
return sem_post(sem);
}
int PS4_SYSV_ABI scePthreadGetschedparam(ScePthread thread, int *policy, SceKernelSchedParam *param) {
return pthread_getschedparam(thread->pth, policy, param);
}
int PS4_SYSV_ABI scePthreadSetschedparam(ScePthread thread, int policy, const SceKernelSchedParam *param) {
return pthread_setschedparam(thread->pth, policy, param);
}
void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("lZzFeSxPl08", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setcancelstate);
LIB_FUNCTION("0TyVk4MSLt0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_init);
LIB_FUNCTION("mqULNdimTn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_key_create);
LIB_FUNCTION("0-KXaS70xy4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_getspecific);
LIB_FUNCTION("WrOLvHU0yQM", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setspecific);
LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetschedpolicy);
LIB_FUNCTION("-Wreprtu0Qs", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetdetachstate);
@ -1195,6 +1249,9 @@ void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
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("FXPWHNk8Of0", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetschedparam);
LIB_FUNCTION("P41kTWUS3EI", "libkernel", 1, "libkernel", 1, 1, scePthreadGetschedparam);
LIB_FUNCTION("oIRFTjoILbg", "libkernel", 1, "libkernel", 1, 1, scePthreadSetschedparam);
LIB_FUNCTION("UTXzJbWhhTE", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrSetstacksize);
LIB_FUNCTION("vNe1w4diLCs", "libkernel", 1, "libkernel", 1, 1, __tls_get_addr);
@ -1202,6 +1259,8 @@ void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("6UgtwV+0zb4", "libkernel", 1, "libkernel", 1, 1, scePthreadCreate);
LIB_FUNCTION("T72hz6ffq08", "libkernel", 1, "libkernel", 1, 1, scePthreadYield);
LIB_FUNCTION("-quPa4SEJUw", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetstack);
LIB_FUNCTION("Ru36fiTtJzA", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetstackaddr);
LIB_FUNCTION("-fA+7ZlGDQs", "libkernel", 1, "libkernel", 1, 1, scePthreadAttrGetstacksize);
// mutex calls
LIB_FUNCTION("cmo1RIYva9o", "libkernel", 1, "libkernel", 1, 1, scePthreadMutexInit);
@ -1250,8 +1309,13 @@ void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
posix_pthread_attr_setdetachstate);
LIB_FUNCTION("zHchY8ft5pk", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_destroy);
LIB_FUNCTION("Jmi+9w9u0E4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create_name_np);
LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create);
LIB_FUNCTION("+U1R4WtXvoc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_detach);
LIB_FUNCTION("CBNtXOoef-E", "libScePosix", 1, "libkernel", 1, 1, posix_sched_get_priority_max);
LIB_FUNCTION("m0iS6jNsXds", "libScePosix", 1, "libkernel", 1, 1, posix_sched_get_priority_min);
LIB_FUNCTION("pDuPEf3m4fI", "libScePosix", 1, "libkernel", 1, 1, posix_sem_init);
LIB_FUNCTION("YCV5dGGBcCo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_wait);
LIB_FUNCTION("IKP8typ0QUk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_post);
// libs
RwlockSymbolsRegister(sym);
SemaphoreSymbolsRegister(sym);

View file

@ -35,6 +35,9 @@ int PS4_SYSV_ABI posix_pthread_rwlock_init(OrbisPthreadRwlock* rwlock,
}
int PS4_SYSV_ABI posix_pthread_rwlock_rdlock(OrbisPthreadRwlock* rwlock) {
if (*rwlock == nullptr) {
posix_pthread_rwlock_init(rwlock, nullptr, nullptr);
}
int result = pthread_rwlock_rdlock(&(*rwlock)->pth_rwlock);
if (result != 0) {
LOG_ERROR(Kernel_Pthread, "posix_pthread_rwlock_rdlock: error = {}", result);

View file

@ -463,10 +463,10 @@ void libcSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
// stdio functions
LIB_FUNCTION("xeYO4u7uyJ0", "libc", 1, "libc", 1, 1, ps4_fopen);
LIB_FUNCTION("hcuQgD53UxM", "libc", 1, "libc", 1, 1, ps4_printf);
//LIB_FUNCTION("hcuQgD53UxM", "libc", 1, "libc", 1, 1, ps4_printf);
LIB_FUNCTION("Q2V+iqvjgC0", "libc", 1, "libc", 1, 1, ps4_vsnprintf);
LIB_FUNCTION("YQ0navp+YIc", "libc", 1, "libc", 1, 1, ps4_puts);
LIB_FUNCTION("fffwELXNVFA", "libc", 1, "libc", 1, 1, ps4_fprintf);
//LIB_FUNCTION("fffwELXNVFA", "libc", 1, "libc", 1, 1, ps4_fprintf);
LIB_FUNCTION("QMFyLoqNxIg", "libc", 1, "libc", 1, 1, ps4_setvbuf);
LIB_FUNCTION("uodLYyUip20", "libc", 1, "libc", 1, 1, ps4_fclose);
LIB_FUNCTION("rQFVBXp-Cxg", "libc", 1, "libc", 1, 1, ps4_fseek);

View file

@ -3,9 +3,50 @@
#pragma once
#include <functional>
#include "common/logging/log.h"
#include "core/loader/elf.h"
#include "core/loader/symbols_resolver.h"
template <size_t N>
struct StringLiteral {
constexpr StringLiteral(const char (&str)[N]) {
std::copy_n(str, N, value);
}
char value[N];
};
template <StringLiteral name, class F, F f>
struct wrapper_impl;
template <StringLiteral name, class R, class... Args, PS4_SYSV_ABI R (*f)(Args...)>
struct wrapper_impl<name, PS4_SYSV_ABI R (*)(Args...), f> {
static R PS4_SYSV_ABI wrap(Args... args) {
if (std::string_view(name.value) != "scePthreadEqual" &&
std::string_view(name.value) != "sceUserServiceGetEvent" &&
!std::string_view(name.value).contains("mutex") &&
!std::string_view(name.value).contains("Mutex")) {
//LOG_WARNING(Core_Linker, "Function {} called", name.value);
}
if constexpr (std::is_same_v<R, s32> || std::is_same_v<R, u32>) {
const int ret = f(args...);
if (ret != 0 && std::string_view(name.value) != "scePthreadEqual") {
LOG_WARNING(Core_Linker, "Function {} returned {}", name.value, ret);
}
return ret;
}
// stuff
return f(args...);
}
};
template <StringLiteral name, class F, F f>
constexpr auto wrapper = wrapper_impl<name, F, f>::wrap;
#define W(foo) wrapper<#foo, decltype(&foo), foo>
//#define W(foo) foo
#define LIB_FUNCTION(nid, lib, libversion, mod, moduleVersionMajor, moduleVersionMinor, function) \
{ \
Core::Loader::SymbolResolver sr{}; \
@ -16,7 +57,7 @@
sr.module_version_major = moduleVersionMajor; \
sr.module_version_minor = moduleVersionMinor; \
sr.type = Core::Loader::SymbolType::Function; \
auto func = reinterpret_cast<u64>(function); \
auto func = reinterpret_cast<u64>(W(function)); \
sym->AddSymbol(sr, func); \
}

View file

@ -201,18 +201,25 @@ void VideoOutDriver::Flip(std::chrono::microseconds timeout) {
}
// Reset flip label
req.port->buffer_labels[req.index] = 0;
if (req.index != -1) {
req.port->buffer_labels[req.index] = 0;
}
}
bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg,
bool is_eop /*= false*/) {
const auto& buffer = port->buffer_slots[index];
const auto& group = port->groups[buffer.group_index];
auto* frame = renderer->PrepareFrame(group, buffer.address_left);
Vulkan::Frame* frame;
if (index == -1) {
frame = renderer->PrepareBlankFrame();
} else {
const auto& buffer = port->buffer_slots[index];
const auto& group = port->groups[buffer.group_index];
frame = renderer->PrepareFrame(group, buffer.address_left);
}
std::scoped_lock lock{mutex};
if (requests.size() >= port->NumRegisteredBuffers()) {
if (index != -1 && requests.size() >= port->NumRegisteredBuffers()) {
LOG_ERROR(Lib_VideoOut, "Flip queue is full");
return false;
}

View file

@ -124,14 +124,12 @@ s32 PS4_SYSV_ABI sceVideoOutSubmitFlip(s32 handle, s32 bufferIndex, s32 flipMode
LOG_WARNING(Lib_VideoOut, "flipmode = {}", flipMode);
}
ASSERT_MSG(bufferIndex != -1, "Blank output not supported");
if (bufferIndex < -1 || bufferIndex > 15) {
LOG_ERROR(Lib_VideoOut, "Invalid bufferIndex = {}", bufferIndex);
return ORBIS_VIDEO_OUT_ERROR_INVALID_INDEX;
}
if (port->buffer_slots[bufferIndex].group_index < 0) {
if (bufferIndex != -1 && port->buffer_slots[bufferIndex].group_index < 0) {
LOG_ERROR(Lib_VideoOut, "Slot in bufferIndex = {} is not registered", bufferIndex);
return ORBIS_VIDEO_OUT_ERROR_INVALID_INDEX;
}
@ -196,7 +194,6 @@ s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 i
LOG_INFO(Lib_VideoOut, "called");
ASSERT(userId == UserService::ORBIS_USER_SERVICE_USER_ID_SYSTEM || userId == 0);
ASSERT(busType == SCE_VIDEO_OUT_BUS_TYPE_MAIN);
ASSERT(param == nullptr);
if (index != 0) {
LOG_ERROR(Lib_VideoOut, "Index != 0");
@ -259,6 +256,11 @@ s32 sceVideoOutSubmitEopFlip(s32 handle, u32 buf_id, u32 mode, u32 arg, void** u
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVideoOutGetDeviceCapabilityInfo(s32 handle, SceVideoOutDeviceCapabilityInfo *pDeviceCapabilityInfo) {
pDeviceCapabilityInfo->capability = 0;
return ORBIS_OK;
}
void RegisterLib(Core::Loader::SymbolsResolver* sym) {
driver = std::make_unique<VideoOutDriver>(Config::getScreenWidth(), Config::getScreenHeight());
@ -299,6 +301,8 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("U46NwOiJpys", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, sceVideoOutSubmitFlip);
LIB_FUNCTION("SbU3dwp80lQ", "libSceVideoOut", 1, "libSceVideoOut", 1, 1,
sceVideoOutGetFlipStatus);
LIB_FUNCTION("kGVLc3htQE8", "libSceVideoOut", 1, "libSceVideoOut", 1, 1,
sceVideoOutGetDeviceCapabilityInfo);
}
} // namespace Libraries::VideoOut

View file

@ -84,6 +84,10 @@ struct SceVideoOutVblankStatus {
u8 pad1[7] = {};
};
struct SceVideoOutDeviceCapabilityInfo {
u64 capability;
};
void PS4_SYSV_ABI sceVideoOutSetBufferAttribute(BufferAttribute* attribute, PixelFormat pixelFormat,
u32 tilingMode, u32 aspectRatio, u32 width,
u32 height, u32 pitchInPixel);

View file

@ -57,9 +57,6 @@ void Linker::Execute() {
// 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;
}
@ -101,7 +98,7 @@ s32 Linker::LoadModule(const std::filesystem::path& elf_name) {
return -1;
}
auto module = std::make_unique<Module>(elf_name);
auto module = std::make_unique<Module>(elf_name, max_tls_index);
if (!module->IsValid()) {
LOG_ERROR(Core_Linker, "Provided file {} is not valid ELF file", elf_name.string());
return -1;
@ -111,8 +108,22 @@ s32 Linker::LoadModule(const std::filesystem::path& elf_name) {
return m_modules.size() - 1;
}
Module* Linker::FindByAddress(VAddr address) {
for (auto& module : m_modules) {
const VAddr base = module->GetBaseAddress();
if (address >= base && address < base + module->aligned_base_size) {
return module.get();
}
}
return nullptr;
}
void Linker::Relocate(Module* module) {
module->ForEachRelocation([&](elf_relocation* rel, bool isJmpRel) {
module->ForEachRelocation([&](elf_relocation* rel, u32 i, bool isJmpRel) {
const u32 bit_idx = (isJmpRel ? module->dynamic_info.relocation_table_size / sizeof(elf_relocation) : 0) + i;
if (module->TestRelaBit(bit_idx)) {
return;
}
auto type = rel->GetType();
auto symbol = rel->GetSymbol();
auto addend = rel->rel_addend;
@ -167,11 +178,15 @@ void Linker::Relocate(Module* module) {
switch (sym_bind) {
case STB_LOCAL:
symbol_vitrual_addr = rel_base_virtual_addr + sym.st_value;
module->SetRelaBit(bit_idx);
break;
case STB_GLOBAL:
case STB_WEAK: {
rel_name = namesTlb + sym.st_name;
Resolve(rel_name, rel_sym_type, module, &symrec);
if (Resolve(rel_name, rel_sym_type, module, &symrec)) {
// Only set the rela bit if the symbol was actually resolved and not stubbed.
module->SetRelaBit(bit_idx);
}
symbol_vitrual_addr = symrec.virtual_address;
break;
}
@ -203,14 +218,14 @@ const Module* Linker::FindExportedModule(const ModuleInfo& module, const Library
return it == m_modules.end() ? nullptr : it->get();
}
void Linker::Resolve(const std::string& name, Loader::SymbolType sym_type, Module* m,
bool 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;
return false;
}
const LibraryInfo* library = m->FindLibrary(ids[1]);
@ -236,7 +251,7 @@ void Linker::Resolve(const std::string& name, Loader::SymbolType sym_type, Modul
}
if (record) {
*return_info = *record;
return;
return true;
}
const auto aeronid = AeroLib::FindByNid(sr.name.c_str());
@ -249,18 +264,42 @@ void Linker::Resolve(const std::string& name, Loader::SymbolType sym_type, Modul
}
LOG_ERROR(Core_Linker, "Linker: Stub resolved {} as {} (lib: {}, mod: {})", sr.name,
return_info->name, library->name, module->name);
return false;
}
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");
if (dtv_table[0].counter != dtv_generation_counter) {
// Generation counter changed, a dynamic module was either loaded or unloaded.
const u32 old_num_dtvs = dtv_table[1].counter;
ASSERT_MSG(max_tls_index > old_num_dtvs, "Module unloading unsupported");
// Module was loaded, increase DTV table size.
DtvEntry* new_dtv_table = new DtvEntry[max_tls_index + 2];
std::memcpy(new_dtv_table + 2, dtv_table + 2, old_num_dtvs * sizeof(DtvEntry));
new_dtv_table[0].counter = dtv_generation_counter;
new_dtv_table[1].counter = max_tls_index;
delete[] dtv_table;
void* module = (u8*)dtv_table[module_index + 1].pointer + offset;
ASSERT_MSG(module, "DTV allocation is not supported");
return module;
// Update TCB pointer.
GetTcbBase()->tcb_dtv = new_dtv_table;
dtv_table = new_dtv_table;
}
u8* addr = dtv_table[module_index + 1].pointer;
if (!addr) {
// Module was just loaded by above code. Allocate TLS block for it.
Module* module = m_modules[module_index - 1].get();
const u32 init_image_size = module->tls.init_image_size;
u8* dest = reinterpret_cast<u8*>(heap_api_func(module->tls.image_size));
const u8* src = reinterpret_cast<const u8*>(module->tls.image_virtual_addr);
std::memcpy(dest, src, init_image_size);
std::memset(dest + init_image_size, 0, module->tls.image_size - init_image_size);
dtv_table[module_index + 1].pointer = dest;
addr = dest;
}
return addr + offset;
}
void Linker::InitTlsForThread(bool is_primary) {

View file

@ -37,17 +37,32 @@ public:
return m_modules.at(index).get();
}
void RelocateAnyImports(Module* m) {
Relocate(m);
for (auto& module : m_modules) {
const auto imports = module->GetImportModules();
if (std::ranges::contains(imports, m->name, &ModuleInfo::name)) {
Relocate(module.get());
}
}
}
void SetHeapApiFunc(void* func) {
heap_api_func = *reinterpret_cast<HeapApiFunc*>(func);
}
void AdvanceGenerationCounter() noexcept {
dtv_generation_counter++;
}
void* TlsGetAddr(u64 module_index, u64 offset);
void InitTlsForThread(bool is_primary = false);
s32 LoadModule(const std::filesystem::path& elf_name);
Module* FindByAddress(VAddr address);
void Relocate(Module* module);
void Resolve(const std::string& name, Loader::SymbolType type, Module* module,
bool Resolve(const std::string& name, Loader::SymbolType type, Module* module,
Loader::SymbolRecord* return_info);
void Execute();
void DebugDump();
@ -58,7 +73,7 @@ private:
std::mutex mutex;
u32 dtv_generation_counter{1};
size_t static_tls_size{};
size_t max_tls_index{};
u32 max_tls_index{};
HeapApiFunc heap_api_func{};
std::vector<std::unique_ptr<Module>> m_modules;
Loader::SymbolsResolver m_hle_symbols{};

139
src/core/loader/dwarf.cpp Normal file
View file

@ -0,0 +1,139 @@
// SPDX-FileCopyrightText: Copyright (C) 2001-2024 Free Software Foundation, Inc.
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/loader/dwarf.h"
namespace Dwarf {
template <typename T>
T get(uintptr_t addr) {
T val;
memcpy(&val, reinterpret_cast<void*>(addr), sizeof(T));
return val;
}
static uintptr_t getEncodedP(uintptr_t& addr, uintptr_t end, u8 encoding,
uintptr_t datarelBase) {
const uintptr_t startAddr = addr;
const u8 *p = (u8*)addr;
uintptr_t result;
// First get value
switch (encoding & 0x0F) {
case DW_EH_PE_ptr:
result = get<uintptr_t>(addr);
p += sizeof(uintptr_t);
addr = (uintptr_t)p;
break;
case DW_EH_PE_udata2:
result = get<u16>(addr);
p += sizeof(u16);
addr = (uintptr_t)p;
break;
case DW_EH_PE_udata4:
result = get<u32>(addr);
p += sizeof(u32);
addr = (uintptr_t)p;
break;
case DW_EH_PE_udata8:
result = get<u64>(addr);
p += sizeof(u64);
addr = (uintptr_t)p;
break;
case DW_EH_PE_sdata2:
// Sign extend from signed 16-bit value.
result = get<s16>(addr);
p += sizeof(s16);
addr = (uintptr_t)p;
break;
case DW_EH_PE_sdata4:
// Sign extend from signed 32-bit value.
result = get<s32>(addr);
p += sizeof(s32);
addr = (uintptr_t)p;
break;
case DW_EH_PE_sdata8:
result = get<s64>(addr);
p += sizeof(s64);
addr = (uintptr_t)p;
break;
default:
UNREACHABLE_MSG("unknown pointer encoding");
}
// Then add relative offset
switch (encoding & 0x70) {
case DW_EH_PE_absptr:
// do nothing
break;
case DW_EH_PE_pcrel:
result += startAddr;
break;
case DW_EH_PE_textrel:
UNREACHABLE_MSG("DW_EH_PE_textrel pointer encoding not supported");
break;
case DW_EH_PE_datarel:
// DW_EH_PE_datarel is only valid in a few places, so the parameter has a
// default value of 0, and we abort in the event that someone calls this
// function with a datarelBase of 0 and DW_EH_PE_datarel encoding.
if (datarelBase == 0)
UNREACHABLE_MSG("DW_EH_PE_datarel is invalid with a datarelBase of 0");
result += datarelBase;
break;
case DW_EH_PE_funcrel:
UNREACHABLE_MSG("DW_EH_PE_funcrel pointer encoding not supported");
break;
case DW_EH_PE_aligned:
UNREACHABLE_MSG("DW_EH_PE_aligned pointer encoding not supported");
break;
default:
UNREACHABLE_MSG("unknown pointer encoding");
break;
}
if (encoding & DW_EH_PE_indirect) {
result = get<uintptr_t>(result);
}
return result;
}
bool DecodeEHHdr(uintptr_t ehHdrStart, uintptr_t ehHdrEnd, EHHeaderInfo& ehHdrInfo) {
auto p = ehHdrStart;
// Ensure that we don't read data beyond the end of .eh_frame_hdr
if (ehHdrEnd - ehHdrStart < 4) {
// Don't print a message for an empty .eh_frame_hdr (this can happen if
// the linker script defines symbols for it even in the empty case).
if (ehHdrEnd == ehHdrStart) {
return false;
}
LOG_ERROR(Core_Linker, "Unsupported .eh_frame_hdr at {:#x} "
"need at least 4 bytes of data but only got {:#x}",
ehHdrStart, ehHdrEnd - ehHdrStart);
return false;
}
const u8 version = get<u8>(p++);
if (version != 1) {
LOG_CRITICAL(Core_Linker, "Unsupported .eh_frame_hdr version: {:#x} at {:#x}",
version, ehHdrStart);
return false;
}
const u8 eh_frame_ptr_enc = get<u8>(p++);
const u8 fde_count_enc = get<u8>(p++);
ehHdrInfo.table_enc = get<u8>(p++);
ehHdrInfo.eh_frame_ptr = getEncodedP(p, ehHdrEnd, eh_frame_ptr_enc, ehHdrStart);
ehHdrInfo.fde_count =
fde_count_enc == DW_EH_PE_omit
? 0
: getEncodedP(p, ehHdrEnd, fde_count_enc, ehHdrStart);
ehHdrInfo.table = p;
return true;
}
} // namespace Dwarf

41
src/core/loader/dwarf.h Normal file
View file

@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: Copyright (C) 2001-2024 Free Software Foundation, Inc.
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
namespace Dwarf {
enum {
DW_EH_PE_ptr = 0x00,
DW_EH_PE_uleb128 = 0x01,
DW_EH_PE_udata2 = 0x02,
DW_EH_PE_udata4 = 0x03,
DW_EH_PE_udata8 = 0x04,
DW_EH_PE_signed = 0x08,
DW_EH_PE_sleb128 = 0x09,
DW_EH_PE_sdata2 = 0x0A,
DW_EH_PE_sdata4 = 0x0B,
DW_EH_PE_sdata8 = 0x0C,
DW_EH_PE_absptr = 0x00,
DW_EH_PE_pcrel = 0x10,
DW_EH_PE_textrel = 0x20,
DW_EH_PE_datarel = 0x30,
DW_EH_PE_funcrel = 0x40,
DW_EH_PE_aligned = 0x50,
DW_EH_PE_indirect = 0x80,
DW_EH_PE_omit = 0xFF
};
/// Information encoded in the EH frame header.
struct EHHeaderInfo {
uintptr_t eh_frame_ptr;
size_t fde_count;
uintptr_t table;
u8 table_enc;
};
bool DecodeEHHdr(uintptr_t ehHdrStart, uintptr_t ehHdrEnd, EHHeaderInfo &ehHdrInfo);
} // namespace Dwarf

View file

@ -449,6 +449,15 @@ constexpr u32 R_X86_64_JUMP_SLOT = 7; // Create PLT entry
constexpr u32 R_X86_64_RELATIVE = 8; // Adjust by program base
constexpr u32 R_X86_64_DTPMOD64 = 16;
struct eh_frame_hdr {
uint8_t version;
uint8_t eh_frame_ptr_enc;
uint8_t fde_count_enc;
uint8_t table_enc;
uint32_t eh_frame_ptr;
uint32_t fde_count;
};
namespace Core::Loader {
class Elf {

View file

@ -121,7 +121,7 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M
auto it = FindVMA(mapped_addr);
// If the VMA is free and contains the requested mapping we are done.
if (it->second.type == VMAType::Free && it->second.Contains(virtual_addr, size)) {
mapped_addr = alignment > 0 ? Common::AlignUp(base, alignment) : base;
mapped_addr = virtual_addr;
} else {
// Search for the first free VMA that fits our mapping.
while (it->second.type != VMAType::Free || it->second.size < size) {
@ -142,20 +142,21 @@ int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, Mem
MemoryMapFlags flags, void* fd, size_t offset) {
ASSERT(virtual_addr == 0);
virtual_addr = impl.VirtualBase();
const size_t size_aligned = Common::AlignUp(size, 16_KB);
// Find first free area to map the file.
auto it = FindVMA(virtual_addr);
while (it->second.type != VMAType::Free || it->second.size < size) {
while (it->second.type != VMAType::Free || it->second.size < size_aligned) {
it++;
}
ASSERT(it != vma_map.end());
// Map the file.
const VAddr mapped_addr = it->second.base;
impl.MapFile(mapped_addr, size, offset, fd);
impl.MapFile(mapped_addr, Common::AlignDown(size, 4_KB), offset, fd);
// Add virtual memory area
auto& new_vma = AddMapping(mapped_addr, size);
auto& new_vma = AddMapping(mapped_addr, size_aligned);
new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
new_vma.prot = prot;
new_vma.name = "File";

View file

@ -7,6 +7,7 @@
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/aerolib/aerolib.h"
#include "core/loader/dwarf.h"
#include "core/memory.h"
#include "core/module.h"
#include "core/tls.h"
@ -54,10 +55,11 @@ static std::string EncodeId(u64 nVal) {
return enc;
}
Module::Module(const std::filesystem::path& file_) : file{file_} {
Module::Module(const std::filesystem::path& file_, u32& max_tls_index)
: file{file_}, name{file.stem().string()} {
elf.Open(file);
if (elf.IsElfFile()) {
LoadModuleToMemory();
LoadModuleToMemory(max_tls_index);
LoadDynamicInfo();
LoadSymbols();
}
@ -65,13 +67,13 @@ Module::Module(const std::filesystem::path& file_) : file{file_} {
Module::~Module() = default;
void Module::Start(size_t args, const void* argp, void* param) {
LOG_INFO(Core_Linker, "Module started : {}", file.filename().string());
s32 Module::Start(size_t args, const void* argp, void* param) {
LOG_INFO(Core_Linker, "Module started : {}", name);
const VAddr addr = dynamic_info.init_virtual_addr + GetBaseAddress();
reinterpret_cast<EntryFunc>(addr)(args, argp, param);
return reinterpret_cast<EntryFunc>(addr)(args, argp, param);
}
void Module::LoadModuleToMemory() {
void Module::LoadModuleToMemory(u32& max_tls_index) {
static constexpr size_t BlockAlign = 0x1000;
static constexpr u64 TrampolineSize = 8_MB;
@ -84,7 +86,6 @@ void Module::LoadModuleToMemory() {
// Map module segments (and possible TLS trampolines)
auto* memory = Core::Memory::Instance();
void** out_addr = reinterpret_cast<void**>(&base_virtual_addr);
const auto name = file.filename().string();
memory->MapMemory(out_addr, LoadAddress, aligned_base_size + TrampolineSize,
MemoryProt::CpuReadWrite, MemoryMapFlags::Fixed, VMAType::Code, name, true);
LoadAddress += CODE_BASE_INCR * (1 + aligned_base_size / CODE_BASE_INCR);
@ -98,6 +99,17 @@ void Module::LoadModuleToMemory() {
LOG_INFO(Core_Linker, "base_size ..............: {:#018x}", base_size);
LOG_INFO(Core_Linker, "aligned_base_size ......: {:#018x}", aligned_base_size);
const auto add_segment = [this](const elf_program_header& phdr, bool do_map = true) {
const VAddr segment_addr = base_virtual_addr + phdr.p_vaddr;
if (do_map) {
elf.LoadSegment(segment_addr, phdr.p_offset, phdr.p_filesz);
}
auto& segment = info.segments[info.num_segments++];
segment.address = segment_addr;
segment.prot = phdr.p_flags;
segment.size = GetAlignedSize(phdr);
};
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) {
@ -118,13 +130,14 @@ void Module::LoadModuleToMemory() {
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);
add_segment(elf_pheader[i]);
if (elf_pheader[i].p_flags & PF_EXEC) {
PatchTLS(segment_addr, segment_memory_size, c);
PatchTLS(segment_addr, segment_file_size, c);
}
break;
}
case PT_DYNAMIC:
add_segment(elf_pheader[i], false);
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());
@ -147,12 +160,31 @@ void Module::LoadModuleToMemory() {
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]);
if (tls.image_size != 0) {
tls.modid = ++max_tls_index;
}
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;
case PT_GNU_EH_FRAME: {
eh_frame_hdr_addr = elf_pheader[i].p_vaddr;
eh_frame_hdr_size = elf_pheader[i].p_memsz;
const VAddr eh_hdr_start = base_virtual_addr + eh_frame_hdr_addr;
const VAddr eh_hdr_end = eh_hdr_start + eh_frame_hdr_size;
Dwarf::EHHeaderInfo hdr_info;
if (Dwarf::DecodeEHHdr(eh_hdr_start, eh_hdr_end, hdr_info)) {
eh_frame_addr = hdr_info.eh_frame_ptr - base_virtual_addr;
if (eh_frame_hdr_addr > eh_frame_addr) {
eh_frame_size = (eh_frame_hdr_addr - eh_frame_addr);
} else {
eh_frame_size = (aligned_base_size-eh_frame_hdr_addr);
}
}
break;
}
default:
LOG_ERROR(Core_Linker, "Unimplemented type {}", header_type);
}
@ -287,8 +319,8 @@ void Module::LoadDynamicInfo() {
// 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);
LOG_INFO(Core_Linker, "DT_SCE_FINGERPRINT value = {:#018x}", dyn->d_un.d_val);
std::memcpy(info.fingerprint.data(), &dyn->d_un.d_val, sizeof(SCE_DBG_NUM_FINGERPRINT));
break;
case DT_SCE_IMPORT_LIB_ATTR:
// The upper 32-bits should contain the module index multiplied by 0x10000. The lower
@ -304,6 +336,8 @@ void Module::LoadDynamicInfo() {
info.value = dyn->d_un.d_val;
info.name = dynamic_info.str_table + info.name_offset;
info.enc_id = EncodeId(info.id);
const std::string full_name = info.name + ".sprx";
full_name.copy(this->info.name.data(), full_name.size());
break;
};
case DT_SCE_MODULE_ATTR:
@ -321,6 +355,9 @@ void Module::LoadDynamicInfo() {
LOG_INFO(Core_Linker, "unsupported dynamic tag ..........: {:#018x}", dyn->d_tag);
}
}
const u32 relabits_num = dynamic_info.relocation_table_size / sizeof(elf_relocation) +
dynamic_info.jmp_relocation_table_size / sizeof(elf_relocation);
rela_bits.resize((relabits_num + 7) / 8);
}
void Module::LoadSymbols() {
@ -384,6 +421,26 @@ void Module::LoadSymbols() {
symbol_database(import_sym, false);
}
OrbisKernelModuleInfoEx Module::GetModuleInfoEx() const {
return OrbisKernelModuleInfoEx{
.name = info.name,
.tls_index = tls.modid,
.tls_init_addr = tls.image_virtual_addr,
.tls_init_size = tls.init_image_size,
.tls_size = tls.image_size,
.tls_offset = tls.offset,
.tls_align = tls.align,
.init_proc_addr = base_virtual_addr + dynamic_info.init_virtual_addr,
.fini_proc_addr = base_virtual_addr + dynamic_info.fini_virtual_addr,
.eh_frame_hdr_addr = eh_frame_hdr_addr,
.eh_frame_addr = eh_frame_addr,
.eh_frame_hdr_size = eh_frame_hdr_size,
.eh_frame_size = eh_frame_size,
.segments = info.segments,
.segment_count = info.num_segments,
};
}
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) {

View file

@ -11,6 +11,46 @@
namespace Core {
static constexpr size_t SCE_DBG_MAX_NAME_LENGTH = 256;
static constexpr size_t SCE_DBG_MAX_SEGMENTS = 4;
static constexpr size_t SCE_DBG_NUM_FINGERPRINT = 20;
struct OrbisKernelModuleSegmentInfo {
VAddr address;
u64 size;
s32 prot;
};
struct OrbisKernelModuleInfo {
u64 st_size = sizeof(OrbisKernelModuleInfo);
std::array<char, SCE_DBG_MAX_NAME_LENGTH> name;
std::array<OrbisKernelModuleSegmentInfo, SCE_DBG_MAX_SEGMENTS> segments;
u32 num_segments;
std::array<u8, SCE_DBG_NUM_FINGERPRINT> fingerprint;
};
struct OrbisKernelModuleInfoEx {
u64 st_size = sizeof(OrbisKernelModuleInfoEx);
std::array<char, SCE_DBG_MAX_NAME_LENGTH> name;
s32 id;
u32 tls_index;
VAddr tls_init_addr;
u32 tls_init_size;
u32 tls_size;
u32 tls_offset;
u32 tls_align;
VAddr init_proc_addr;
VAddr fini_proc_addr;
u64 reserved1;
u64 reserved2;
VAddr eh_frame_hdr_addr;
VAddr eh_frame_addr;
u32 eh_frame_hdr_size;
u32 eh_frame_size;
std::array<OrbisKernelModuleSegmentInfo, SCE_DBG_MAX_SEGMENTS> segments;
u32 segment_count;
};
struct ModuleInfo {
bool operator==(const ModuleInfo& other) const {
return version_major == other.version_major && version_minor == other.version_minor &&
@ -46,12 +86,12 @@ struct LibraryInfo {
};
struct ThreadLocalImage {
u64 align;
u64 image_size;
u64 offset;
u32 align;
u32 image_size;
u32 offset;
u32 modid;
VAddr image_virtual_addr;
u64 init_image_size;
u32 init_image_size;
};
struct DynamicModuleInfo {
@ -100,7 +140,7 @@ using ModuleFunc = int (*)(size_t, const void*);
class Module {
public:
explicit Module(const std::filesystem::path& file);
explicit Module(const std::filesystem::path& file, u32& max_tls_index);
~Module();
VAddr GetBaseAddress() const noexcept {
@ -111,6 +151,10 @@ public:
return base_virtual_addr + elf.GetElfEntry();
}
OrbisKernelModuleInfo GetModuleInfo() const noexcept {
return info;
}
bool IsValid() const noexcept {
return base_virtual_addr != 0;
}
@ -151,33 +195,49 @@ public:
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);
func(&dynamic_info.relocation_table[i], 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);
func(&dynamic_info.jmp_relocation_table[i], i, true);
}
}
void Start(size_t args, const void* argp, void* param);
void LoadModuleToMemory();
void SetRelaBit(u32 index) {
rela_bits[index >> 3] |= (1 << (index & 7));
}
bool TestRelaBit(u32 index) const {
return (rela_bits[index >> 3] >> (index & 7)) & 1;
}
s32 Start(size_t args, const void* argp, void* param);
void LoadModuleToMemory(u32& max_tls_index);
void LoadDynamicInfo();
void LoadSymbols();
OrbisKernelModuleInfoEx GetModuleInfoEx() const;
const ModuleInfo* FindModule(std::string_view id);
const LibraryInfo* FindLibrary(std::string_view id);
public:
std::filesystem::path file;
std::string name;
Loader::Elf elf;
u64 aligned_base_size{};
VAddr base_virtual_addr{};
VAddr proc_param_virtual_addr{};
VAddr eh_frame_hdr_addr{};
VAddr eh_frame_addr{};
u32 eh_frame_hdr_size{};
u32 eh_frame_size{};
DynamicModuleInfo dynamic_info{};
std::vector<u8> m_dynamic;
std::vector<u8> m_dynamic_data;
Loader::SymbolsResolver export_sym;
Loader::SymbolsResolver import_sym;
ThreadLocalImage tls{};
OrbisKernelModuleInfo info{};
std::vector<u8> rela_bits;
};
} // namespace Core

View file

@ -13,7 +13,7 @@ namespace Core {
union DtvEntry {
size_t counter;
void* pointer;
u8* pointer;
};
struct Tcb {

View file

@ -98,7 +98,11 @@ void Emulator::Run(const std::filesystem::path& file) {
if (std::filesystem::is_directory(sce_module_folder)) {
for (const auto& entry : std::filesystem::directory_iterator(sce_module_folder)) {
if (entry.path().filename() == "libc.prx" ||
entry.path().filename() == "libSceFios2.prx") {
entry.path().filename() == "libSceFios2.prx" /*||
entry.path().filename() == "libSceAudioLatencyEstimation.prx" ||
entry.path().filename() == "libSceJobManager.prx" ||
entry.path().filename() == "libSceNpToolkit2.prx" ||
entry.path().filename() == "libSceS3DConversion.prx"*/) {
found = true;
LOG_INFO(Loader, "Loading {}", entry.path().string().c_str());
linker->LoadModule(entry.path());
@ -115,10 +119,11 @@ void Emulator::Run(const std::filesystem::path& file) {
std::jthread([this](std::stop_token stop_token) { linker->Execute(); });
// Begin main window loop until the application exits
static constexpr std::chrono::microseconds FlipPeriod{100000};
static constexpr std::chrono::milliseconds FlipPeriod{16};
while (window.isOpen()) {
window.waitEvent();
std::this_thread::sleep_for(FlipPeriod);
Libraries::VideoOut::Flip(FlipPeriod);
Libraries::VideoOut::Vblank();
}
@ -127,10 +132,11 @@ void Emulator::Run(const std::filesystem::path& file) {
}
void Emulator::LoadSystemModules(const std::filesystem::path& file) {
return;
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" || entry.path().filename() == "libSceRtc.sprx" ||
entry.path().filename() == "libSceDiscMap.sprx") {
if (/*entry.path().filename() == "libSceNgs2.sprx" || entry.path().filename() == "libSceRtc.sprx" ||
entry.path().filename() == "libSceDiscMap.sprx" || entry.path().filename() == "libSceLibcInternal.sprx"*/false) {
LOG_INFO(Loader, "Loading {}", entry.path().string().c_str());
linker->LoadModule(entry.path());
}

View file

@ -6,6 +6,7 @@
#include "core/file_format/splash.h"
#include "core/libraries/system/systemservice.h"
#include "sdl_window.h"
#include "video_core/texture_cache/image.h"
#include "video_core/renderer_vulkan/renderer_vulkan.h"
#include "video_core/renderer_vulkan/vk_rasterizer.h"
@ -198,6 +199,11 @@ Frame* RendererVulkan::PrepareFrame(const Libraries::VideoOut::BufferAttributeGr
return PrepareFrameInternal(image);
}
Frame* RendererVulkan::PrepareBlankFrame() {
auto& image = texture_cache.GetImage(VideoCore::NULL_IMAGE_ID);
return PrepareFrameInternal(image);
}
Frame* RendererVulkan::PrepareFrameInternal(VideoCore::Image& image) {
// Request a free presentation frame.
Frame* frame = GetRenderFrame();

View file

@ -39,6 +39,7 @@ public:
Frame* PrepareFrame(const Libraries::VideoOut::BufferAttributeGroup& attribute,
VAddr cpu_address);
Frame* PrepareBlankFrame();
bool ShowSplash(Frame* frame = nullptr);
void Present(Frame* frame);

View file

@ -88,6 +88,8 @@ private:
vk::Image image{};
};
constexpr SlotId NULL_IMAGE_ID{0};
struct Image {
explicit Image(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler,
const ImageInfo& info, VAddr cpu_addr);

View file

@ -55,6 +55,11 @@ public:
/// Retrieves the sampler that matches the provided S# descriptor.
[[nodiscard]] vk::Sampler GetSampler(const AmdGpu::Sampler& sampler);
/// Retrieves the image with the specified id.
[[nodiscard]] Image& GetImage(ImageId id) {
return slot_images[id];
}
private:
ImageView& RegisterImageView(Image& image, const ImageViewInfo& view_info);