From f80a2f55359dcd62276c0dc06f5a30bd592eb85b Mon Sep 17 00:00:00 2001 From: Eli Yarson Date: Mon, 12 Oct 2026 15:07:05 -0300 Subject: [PATCH] fix(libkernel): resolve original .prx names to relinked .guest.prx in stat, open and reachability Titles check for a plugin with sceKernelStat before loading it through the engine path. The relinker emits only X.prx.guest.prx, so the check failed and PSNCore started with the raw (0, NULL) arguments. Share the dlopen mapping with sceKernelStat, read-only sceKernelOpen and sceKernelCheckReachability when the original name is absent. --- core/libs/prx/libc/include/General.hpp | 1 + core/libs/prx/libc/src/General.cpp | 7 +++++ .../prx/libkernel/File/include/NativeStat.hpp | 1 + .../prx/libkernel/File/src/NativeStat.cpp | 6 ++++ core/libs/prx/libkernel/File/src/Open.cpp | 2 ++ core/libs/prx/libkernel/File/src/Stdio.cpp | 1 + .../libkernel/Module/src/DynamicLoader.cpp | 11 ++------ core/libs/tests/GuestFilesystem.cpp | 28 +++++++++++++++++++ 8 files changed, 48 insertions(+), 9 deletions(-) diff --git a/core/libs/prx/libc/include/General.hpp b/core/libs/prx/libc/include/General.hpp index 7de9ed967..bb4a36870 100644 --- a/core/libs/prx/libc/include/General.hpp +++ b/core/libs/prx/libc/include/General.hpp @@ -14,6 +14,7 @@ extern "C" void CxaFinalize_nid_no_patch(void* dsoHandle); extern "C" bool StopAtPrefixLetter_nid_no_patch(const char* str, char** endptr, int base); extern "C" std::filesystem::path ResolvePath_nid_no_patch(const char* path); +extern "C" std::filesystem::path RelinkedModulePath_nid_no_patch(const std::filesystem::path& path); extern "C" int SetWorkingDirectory_nid_no_patch(const char* resolved); extern "C" bool FinalPathComponentIsDot_nid_no_patch(const char* path); // Guest path aliases: a guest prefix such as "/savedata0" (a save-data mount point) resolves to a host diff --git a/core/libs/prx/libc/src/General.cpp b/core/libs/prx/libc/src/General.cpp index a2a57370b..3af0807f5 100644 --- a/core/libs/prx/libc/src/General.cpp +++ b/core/libs/prx/libc/src/General.cpp @@ -345,6 +345,13 @@ extern "C" std::filesystem::path ResolvePath_nid_no_patch(const char* path) { return Resolve(state, path); } +extern "C" std::filesystem::path RelinkedModulePath_nid_no_patch(const std::filesystem::path& path) { + auto relinked = path; + relinked += ".guest.prx"; + std::error_code error; + return std::filesystem::is_regular_file(relinked, error) ? relinked : path; +} + extern "C" int SetWorkingDirectory_nid_no_patch(const char* resolved) { if (resolved == nullptr) return 14; auto& state = Directories(); diff --git a/core/libs/prx/libkernel/File/include/NativeStat.hpp b/core/libs/prx/libkernel/File/include/NativeStat.hpp index 5715f0211..0d0e00369 100644 --- a/core/libs/prx/libkernel/File/include/NativeStat.hpp +++ b/core/libs/prx/libkernel/File/include/NativeStat.hpp @@ -6,6 +6,7 @@ namespace File { +std::filesystem::path PathOrRelinkedModule(const std::filesystem::path& nativePath); void FillFileStat(const std::filesystem::path& nativePath, FileStat* sb); void FillFileStat(int nativeDescriptor, FileStat* sb); bool FillFileStatFromDescriptor(int fd, FileStat* sb); diff --git a/core/libs/prx/libkernel/File/src/NativeStat.cpp b/core/libs/prx/libkernel/File/src/NativeStat.cpp index 4495fb59f..5e6e304a7 100644 --- a/core/libs/prx/libkernel/File/src/NativeStat.cpp +++ b/core/libs/prx/libkernel/File/src/NativeStat.cpp @@ -125,6 +125,12 @@ static void CopyNativeStat(const NativeStat& st, FileStat* sb) { namespace File { +std::filesystem::path PathOrRelinkedModule(const std::filesystem::path& nativePath) { + std::error_code error; + if (std::filesystem::exists(std::filesystem::symlink_status(nativePath, error))) return nativePath; + return RelinkedModulePath_nid_no_patch(nativePath); +} + void FillFileStat(const std::filesystem::path& nativePath, FileStat* sb) { NativeStat st{}; if (DoStat(nativePath, &st) != 0) { diff --git a/core/libs/prx/libkernel/File/src/Open.cpp b/core/libs/prx/libkernel/File/src/Open.cpp index f9a6944ca..f3df2564d 100644 --- a/core/libs/prx/libkernel/File/src/Open.cpp +++ b/core/libs/prx/libkernel/File/src/Open.cpp @@ -208,6 +208,7 @@ int APS5_VABI sceKernelOpen(const char* path, int flags, std::uint16_t mode) { } std::filesystem::path native; if (const int error = ResolvePathError_nid_no_patch(path, native)) return SceKernelError(error); + if ((flags & (SCE_KERNEL_O_ACCMODE | SCE_KERNEL_O_CREAT)) == SCE_KERNEL_O_RDONLY) native = File::PathOrRelinkedModule(native); return OpenNative(native, flags, mode); } @@ -287,6 +288,7 @@ int APS5_VABI sceKernelStat(const char* path, FileStat* sb) { if (*path == '\0') return SceErrorFromErrno(2); std::filesystem::path native; if (const int error = ResolvePathError_nid_no_patch(path, native)) return SceKernelError(error); + native = File::PathOrRelinkedModule(native); std::error_code error; constexpr int GuestEnotdir = 20; const auto status = std::filesystem::status(native, error); diff --git a/core/libs/prx/libkernel/File/src/Stdio.cpp b/core/libs/prx/libkernel/File/src/Stdio.cpp index 1c5a227a9..479f15e86 100644 --- a/core/libs/prx/libkernel/File/src/Stdio.cpp +++ b/core/libs/prx/libkernel/File/src/Stdio.cpp @@ -789,6 +789,7 @@ int APS5_VABI sceKernelCheckReachability(const char* path) { if (std::strlen(path) > 255) return SCE_KERNEL_ERROR_ENAMETOOLONG; std::filesystem::path native; if (const int error = ResolvePathError_nid_no_patch(path, native)) return SceKernelError(error); + native = File::PathOrRelinkedModule(native); std::error_code error; return std::filesystem::exists(native, error) ? 0 : SCE_KERNEL_ERROR_ENOENT; } diff --git a/core/libs/prx/libkernel/Module/src/DynamicLoader.cpp b/core/libs/prx/libkernel/Module/src/DynamicLoader.cpp index 769bf79ca..1dc372c38 100644 --- a/core/libs/prx/libkernel/Module/src/DynamicLoader.cpp +++ b/core/libs/prx/libkernel/Module/src/DynamicLoader.cpp @@ -162,13 +162,6 @@ static void InitializeDeferredModule(HMODULE native) { } #endif -static std::filesystem::path RelinkedModulePath(const std::filesystem::path& path) { - auto relinked = path; - relinked += ".guest.prx"; - std::error_code error; - return std::filesystem::is_regular_file(relinked, error) ? relinked : path; -} - void* APS5_VABI dlopen_nid_postfix(const char* path, int flags) { if ((flags & ~0x103) || (flags & 3) == 0 || (flags & 3) == 3) { Error("dlopen: unsupported flags"); return nullptr; @@ -182,7 +175,7 @@ void* APS5_VABI dlopen_nid_postfix(const char* path, int flags) { module->owned = false; } else { if (!*path) { Error("dlopen: empty module path"); return nullptr; } - const auto resolved = RelinkedModulePath(ResolvePath_nid_no_patch(path)); + const auto resolved = RelinkedModulePath_nid_no_patch(ResolvePath_nid_no_patch(path)); module->native = LoadLibraryExW(resolved.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH); if (module->native) InitializeDeferredModule(static_cast(module->native)); } @@ -192,7 +185,7 @@ void* APS5_VABI dlopen_nid_postfix(const char* path, int flags) { Error(message); return nullptr; } #else - const auto resolved = path ? RelinkedModulePath(ResolvePath_nid_no_patch(path)).string() : std::string{}; + const auto resolved = path ? RelinkedModulePath_nid_no_patch(ResolvePath_nid_no_patch(path)).string() : std::string{}; const int nativeFlags = ((flags & 3) == 1 ? RTLD_LAZY : RTLD_NOW) | ((flags & 0x100) ? RTLD_GLOBAL : RTLD_LOCAL); module->native = ::dlopen(path ? resolved.c_str() : nullptr, nativeFlags); diff --git a/core/libs/tests/GuestFilesystem.cpp b/core/libs/tests/GuestFilesystem.cpp index d32843235..484b7ee24 100644 --- a/core/libs/tests/GuestFilesystem.cpp +++ b/core/libs/tests/GuestFilesystem.cpp @@ -174,8 +174,36 @@ static void TestParentTraversal() { std::filesystem::remove_all(root); } +static void TestRelinkedModuleNames() { + const auto root = std::filesystem::path("anyps5-relinked-names-" + + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count())); + Require(std::filesystem::create_directories(root)); + const auto original = (root / "Plugin.prx").generic_string(); + const auto relinked = original + ".guest.prx"; + FileStat status{}; + Require(sceKernelStat(original.c_str(), &status) == static_cast(0x80020002u)); + Require(sceKernelCheckReachability(original.c_str()) == static_cast(0x80020002u)); + Require(sceKernelOpen(original.c_str(), 0, 0) == static_cast(0x80020002u)); + { std::ofstream file(relinked); file << "relinked"; } + Require(sceKernelStat(original.c_str(), &status) == 0 && status.st_size == 8); + Require(stat_nid_postfix(original.c_str(), &status) == 0 && status.st_size == 8); + Require(sceKernelCheckReachability(original.c_str()) == 0); + const int opened = sceKernelOpen(original.c_str(), 0, 0); + Require(opened >= 0 && sceKernelClose(opened) == 0); + Require(sceKernelStat((root / "Other.prx").generic_string().c_str(), &status) == static_cast(0x80020002u)); + Require(!std::filesystem::exists(original)); + { std::ofstream file(original); file << "original-bytes"; } + Require(sceKernelStat(original.c_str(), &status) == 0 && status.st_size == 14); + Require(std::filesystem::remove(original)); + const int created = sceKernelOpen(original.c_str(), 0x601, 0600); + Require(created >= 0 && sceKernelClose(created) == 0); + Require(std::filesystem::file_size(original) == 0 && std::filesystem::file_size(relinked) == 8); + std::filesystem::remove_all(root); +} + int main() { TestParentTraversal(); + TestRelinkedModuleNames(); Require(chmod_nid_postfix(nullptr, 0600) == -1 && *__error_nid_postfix() == 14); Require(sceKernelChmod_nid_postfix(nullptr, 0600) == static_cast(0x8002000eu)); Require(chmod_nid_postfix("", 0600) == -1 && *__error_nid_postfix() == 2);