mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 00:22:57 -05:00
The three JIT shared memory exports (avvJ3J0H0EY, MR221Mwo0Pc,
YKT49TOLQWs) are absent imports of three homebrew titles that cannot
finish loading without them. Predicted next stops (registry order):
- VLC PPSA85300 after getdtablesize (idx 354):
"FAIL: unresolved ELF import YKT49TOLQWs from libkernel.prx"
(Map idx 452, Create idx 462)
- XPSemu PPSA97358 after tcsendbreak (idx 214):
"FAIL: unresolved ELF import avvJ3J0H0EY from libkernel.prx"
(Create idx 398, Map idx 399)
- RetroArch PPSA99169:
"FAIL: unresolved ELF import avvJ3J0H0EY from libkernel.prx"
(Create 476, Alias 477, Map 478)
XPSemu and VLC (ps5/compat/mmap.c, __wrap_mmap) create an object for
every anonymous PROT_EXEC mapping (QEMU's TCG buffer) and map it with
the same protection, falling back to flexible memory on any non-zero
return. RetroArch's eboot runs a probe: Create("ps5-platform-probe",
0x10000, 7, &fd), Alias(fd, 3, &alias), Map(fd, 5, &address), then logs
"jit create=0x%08x alias=0x%08x map=0x%08x".
Signatures from Orbital cc408367 tools/ida/db_types.json (name, len,
maxProt, fdOut; fd, maxProt, fdOut; fd, prot, startOut), matching
ps4sdk 4df9d001 include/sce/kernel.h, ps5-jar-loader 8034177e
LibKernel.java and the three titles' call sites. SharpProspero 5dfdb4b0
declares the alias as returning the descriptor; the RetroArch call site
passes an out pointer.
The object is a host anonymous file (memfd on Linux, a delete-on-close
temporary file mapped as a PAGE_EXECUTE_READWRITE section on Windows),
the alias a duplicate descriptor, and each map maps the whole object
through the direct memory placement path, so a write through one view
is visible through the others. Bad lengths and protection bits are
EINVAL, protection past the descriptor's maximum is EACCES (FreeBSD 11
vm_map_protect), unknown descriptors are EBADF; close, dup and dup2
keep the descriptor table in step. Unverified details are recorded in
TechnicalDebt.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
145 lines
6.0 KiB
C++
145 lines
6.0 KiB
C++
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
#include "prx/libkernel/KernelErrors.hpp"
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <source_location>
|
|
|
|
extern "C" {
|
|
int APS5_VABI sceKernelJitCreateSharedMemory(const char*, std::size_t, int, int*);
|
|
int APS5_VABI sceKernelJitCreateAliasOfSharedMemory(int, int, int*);
|
|
int APS5_VABI sceKernelJitMapSharedMemory(int, int, void**);
|
|
int APS5_VABI sceKernelMunmap(void*, std::size_t);
|
|
int APS5_VABI close_nid_postfix(int);
|
|
int APS5_VABI dup_nid_postfix(int);
|
|
int APS5_VABI shm_open_nid_postfix(const char*, int, int);
|
|
}
|
|
|
|
namespace {
|
|
|
|
constexpr std::size_t Size = 0x10000;
|
|
constexpr int Read = 0x1;
|
|
constexpr int Write = 0x2;
|
|
constexpr int Execute = 0x4;
|
|
|
|
void Require(bool condition, std::source_location location = std::source_location::current()) {
|
|
if (condition) return;
|
|
std::fprintf(stderr, "requirement failed at line %u\n", static_cast<unsigned>(location.line()));
|
|
std::abort();
|
|
}
|
|
|
|
int Create(const char* name, int maxProt) {
|
|
int fd = -1;
|
|
Require(sceKernelJitCreateSharedMemory(name, Size, maxProt, &fd) == 0);
|
|
Require(fd >= 0);
|
|
return fd;
|
|
}
|
|
|
|
void* Map(int fd, int prot, void* hint = nullptr) {
|
|
void* address = hint;
|
|
Require(sceKernelJitMapSharedMemory(fd, prot, &address) == 0);
|
|
Require(address != nullptr && (reinterpret_cast<std::uintptr_t>(address) & 0x3fff) == 0);
|
|
return address;
|
|
}
|
|
|
|
int Call(const void* code) {
|
|
using Function = int (*)();
|
|
Function function;
|
|
std::memcpy(&function, &code, sizeof(function));
|
|
return function();
|
|
}
|
|
|
|
void WriteReturn(void* code, std::uint32_t value) {
|
|
unsigned char bytes[] = {0xb8, 0, 0, 0, 0, 0xc3};
|
|
std::memcpy(bytes + 1, &value, sizeof(value));
|
|
std::memcpy(code, bytes, sizeof(bytes));
|
|
}
|
|
|
|
void WriteThroughAliasRunsThroughExecutableView() {
|
|
const int fd = Create(nullptr, Read | Write | Execute);
|
|
int alias = -1;
|
|
Require(sceKernelJitCreateAliasOfSharedMemory(fd, Read | Write, &alias) == 0);
|
|
Require(alias >= 0 && alias != fd);
|
|
auto* writable = static_cast<unsigned char*>(Map(alias, Read | Write));
|
|
auto* executable = static_cast<unsigned char*>(Map(fd, Read | Execute));
|
|
Require(writable != executable);
|
|
Require(executable[0] == 0 && executable[Size - 1] == 0);
|
|
WriteReturn(writable, 0x12345678);
|
|
Require(std::memcmp(writable, executable, 6) == 0);
|
|
Require(Call(executable) == 0x12345678);
|
|
WriteReturn(writable + 0x4000, 0x0badcafe);
|
|
Require(Call(executable + 0x4000) == 0x0badcafe);
|
|
writable[Size - 1] = 0x5a;
|
|
Require(executable[Size - 1] == 0x5a);
|
|
Require(close_nid_postfix(fd) == 0);
|
|
auto* again = static_cast<unsigned char*>(Map(alias, Read));
|
|
Require(again[Size - 1] == 0x5a);
|
|
Require(sceKernelMunmap(again, Size) == 0);
|
|
Require(sceKernelMunmap(writable, Size) == 0);
|
|
Require(Call(executable) == 0x12345678);
|
|
Require(sceKernelMunmap(executable, Size) == 0);
|
|
Require(close_nid_postfix(alias) == 0);
|
|
}
|
|
|
|
void OneViewWritesAndRuns() {
|
|
const int fd = Create("ps5-platform-probe", Read | Write | Execute);
|
|
void* hint = reinterpret_cast<void*>(std::uintptr_t{0x1000000000});
|
|
auto* code = static_cast<unsigned char*>(Map(fd, Read | Write | Execute, hint));
|
|
Require(reinterpret_cast<std::uintptr_t>(code) >= 0x1000000000);
|
|
WriteReturn(code, 42);
|
|
Require(Call(code) == 42);
|
|
const int duplicate = dup_nid_postfix(fd);
|
|
Require(duplicate >= 0);
|
|
Require(close_nid_postfix(fd) == 0);
|
|
auto* view = static_cast<unsigned char*>(Map(duplicate, Read | Execute));
|
|
Require(Call(view) == 42);
|
|
Require(sceKernelMunmap(view, Size) == 0);
|
|
Require(sceKernelMunmap(code, Size) == 0);
|
|
Require(close_nid_postfix(duplicate) == 0);
|
|
}
|
|
|
|
void InvalidArgumentsAreRejected() {
|
|
int fd = -1;
|
|
Require(sceKernelJitCreateSharedMemory(nullptr, 0, Read | Write | Execute, &fd) == SCE_KERNEL_ERROR_EINVAL);
|
|
Require(sceKernelJitCreateSharedMemory(nullptr, 0x1000, Read | Write | Execute, &fd) == SCE_KERNEL_ERROR_EINVAL);
|
|
Require(sceKernelJitCreateSharedMemory(nullptr, Size + 0x2000, Read | Write | Execute, &fd) == SCE_KERNEL_ERROR_EINVAL);
|
|
Require(sceKernelJitCreateSharedMemory(nullptr, Size, 0x10, &fd) == SCE_KERNEL_ERROR_EINVAL);
|
|
Require(sceKernelJitCreateSharedMemory(nullptr, Size, Read | Write | Execute, nullptr) == SCE_KERNEL_ERROR_EINVAL);
|
|
Require(fd == -1);
|
|
|
|
fd = Create(nullptr, Read | Execute);
|
|
int alias = -1;
|
|
void* address = nullptr;
|
|
Require(sceKernelJitCreateAliasOfSharedMemory(fd, Read | Write, &alias) == SCE_KERNEL_ERROR_EACCES);
|
|
Require(sceKernelJitCreateAliasOfSharedMemory(fd, 0x20, &alias) == SCE_KERNEL_ERROR_EINVAL);
|
|
Require(sceKernelJitCreateAliasOfSharedMemory(fd, Read, nullptr) == SCE_KERNEL_ERROR_EINVAL);
|
|
Require(sceKernelJitCreateAliasOfSharedMemory(-1, Read, &alias) == SCE_KERNEL_ERROR_EBADF);
|
|
Require(alias == -1);
|
|
Require(sceKernelJitMapSharedMemory(fd, Read | Write, &address) == SCE_KERNEL_ERROR_EACCES);
|
|
Require(sceKernelJitMapSharedMemory(fd, 0x8, &address) == SCE_KERNEL_ERROR_EINVAL);
|
|
Require(sceKernelJitMapSharedMemory(fd, Read, nullptr) == SCE_KERNEL_ERROR_EINVAL);
|
|
Require(sceKernelJitMapSharedMemory(-1, Read, &address) == SCE_KERNEL_ERROR_EBADF);
|
|
Require(address == nullptr);
|
|
Require(sceKernelJitCreateAliasOfSharedMemory(fd, Read, &alias) == 0);
|
|
Require(sceKernelJitMapSharedMemory(alias, Read | Execute, &address) == SCE_KERNEL_ERROR_EACCES);
|
|
Require(close_nid_postfix(alias) == 0);
|
|
Require(close_nid_postfix(fd) == 0);
|
|
Require(sceKernelJitMapSharedMemory(fd, Read, &address) == SCE_KERNEL_ERROR_EBADF);
|
|
Require(sceKernelJitCreateAliasOfSharedMemory(alias, Read, &alias) == SCE_KERNEL_ERROR_EBADF);
|
|
|
|
const int shared = shm_open_nid_postfix(reinterpret_cast<const char*>(1), 0x2, 0600);
|
|
Require(shared >= 0);
|
|
Require(sceKernelJitMapSharedMemory(shared, Read, &address) == SCE_KERNEL_ERROR_EBADF);
|
|
Require(close_nid_postfix(shared) == 0);
|
|
}
|
|
|
|
}
|
|
|
|
int main() {
|
|
WriteThroughAliasRunsThroughExecutableView();
|
|
OneViewWritesAndRuns();
|
|
InvalidArgumentsAreRejected();
|
|
}
|