Repository navigation
Linux: a Unity title's il2cpp code starts PSNCore.prx with sceKernelLoadStartModule(path, 0, NULL), so its start fails with "PSNCore is an old version" and the title faults (PPSA13579) #4225
Copy link
Copy link
Closed
Description
Symptom
Blasphemous 2 (PPSA13579) on Linux, main a11c8f7f7 + #3295, stops at start-up right after printing:
PSNCore is an old version that cannot be used by the current player runtime.
Plugin args version, found 0000, expected 0200
then faults inside PSNCore.prx reading through a null argp (exit 139, 3 of 3 runs). It is the same validator as #918, but a different cause: here the module is started with no arguments at all.
What the title does
- The engine (eboot) loads
PS5Utilwith(0, NULL)andIl2CppUserAssemblieswith a 52-byte block:sceKernelLoadStartModule(..., 0x34, argp, ...). The block iscalloc(1, 0x34)in the eboot, kept in a global and never freed during the runs we observed. - At that load the block is still all zero. Later, a Unity platform-init function in the eboot fills it:
{0x10, 0x200, <eboot interface getter>, ...}. Il2CppUserAssemblieshas no module start, so it ignores the block.- Managed code then loads
/app0/Media/Plugins/PSNCore.prxthrough a generic loader shim inIl2CppUserAssemblies, which callssceKernelLoadStartModulewithargs,argp,flagsandopthard-coded to zero. PSNCore's module start requiresargs >= 16,argp[0] == 0x10andargp[1] == 0x200, storesargp[2](the engine's interface getter) and returns 0. With(0, NULL)it prints the message above and then reads throughargp.- Later,
PSNCorestartsPSNCommonwith a block on its own stack (args = 1). The title also loadslibfmodandlibfmodstudiowith(0, NULL).
Evidence
- Patching the
PSNCorecall in gdb to pass the engine's block (at that point already filled) letsPSNCorestart without the message, and the title continues. - Passing a copy taken when
Il2CppUserAssembliesloaded (all zero) avoids the fault, butPSNCorestill prints "found 0000" and runs without the engine's interface.
Unknown
We have not established how the console's libkernel hands PSNCore the engine's block for this zero-argument load. The local change below reproduces what the title needs, but it is a heuristic, not a measured console behaviour.
Local change used to measure this
Branch on a11c8f7f7 (not submitted; PR intake is paused):
sceKernelLoadStartModuleremembers the first non-empty(args, argp)pair from any thread and passes it, as the same pointer (not a copy), to later loads that pass(0, NULL).- A block on the calling thread's stack is passed to its own load but not remembered.
- The
guest_load_start_module_argstest covers the replay, the first-not-last rule and the stack case. It is Linux-only: on Windows the test fixture would need to link libkernel and export the pending-arguments accessor, asguest_module_fixturedoes, which I could not build or verify.
Results, 3 runs × 240 s each (Linux, CachyOS, RTX 3080 Ti, NVIDIA 610.57.04):
| Build | Result |
|---|---|
a11c8f7f7 + #3295 |
3/3 SIGSEGV at PSNCore start |
| + replay of a copy | 3/3 reach the title screen; "found 0000" in 3/3 |
| + replay of the live pointer | 3/3 reach the title screen ("Press ✕ to continue", ~60 FPS); no message |
Related
- sceKernelLoadStartModule discards the module parameter block, so modules that validate it fault at start-up #918 (closed): same
PSNCore/PSNvalidator. That title passed a block, and fix(libkernel): pass module args and argp to runtime-loaded modules on Linux #1102 fixed passing it on Linux. This title passes none. - fix(libkernel): pass module args and argp to runtime-loaded modules on Linux #1102 (merged), feat(libkernel): start runtime-loaded modules with their arguments on Linux #2323 (closed as a duplicate of fix(libkernel): pass module args and argp to runtime-loaded modules on Linux #1102), fix(relinker): pass module start arguments to macOS guest initializers #3492 (merged, macOS): pass a load's own
args/argp. None of them covers(0, NULL)loads. - fix(libkernel): keep host standard streams open on guest close #3295 (closed by the intake pause): needed for this title to get this far (see Linux: sceKernelClose(0) closes the host's stdin, the next opened file gets descriptor 0, and a later close(0) closes it mid-read (PPSA13579); fixed by #3295 #4223).
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
eliyarson commentedon Oct 12, 2026
Follow-up, as promised on the draft #4296. I checked whether PPSA13579's engine skips a plugin preload because of something AnyPS5 answers differently from the console. It doesn't:
preloadedPluginslist inBuildSettings. In this title'sMedia/globalgamemanagersthat list is empty (count 0), andPSNCore,lib_burst_generatedand.prxappear nowhere inglobalgamemanagers,level0orresources.assets.BurstCompilerService::LoadBurstLibraryicall. Under gdb up to the title screen neither the loader nor that icall ever runs, and there is no directory listing of/app0/Media/Pluginsor manifest read.sceKernelLoadStartModulecalls arePS5Util(0, NULL),Il2CppUserAssemblies(52, the engine block),PSNCore(0, NULL, from the il2cpp-side wrapper) andPSNCommon(1, a stack block).So the
(0, NULL)load ofPSNCoreis also what the title does on a console, and something on the console must makePSNCore's validator (argp[0] == 0x10,argp[1] == 0x200) pass for it. We could not find what: the block may be substituted for a NULLargpby the kernel or libkernel, or supplied some other way. Without a console we cannot check.Question for the maintainers: is a fallback like #4296 (a zero-argument load gets the first non-empty block an earlier load passed; documented in TechnicalDebt as unverified) acceptable in that situation, or would you rather keep this issue open until someone can trace it on hardware? If anyone with a dev console can log
module_start'sargs/argpforPSNCore.prxin PPSA13579, that would settle it.fma-rc commentedon Oct 12, 2026
Cult of the Lamb (PPSA06464, Unity) reaches the same validator one step earlier. On Linux with
main0ef9dc9 it faults at 1 s before any log line (exit 139, 2 of 2 runs).I relinked it with
--module-dir Media/Modules --module-dir Media/Plugins.app.elfthen listsUnifyPS5Plugin.prxas a needed library, along withIl2cppUserAssemblies,PS5Util,SavedGamesandlibfmod. Its start therefore runs from the dynamic loader at process start with(0, NULL), before the title has calledsceKernelLoadStartModuleat all. The backtrace isUnifyPS5Plugin.prx+0x19a, +0x500, +0x10eb5, thenld-linux.The start has the same shape as the one you describe for PSNCore: at +0x19a it reads
argp[0]before comparingargswith 16, then expects{0x10, 0x200, ptr}(or{0x58, 0x103}).So a plugin that another module needs gets the null block at startup, without any zero-argument load call.
ducloc080202 commentedon Oct 12, 2026
Cross-platform data point from Windows, same validator.
Cat Quest III (PPSA15065, Unity IL2CPP,
contentVersion01.000.001) reaches it and faults.main.sce_module/libc.prxand itsMedia/Modules/*.prxandMedia/Plugins/*.prxwere unwrapped from their SELF/fSELF containers and relinked with--module-dir Media/Plugins --module-dir Media/Modules.0xC0000005(read of0x4) inside the title's ownPSN.prx.guest.prx+0x13f37. Deterministic, 2 of 2 runs.Same shape as this issue: the fault is in the title's bundled PSN module, right after the version/argument validator rejects the block. Two details worth noting for whoever reconciles this:
PSN.prx, notPSNCore.prx(the message text differs slightly too), so the validator is shared across the family rather than specific to PSNCore./app0/Media/Pluginsin thesceKernelOpentrace. The engine opensboot.config,globalgamemanagers*,Resources/unity_builtin_extra,Metadata/global-metadata.dat,il2cpp.usymandUnitySubsystems, but never listsPlugins, so (matching your finding) only the il2cpp wrapper's zero-argument load is left.The new test in #4296 is registered only outside Windows (
if(NOT WIN32)); the author notes the fixture would need to link libkernel and export the pending-arguments accessor. I'm on Windows and can try to build and run it there if that is useful.eliyarson commentedon Oct 12, 2026
Root cause found; it is not the module arguments. Details and a PR follow.
PSNCore is loaded through the il2cpp find-plugin callback registered by the eboot (0xa46860). It looks the name up, builds
/app0/Media/Plugins/<name>.prx, callssceKernelStaton it and only when that succeeds takes the engine load path, which callssceKernelLoadStartModulewithrsi = 0x34and the{0x10, 0x200, getInterface}block that PSNCore'smodule_startvalidates. If the stat fails the callback returns the bare name and il2cpp loads PSNCore itself with(0, NULL), which is the "PSNCore is an old version" message.In a relinked app0,
Media/Pluginscontains onlyPSNCore.prx.guest.prx.dlopenmapsXtoX.guest.prx(RelinkedModulePath), butsceKernelStat/sceKernelOpen/sceKernelCheckReachabilityhave no such mapping, so the stat returns 0x80020002. gdb on an unmodified build:and with the original
.prxfiles symlinked beside the guest images the same run showsSTAT-RET rax=0, the engine load site is reached and the message disappears. The same happens with this PR and no symlinks.PR: #4688 (bring-up discussion: #4687). It shares the
X -> X.guest.prxmapping betweendlopenand the file queries instead of replaying arguments. It is an alternative to my draft #4296 (module-argument replay), which it makes unnecessary for PPSA13579; I will close #4296 once this is merged.On
mainplus the PR the title then crashes while loading Addressables bundles (SIGSEGV incore::string::reserve); this is the host fd 0 reuse fixed by #3295. With #3295 applied the title screen is stable at ~59.9 FPS for 240 s (gameplay not tested).If this does not match your setup, please post
ls app0/Media/Pluginsof the relinked output.oneandonlydean commentedon Oct 13, 2026
#4688 is on main:
sceKernelStatfinds the relinked plugin, so the engine load path passes the arguments block. Does any title still printPSNCore is an old versionon current main? Please post the title and the log line; if none does, this can close.fma-rc commentedon Oct 13, 2026
Cult of the Lamb (PPSA06464) does not print
PSNCore is an old versiononmainb1328fc, but only because it does not get that far. Relinked again with that commit's relinker (--module-dir Media/Modules --module-dir Media/Plugins) and run on its libraries, it still dies at 1 s with an empty log, 2 of 2 runs:That is the same place as before: the plugin is a needed library of the relinked executable, so the dynamic linker runs its start at process start and it reads through a null argument pointer at +0x19a. So this title can't confirm the
sceKernelStatfix either way.oneandonlydean commentedon Oct 14, 2026
Closing: #4688 is on main, so
sceKernelStatfinds the relinked plugin and the engine passes its arguments block to PSNCore. The UnifyPS5Plugin case (a needed library started by the dynamic loader with no arguments) is a different path; please open a new issue for it. Comment here if PSNCore still prints this on current main.