Skip to content
Closed
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
8 changes: 0 additions & 8 deletions core/libs/prx/libc/Export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,6 @@ extern "C" {
LibcHeapTraceInfo_nid_no_patch(info);
}

// Dead import of Cyberpunk 2077 (PPSA04029): no call sites, but the
// Windows loader resolves imports strictly, so it must be present.
int APS5_VABI _ZSt14_Atomic_assertPKcS0__nid_postfix() {
NotImplemented_nid_no_patch(__func__);
return 0;
}


APS5_EXPORT("H+8UBOwfScI", libcCyberUnknown08);
std::uint64_t APS5_VABI libcCyberUnknown08(void) {
NotImplemented_nid_no_patch("H+8UBOwfScI");
Expand Down
35 changes: 31 additions & 4 deletions core/libs/prx/libc/src/Process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
#include <vector>
#ifdef _WIN32
#include <windows.h>
#include <fcntl.h>
#include <io.h>
#else
#include <fcntl.h>
#include <unistd.h>
#endif
#include <atomic>
#include <stdexcept>
Expand Down Expand Up @@ -196,11 +201,33 @@ int APS5_VABI at_quick_exit_nid_postfix(GuestExitCallback func) {
_Exit_nid_postfix(status);
}

int APS5_VABI chdir_nid_postfix(const char* path);

int APS5_VABI daemon_nid_postfix(int noChdir, int noClose) {
(void)noChdir;
(void)noClose;
NotImplemented_nid_no_patch("daemon: forking into the background");
return -1;
if (noChdir == 0 && chdir_nid_postfix("/") != 0) return -1;
if (noClose != 0) return 0;
#ifdef _WIN32
const int null = ::_open("NUL", _O_RDWR);
if (null < 0) return -1;
for (int descriptor = 0; descriptor < 3; ++descriptor) {
if (::_dup2(null, descriptor) != 0) {
::_close(null);
return -1;
}
}
if (null > 2) ::_close(null);
#else
const int null = ::open("/dev/null", O_RDWR);
if (null < 0) return -1;
for (int descriptor = 0; descriptor < 3; ++descriptor) {
if (::dup2(null, descriptor) < 0) {
::close(null);
return -1;
}
}
if (null > 2) ::close(null);
#endif
return 0;
}

}
6 changes: 6 additions & 0 deletions core/libs/prx/libc/src/StdioExtras.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ int APS5_VABI sprintf_s_nid_postfix(char* buffer, size_t size, const char* forma
std::abort();
}

[[noreturn]] void APS5_VABI _ZSt14_Atomic_assertPKcS0__nid_postfix(const char* message, const char* location) {
std::fprintf(stderr, "[libc] guest atomic assertion failed: %s (%s)\n", message ? message : "?", location ? location : "?");
std::fflush(stderr);
std::abort();
}

// Only the "C" locale exists.
const char* APS5_VABI setlocale_nid_postfix(int category, const char* locale) {
(void)category;
Expand Down
47 changes: 33 additions & 14 deletions core/libs/tests/GuestDaemon.cpp
Original file line number Diff line number Diff line change
@@ -1,19 +1,38 @@
#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstddef>
#include <cstdlib>
#include <stdexcept>
#include <string>
extern "C" int APS5_VABI daemon_nid_postfix(int, int);
static void Require(bool value) { if (!value) std::abort(); }
static bool ReportsNotImplemented(int noChdir, int noClose) {
try {
daemon_nid_postfix(noChdir, noClose);
} catch (const std::runtime_error& error) {
return std::string(error.what()) == "daemon: forking into the background not implemented";
}
return false;
#include <cstring>
#ifndef _WIN32
#include <sys/stat.h>
#include <unistd.h>
#endif

extern "C" {
int APS5_VABI daemon_nid_postfix(int, int);
char* APS5_VABI getcwd_nid_postfix(char*, std::size_t);
}

static void Require(bool value) { if (!value) std::abort(); }

int main() {
Require(ReportsNotImplemented(1, 0));
Require(ReportsNotImplemented(0, 0));
Require(ReportsNotImplemented(1, 1));
Require(daemon_nid_postfix(1, 1) == 0);
#ifndef _WIN32
const int savedOut = ::dup(1);
const int savedErr = ::dup(2);
Require(savedOut >= 0 && savedErr >= 0);
Require(daemon_nid_postfix(1, 0) == 0);
struct stat null{};
Require(::stat("/dev/null", &null) == 0);
for (int descriptor = 0; descriptor < 3; ++descriptor) {
struct stat redirected{};
Require(::fstat(descriptor, &redirected) == 0 && S_ISCHR(redirected.st_mode) && redirected.st_rdev == null.st_rdev);
}
Require(::dup2(savedOut, 1) == 1 && ::dup2(savedErr, 2) == 2);
#else
Require(daemon_nid_postfix(1, 0) == 0);
#endif
Require(daemon_nid_postfix(0, 1) == 0);
char directory[64];
Require(getcwd_nid_postfix(directory, sizeof(directory)) == directory && std::strcmp(directory, "/") == 0);
return 0;
}
2 changes: 1 addition & 1 deletion docs/dev/TechnicalDebt.md
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ Throughout the project, every function at every stage either **does exactly what
- [s_call_b64](../../core/shader/recompiler/Translation/src/ScalarInstructions.cpp) saves `PC + 4` over `GetShaderBase` and branches to `PC + 4 + signext(SIMM16) * 4`, following [AMD RDNA2 ISA](https://gpuopen.com/download/rdna2-shader-instruction-set-architecture.pdf) sections 3.6.3, 12.2 and 12.3. Forward callees after `s_endpgm` are decoded. Measured on RDNA2 (gfx1036) in wave32 and wave64 with SIMM16 = 3: the link pair receives `PC + 4`, the branch goes to `PC + 4 + signext(SIMM16) * 4`, and `s_setpc_b64` of the link returns to the instruction after the call. Execution on the PS5 (gfx1013) and title compatibility for these bounded scalar calls remain unverified; `s_rfe_b64` still throws in translation.
- [sceVideodec2CreateDecoder](../../core/libs/prx/libSceVideodec2/Export.cpp) (libSceVideodec2) decodes HEVC (codec type 974921) and VP9 (2382845) through FFmpeg, besides H.264. The tags come from SharpProspero's `Videodec2CodecType` and EVO-PLAYER-PS5's `sce_videodec2.h`, which records them from hardware runs. HEVC input is converted from length-prefixed NAL units to Annex B as for H.264, and VP9 frames are passed through. Output is the same 8-bit NV12 for every codec; a decoder for HEVC Main10 or VP9 Profile 2 is created, but a frame deeper than 8 bits throws, since the console's 10-bit output layout is unknown, and `QueryDecoderMemoryInfo` reports the 8-bit frame size for it. `sceVideodec2GetPictureInfo` and `sceVideodec2GetAvcPictureInfo` throw for HEVC and VP9 pictures, which use other layouts (`SceVideodec2HevcPictureInfo`, `SceVideodec2Vp9PictureInfo`), and `sceVideodec2GetHevcPictureInfo` and `sceVideodec2GetVp9PictureInfo` are not exported
- [syslog](../../core/libs/prx/libc/src/Syslog.cpp) (libc) writes each message to `stderr` as `[syslog:<priority>] <message>`, with a newline added when missing, instead of sending it to the console's system log. Priority and facility handling follow FreeBSD's `vsyslog`: `%m` expands to the guest errno's message, unknown bits are reported (as priority 0x23, since FreeBSD passes `LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID` as the priority) and masked, a missing facility becomes `LOG_USER`, and errno is preserved. `setlogmask` follows [FreeBSD 11](https://github.com/freebsd/freebsd-src/blob/8d2708de5d6112a492744d367cfdfb8fd219227a/lib/libc/gen/syslog.c): the process-wide mask defaults to 0xff, zero queries it, and nonzero values replace it; masked messages are skipped before formatting, including the unknown-priority diagnostic. The console's mask behaviour is unverified. `openlog` and `closelog` are not implemented, so their identity and options are not applied
- [daemon](../../core/libs/prx/libc/src/Process.cpp) (libc) throws: forking into the background is not supported. PPSA85300 imports it and fails to load without it. VLC 3.0 calls it only when its `daemon` option is set ([libvlc.c](https://github.com/videolan/vlc/blob/6de05adcbaf2e8b85fe86aad4169393098628119/src/libvlc.c#L190-L197)), and the title starts libVLC with `--ignore-config` and without `--daemon` ([player.cc](https://github.com/ZiZc3/VLC/blob/f3253020a64368153233ede29183014c0b6c8979/src/player.cc#L960-L981))
- [daemon](../../core/libs/prx/libc/src/Process.cpp) (libc) continues in the calling process instead of forking: it changes the working directory to `/` unless `nochdir` is set, redirects descriptors 0, 1 and 2 to the null device unless `noclose` is set, and returns 0. FreeBSD's `daemon` also forks, exits the parent and calls `setsid`, so the caller keeps its process id and session; a title cannot observe the difference through `daemon` itself. PPSA85300 imports it and fails to load without it; VLC 3.0 calls it only when its `daemon` option is set.
- [sigtimedwait](../../core/libs/prx/libkernel/System/src/Signals.cpp) (libkernel) throws: guest signals are delivered when raised and never left pending, so there is nothing to wait for or take. PPSA85300 imports it and fails to load without it. Its only call site is VLC's `vlc_writev` ([filesystem.c](https://github.com/videolan/vlc/blob/6de05adcbaf2e8b85fe86aad4169393098628119/src/posix/filesystem.c#L243-L278)), which polls with a zero timeout to drain a `SIGPIPE` only after `writev` fails with `EPIPE`
- [getsid](../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) throws: process sessions are not modelled. PPSA74126 imports it and fails to load without it; the title made no call to it in a 60 s run
- [sigaction](../../core/libs/prx/libkernel/System/src/Signals.cpp) (libkernel) records the handler, flags and mask of every signal from 1 to 128 and reports them back, following FreeBSD's `sigaction` (`EINVAL` for other signals, and for a handler on `SIGKILL` or `SIGSTOP`); `signal` records its handler with `SA_RESTART`, as FreeBSD's libc does. Only handlers without `SA_SIGINFO` on the signals `signal` already delivers (`SIGINT`, `SIGILL`, `SIGABRT`, `SIGFPE`, `SIGSEGV`, `SIGTERM`) run, the same way `signal` handlers do; their mask and flags are not applied. `SA_SIGINFO` handlers are recorded but never called, since that needs FreeBSD's `siginfo_t` and `ucontext_t`, and neither are the handlers of other signals (`SIGBUS`, `SIGHUP`, ...), whether they are set by `sigaction` or by `signal`, which records them too (every number from 1 to 128, with `SA_RESTART` and an empty mask replacing what `sigaction` stored; `SIGKILL` and `SIGSTOP` accept only the default handler in both calls, as in FreeBSD's `kern_sigaction`). `raise` of an `SA_SIGINFO` handler on a signal `signal` delivers throws instead of terminating, and `raise` and `kill` of the other signals still fail with `EINVAL`, recorded handler or not
Expand Down
Loading