From e6a9b505126730bf1c9d91530cf621b879395b2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C5=A0l=C3=A9gl?= Date: Thu, 8 Oct 2026 17:25:19 +0200 Subject: [PATCH] feat(libkernel): implement the sigset functions Add sigemptyset, sigfillset, sigaddset, sigdelset and sigismember with FreeBSD 11 semantics (lib/libc/gen/sigsetops.c): a 128-bit set of four 32-bit words, signal n at bit n - 1, signals 1 to 128 valid. sigfillset sets every bit. An invalid signal returns -1 with errno EINVAL and leaves the set untouched; sigismember returns 1 or 0. A null set throws. Co-Authored-By: Claude Fable 5.1 --- core/libs/prx/libkernel/CMakeLists.txt | 11 ++ .../prx/libkernel/System/src/SignalSets.cpp | 62 +++++++++++ core/libs/tests/GuestSignalSets.cpp | 104 ++++++++++++++++++ 3 files changed, 177 insertions(+) create mode 100644 core/libs/prx/libkernel/System/src/SignalSets.cpp create mode 100644 core/libs/tests/GuestSignalSets.cpp diff --git a/core/libs/prx/libkernel/CMakeLists.txt b/core/libs/prx/libkernel/CMakeLists.txt index 2520ab4772fb..5de7ab0e671f 100644 --- a/core/libs/prx/libkernel/CMakeLists.txt +++ b/core/libs/prx/libkernel/CMakeLists.txt @@ -47,6 +47,7 @@ add_library(libkernel SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/System/src/Process.cpp ${CMAKE_CURRENT_SOURCE_DIR}/System/src/SystemConfiguration.cpp ${CMAKE_CURRENT_SOURCE_DIR}/System/src/Signals.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/System/src/SignalSets.cpp ${CMAKE_CURRENT_SOURCE_DIR}/System/src/Notification.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Time/Time.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Time/TimedWait.cpp @@ -289,6 +290,16 @@ if(BUILD_TESTING) set_tests_properties(guest_signals PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + add_test_executable(guest_signal_set_tests ${LIBS_INCLUDE_DIR}/tests/GuestSignalSets.cpp) + target_include_directories(guest_signal_set_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_signal_set_tests PRIVATE libkernel libc) + configure_windows_unwind(guest_signal_set_tests) + add_test(NAME guest_signal_sets COMMAND guest_signal_set_tests) + set_tests_properties(guest_signal_sets PROPERTIES TIMEOUT 20) + if(WIN32) + set_tests_properties(guest_signal_sets PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() add_test_executable(guest_exception_handler_tests ${LIBS_INCLUDE_DIR}/tests/GuestExceptionHandler.cpp) target_include_directories(guest_exception_handler_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(guest_exception_handler_tests PRIVATE libkernel libc) diff --git a/core/libs/prx/libkernel/System/src/SignalSets.cpp b/core/libs/prx/libkernel/System/src/SignalSets.cpp new file mode 100644 index 000000000000..f2831d86c85c --- /dev/null +++ b/core/libs/prx/libkernel/System/src/SignalSets.cpp @@ -0,0 +1,62 @@ +#include "prx/libc/include/general/VabiMacros.hpp" +#include +#include +#include +#include + +extern "C" int* APS5_VABI __error_nid_postfix(); + +struct GuestSignalSet { + std::uint32_t bits[4]; +}; +static_assert(sizeof(GuestSignalSet) == 16); + +namespace { +constexpr int MaximumSignal = 128; +constexpr int InvalidArgument = 22; + +bool ValidSignal(int signal) { return signal > 0 && signal <= MaximumSignal; } +std::size_t WordIndex(int signal) { return static_cast(signal - 1) >> 5; } +std::uint32_t Bit(int signal) { return 1u << ((signal - 1) & 31); } +int RejectSignal() { + *__error_nid_postfix() = InvalidArgument; + return -1; +} +void RequireSet(const GuestSignalSet* set, const char* function) { + if (set == nullptr) throw std::invalid_argument(std::string(function) + ": set is null"); +} +} + +extern "C" { +int APS5_VABI sigemptyset_nid_postfix(GuestSignalSet* set) { + RequireSet(set, "sigemptyset"); + for (auto& word : set->bits) word = 0; + return 0; +} + +int APS5_VABI sigfillset_nid_postfix(GuestSignalSet* set) { + RequireSet(set, "sigfillset"); + for (auto& word : set->bits) word = ~0u; + return 0; +} + +int APS5_VABI sigaddset_nid_postfix(GuestSignalSet* set, int signal) { + RequireSet(set, "sigaddset"); + if (!ValidSignal(signal)) return RejectSignal(); + set->bits[WordIndex(signal)] |= Bit(signal); + return 0; +} + +int APS5_VABI sigdelset_nid_postfix(GuestSignalSet* set, int signal) { + RequireSet(set, "sigdelset"); + if (!ValidSignal(signal)) return RejectSignal(); + set->bits[WordIndex(signal)] &= ~Bit(signal); + return 0; +} + +int APS5_VABI sigismember_nid_postfix(const GuestSignalSet* set, int signal) { + RequireSet(set, "sigismember"); + if (!ValidSignal(signal)) return RejectSignal(); + return (set->bits[WordIndex(signal)] & Bit(signal)) != 0 ? 1 : 0; +} +} diff --git a/core/libs/tests/GuestSignalSets.cpp b/core/libs/tests/GuestSignalSets.cpp new file mode 100644 index 000000000000..f9df0f15318f --- /dev/null +++ b/core/libs/tests/GuestSignalSets.cpp @@ -0,0 +1,104 @@ +#include "prx/libc/include/general/VabiMacros.hpp" +#include +#include +#include +#include + +struct GuestSignalSet { + std::uint32_t bits[4]; +}; + +extern "C" { +int APS5_VABI sigemptyset_nid_postfix(GuestSignalSet*); +int APS5_VABI sigfillset_nid_postfix(GuestSignalSet*); +int APS5_VABI sigaddset_nid_postfix(GuestSignalSet*, int); +int APS5_VABI sigdelset_nid_postfix(GuestSignalSet*, int); +int APS5_VABI sigismember_nid_postfix(const GuestSignalSet*, int); +int* APS5_VABI __error_nid_postfix(); +} + +static void Require(bool value) { if (!value) std::abort(); } + +static bool Equals(const GuestSignalSet& set, std::uint32_t w0, std::uint32_t w1, std::uint32_t w2, std::uint32_t w3) { + return set.bits[0] == w0 && set.bits[1] == w1 && set.bits[2] == w2 && set.bits[3] == w3; +} + +int main() { + static constexpr int Untouched = 5; + GuestSignalSet set{{0x12345678u, 0x9abcdef0u, 0xffffffffu, 1u}}; + *__error_nid_postfix() = Untouched; + Require(sigemptyset_nid_postfix(&set) == 0); + Require(Equals(set, 0, 0, 0, 0)); + Require(*__error_nid_postfix() == Untouched); + + Require(sigfillset_nid_postfix(&set) == 0); + Require(Equals(set, 0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu)); + for (int signal = 1; signal <= 128; ++signal) Require(sigismember_nid_postfix(&set, signal) == 1); + Require(*__error_nid_postfix() == Untouched); + + Require(sigemptyset_nid_postfix(&set) == 0); + Require(sigaddset_nid_postfix(&set, 1) == 0); + Require(Equals(set, 1u, 0, 0, 0)); + Require(sigaddset_nid_postfix(&set, 32) == 0); + Require(Equals(set, 0x80000001u, 0, 0, 0)); + Require(sigaddset_nid_postfix(&set, 33) == 0); + Require(Equals(set, 0x80000001u, 1u, 0, 0)); + Require(sigaddset_nid_postfix(&set, 65) == 0); + Require(Equals(set, 0x80000001u, 1u, 1u, 0)); + Require(sigaddset_nid_postfix(&set, 96) == 0); + Require(Equals(set, 0x80000001u, 1u, 0x80000001u, 0)); + Require(sigaddset_nid_postfix(&set, 128) == 0); + Require(Equals(set, 0x80000001u, 1u, 0x80000001u, 0x80000000u)); + Require(sigaddset_nid_postfix(&set, 33) == 0); + Require(Equals(set, 0x80000001u, 1u, 0x80000001u, 0x80000000u)); + Require(*__error_nid_postfix() == Untouched); + + Require(sigismember_nid_postfix(&set, 1) == 1); + Require(sigismember_nid_postfix(&set, 2) == 0); + Require(sigismember_nid_postfix(&set, 32) == 1); + Require(sigismember_nid_postfix(&set, 33) == 1); + Require(sigismember_nid_postfix(&set, 64) == 0); + Require(sigismember_nid_postfix(&set, 65) == 1); + Require(sigismember_nid_postfix(&set, 96) == 1); + Require(sigismember_nid_postfix(&set, 97) == 0); + Require(sigismember_nid_postfix(&set, 128) == 1); + Require(*__error_nid_postfix() == Untouched); + + Require(sigdelset_nid_postfix(&set, 1) == 0); + Require(Equals(set, 0x80000000u, 1u, 0x80000001u, 0x80000000u)); + Require(sigdelset_nid_postfix(&set, 32) == 0); + Require(Equals(set, 0, 1u, 0x80000001u, 0x80000000u)); + Require(sigdelset_nid_postfix(&set, 33) == 0); + Require(Equals(set, 0, 0, 0x80000001u, 0x80000000u)); + Require(sigdelset_nid_postfix(&set, 96) == 0); + Require(Equals(set, 0, 0, 1u, 0x80000000u)); + Require(sigdelset_nid_postfix(&set, 128) == 0); + Require(Equals(set, 0, 0, 1u, 0)); + Require(sigdelset_nid_postfix(&set, 2) == 0); + Require(Equals(set, 0, 0, 1u, 0)); + Require(sigismember_nid_postfix(&set, 128) == 0); + Require(*__error_nid_postfix() == Untouched); + + for (const int invalid : {0, -1, 129, 1000, INT_MIN, INT_MAX}) { + *__error_nid_postfix() = 0; + Require(sigaddset_nid_postfix(&set, invalid) == -1 && *__error_nid_postfix() == 22); + *__error_nid_postfix() = 0; + Require(sigdelset_nid_postfix(&set, invalid) == -1 && *__error_nid_postfix() == 22); + *__error_nid_postfix() = 0; + Require(sigismember_nid_postfix(&set, invalid) == -1 && *__error_nid_postfix() == 22); + Require(Equals(set, 0, 0, 1u, 0)); + } + + Require(sigfillset_nid_postfix(&set) == 0); + for (const int invalid : {0, -1, 129, 1000, INT_MIN, INT_MAX}) { + Require(sigaddset_nid_postfix(&set, invalid) == -1 && *__error_nid_postfix() == 22); + Require(sigdelset_nid_postfix(&set, invalid) == -1 && *__error_nid_postfix() == 22); + Require(sigismember_nid_postfix(&set, invalid) == -1 && *__error_nid_postfix() == 22); + Require(Equals(set, 0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu)); + } + + const GuestSignalSet empty{{0, 0, 0, 0}}; + Require(sigismember_nid_postfix(&empty, 1) == 0); + Require(sigismember_nid_postfix(&empty, 128) == 0); + return 0; +}