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 <noreply@anthropic.com>
(cherry picked from commit 8a609a5a1f189dce107a871e7da0fd55c746921f)
This commit is contained in:
David 2026-10-11 12:20:51 +02:00 committed by MarcZX100
parent 0f863c1022
commit b79c8e2143
2 changed files with 118 additions and 24 deletions

View File

@ -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"

View File

@ -1,7 +1,9 @@
#include "prx/libc/include/general/VabiMacros.hpp"
#include <climits>
#include <cstdint>
#include <cstddef>
#include <cstdlib>
#include <cstring>
#include <initializer_list>
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<const unsigned char*>(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}};