feat(libSceLibcInternal): export sys_siglist and sys_nsig

PS5CEMU-HAR (PPSA99360, ps5-payload-dev build, relinks with
--trap-sdk-syscall-stubs) imports the data objects sys_siglist
(yCdGspbNHZ8) and sys_nsig (ZD+Dp+-LsGg) from libSceLibcInternal.prx;
the import audit lists both as absent (registry entries 364 and 378,
after pthread_setaffinity_np at 276). No run has reached them yet; the
predicted stop line is

  FAIL: unresolved ELF import ZD+Dp+-LsGg (sys_nsig) from libSceLibcInternal.prx

or yCdGspbNHZ8 (sys_siglist), whichever the loader resolves first.

The table and count are FreeBSD 11.4 lib/libc/gen/siglist.c (8c639945):
32 descriptions, signal 0 to SIGUSR2 (NSIG 32), and sys_nsig is their
number, 32. ps5-payload-dev declares both as objects exported by
libSceLibcInternal and its strsignal reads them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
screamm 2026-10-12 14:15:27 +02:00 committed by Dean Galvin
parent ee9037d851
commit 9849a1db64
4 changed files with 77 additions and 1 deletions

View File

@ -1,4 +1,4 @@
add_library(libSceLibcInternal SHARED EXCLUDE_FROM_ALL Export.cpp FileTime.cpp Getopt.cpp Spawn.cpp UserContext.cpp)
add_library(libSceLibcInternal SHARED EXCLUDE_FROM_ALL Export.cpp FileTime.cpp Getopt.cpp SignalList.cpp Spawn.cpp UserContext.cpp)
target_include_directories(libSceLibcInternal PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(libSceLibcInternal PRIVATE ${CMAKE_DL_LIBS})
target_sources(libSceLibcInternal PRIVATE Directory.cpp)
@ -115,4 +115,14 @@ if(BUILD_TESTING)
set_tests_properties(guest_fminf PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceLibcInternal>")
endif()
add_test_executable(guest_signal_list_tests ${LIBS_INCLUDE_DIR}/tests/GuestSignalList.cpp)
target_include_directories(guest_signal_list_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_signal_list_tests PRIVATE libSceLibcInternal)
configure_windows_unwind(guest_signal_list_tests)
add_test(NAME guest_signal_list COMMAND guest_signal_list_tests)
set_tests_properties(guest_signal_list PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_signal_list PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceLibcInternal>")
endif()
endif()

View File

@ -0,0 +1,40 @@
extern "C" {
extern const char* const sys_siglist_nid_postfix[32] = {
"Signal 0",
"Hangup",
"Interrupt",
"Quit",
"Illegal instruction",
"Trace/BPT trap",
"Abort trap",
"EMT trap",
"Floating point exception",
"Killed",
"Bus error",
"Segmentation fault",
"Bad system call",
"Broken pipe",
"Alarm clock",
"Terminated",
"Urgent I/O condition",
"Suspended (signal)",
"Suspended",
"Continued",
"Child exited",
"Stopped (tty input)",
"Stopped (tty output)",
"I/O possible",
"Cputime limit exceeded",
"Filesize limit exceeded",
"Virtual timer expired",
"Profiling timer expired",
"Window size changes",
"Information request",
"User defined signal 1",
"User defined signal 2",
};
extern const int sys_nsig_nid_postfix = sizeof(sys_siglist_nid_postfix) / sizeof(sys_siglist_nid_postfix[0]);
}

View File

@ -0,0 +1,25 @@
#include <cstdlib>
#include <cstring>
extern "C" {
extern const char* const sys_siglist_nid_postfix[32];
extern const int sys_nsig_nid_postfix;
}
static constexpr int GUEST_NSIG = 32;
static constexpr int GUEST_SIGHUP = 1;
static constexpr int GUEST_SIGINT = 2;
static constexpr int GUEST_SIGSEGV = 11;
static constexpr int GUEST_SIGUSR2 = 31;
static void Require(bool value) { if (!value) std::abort(); }
int main() {
Require(sys_nsig_nid_postfix == GUEST_NSIG);
Require(std::strcmp(sys_siglist_nid_postfix[0], "Signal 0") == 0);
Require(std::strcmp(sys_siglist_nid_postfix[GUEST_SIGHUP], "Hangup") == 0);
Require(std::strcmp(sys_siglist_nid_postfix[GUEST_SIGINT], "Interrupt") == 0);
Require(std::strcmp(sys_siglist_nid_postfix[GUEST_SIGSEGV], "Segmentation fault") == 0);
Require(std::strcmp(sys_siglist_nid_postfix[GUEST_SIGUSR2], "User defined signal 2") == 0);
for (int signal = 0; signal < sys_nsig_nid_postfix; ++signal) Require(sys_siglist_nid_postfix[signal] != nullptr && sys_siglist_nid_postfix[signal][0] != '\0');
}

View File

@ -12,5 +12,6 @@
- [Thread exit destructors](../../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) run when a libkernel thread finishes; on the main or a host thread only `_sceLibcInternalThreadDtors` runs them, as `exit` does, else they are dropped
- [fdopendir](../../../core/libs/prx/libSceLibcInternal/Directory.cpp) (libSceLibcInternal) - the directory stream starts at the directory's first entry, while FreeBSD's [`fdopendir`](https://github.com/freebsd/freebsd-src/blob/release/11.0.0/lib/libc/gen/opendir.c#L67-L74) reads from the descriptor's current offset, and `FD_CLOEXEC` is not set on the descriptor. As in FreeBSD, a descriptor that is not open fails with `EBADF`, one that is not a directory with `ENOTDIR` (both leave it open), and `closedir` closes it
- [fstatvfs](../../../core/libs/prx/libSceLibcInternal/FilesystemStatistics.cpp) (libSceLibcInternal) converts `_fstatfs` to the 88-byte `struct statvfs` as FreeBSD 9.2's `sfs2svfs` does (`f_bsize` from the transfer size, `f_frsize` from the block size, `f_favail` equal to `f_ffree`, `f_fsid` 0, `ST_RDONLY` and `ST_NOSUID` only). Differences: `f_namemax` is the `statfs` value, not `fpathconf(_PC_NAME_MAX)`; negative `f_bavail` and `f_ffree` wrap to unsigned without `EOVERFLOW`, as the same-width copy in FreeBSD does; inode counts are 0 on Windows, inherited from `_fstatfs`; a null output returns `EFAULT` after the descriptor is validated, where FreeBSD would fault. Layout and conversion are unverified against the PS5 library (PS5MC imports `fstatvfs` but was not run)
- [sys_siglist and sys_nsig](../../../core/libs/prx/libSceLibcInternal/SignalList.cpp) (libSceLibcInternal) hold the 32 descriptions and the count of [FreeBSD 11.4](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/gen/siglist.c#L73-L107) `siglist.c` (`NSIG` 32, signal 0 to `SIGUSR2`); the console's table was not read
- [posix_spawn and posix_spawnp](../../../core/libs/prx/libSceLibcInternal/Spawn.cpp) (libSceLibcInternal) throw: creating a child process is not supported. PPSA99305 (Dusklight) and PPSA99309 (Wind Waker) stop on `main` at unresolved import `44hlATrd47U` (`posix_spawnattr_init`); a run with this PR has not established how far either title gets, and process creation still throws. The attribute and file action calls keep FreeBSD 11.4's objects, so a real `posix_spawn` would have to apply them in the child. Null or uninitialized objects, which FreeBSD dereferences, throw.
- [getopt, getopt_long](../../../core/libs/prx/libSceLibcInternal/Getopt.cpp) (libSceLibcInternal) follow FreeBSD 11 [getopt.c](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/stdlib/getopt.c) and [getopt_long.c](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/stdlib/getopt_long.c) with `GNU_COMPATIBLE`: `getopt` does not permute, `getopt_long` permutes unless `POSIXLY_CORRECT` is set in the guest environment or the option string starts with `+`, and the exported `opterr`, `optind`, `optopt`, `optreset` and `optarg` start at 1, 1, 0, 0 and null. Warnings go to the guest's current `stderr` prefixed with `__progname`, where FreeBSD's `warnx` keeps the `stderr` of its first call. A null `getopt` option string, a null argument vector or argument, a negative `optind` and an `optind` moved inside a pending permutation throw where FreeBSD faults. `getopt_long_only` is not exported. That the console's `libSceLibcInternal` uses this FreeBSD code is unverified