sceSystemServiceLoadExec found its own executable through /proc/self/exe
and replaced the process with fexecve after close_range, none of which
macOS has, so on macOS every call except "exit" threw. On macOS:
- the running executable's path comes from _NSGetExecutablePath, resolved
with std::filesystem::canonical, as libSceNpEntitlementAccess and
libSceFont already do;
- the target must be an x86-64 Mach-O executable (MH_MAGIC_64,
CPU_TYPE_X86_64, MH_EXECUTE) that the user may run;
- macOS has no fexecve, so the process is replaced by path with
posix_spawn and POSIX_SPAWN_SETEXEC; POSIX_SPAWN_CLOEXEC_DEFAULT closes
every descriptor but the standard streams, as close_range does on Linux;
- before the replacement the exit starts and the shutdown callbacks run
(LibcStartExit, then LibcRunShutdown), as on Linux, so guest threads
that call a stopped library wait instead of unwinding;
- descriptor 2 gets the host standard error back before the replacement,
as on Linux, and guest_system_load_exec_standard_error runs on macOS.
A launch without arguments keeps the Linux execv path, which macOS has.
guest_system_exit finds its own executable the same way on macOS.
Metal Gear Solid 4 (PPSA18194) aborts on main when its launcher starts
the game: sceSystemServiceLoadExec runs the guest shutdown, a render
thread that then calls into the stopped AGC driver gets ProcessShutdown
back from LibcAwaitExit, and the exception unwinds into title code,
which terminates the process before the new executable starts.
LibcAwaitExit parks a thread only once an exit has been requested, and
LoadExec ran the shutdown without requesting one. It now starts the
shutdown the way exit does, so such threads wait until the process is
replaced.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sceSystemServiceParamGetInt answered 0 for the time zone (parameter 4)
and summer time (parameter 5), so the console looked set to UTC while
libSceRtc and libc convert local time with the host's zone. Titles add
the time zone in minutes and 60 more minutes when summer time is 1 to
UTC to get the local time (Apollo, GoldHEN's frame logger). Both now
follow the host's local time zone: summer time is whether it is in
effect, and the time zone is the current offset east of UTC less the
summer time shift while it is.
On Windows that shift is the C runtime's own summer time bias
(_get_dstbias), the one localtime_s applied, not a fixed 60 minutes.
The UCRT reads the system zone when libc.prx loads and keeps that
zone's bias after TZ is set later, so on a host whose system zone has
no summer time (the CI runner's UTC) a TZ of CET-1CEST shifted by 0 and
the time zone came out as 0. The cause and the change are screamm's,
from the review.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The relinker's repeatable --executable converts another executable of the
title beside the output, sharing its libs, app0 and guest modules.
sceSystemServiceLoadExec maps /app0 paths to those executables, passes the
argument list on the new command line and finds the main executable through
APS5_EBOOT. RetroArch (PPSA99169) starts its frontend picker this way.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>