Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/libs/prx/libSceSystemService/Export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ int APS5_VABI sceSystemServiceLoadExec(const char* path, const char* const* argu
#if defined(_WIN32) || defined(__linux__)
LoadConvertedExecutable_nid_no_patch(std::filesystem::path(launch.executable), arguments);
#endif
LibcStartExit_nid_postfix();
LibcRunShutdown_nid_postfix();
std::fflush(nullptr);
StartHostExecutable(launch);
Expand Down
2 changes: 2 additions & 0 deletions core/libs/prx/libSceSystemService/LoadExec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ Handle Duplicate(HANDLE source, DWORD access) {
throw std::system_error(GetLastError(), std::system_category(), "LoadExec: creating the converted executable");
Handle child(process.hProcess, CloseHandle), thread(process.hThread, CloseHandle);
try {
LibcStartExit_nid_postfix();
LibcRunShutdown_nid_postfix();
std::fflush(nullptr);
if (ResumeThread(thread.get()) == static_cast<DWORD>(-1))
Expand Down Expand Up @@ -152,6 +153,7 @@ Handle Duplicate(HANDLE source, DWORD access) {
std::vector<char*> pointers;
for (const auto& argument : arguments) pointers.push_back(const_cast<char*>(argument.c_str()));
pointers.push_back(nullptr);
LibcStartExit_nid_postfix();
LibcRunShutdown_nid_postfix();
std::fflush(nullptr);
if (const int host = HostStandardError_nid_no_patch(); host >= 0 && dup2(host, 2) != 2)
Expand Down
1 change: 1 addition & 0 deletions core/libs/prx/libc/include/Shutdown.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ extern "C" std::stop_token LibcShutdownToken_nid_postfix();
extern "C" void LibcRequestShutdown_nid_postfix();
extern "C" void LibcRequestExit_nid_postfix(int code);
extern "C" [[noreturn]] void LibcAwaitExit_nid_postfix();
extern "C" void LibcStartExit_nid_postfix();
extern "C" [[noreturn]] void LibcExit_nid_no_patch(int code);
extern "C" [[noreturn]] void LibcExitWithoutHandlers_nid_no_patch(int code);
extern "C" [[noreturn]] void LibcTerminate_nid_no_patch(int code);
Expand Down
4 changes: 4 additions & 0 deletions core/libs/prx/libc/src/Process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,10 @@ extern "C" {
std::_Exit(code);
}

void LibcStartExit_nid_postfix() {
StartExit();
}

[[noreturn]] void LibcExit_nid_no_patch(int code) {
StartExit();
CxaFinalize_nid_no_patch(nullptr);
Expand Down
7 changes: 6 additions & 1 deletion core/libs/tests/GuestSystemExit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,12 @@ void AppendMarker(const std::string& line) {
Require(marker != nullptr);
std::ofstream(marker, std::ios::app) << line << '\n';
}
void MainShutdown() { AppendMarker("shutdown"); }
void MainShutdown() {
AppendMarker("shutdown");
std::thread(AwaitExit).detach();
awaiting.wait(false);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
void SecondaryShutdown() { AppendMarker("secondary shutdown"); }
void APS5_VABI RemainingHandler(void*) { AppendMarker("remaining handler"); }
void APS5_VABI ExitingHandler(void*) { exit_nid_postfix(7); }
Expand Down
2 changes: 1 addition & 1 deletion docs/dev/debt/libSceSystemService.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@

### Functional

- [sceSystemServiceLoadExec](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - besides `"exit"`, it starts the title's own `/app0/eboot.bin` or another executable under `/app0` that the relinker converted with `--executable`, which is looked up beside the running one (`/app0/picker/picker.bin` as `picker.picker.bin` with the running executable's extension). The main executable is the one `APS5_EBOOT` names, which the first call sets to its own path. After the guest shutdown the old process ends without running guest exit handlers or host destructors. `"exit"` ends the process after the host shutdown without running the title's `atexit` and `__cxa_atexit` handlers, since the console's shell closes the title instead; a line the title writes just before through its own thread, such as a forwarded standard error line, may not be printed. [VLC for PS5](https://github.com/ZiZc3/VLC/blob/86bb7d474a303f62bb3c2fc12f728db4846085b8/src/platform_ps5.c#L565-L571) (PPSA85300) relaunches itself with no arguments; RetroArch (PPSA99169) starts its frontend picker with `{"", NULL}`, and the picker starts `eboot.bin` with `{"--ps5-mode=retroarch", NULL}` or `/app0/es-de/es-de.bin` ([picker.cpp](https://github.com/mihawk-99/PS5_RetroArch/blob/bead80725cc11e3c5c65bd9424b0a4494ae36d45/frontends/picker/picker.cpp#L444-L446)), so the list holds only arguments. A list with a first entry becomes the whole guest `argv`, with no program name in front ([LoadExec.cpp](../../../core/libs/prx/libSceSystemService/LoadExec.cpp)): a [PS5 RetroArch console transcript](https://github.com/mihawk-99/PS5_RetroArch/blob/bead80725cc11e3c5c65bd9424b0a4494ae36d45/evidence/loadexec-second-image/relaunch-test.jsonl) records a restart with one argument entering with `argc=1`, `argv[0]="--ps5-relaunch=1"`, and a launch from the home screen with `argc=1`, `argv[0]=""` ([relaunch_ps5.cpp](https://github.com/mihawk-99/PS5_RetroArch/blob/bead80725cc11e3c5c65bd9424b0a4494ae36d45/src/relaunch_ps5.cpp#L161-L164)). For such a list Linux replaces the process with `fexecve`: the PID and the standard streams stay, the other descriptors are closed and the working directory becomes the executable's. Windows starts a suspended process that inherits only the standard streams and a handle of the old process, and its entry stub waits for the old process to end before it loads the title's libraries, so a shell or script that started the title sees it end. A null list or one without a first entry keeps the earlier launch: the new executable gets only its own path as its command line (`CreateProcessW` on Windows, `execv` on Linux), so its `argv[0]` is the host path, and the old process ends without waiting for it. What the console passes for such a list is not known, nor are its argument limits and error codes. Paths outside `/app0`, unconverted executables and macOS hosts throw
- [sceSystemServiceLoadExec](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - besides `"exit"`, it starts the title's own `/app0/eboot.bin` or another executable under `/app0` that the relinker converted with `--executable`, which is looked up beside the running one (`/app0/picker/picker.bin` as `picker.picker.bin` with the running executable's extension). The main executable is the one `APS5_EBOOT` names, which the first call sets to its own path. After the guest shutdown the old process ends without running guest exit handlers or host destructors; as during `exit`, a guest thread that calls a library the shutdown has stopped waits for the end instead of unwinding. `"exit"` ends the process after the host shutdown without running the title's `atexit` and `__cxa_atexit` handlers, since the console's shell closes the title instead; a line the title writes just before through its own thread, such as a forwarded standard error line, may not be printed. [VLC for PS5](https://github.com/ZiZc3/VLC/blob/86bb7d474a303f62bb3c2fc12f728db4846085b8/src/platform_ps5.c#L565-L571) (PPSA85300) relaunches itself with no arguments; RetroArch (PPSA99169) starts its frontend picker with `{"", NULL}`, and the picker starts `eboot.bin` with `{"--ps5-mode=retroarch", NULL}` or `/app0/es-de/es-de.bin` ([picker.cpp](https://github.com/mihawk-99/PS5_RetroArch/blob/bead80725cc11e3c5c65bd9424b0a4494ae36d45/frontends/picker/picker.cpp#L444-L446)), so the list holds only arguments. A list with a first entry becomes the whole guest `argv`, with no program name in front ([LoadExec.cpp](../../../core/libs/prx/libSceSystemService/LoadExec.cpp)): a [PS5 RetroArch console transcript](https://github.com/mihawk-99/PS5_RetroArch/blob/bead80725cc11e3c5c65bd9424b0a4494ae36d45/evidence/loadexec-second-image/relaunch-test.jsonl) records a restart with one argument entering with `argc=1`, `argv[0]="--ps5-relaunch=1"`, and a launch from the home screen with `argc=1`, `argv[0]=""` ([relaunch_ps5.cpp](https://github.com/mihawk-99/PS5_RetroArch/blob/bead80725cc11e3c5c65bd9424b0a4494ae36d45/src/relaunch_ps5.cpp#L161-L164)). For such a list Linux replaces the process with `fexecve`: the PID and the standard streams stay, the other descriptors are closed and the working directory becomes the executable's. Windows starts a suspended process that inherits only the standard streams and a handle of the old process, and its entry stub waits for the old process to end before it loads the title's libraries, so a shell or script that started the title sees it end. A null list or one without a first entry keeps the earlier launch: the new executable gets only its own path as its command line (`CreateProcessW` on Windows, `execv` on Linux), so its `argv[0]` is the host path, and the old process ends without waiting for it. What the console passes for such a list is not known, nor are its argument limits and error codes. Paths outside `/app0`, unconverted executables and macOS hosts throw
- [Running application ID](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - `sceSystemServiceGetAppIdOfRunningBigApp` reports 1 as the running title's app ID. The value is AnyPS5's own: the console's shell assigns app IDs and their values are not known here, so other calls that take an app ID must use the same one. `sceSystemServiceKillApp` with that ID and the arguments -1, 0 and 0, which [SwanStationPS5](https://github.com/darkxex/SwanStationPS5/blob/4cdc981d1a64ffb7f2af73f7a27a9e91a3cb5eb4/src/main.c#L1050-L1058) passes to close itself and [ps5-payload-dev's hbldr](https://github.com/ps5-payload-dev/websrv/blob/46374edee6627885abac717de24da7a1d73af6f1/src/ps5/hbldr.c#L425-L435) passes to close the running game, ends the title as its own `exit(0)` would: the guest's shutdown and `atexit` handlers run and the process exits with status 0. On the console the call returns and the shell then ends the title, and whether those handlers run there is unknown; here there is no shell, so the call does not return. Another app ID or other arguments throw.
- [LoadExec descriptors](../../../core/libs/prx/libSceSystemService/LoadExec.cpp) (libSceSystemService) - on Linux, a call with an argument list replaces the process with `fexecve`, which first closes every descriptor above 2; a call without one uses `execv`, and the guest's descriptors stay open across it. On both paths descriptor 2 first gets back the host standard error a guest redirect kept, so the runtime's output does not follow the title's redirect (RetroArch's trace file, VLC's log) into the next executable. Which descriptors the console passes to the new process is not known
Loading