From b79c8e21436bb7ec90eeb73dcd0b3de5228f6e69 Mon Sep 17 00:00:00 2001 From: David Date: Sun, 11 Oct 2026 12:20:51 +0200 Subject: [PATCH] feat(libkernel): implement sigsetjmp XPSemu (PPSA97358) stops next at sigsetjmp (aRo9AhFUXcM) from libkernel, per the body of #3323 "feat(libkernel): implement cpuset_setaffinity for the calling thread or process": "XPSemu: with this PR, the next stop is `sigsetjmp` (`aRo9AhFUXcM`)". RetroArch and PS5MC list it under libkernel in the absent-import tables of #1578; aerolib names aRo9AhFUXcM sigsetjmp. sigsetjmp is FreeBSD's amd64 sigsetjmp (lib/libc/amd64/gen/sigsetjmp.S, release/11.4.0 8c639945), the counterpart of the merged siglongjmp (#2935) and its sigjmp_buf layout (sys/x86/include/setjmp.h, _JBLEN 12): it stores savemask at offset 88 and, when it is nonzero, saves the current mask at 72 through _sigprocmask(SIG_BLOCK, NULL, oset); then it records the return address, rbx, rsp, rbp, r12-r15 and the x87 control word, and returns 0. guest_signal_sets now jumps through the export instead of its copy of FreeBSD's sigsetjmp, and checks that savemask 0 leaves the mask slot untouched, a nonzero savemask records the current mask and its own value, and the callee-saved registers come back after siglongjmp. Co-Authored-By: Claude Fable 5.1 (cherry picked from commit 8a609a5a1f189dce107a871e7da0fd55c746921f) --- .../prx/libkernel/System/src/SignalSets.cpp | 25 ++++ core/libs/tests/GuestSignalSets.cpp | 117 ++++++++++++++---- 2 files changed, 118 insertions(+), 24 deletions(-) diff --git a/core/libs/prx/libkernel/System/src/SignalSets.cpp b/core/libs/prx/libkernel/System/src/SignalSets.cpp index 8345aad33..871866cb3 100644 --- a/core/libs/prx/libkernel/System/src/SignalSets.cpp +++ b/core/libs/prx/libkernel/System/src/SignalSets.cpp @@ -69,6 +69,31 @@ int APS5_VABI sigismember_nid_postfix(const GuestSignalSet* set, int signal) { #endif asm(".text\n" + APS5_SIGNAL_ASM_FUNCTION("sigsetjmp_nid_postfix") + " movl %esi, 88(%rdi)\n" + " testl %esi, %esi\n" + " jz 2f\n" + " pushq %rdi\n" + " movq %rdi, %rcx\n" + " movl $1, %edi\n" + " xorl %esi, %esi\n" + " leaq 72(%rcx), %rdx\n" + APS5_SIGNAL_ASM_CALL("_sigprocmask_nid_postfix") + " popq %rdi\n" + "2:\n" + " movq %rdi, %rcx\n" + " movq 0(%rsp), %rdx\n" + " movq %rdx, 0(%rcx)\n" + " movq %rbx, 8(%rcx)\n" + " movq %rsp, 16(%rcx)\n" + " movq %rbp, 24(%rcx)\n" + " movq %r12, 32(%rcx)\n" + " movq %r13, 40(%rcx)\n" + " movq %r14, 48(%rcx)\n" + " movq %r15, 56(%rcx)\n" + " fnstcw 64(%rcx)\n" + " xorl %eax, %eax\n" + " ret\n" APS5_SIGNAL_ASM_FUNCTION("siglongjmp_nid_postfix") " cmpl $0, 88(%rdi)\n" " jz 2f\n" diff --git a/core/libs/tests/GuestSignalSets.cpp b/core/libs/tests/GuestSignalSets.cpp index 018783bf4..798c73651 100644 --- a/core/libs/tests/GuestSignalSets.cpp +++ b/core/libs/tests/GuestSignalSets.cpp @@ -1,7 +1,9 @@ #include "prx/libc/include/general/VabiMacros.hpp" #include #include +#include #include +#include #include struct GuestSignalSet { @@ -16,8 +18,9 @@ int APS5_VABI sigdelset_nid_postfix(GuestSignalSet*, int); int APS5_VABI sigismember_nid_postfix(const GuestSignalSet*, int); int* APS5_VABI __error_nid_postfix(); int APS5_VABI _sigprocmask_nid_postfix(int, const GuestSignalSet*, GuestSignalSet*); -int APS5_VABI TitleSigsetjmp(std::uint64_t* buffer, int saveMask); +[[gnu::returns_twice]] int APS5_VABI sigsetjmp_nid_postfix(std::uint64_t* buffer, int saveMask); [[noreturn]] void APS5_VABI siglongjmp_nid_postfix(std::uint64_t* buffer, int value); +int APS5_VABI JumpKeepingCalleeSaved(std::uint64_t* buffer); } #ifdef _WIN32 @@ -29,30 +32,62 @@ int APS5_VABI TitleSigsetjmp(std::uint64_t* buffer, int saveMask); #endif asm(".text\n" - TEST_ASM_FUNCTION("TitleSigsetjmp") - " movl %esi, 88(%rdi)\n" - " testl %esi, %esi\n" - " jz 2f\n" + TEST_ASM_FUNCTION("JumpKeepingCalleeSaved") + " pushq %rbx\n" + " pushq %rbp\n" + " pushq %r12\n" + " pushq %r13\n" + " pushq %r14\n" + " pushq %r15\n" " pushq %rdi\n" - " movq %rdi, %rcx\n" - " movq $1, %rdi\n" - " movq $0, %rsi\n" - " leaq 72(%rcx), %rdx\n" - TEST_ASM_CALL("_sigprocmask_nid_postfix") - " popq %rdi\n" + " movabsq $0x1111111111111111, %rbx\n" + " movabsq $0x2222222222222222, %rbp\n" + " movabsq $0x3333333333333333, %r12\n" + " movabsq $0x4444444444444444, %r13\n" + " movabsq $0x5555555555555555, %r14\n" + " movabsq $0x6666666666666666, %r15\n" + " movl $1, %esi\n" + TEST_ASM_CALL("sigsetjmp_nid_postfix") + " testl %eax, %eax\n" + " jnz 1f\n" + " xorl %ebx, %ebx\n" + " xorl %ebp, %ebp\n" + " xorl %r12d, %r12d\n" + " xorl %r13d, %r13d\n" + " xorl %r14d, %r14d\n" + " xorl %r15d, %r15d\n" + " movq 0(%rsp), %rdi\n" + " movl $42, %esi\n" + TEST_ASM_CALL("siglongjmp_nid_postfix") + "1:\n" + " movabsq $0x1111111111111111, %rcx\n" + " cmpq %rcx, %rbx\n" + " jne 2f\n" + " movabsq $0x2222222222222222, %rcx\n" + " cmpq %rcx, %rbp\n" + " jne 2f\n" + " movabsq $0x3333333333333333, %rcx\n" + " cmpq %rcx, %r12\n" + " jne 2f\n" + " movabsq $0x4444444444444444, %rcx\n" + " cmpq %rcx, %r13\n" + " jne 2f\n" + " movabsq $0x5555555555555555, %rcx\n" + " cmpq %rcx, %r14\n" + " jne 2f\n" + " movabsq $0x6666666666666666, %rcx\n" + " cmpq %rcx, %r15\n" + " je 3f\n" "2:\n" - " movq %rdi, %rcx\n" - " movq 0(%rsp), %rdx\n" - " movq %rdx, 0(%rcx)\n" - " movq %rbx, 8(%rcx)\n" - " movq %rsp, 16(%rcx)\n" - " movq %rbp, 24(%rcx)\n" - " movq %r12, 32(%rcx)\n" - " movq %r13, 40(%rcx)\n" - " movq %r14, 48(%rcx)\n" - " movq %r15, 56(%rcx)\n" - " fnstcw 64(%rcx)\n" - " xorq %rax, %rax\n" + " movl $-1, %eax\n" + "3:\n" + " popq %rdi\n" + " popq %r15\n" + " popq %r14\n" + " popq %r13\n" + " popq %r12\n" + " popq %rbp\n" + " popq %rbx\n" " ret\n"); static void Require(bool value) { if (!value) std::abort(); } @@ -76,7 +111,7 @@ static int JumpBack(int saveMask, int value, std::uint32_t maskAtSet, std::uint3 std::uint64_t buffer[12]{}; volatile int jumped = 0; SetBlockedMask(maskAtSet); - const int result = TitleSigsetjmp(buffer, saveMask); + const int result = sigsetjmp_nid_postfix(buffer, saveMask); if (jumped == 0) { Require(result == 0); jumped = 1; @@ -93,10 +128,44 @@ static void CheckSiglongjmp() { Require(BlockedMask() == 0x9u); Require(JumpBack(0, -3, 0x5u, 0x30u) == -3); Require(BlockedMask() == 0x30u); + Require(JumpBack(2, 5, 0x3u, 0x40u) == 5); + Require(BlockedMask() == 0x3u); + SetBlockedMask(0); +} + +static std::uint32_t BufferWord(const std::uint64_t* buffer, std::size_t offset) { + std::uint32_t word = 0; + std::memcpy(&word, reinterpret_cast(buffer) + offset, sizeof(word)); + return word; +} + +static void CheckSigsetjmp() { + static constexpr std::uint32_t Filler = 0xa5a5a5a5u; + std::uint64_t buffer[12]; + for (auto& word : buffer) word = 0xa5a5a5a5a5a5a5a5ull; + SetBlockedMask(0x30u); + Require(sigsetjmp_nid_postfix(buffer, 0) == 0); + for (std::size_t offset = 72; offset < 88; offset += 4) Require(BufferWord(buffer, offset) == Filler); + Require(BufferWord(buffer, 88) == 0); + Require(BufferWord(buffer, 92) == Filler); + Require(BlockedMask() == 0x30u); + + SetBlockedMask(0x80000005u); + Require(sigsetjmp_nid_postfix(buffer, 1) == 0); + Require(BufferWord(buffer, 72) == 0x80000005u); + for (std::size_t offset = 76; offset < 88; offset += 4) Require(BufferWord(buffer, offset) == 0); + Require(BufferWord(buffer, 88) == 1); + Require(BufferWord(buffer, 92) == Filler); + Require(BlockedMask() == 0x80000005u); + + SetBlockedMask(0x6u); + Require(JumpKeepingCalleeSaved(buffer) == 42); + Require(BlockedMask() == 0x6u); SetBlockedMask(0); } int main() { + CheckSigsetjmp(); CheckSiglongjmp(); static constexpr int Untouched = 5; GuestSignalSet set{{0x12345678u, 0x9abcdef0u, 0xffffffffu, 1u}};