Files
AnyPS5/core/libs/tests/GuestSigtimedwait.cpp
T
DavidandClaude Fable 5.1 059e1319d1 feat(libkernel): export pause, sigwait and sigwaitinfo
Exports pause (EDyhWelj7UY), sigwait (mrbHXqK8wkg) and sigwaitinfo
(Uq5BGthgbl4) from libkernel. NIDs computed with tools/nid_names.py and
matched in aerolib; ps5-payload-dev/sdk f5d40f02 sce_stubs/libkernel.c
places all three in libkernel.

They throw like the merged sigtimedwait (#3430), with the same message
pattern: "sigwait: waiting for a pending signal", "sigwaitinfo: waiting
for a pending signal" and "pause: waiting for a delivered signal". In
FreeBSD 11.4 (8c639945) sigwaitinfo is kern_sigtimedwait without a
timeout (sys/kern/kern_sig.c:1212), sigwait is the same wait returning
the error number (kern_sig.c:1150, lib/libthr/thread/thr_sig.c:676-728)
and pause is sigsuspend of the current mask (lib/libc/gen/pause.c:46-56).
AnyPS5 never leaves a guest signal pending (blocked signals are dropped
in Dispatch, raise delivers at once), so there is nothing to take, and
raise, kill and pthread_kill run a handler only on the calling thread,
so nothing can end a pause.

Titles (predicted stops, registry order; not observed runs):
- Ruffle PPSA68091, observed today
  "FAIL: unresolved ELF import 1t1-JoZ0sZQ from libSceLibcInternal.prx"
  (sinhf, #3519), predicted next
  "FAIL: unresolved ELF import EDyhWelj7UY from libkernel.prx"
  (pause, call site 0x8F7266).
- RetroArch PPSA99169: pause (0x30DB1, an address table entry).
- PS5MC PPSA99420: pause (0x3BD80F6, 0x3C9E220), sigwait (0x3C9E4C7),
  sigwaitinfo (0x3C9E588).
- XPSemu PPSA97358: sigwait (0xE02AEC, 0x1101294, 0x1101368), QEMU's
  dummy vCPU thread and sigwait_compat thread.

guest_sigtimedwait now also checks the three throws, with an empty set,
a null info, a SIGINT raised while blocked, and that neither the output
nor the blocked mask changes. TechnicalDebt records the call sites.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-11 23:52:33 +02:00

82 lines
3.7 KiB
C++

#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <stdexcept>
#include <string>
struct GuestSignalSet {
std::uint32_t bits[4];
};
struct GuestTimespec {
std::int64_t seconds;
std::int64_t nanoseconds;
};
using Handler = void (APS5_VABI *)(int);
extern "C" {
int APS5_VABI sigtimedwait_nid_postfix(const GuestSignalSet*, void*, const GuestTimespec*);
int APS5_VABI sigwaitinfo_nid_postfix(const GuestSignalSet*, void*);
int APS5_VABI sigwait_nid_postfix(const GuestSignalSet*, int*);
int APS5_VABI pause_nid_postfix(void);
Handler APS5_VABI signal_nid_postfix(int, Handler);
int APS5_VABI raise_nid_postfix(int);
int APS5_VABI _sigprocmask_nid_postfix(int, const GuestSignalSet*, GuestSignalSet*);
}
volatile int received = 0;
void APS5_VABI Callback(int value) { received = value; }
static void Require(bool value) { if (!value) std::abort(); }
template <typename TCall>
static bool ReportsNotImplemented(TCall call, const char* message) {
try {
call();
} catch (const std::runtime_error& error) {
return std::string(error.what()) == message;
}
return false;
}
static bool SigtimedwaitReportsNotImplemented(const GuestSignalSet* set, void* info, const GuestTimespec* timeout) {
return ReportsNotImplemented([&] { sigtimedwait_nid_postfix(set, info, timeout); }, "sigtimedwait: waiting for a pending signal not implemented");
}
static bool SigwaitinfoReportsNotImplemented(const GuestSignalSet* set, void* info) {
return ReportsNotImplemented([&] { sigwaitinfo_nid_postfix(set, info); }, "sigwaitinfo: waiting for a pending signal not implemented");
}
static bool SigwaitReportsNotImplemented(const GuestSignalSet* set, int* taken) {
return ReportsNotImplemented([&] { sigwait_nid_postfix(set, taken); }, "sigwait: waiting for a pending signal not implemented");
}
int main() {
const GuestSignalSet pipe{{1u << 12, 0, 0, 0}};
const GuestSignalSet empty{{0, 0, 0, 0}};
const GuestSignalSet interrupt{{1u << 1, 0, 0, 0}};
unsigned char info[64] = {};
const unsigned char untouched[64] = {};
const GuestTimespec poll{0, 0};
const GuestTimespec second{1, 0};
Require(SigtimedwaitReportsNotImplemented(&pipe, info, &poll));
Require(SigtimedwaitReportsNotImplemented(&pipe, nullptr, &second));
Require(SigtimedwaitReportsNotImplemented(&pipe, info, nullptr));
Require(SigwaitinfoReportsNotImplemented(&pipe, info));
Require(SigwaitinfoReportsNotImplemented(&pipe, nullptr));
Require(SigwaitinfoReportsNotImplemented(&empty, info));
Require(std::memcmp(info, untouched, sizeof(info)) == 0);
int taken = -1;
Require(SigwaitReportsNotImplemented(&pipe, &taken));
Require(SigwaitReportsNotImplemented(&empty, &taken));
Require(taken == -1);
Require(ReportsNotImplemented([] { pause_nid_postfix(); }, "pause: waiting for a delivered signal not implemented"));
GuestSignalSet previous{};
Require(signal_nid_postfix(2, Callback) != reinterpret_cast<Handler>(static_cast<std::uintptr_t>(-1)));
Require(_sigprocmask_nid_postfix(1, &interrupt, &previous) == 0);
Require(raise_nid_postfix(2) == 0 && received == 0);
Require(SigwaitReportsNotImplemented(&interrupt, &taken) && taken == -1);
Require(SigwaitinfoReportsNotImplemented(&interrupt, info));
Require(std::memcmp(info, untouched, sizeof(info)) == 0);
GuestSignalSet current{};
Require(_sigprocmask_nid_postfix(1, nullptr, &current) == 0 && current.bits[0] == (previous.bits[0] | interrupt.bits[0]));
Require(_sigprocmask_nid_postfix(3, &previous, nullptr) == 0);
Require(raise_nid_postfix(2) == 0 && received == 2);
Require(ReportsNotImplemented([] { pause_nid_postfix(); }, "pause: waiting for a delivered signal not implemented"));
}