A module started as a dependency or by the sweep kept its start result, so a later
load of it by the title returned that result instead of 0 (guest_deferred_module_*
failed in the Windows job with 'module start result').
A swept module whose start throws is skipped with a log line, whatever it throws, and
the load that triggered the sweep, and later loads, still succeed.
Concurrent first loads now wait for the one sweep; a failed sweep is retried by the
next first load. The sweep no longer skips the module that triggered it, so a swept
module that depends on it starts after it, and the trigger keeps its start result.
A deferred module that only another deferred module names (PSNCommon, needed by
PSNCore) started after the plugins that use it (SPFXUnityPlugin) and crashed
Soul Hackers 2 on Windows. The first title load of any deferred module now starts
every deferred module some module depends on, in dependency order, with that
load's start arguments, and logs the sweep. Boot is unchanged.
On Windows a module the executable needs or imports from stays deferred, but its
dependencies were deferred too and started only when the title loads it, so a title
that calls it first ran them uninitialised (Soul Hackers 2, #15370 rerun). They start
at startup again, as on main. The loader prints one line per deferred start.
Since #12621 a Windows runtime module that another runtime module needs is deferred instead of started at boot, but loading a deferred module only started that module. A Unity plugin such as SPFXUnityPlugin that calls into a deferred module it needs (Il2CppUserAssemblies in Media/Modules) ran against code whose initializers had never run, and PPSA03743 crashed about 2.6 s in.
A deferred module's initialization table now lists the deferred modules it depends on (names, after the initializer array, behind a magic number so older output reads as having none). DynamicLoader starts those first, transitively and once each through the existing per-module state, with the start arguments of the load that began it, then starts the module itself. The module start result stays the module's own. Boot still starts no deferred module.
Linux is unchanged: its deferred modules still start only when loaded.
NativeStat, SharedMemory, Stdio, DynamicLoader, ModuleInfo and Rtld used
Linux-only APIs (st_atim, memfd_create, sys/vfs.h, link.h and dl_iterate_phdr),
so libkernel did not compile on macOS. Add macOS paths behind __APPLE__:
st_*timespec and a long-path fstatat walk, an unlinked temp file for shared
memory, statfs, dlopen/dladdr and Mach-O segment walks for module and rtld
queries. The Linux and Windows paths are unchanged.
Extracted from feat/macos-port (#8658).
Co-authored-by: Himansh Raj <[email protected]>
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Return the main program's handle, the first one sceKernelGetModuleList
lists and one sceKernelGetModuleInfo accepts. reSOT (PPSA99023) imports
it from libkernel and passes the handle to sceKernelGetModuleInfo to find
its own code segment; before this its eboot failed to load on the
unresolved import.
The PS5 3.20 export lists have it in libkernel as well as libkernel_sys,
and on Windows a libkernel import never reaches libkernel_sys, so the
throwing libkernel_sys stub moves to libkernel as pselect did.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The public header declared the unimplemented placeholder int __tls_get_addr(void). It now declares void* __tls_get_addr(const KernelTlsIndex*), the tls_index pointer the ABI passes, and the implementation and the test use that declaration and type instead of their own.
Resolve a FreeBSD/ELF tls_index {module, offset} the way the relinker's
DTPMOD64 values are laid out: on Windows through the PE _tls_index the
module word points at and the thread's ThreadLocalStoragePointer, on
Linux through glibc's __tls_get_addr.
dlerror returned the last loader error from a 512-byte buffer, so a
longer message was cut off. On macOS the reason dyld gives for a failed
dlopen lists every path it tried, and the message for a module that was
not relinked runs to over 1,000 characters; the end of it, the hint to
relink with --module-dir, was lost, and guest_dynamic_loader failed.
Keep the message in a host thread-local string, through
HostThreadLocal so MinGW does not run its destructor on freed
thread-local storage.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Flags 1 finds any module and flags 2 only non-system ones; other flags return EINVAL instead of throwing, every failure clears the 0x1A8 bytes, and st_size is left 0.
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.
Two unwind queries a title makes went wrong on macOS, so a C++ exception the title would catch ended the process instead: God of War Sons of Sparta (PPSA28997) stops on an IL2CPP exception it normally handles.
- sceKernelGetModuleInfoForUnwind found a relinked image and its PT_GNU_EH_FRAME header but left eh_frame_addr and eh_frame_size at zero, so the title's unwinder found no FDE for its own frame. The tables are now read with EhFrame::ReadTables over the image's load segments, as ModuleInfo does on Linux, and eh_frame_size includes the zero terminator, as on Linux.
- sceSysmoduleGetModuleInfoForUnwind read /proc/self/maps outside Windows and threw on every call on macOS. It now answers from mach_vm_region, with the image path from dladdr, as the Linux branch answers from the mapping.
guest_unwind_module_info now runs on macOS: the test executable carries the __APS5DATA,__meta program headers that the macOS dl_iterate_phdr reads from a relinked image. guest_sysmodule checks the macOS answers against mach_vm_region.
Co-authored-by: mugurc <[email protected]>
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/.
The cache key of relinked images has no time stamp or check sum, so a
different module of the same size loaded at the same base was served the
old tables. The loader now forgets the image when it frees the module, the
cache has a separate valid flag, and the sysmodule name cache is left out
of this change.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>