RelinkedModulePath looked in prx, sce_module and sce_modules before the
case-insensitive match in the module's own directory, so a stray nested file
could shadow a module next to the title. Check the directory of the module
first, then the nested folders, and match the nested names case-insensitively.
Tests: a same-directory module with a differently cased name wins over an
exact-case nested file, and a nested module is found case-insensitively.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Unbound copies next to eboot concurrently LoadLibrary the same PE and leave Job0 with a null vtable; reuse the already-loaded nested guest module instead.
Co-authored-by: Cursor <[email protected]>
Increment returns -1 for an address outside every module and Decrement never fails, instead of throwing.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
On Windows, dlopen passed a module that was never relinked to LoadLibraryExW, which raised the
modal Bad Image hard-error dialog and blocked the calling thread until someone answered it. The
load now runs with SEM_FAILCRITICALERRORS set for that thread only, so it fails at once with the
same result as before. Every failed load is reported once per file on stderr with a [module]
prefix; a file that exists with no relinked .guest.prx names the relinker's --module-dir option.
PS4 programs load system libraries with sceKernelLoadStartModule and
their console path. The OpenOrbis piglet sample loads
/<sandbox word>/common/lib/libScePigletv2VSH.sprx and
libScePrecompiledShaders.sprx, the sandboxed form of
/system/common/lib/ that sceKernelGetFsSandboxRandomWord names. The path
was opened as a guest file and the load failed with ENOENT.
A path in /system/common/lib/ or in the sandboxed /<word>/common/lib/
whose file name ends in .sprx now loads the host libs/<name>.prx, as
libSceSysmodule does for module IDs. Names with a directory part are
not mapped, so such a path cannot leave libs/.
Titles pass handle 0 (RTLD_DEFAULT) for a process-wide lookup, and the loader rejected it: the handle is
not in the module map, so every such lookup failed even when a loaded module exports the symbol. Handle 0
and -2 now take the default scope, searching the global modules by plain name, NID and `#guest` form, and
then the process-wide scope.
The process-wide scope of the real loader also contains glibc and libstdc++, so an unfiltered fallback
would answer `malloc`, `dlopen` and `pthread_create` where PS5 reports the symbol as missing: handle 0 is
the main program, whose lookup sees the program's exports, not the host C++ runtime. A result from the
process scope is therefore accepted only when `dladdr` attributes it to a converted or replacement module
(a `.prx` library or the executable), which keeps the fallback for guest symbols and removes the host
leak. `guest_dynamic_loader` asserts the three host names are not found; disabling the filter makes that
test fail.
The scripting heap that an earlier revision of this branch added has been dropped: the title reaches the
same boot milestone with the lookup failing honestly, so the export was an inference rather than a
requirement, and prosper's bring-up notes report `ESRCH` for that name on hardware.
A title's libc.prx passes the dso address of __cxa_thread_atexit to
_sceKernelRtldThreadAtexitIncrement and fails the registration when it
returns nonzero. Increment takes a reference on the loaded image that
contains the address, so it stays mapped while thread_local destructors
are pending, and Decrement releases one. An address outside every
loaded image and a Decrement without a reference throw.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
On Linux every module resolves its references through one global scope,
and the guest modules are listed ahead of the host prx. When a title
bundles its own libc.prx, the host libc and the other host prx bound
their references to their own NIDs to the guest libc, so the host libc
built its standard streams on top of the guest _Stdout.
The exports of guest modules, and the guest and executable imports that
a guest module satisfies, now carry a #guest suffix. The title's imports
still reach the guest modules first, and a host prx only sees host
definitions, as on Windows. sceKernelDlsym also looks for the suffixed
name.
dlopen and sceKernelLoadStartModule passed the guest path to the host
loader, which cannot load the original ELF module. When a relinked module
<path>.guest.prx exists beside it, that module is loaded instead; for a
module the executable already loaded at startup the host loader returns
the same module. PPSA12544 loads /app0/Media/Modules/Il2CppUserAssemblies.prx
at startup and resolves IL2CPP functions through it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>