From: Benjamin Berg benjamin.berg@intel.com
Hi,
This patchset adds signal handling to nolibc. Initially, I would like to use this for tests. But in the long run, the goal is to use nolibc for the UML kernel itself. In both cases, signal handling will be needed.
v2 contains some bugfixes and has a better test coverage. Also addressed are various review comments.
Benjamin
Benjamin Berg (4): selftests/nolibc: fix EXPECT_NZ macro selftests/nolibc: validate order of constructor calls tools/nolibc: add more generic bitmask macros for FD_* tools/nolibc: add signal support
tools/include/nolibc/arch-arm.h | 7 + tools/include/nolibc/arch-arm64.h | 3 + tools/include/nolibc/arch-loongarch.h | 3 + tools/include/nolibc/arch-m68k.h | 10 ++ tools/include/nolibc/arch-mips.h | 3 + tools/include/nolibc/arch-powerpc.h | 8 + tools/include/nolibc/arch-riscv.h | 3 + tools/include/nolibc/arch-s390.h | 8 +- tools/include/nolibc/arch-sh.h | 5 + tools/include/nolibc/arch-sparc.h | 47 ++++++ tools/include/nolibc/arch-x86.h | 13 ++ tools/include/nolibc/signal.h | 103 ++++++++++++ tools/include/nolibc/sys.h | 2 +- tools/include/nolibc/time.h | 3 +- tools/include/nolibc/types.h | 81 ++++++---- .../selftests/nolibc/nolibc-test-linkage.c | 17 +- tools/testing/selftests/nolibc/nolibc-test.c | 150 +++++++++++++++++- 17 files changed, 422 insertions(+), 44 deletions(-)
From: Benjamin Berg benjamin.berg@intel.com
The expect non-zero macro was incorrect and never used. Fix its definition. --- tools/testing/selftests/nolibc/nolibc-test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index a297ee0d6d07..97efc98b6a3d 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -196,8 +196,8 @@ int expect_zr(int expr, int llen) }
-#define EXPECT_NZ(cond, expr, val) \ - do { if (!(cond)) result(llen, SKIPPED); else ret += expect_nz(expr, llen; } while (0) +#define EXPECT_NZ(cond, expr) \ + do { if (!(cond)) result(llen, SKIPPED); else ret += expect_nz(expr, llen); } while (0)
static __attribute__((unused)) int expect_nz(int expr, int llen)
On 2025-07-10 12:39:47+0200, Benjamin Berg wrote:
From: Benjamin Berg benjamin.berg@intel.com
The expect non-zero macro was incorrect and never used. Fix its definition.
Missing sign-off. A Fixes would also be nice.
(...)
From: Benjamin Berg benjamin.berg@intel.com
Add new helpers to track multiple steps as bits in an integer. Store each step in a bit and use the lowest bit to store whether all steps occurred in the correct order and only once.
Use this for the constructor tests.
Signed-off-by: Benjamin Berg benjamin.berg@intel.com
---
v2: - Newly added patch --- .../selftests/nolibc/nolibc-test-linkage.c | 17 ++++++++-- tools/testing/selftests/nolibc/nolibc-test.c | 31 ++++++++++++++++--- 2 files changed, 42 insertions(+), 6 deletions(-)
diff --git a/tools/testing/selftests/nolibc/nolibc-test-linkage.c b/tools/testing/selftests/nolibc/nolibc-test-linkage.c index 0636d1b6e808..4435f389570b 100644 --- a/tools/testing/selftests/nolibc/nolibc-test-linkage.c +++ b/tools/testing/selftests/nolibc/nolibc-test-linkage.c @@ -4,6 +4,19 @@
#include <errno.h>
+/* + * Set BIT(step + 1), BIT(0) shows whether all steps ran once and in order + * + * Copied from nolibc-test.c. + */ +#define MARK_STEP_DONE(val, step) do { \ + if ((val) == 0 && (step) == 0) \ + (val) |= 0x1; \ + else if (!(val & (1 << (step))) || (val) & (1 << ((step) + 1))) \ + (val) &= ~0x1; \ + (val) |= 1 << ((step) + 1); \ + } while (0) + void *linkage_test_errno_addr(void) { return &errno; @@ -14,11 +27,11 @@ int linkage_test_constructor_test_value = 0; __attribute__((constructor)) static void constructor1(void) { - linkage_test_constructor_test_value |= 1 << 0; + MARK_STEP_DONE(linkage_test_constructor_test_value, 0); }
__attribute__((constructor)) static void constructor2(void) { - linkage_test_constructor_test_value |= 1 << 1; + MARK_STEP_DONE(linkage_test_constructor_test_value, 1); } diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 97efc98b6a3d..d612150d2ea3 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -72,6 +72,15 @@ static const int is_nolibc = #endif ;
+/* Set BIT(step + 1), BIT(0) shows whether all steps ran once and in order */ +#define MARK_STEP_DONE(val, step) do { \ + if ((val) == 0 && (step) == 0) \ + (val) |= 0x1; \ + else if ((val) & (1 << ((step) + 1)) || !(val & (1 << (step)))) \ + (val) &= ~0x1; \ + (val) |= 1 << ((step) + 1); \ + } while (0) + /* definition of a series of tests */ struct test { const char *name; /* test name */ @@ -389,6 +398,20 @@ int expect_syserr2(int expr, int expret, int experr1, int experr2, int llen) }
+#define EXPECT_STEPS(cond, expr, num_steps) \ + do { if (!(cond)) result(llen, SKIPPED); else ret += expect_steps(expr, llen, num_steps); } while (0) + +static __attribute__((unused)) +int expect_steps(uint64_t expr, int llen, int num_steps) +{ + int ret = !(expr == ((uint64_t)1 << (num_steps + 1)) - 1); + + llen += printf(" = %llx ", (long long)expr); + result(llen, ret ? FAIL : OK); + return ret; +} + + #define EXPECT_PTRZR(cond, expr) \ do { if (!(cond)) result(llen, SKIPPED); else ret += expect_ptrzr(expr, llen); } while (0)
@@ -690,14 +713,14 @@ int expect_strtox(int llen, void *func, const char *input, int base, intmax_t ex __attribute__((constructor)) static void constructor1(void) { - constructor_test_value |= 1 << 0; + MARK_STEP_DONE(constructor_test_value, 0); }
__attribute__((constructor)) static void constructor2(int argc, char **argv, char **envp) { if (argc && argv && envp) - constructor_test_value |= 1 << 1; + MARK_STEP_DONE(constructor_test_value, 1); }
int run_startup(int min, int max) @@ -736,9 +759,9 @@ int run_startup(int min, int max) CASE_TEST(environ_HOME); EXPECT_PTRNZ(1, getenv("HOME")); break; CASE_TEST(auxv_addr); EXPECT_PTRGT(test_auxv != (void *)-1, test_auxv, brk); break; CASE_TEST(auxv_AT_UID); EXPECT_EQ(1, getauxval(AT_UID), getuid()); break; - CASE_TEST(constructor); EXPECT_EQ(is_nolibc, constructor_test_value, 0x3); break; + CASE_TEST(constructor); EXPECT_STEPS(is_nolibc, constructor_test_value, 2); break; CASE_TEST(linkage_errno); EXPECT_PTREQ(1, linkage_test_errno_addr(), &errno); break; - CASE_TEST(linkage_constr); EXPECT_EQ(1, linkage_test_constructor_test_value, 0x3); break; + CASE_TEST(linkage_constr); EXPECT_STEPS(1, linkage_test_constructor_test_value, 2); break; case __LINE__: return ret; /* must be last */ /* note: do not set any defaults so as to permit holes above */
On 2025-07-10 12:39:48+0200, Benjamin Berg wrote:
From: Benjamin Berg benjamin.berg@intel.com
Add new helpers to track multiple steps as bits in an integer. Store each step in a bit and use the lowest bit to store whether all steps occurred in the correct order and only once.
Use this for the constructor tests.
We actually stopped validating the constructor execution order in commit a782d45c867c ("selftests/nolibc: stop testing constructor order") Sorry for my misinformation.
I think we can keep the scheme for the signal tests. but I would prefer a function instead of a macro.
(...)
From: Benjamin Berg benjamin.berg@intel.com
The FD_* macros are assuming a specific type for the bitmask. Add new macros that introspect the type of the passed variable in order to know the size of the bitmask. This way the same macros can be used for other purposes.
Signed-off-by: Benjamin Berg benjamin.berg@intel.com
---
v2: - Rename the macros for consistency and to mark them internal - Fix shift to use the correct element type --- tools/include/nolibc/types.h | 72 ++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 32 deletions(-)
diff --git a/tools/include/nolibc/types.h b/tools/include/nolibc/types.h index 16c6e9ec9451..f7f2ddf41e89 100644 --- a/tools/include/nolibc/types.h +++ b/tools/include/nolibc/types.h @@ -115,48 +115,56 @@ #define EXIT_SUCCESS 0 #define EXIT_FAILURE 1
-#define FD_SETIDXMASK (8 * sizeof(unsigned long)) -#define FD_SETBITMASK (8 * sizeof(unsigned long)-1) - -/* for select() */ -typedef struct { - unsigned long fds[(FD_SETSIZE + FD_SETBITMASK) / FD_SETIDXMASK]; -} fd_set; - -#define FD_CLR(fd, set) do { \ - fd_set *__set = (set); \ - int __fd = (fd); \ - if (__fd >= 0) \ - __set->fds[__fd / FD_SETIDXMASK] &= \ - ~(1U << (__fd & FD_SETBITMASK)); \ +#define __NOLIBC_BITMASK_CLEAR(num, set) do { \ + __typeof__(set) *__set = &(set); \ + int __num = (num); \ + __typeof__(**__set) __bit = 1; \ + if (__num >= 0 && __num < 8 * (ssize_t)sizeof(*__set)) \ + (*__set)[__num / (8 * sizeof(**__set))] &= \ + ~(__bit << (__num % (8 * sizeof(**__set)))); \ } while (0)
-#define FD_SET(fd, set) do { \ - fd_set *__set = (set); \ - int __fd = (fd); \ - if (__fd >= 0) \ - __set->fds[__fd / FD_SETIDXMASK] |= \ - 1 << (__fd & FD_SETBITMASK); \ +#define __NOLIBC_BITMASK_SET(num, set) do { \ + __typeof__(set) *__set = &(set); \ + int __num = (num); \ + __typeof__(**__set) __bit = 1; \ + if (__num >= 0 && __num < 8 * (ssize_t)sizeof(*__set)) \ + (*__set)[__num / (8 * sizeof(**__set))] |= \ + __bit << (__num % (8 * sizeof(**__set))); \ } while (0)
-#define FD_ISSET(fd, set) ({ \ - fd_set *__set = (set); \ - int __fd = (fd); \ - int __r = 0; \ - if (__fd >= 0) \ - __r = !!(__set->fds[__fd / FD_SETIDXMASK] & \ -1U << (__fd & FD_SETBITMASK)); \ - __r; \ +#define __NOLIBC_BITMASK_TEST(num, set) ({ \ + __typeof__(set) *__set = &(set); \ + int __num = (num); \ + __typeof__(**__set) __r = 0; \ + __typeof__(**__set) __bit = 1; \ + if (__num >= 0 && __num < 8 * (ssize_t)sizeof(*__set)) \ + __r = (*__set)[__num / (8 * sizeof(**__set))] & \ + (__bit << (__num % (8 * sizeof(**__set)))); \ + !!__r; \ })
-#define FD_ZERO(set) do { \ - fd_set *__set = (set); \ +#define __NOLIBC_BITMASK_ZERO(set) do { \ + __typeof__(set) *__set = &(set); \ int __idx; \ - int __size = (FD_SETSIZE+FD_SETBITMASK) / FD_SETIDXMASK;\ + int __size = sizeof(*__set) / sizeof(**__set); \ for (__idx = 0; __idx < __size; __idx++) \ - __set->fds[__idx] = 0; \ + (*__set)[__idx] = 0; \ } while (0)
+#define FD_SETIDXMASK (8 * sizeof(unsigned long)) +#define FD_SETBITMASK (8 * sizeof(unsigned long)-1) + +/* for select() */ +typedef struct { + unsigned long fds[(FD_SETSIZE + FD_SETBITMASK) / FD_SETIDXMASK]; +} fd_set; + +#define FD_CLR(fd, set) __NOLIBC_BITMASK_CLEAR(fd, (set)->fds) +#define FD_SET(fd, set) __NOLIBC_BITMASK_SET(fd, (set)->fds) +#define FD_ISSET(fd, set) __NOLIBC_BITMASK_TEST(fd, (set)->fds) +#define FD_ZERO(set) __NOLIBC_BITMASK_ZERO((set)->fds) + /* for getdents64() */ struct linux_dirent64 { uint64_t d_ino;
From: Benjamin Berg benjamin.berg@intel.com
Add support for sigaction() and implement the normal sa_mask helpers.
On many architectures, linux/signal.h pulls in compatibility definitions for the old sigaction syscall instead of rt_sigaction. However, the kernel can be compiled without support for this compatibility syscall and it also results in sa_mask to be too small for realtime signals.
To work around this, the includes are handled separately for each architecture. This way either linux/signal.h or the asm-generic headers can be used to get the correct definition for the rt_sigaction syscall including sigset_t.
Signed-off-by: Benjamin Berg benjamin.berg@intel.com
---
v2: - Use newly added macros to check signal emission order - Add tests for sigset handling - Restore the default handler after signal test - make signal_check variable static
v1: - Update architecture support (adding sh) - Move sparc sys_rt_sigaction logic into its header - Add sig_atomic_t - Use new BITSET_* macros - Move test into syscall suite - Various other small changes --- tools/include/nolibc/arch-arm.h | 7 ++ tools/include/nolibc/arch-arm64.h | 3 + tools/include/nolibc/arch-loongarch.h | 3 + tools/include/nolibc/arch-m68k.h | 10 ++ tools/include/nolibc/arch-mips.h | 3 + tools/include/nolibc/arch-powerpc.h | 8 ++ tools/include/nolibc/arch-riscv.h | 3 + tools/include/nolibc/arch-s390.h | 8 +- tools/include/nolibc/arch-sh.h | 5 + tools/include/nolibc/arch-sparc.h | 47 ++++++++ tools/include/nolibc/arch-x86.h | 13 +++ tools/include/nolibc/signal.h | 103 +++++++++++++++++ tools/include/nolibc/sys.h | 2 +- tools/include/nolibc/time.h | 3 +- tools/include/nolibc/types.h | 9 ++ tools/testing/selftests/nolibc/nolibc-test.c | 115 +++++++++++++++++++ 16 files changed, 338 insertions(+), 4 deletions(-)
diff --git a/tools/include/nolibc/arch-arm.h b/tools/include/nolibc/arch-arm.h index 1f66e7e5a444..1faf6c2dbeb8 100644 --- a/tools/include/nolibc/arch-arm.h +++ b/tools/include/nolibc/arch-arm.h @@ -10,6 +10,13 @@ #include "compiler.h" #include "crt.h"
+/* Needed to get the correct struct sigaction definition */ +#define SA_RESTORER 0x04000000 + +/* Avoid linux/signal.h, it has an incorrect _NSIG and sigset_t */ +#include <asm-generic/signal.h> +#include <asm-generic/siginfo.h> + /* Syscalls for ARM in ARM or Thumb modes : * - registers are 32-bit * - stack is 8-byte aligned diff --git a/tools/include/nolibc/arch-arm64.h b/tools/include/nolibc/arch-arm64.h index 02a3f74c8ec8..ad14fc0ae5cb 100644 --- a/tools/include/nolibc/arch-arm64.h +++ b/tools/include/nolibc/arch-arm64.h @@ -10,6 +10,9 @@ #include "compiler.h" #include "crt.h"
+/* Architecture has a usable linux/signal.h */ +#include <linux/signal.h> + /* Syscalls for ARM64 : * - registers are 64-bit * - stack is 16-byte aligned diff --git a/tools/include/nolibc/arch-loongarch.h b/tools/include/nolibc/arch-loongarch.h index 5511705303ea..68d60d04ef59 100644 --- a/tools/include/nolibc/arch-loongarch.h +++ b/tools/include/nolibc/arch-loongarch.h @@ -10,6 +10,9 @@ #include "compiler.h" #include "crt.h"
+/* Architecture has a usable linux/signal.h */ +#include <linux/signal.h> + /* Syscalls for LoongArch : * - stack is 16-byte aligned * - syscall number is passed in a7 diff --git a/tools/include/nolibc/arch-m68k.h b/tools/include/nolibc/arch-m68k.h index 6dac1845f298..981b4cc55a69 100644 --- a/tools/include/nolibc/arch-m68k.h +++ b/tools/include/nolibc/arch-m68k.h @@ -13,6 +13,16 @@ #include "compiler.h" #include "crt.h"
+/* + * Needed to get the correct struct sigaction definition. m68k does not use + * sa_restorer, but it is included in the structure. + */ +#define SA_RESTORER 0x04000000 + +/* Avoid linux/signal.h, it has an incorrect _NSIG and sigset_t */ +#include <asm-generic/signal.h> +#include <asm-generic/siginfo.h> + #define _NOLIBC_SYSCALL_CLOBBERLIST "memory"
#define my_syscall0(num) \ diff --git a/tools/include/nolibc/arch-mips.h b/tools/include/nolibc/arch-mips.h index 0cbac63b249a..fb2f503f151f 100644 --- a/tools/include/nolibc/arch-mips.h +++ b/tools/include/nolibc/arch-mips.h @@ -14,6 +14,9 @@ #error Unsupported MIPS ABI #endif
+/* Architecture has a usable linux/signal.h */ +#include <linux/signal.h> + /* Syscalls for MIPS ABI O32 : * - WARNING! there's always a delayed slot! * - WARNING again, the syntax is different, registers take a '$' and numbers diff --git a/tools/include/nolibc/arch-powerpc.h b/tools/include/nolibc/arch-powerpc.h index 204564bbcd32..c846a7ddcf3c 100644 --- a/tools/include/nolibc/arch-powerpc.h +++ b/tools/include/nolibc/arch-powerpc.h @@ -10,6 +10,14 @@ #include "compiler.h" #include "crt.h"
+/* Needed to get the correct struct sigaction definition */ +#define SA_RESTORER 0x04000000 +#define _NOLIBC_ARCH_NEEDS_SA_RESTORER + +/* Avoid linux/signal.h, it has an incorrect _NSIG and sigset_t */ +#include <asm-generic/signal.h> +#include <asm-generic/siginfo.h> + /* Syscalls for PowerPC : * - stack is 16-byte aligned * - syscall number is passed in r0 diff --git a/tools/include/nolibc/arch-riscv.h b/tools/include/nolibc/arch-riscv.h index 885383a86c38..709e6a262d9a 100644 --- a/tools/include/nolibc/arch-riscv.h +++ b/tools/include/nolibc/arch-riscv.h @@ -10,6 +10,9 @@ #include "compiler.h" #include "crt.h"
+/* Architecture has a usable linux/signal.h */ +#include <linux/signal.h> + /* Syscalls for RISCV : * - stack is 16-byte aligned * - syscall number is passed in a7 diff --git a/tools/include/nolibc/arch-s390.h b/tools/include/nolibc/arch-s390.h index df4c3cc713ac..0dccb6d1ad64 100644 --- a/tools/include/nolibc/arch-s390.h +++ b/tools/include/nolibc/arch-s390.h @@ -5,13 +5,19 @@
#ifndef _NOLIBC_ARCH_S390_H #define _NOLIBC_ARCH_S390_H -#include <linux/signal.h> #include <linux/unistd.h>
#include "compiler.h" #include "crt.h" #include "std.h"
+/* Needed to get the correct struct sigaction definition */ +#define SA_RESTORER 0x04000000 + +/* Avoid linux/signal.h, it has an incorrect _NSIG and sigset_t */ +#include <asm-generic/signal.h> +#include <asm-generic/siginfo.h> + /* Syscalls for s390: * - registers are 64-bit * - syscall number is passed in r1 diff --git a/tools/include/nolibc/arch-sh.h b/tools/include/nolibc/arch-sh.h index a96b8914607e..3378afc78e26 100644 --- a/tools/include/nolibc/arch-sh.h +++ b/tools/include/nolibc/arch-sh.h @@ -7,9 +7,14 @@ #ifndef _NOLIBC_ARCH_SH_H #define _NOLIBC_ARCH_SH_H
+#include <linux/unistd.h> + #include "compiler.h" #include "crt.h"
+/* Architecture has a usable linux/signal.h */ +#include <linux/signal.h> + /* * Syscalls for SuperH: * - registers are 32bit wide diff --git a/tools/include/nolibc/arch-sparc.h b/tools/include/nolibc/arch-sparc.h index ca420d843e25..c9574e7f795a 100644 --- a/tools/include/nolibc/arch-sparc.h +++ b/tools/include/nolibc/arch-sparc.h @@ -12,6 +12,10 @@ #include "compiler.h" #include "crt.h"
+/* The includes are sane, if one sets __WANT_POSIX1B_SIGNALS__ */ +#define __WANT_POSIX1B_SIGNALS__ +#include <linux/signal.h> + /* * Syscalls for SPARC: * - registers are native word size @@ -204,4 +208,47 @@ pid_t sys_vfork(void) } #define sys_vfork sys_vfork
+#define __nolibc_sa_restorer __nolibc_sa_restorer +void __nolibc_sa_restorer(void); +void __nolibc_sa_restorer_wrapper(void); +void __attribute__((weak,noreturn)) __nolibc_entrypoint __no_stack_protector +__nolibc_sa_restorer_wrapper(void) +{ + /* The C function will have a prologue corrupting "sp" */ + __asm__ volatile ( + ".section .text\n" + ".align 4\n" + ".type __nolibc_sa_restorer, @function\n" + "__nolibc_sa_restorer:\n" + "nop\n" + "nop\n" + "mov %0, %%g1 \n" +#ifdef __arch64__ + "t 0x6d\n" +#else + "t 0x10\n" +#endif + ".size __nolibc_sa_restorer, .-__nolibc_sa_restorer\n" + :: "n"(__NR_rt_sigreturn) + ); + __nolibc_entrypoint_epilogue(); +} + +/* + * sparc has ODD_RT_SIGACTION, we need to pass the restorer as an argument + * to rt_sigaction. + */ +#define sys_rt_sigaction sys_rt_sigaction +static __attribute__((unused)) +int sys_rt_sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) +{ + struct sigaction real_act = *act; + + /* Otherwise we would need to use sigreturn instead of rt_sigreturn */ + real_act.sa_flags |= SA_SIGINFO; + + return my_syscall5(__NR_rt_sigaction, signum, &real_act, oldact, + __nolibc_sa_restorer, sizeof(act->sa_mask)); +} + #endif /* _NOLIBC_ARCH_SPARC_H */ diff --git a/tools/include/nolibc/arch-x86.h b/tools/include/nolibc/arch-x86.h index d3efc0c3b8ad..1fe75203d834 100644 --- a/tools/include/nolibc/arch-x86.h +++ b/tools/include/nolibc/arch-x86.h @@ -10,8 +10,21 @@ #include "compiler.h" #include "crt.h"
+/* Needed to get the correct struct sigaction definition */ +#define SA_RESTORER 0x04000000 + +/* Restorer must be set on x86 for both 32 and 64 bit */ +#define _NOLIBC_ARCH_NEEDS_SA_RESTORER + +/* Avoid linux/signal.h, it has an incorrect _NSIG and sigset_t */ +#include <asm-generic/signal.h> +#include <asm-generic/siginfo.h> + #if !defined(__x86_64__)
+/* On i386 we need to set SA_SIGINFO to use rt_sigreturn */ +#define _NOLIBC_ARCH_FORCE_SIG_FLAGS SA_SIGINFO + /* Syscalls for i386 : * - mostly similar to x86_64 * - registers are 32-bit diff --git a/tools/include/nolibc/signal.h b/tools/include/nolibc/signal.h index ac13e53ac31d..16b8b17496bc 100644 --- a/tools/include/nolibc/signal.h +++ b/tools/include/nolibc/signal.h @@ -14,6 +14,14 @@ #include "arch.h" #include "types.h" #include "sys.h" +#include "string.h" +/* other signal definitions are included by arch.h */ + +/* The kernel headers do not provide a sig_atomic_t definition */ +#ifndef __sig_atomic_t_defined +#define __sig_atomic_t_defined 1 +typedef int sig_atomic_t; +#endif
/* This one is not marked static as it's needed by libgcc for divide by zero */ int raise(int signal); @@ -23,4 +31,99 @@ int raise(int signal) return sys_kill(sys_getpid(), signal); }
+/* + * sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) + */ +#if defined(_NOLIBC_ARCH_NEEDS_SA_RESTORER) && !defined(__nolibc_sa_restorer) +static __no_stack_protector +void __nolibc_sa_restorer(void) +{ + my_syscall0(__NR_rt_sigreturn); +} +#endif + +#ifndef sys_rt_sigaction +static __attribute__((unused)) +int sys_rt_sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) +{ + struct sigaction real_act = *act; +#if defined(_NOLIBC_ARCH_NEEDS_SA_RESTORER) + if (!(real_act.sa_flags & SA_RESTORER)) { + real_act.sa_flags |= SA_RESTORER; + real_act.sa_restorer = __nolibc_sa_restorer; + } +#endif +#ifdef _NOLIBC_ARCH_FORCE_SIG_FLAGS + real_act.sa_flags |= _NOLIBC_ARCH_FORCE_SIG_FLAGS; +#endif + + return my_syscall4(__NR_rt_sigaction, signum, &real_act, oldact, + sizeof(act->sa_mask)); +} +#endif + +static __attribute__((unused)) +int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) +{ + return __sysret(sys_rt_sigaction(signum, act, oldact)); +} + +/* + * int sigemptyset(sigset_t *set) + */ +static __attribute__((unused)) +int sigemptyset(sigset_t *set) +{ + __NOLIBC_BITMASK_ZERO(set->sig); + return 0; +} + +/* + * int sigfillset(sigset_t *set) + */ +static __attribute__((unused)) +int sigfillset(sigset_t *set) +{ + __NOLIBC_BITMASK_FILL(set->sig); + return 0; +} + +/* + * int sigaddset(sigset_t *set, int signum) + */ +static __attribute__((unused)) +int sigaddset(sigset_t *set, int signum) +{ + if (signum < 1 || signum > _NSIG) + return __sysret(-EINVAL); + + __NOLIBC_BITMASK_SET(signum - 1, set->sig); + return 0; +} + +/* + * int sigdelset(sigset_t *set, int signum) + */ +static __attribute__((unused)) +int sigdelset(sigset_t *set, int signum) +{ + if (signum < 1 || signum > _NSIG) + return __sysret(-EINVAL); + + __NOLIBC_BITMASK_CLEAR(signum - 1, set->sig); + return 0; +} + +/* + * int sigismember(sigset_t *set, int signum) + */ +static __attribute__((unused)) +int sigismember(sigset_t *set, int signum) +{ + if (signum < 1 || signum > _NSIG) + return __sysret(-EINVAL); + + return __NOLIBC_BITMASK_TEST(signum - 1, set->sig); +} + #endif /* _NOLIBC_SIGNAL_H */ diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h index 295e71d34aba..73b935576561 100644 --- a/tools/include/nolibc/sys.h +++ b/tools/include/nolibc/sys.h @@ -14,7 +14,6 @@
/* system includes */ #include <linux/unistd.h> -#include <linux/signal.h> /* for SIGCHLD */ #include <linux/termios.h> #include <linux/mman.h> #include <linux/fs.h> @@ -24,6 +23,7 @@ #include <linux/fcntl.h> /* for O_* and AT_* */ #include <linux/sched.h> /* for clone_args */ #include <linux/stat.h> /* for statx() */ +/* signal definitions are included by arch.h */
#include "errno.h" #include "stdarg.h" diff --git a/tools/include/nolibc/time.h b/tools/include/nolibc/time.h index d02bc44d2643..103574f76515 100644 --- a/tools/include/nolibc/time.h +++ b/tools/include/nolibc/time.h @@ -14,9 +14,8 @@ #include "arch.h" #include "types.h" #include "sys.h" - -#include <linux/signal.h> #include <linux/time.h> +/* signal definitions are included by arch.h */
static __inline__ void __nolibc_timespec_user_to_kernel(const struct timespec *ts, struct __kernel_timespec *kts) diff --git a/tools/include/nolibc/types.h b/tools/include/nolibc/types.h index f7f2ddf41e89..7e205386b72c 100644 --- a/tools/include/nolibc/types.h +++ b/tools/include/nolibc/types.h @@ -152,6 +152,15 @@ (*__set)[__idx] = 0; \ } while (0)
+#define __NOLIBC_BITMASK_FILL(set) do { \ + __typeof__(set) *__set = &(set); \ + int __idx; \ + int __size = sizeof(*__set) / sizeof(**__set); \ + __typeof__(**__set) __zero = 0; \ + for (__idx = 0; __idx < __size; __idx++) \ + (*__set)[__idx] = ~__zero; \ + } while (0) + #define FD_SETIDXMASK (8 * sizeof(unsigned long)) #define FD_SETBITMASK (8 * sizeof(unsigned long)-1)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index d612150d2ea3..fcd44b27cd5e 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1293,6 +1293,120 @@ int test_namespace(void) return ret; }
+sig_atomic_t signal_check; + +static void sighandler(int signum) +{ + if (signum == SIGUSR1) { + kill(getpid(), SIGUSR2); + /* The second step has not run because SIGUSR2 is masked */ + MARK_STEP_DONE(signal_check, 0); + } else { + MARK_STEP_DONE(signal_check, 1); + } +} + +int test_signals(int test_idx) +{ + struct sigaction sa = { + .sa_flags = 0, + .sa_handler = sighandler, + }; + struct sigaction sa_old = { + /* Anything other than SIG_DFL */ + .sa_handler = sighandler, + }; + int llen; /* line length */ + int ret = 0; + int res; + + signal_check = 0; + +#ifdef NOLIBC + /* Do some checks on sa_mask handling */ + sigfillset(&sa.sa_mask); + llen = printf(" sa_mask.sig[0] (full): "); + EXPECT_EQ(1, sa.sa_mask.sig[0], + ~(__typeof__(sa.sa_mask.sig[0]))0); + llen = printf(" sa_mask.sig[%d] (full): ", (int)_NSIG_WORDS - 1); + EXPECT_EQ(1, sa.sa_mask.sig[_NSIG_WORDS - 1], + ~(__typeof__(sa.sa_mask.sig[0]))0); + + sigemptyset(&sa.sa_mask); + llen = printf(" sa_mask.sig[0] (empty): "); + EXPECT_EQ(1, sa.sa_mask.sig[0], 0); + llen = printf(" sa_mask.sig[%d] (empty): ", (int)_NSIG_WORDS - 1); + EXPECT_EQ(1, sa.sa_mask.sig[_NSIG_WORDS - 1], 0); + + /* SIGUSR2 is always in the first word */ + sigaddset(&sa.sa_mask, SIGUSR2); + llen = printf(" sa_mask.sig[0] (SIGUSR2 set): "); + EXPECT_EQ(1, sa.sa_mask.sig[0], 1 << (SIGUSR2 - 1)); + + llen = printf(" sa_mask.sig[0] (test SIGUSR2): "); + EXPECT_NZ(1, sigismember(&sa.sa_mask, SIGUSR2)); + + sigdelset(&sa.sa_mask, SIGUSR2); + llen = printf(" sa_mask.sig[0] (SIGUSR2 unset): "); + EXPECT_ZR(1, sigismember(&sa.sa_mask, SIGUSR2)); + + /* _NSIG is the highest valid number and may not be in the first word */ + sigaddset(&sa.sa_mask, _NSIG); + llen = printf(" sa_mask.sig[%d] (_NSIG set): ", (int)_NSIG_WORDS - 1); + EXPECT_EQ(1, sa.sa_mask.sig[_NSIG_WORDS - 1], + 1UL << (_NSIG - (_NSIG_WORDS - 1) * _NSIG_BPW - 1)); + + llen = printf(" sa_mask.sig[%d] (test _NSIG): ", (int)_NSIG_WORDS - 1); + EXPECT_NZ(1, sigismember(&sa.sa_mask, _NSIG)); + + sigdelset(&sa.sa_mask, _NSIG); + llen = printf(" sa_mask.sig[%d] (_NSIG unset): ", (int)_NSIG_WORDS - 1); + EXPECT_ZR(1, sigismember(&sa.sa_mask, _NSIG)); +#endif + + /* sa_mask is empty at this point, set SIGUSR2 to verify masking */ + sigaddset(&sa.sa_mask, SIGUSR2); + + res = sigaction(SIGUSR1, &sa, &sa_old); + llen = printf(" register SIGUSR1: %d", res); + EXPECT_SYSZR(1, res); + if (res) + goto out; + + llen = printf(" sa_old.sa_handler: SIG_DFL (%p)", SIG_DFL); + EXPECT_PTREQ(1, SIG_DFL, sa_old.sa_handler); + if (res) + goto out; + + res = sigaction(SIGUSR2, &sa, NULL); + llen = printf(" register SIGUSR2: %d", res); + EXPECT_SYSZR(1, res); + if (res) + goto out; + + /* Trigger the first signal. */ + kill(getpid(), SIGUSR1); + + /* Check the two signal handlers ran in the expected order */ + llen = printf(" signal emission: "); + EXPECT_STEPS(1, signal_check, 2); + +out: + sa.sa_handler = SIG_DFL; + res = sigaction(SIGUSR1, &sa, NULL); + llen = printf(" restore SIGUSR1: %d", res); + EXPECT_SYSZR(1, res); + + res = sigaction(SIGUSR1, &sa, NULL); + llen = printf(" restore SIGUSR2: %d", res); + EXPECT_SYSZR(1, res); + + llen = printf("%d %s", test_idx, "sigaction"); + EXPECT_EQ(1, res, 0); + + return ret; +} + /* Run syscall tests between IDs <min> and <max>. * Return 0 on success, non-zero on failure. */ @@ -1421,6 +1535,7 @@ int run_syscall(int min, int max) CASE_TEST(syscall_noargs); EXPECT_SYSEQ(1, syscall(__NR_getpid), getpid()); break; CASE_TEST(syscall_args); EXPECT_SYSER(1, syscall(__NR_statx, 0, NULL, 0, 0, NULL), -1, EFAULT); break; CASE_TEST(namespace); EXPECT_SYSZR(euid0 && proc, test_namespace()); break; + case __LINE__: ret += test_signals(test); break; case __LINE__: return ret; /* must be last */ /* note: do not set any defaults so as to permit holes above */
On 2025-07-10 12:39:50+0200, Benjamin Berg wrote:
From: Benjamin Berg benjamin.berg@intel.com
Add support for sigaction() and implement the normal sa_mask helpers.
On many architectures, linux/signal.h pulls in compatibility definitions for the old sigaction syscall instead of rt_sigaction. However, the kernel can be compiled without support for this compatibility syscall and it also results in sa_mask to be too small for realtime signals.
To work around this, the includes are handled separately for each architecture. This way either linux/signal.h or the asm-generic headers can be used to get the correct definition for the rt_sigaction syscall including sigset_t.
I checked this against my WIP alpha support and there this scheme breaks. linux/signal.h provides the old compat types but the asm-generic variant provides an incorrect SIGCHLD.
Any ideas?
Signed-off-by: Benjamin Berg benjamin.berg@intel.com
v2:
- Use newly added macros to check signal emission order
- Add tests for sigset handling
- Restore the default handler after signal test
- make signal_check variable static
v1:
- Update architecture support (adding sh)
- Move sparc sys_rt_sigaction logic into its header
- Add sig_atomic_t
- Use new BITSET_* macros
- Move test into syscall suite
- Various other small changes
tools/include/nolibc/arch-arm.h | 7 ++ tools/include/nolibc/arch-arm64.h | 3 + tools/include/nolibc/arch-loongarch.h | 3 + tools/include/nolibc/arch-m68k.h | 10 ++ tools/include/nolibc/arch-mips.h | 3 + tools/include/nolibc/arch-powerpc.h | 8 ++ tools/include/nolibc/arch-riscv.h | 3 + tools/include/nolibc/arch-s390.h | 8 +- tools/include/nolibc/arch-sh.h | 5 + tools/include/nolibc/arch-sparc.h | 47 ++++++++ tools/include/nolibc/arch-x86.h | 13 +++ tools/include/nolibc/signal.h | 103 +++++++++++++++++ tools/include/nolibc/sys.h | 2 +- tools/include/nolibc/time.h | 3 +- tools/include/nolibc/types.h | 9 ++ tools/testing/selftests/nolibc/nolibc-test.c | 115 +++++++++++++++++++ 16 files changed, 338 insertions(+), 4 deletions(-)
(...)
diff --git a/tools/include/nolibc/signal.h b/tools/include/nolibc/signal.h index ac13e53ac31d..16b8b17496bc 100644 --- a/tools/include/nolibc/signal.h +++ b/tools/include/nolibc/signal.h @@ -14,6 +14,14 @@ #include "arch.h" #include "types.h" #include "sys.h" +#include "string.h"
Unnecessary now as memset() is not used anymore.
+/* other signal definitions are included by arch.h */
+/* The kernel headers do not provide a sig_atomic_t definition */ +#ifndef __sig_atomic_t_defined +#define __sig_atomic_t_defined 1 +typedef int sig_atomic_t; +#endif
(...)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index d612150d2ea3..fcd44b27cd5e 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1293,6 +1293,120 @@ int test_namespace(void) return ret; } +sig_atomic_t signal_check;
Still not static :-)
+static void sighandler(int signum) +{
- if (signum == SIGUSR1) {
kill(getpid(), SIGUSR2);
/* The second step has not run because SIGUSR2 is masked */
MARK_STEP_DONE(signal_check, 0);
- } else {
MARK_STEP_DONE(signal_check, 1);
- }
+}
+int test_signals(int test_idx) +{
- struct sigaction sa = {
.sa_flags = 0,
.sa_handler = sighandler,
- };
- struct sigaction sa_old = {
/* Anything other than SIG_DFL */
.sa_handler = sighandler,
- };
- int llen; /* line length */
- int ret = 0;
- int res;
- signal_check = 0;
+#ifdef NOLIBC
- /* Do some checks on sa_mask handling */
- sigfillset(&sa.sa_mask);
- llen = printf(" sa_mask.sig[0] (full): ");
- EXPECT_EQ(1, sa.sa_mask.sig[0],
~(__typeof__(sa.sa_mask.sig[0]))0);
- llen = printf(" sa_mask.sig[%d] (full): ", (int)_NSIG_WORDS - 1);
- EXPECT_EQ(1, sa.sa_mask.sig[_NSIG_WORDS - 1],
~(__typeof__(sa.sa_mask.sig[0]))0);
- sigemptyset(&sa.sa_mask);
- llen = printf(" sa_mask.sig[0] (empty): ");
- EXPECT_EQ(1, sa.sa_mask.sig[0], 0);
- llen = printf(" sa_mask.sig[%d] (empty): ", (int)_NSIG_WORDS - 1);
- EXPECT_EQ(1, sa.sa_mask.sig[_NSIG_WORDS - 1], 0);
- /* SIGUSR2 is always in the first word */
- sigaddset(&sa.sa_mask, SIGUSR2);
- llen = printf(" sa_mask.sig[0] (SIGUSR2 set): ");
- EXPECT_EQ(1, sa.sa_mask.sig[0], 1 << (SIGUSR2 - 1));
- llen = printf(" sa_mask.sig[0] (test SIGUSR2): ");
- EXPECT_NZ(1, sigismember(&sa.sa_mask, SIGUSR2));
- sigdelset(&sa.sa_mask, SIGUSR2);
- llen = printf(" sa_mask.sig[0] (SIGUSR2 unset): ");
- EXPECT_ZR(1, sigismember(&sa.sa_mask, SIGUSR2));
- /* _NSIG is the highest valid number and may not be in the first word */
- sigaddset(&sa.sa_mask, _NSIG);
- llen = printf(" sa_mask.sig[%d] (_NSIG set): ", (int)_NSIG_WORDS - 1);
- EXPECT_EQ(1, sa.sa_mask.sig[_NSIG_WORDS - 1],
1UL << (_NSIG - (_NSIG_WORDS - 1) * _NSIG_BPW - 1));
- llen = printf(" sa_mask.sig[%d] (test _NSIG): ", (int)_NSIG_WORDS - 1);
- EXPECT_NZ(1, sigismember(&sa.sa_mask, _NSIG));
- sigdelset(&sa.sa_mask, _NSIG);
- llen = printf(" sa_mask.sig[%d] (_NSIG unset): ", (int)_NSIG_WORDS - 1);
- EXPECT_ZR(1, sigismember(&sa.sa_mask, _NSIG));
+#endif
This is more testing than expected, but that's good. Could you move it to a dedicated test function, gated on "is_nolibc" and the #ifdef.
- /* sa_mask is empty at this point, set SIGUSR2 to verify masking */
- sigaddset(&sa.sa_mask, SIGUSR2);
- res = sigaction(SIGUSR1, &sa, &sa_old);
- llen = printf(" register SIGUSR1: %d", res);
- EXPECT_SYSZR(1, res);
- if (res)
goto out;
- llen = printf(" sa_old.sa_handler: SIG_DFL (%p)", SIG_DFL);
- EXPECT_PTREQ(1, SIG_DFL, sa_old.sa_handler);
- if (res)
goto out;
- res = sigaction(SIGUSR2, &sa, NULL);
- llen = printf(" register SIGUSR2: %d", res);
- EXPECT_SYSZR(1, res);
- if (res)
goto out;
- /* Trigger the first signal. */
- kill(getpid(), SIGUSR1);
- /* Check the two signal handlers ran in the expected order */
- llen = printf(" signal emission: ");
- EXPECT_STEPS(1, signal_check, 2);
+out:
- sa.sa_handler = SIG_DFL;
- res = sigaction(SIGUSR1, &sa, NULL);
- llen = printf(" restore SIGUSR1: %d", res);
- EXPECT_SYSZR(1, res);
- res = sigaction(SIGUSR1, &sa, NULL);
SIGUSR2?
- llen = printf(" restore SIGUSR2: %d", res);
- EXPECT_SYSZR(1, res);
- llen = printf("%d %s", test_idx, "sigaction");
- EXPECT_EQ(1, res, 0);
- return ret;
+}
/* Run syscall tests between IDs <min> and <max>.
- Return 0 on success, non-zero on failure.
*/ @@ -1421,6 +1535,7 @@ int run_syscall(int min, int max) CASE_TEST(syscall_noargs); EXPECT_SYSEQ(1, syscall(__NR_getpid), getpid()); break; CASE_TEST(syscall_args); EXPECT_SYSER(1, syscall(__NR_statx, 0, NULL, 0, 0, NULL), -1, EFAULT); break; CASE_TEST(namespace); EXPECT_SYSZR(euid0 && proc, test_namespace()); break;
case __LINE__: return ret; /* must be last */ /* note: do not set any defaults so as to permit holes above */case __LINE__: ret += test_signals(test); break;
-- 2.50.0
On Fri, 2025-07-11 at 07:40 +0200, Thomas Weißschuh wrote:
On 2025-07-10 12:39:50+0200, Benjamin Berg wrote:
From: Benjamin Berg benjamin.berg@intel.com
Add support for sigaction() and implement the normal sa_mask helpers.
On many architectures, linux/signal.h pulls in compatibility definitions for the old sigaction syscall instead of rt_sigaction. However, the kernel can be compiled without support for this compatibility syscall and it also results in sa_mask to be too small for realtime signals.
To work around this, the includes are handled separately for each architecture. This way either linux/signal.h or the asm-generic headers can be used to get the correct definition for the rt_sigaction syscall including sigset_t.
I checked this against my WIP alpha support and there this scheme breaks. linux/signal.h provides the old compat types but the asm-generic variant provides an incorrect SIGCHLD.
Any ideas?
I had a quick look, and I don't have a good idea really. For sparc there was at least a #define that permitted to get the correct version. But here, there is nothing.
Probably the best is to just copy in a modified version of signal.h with the correct definitions present. i.e. replace the "ifndef __KERNEL__" sections with something reasonable.
Signed-off-by: Benjamin Berg benjamin.berg@intel.com
v2:
- Use newly added macros to check signal emission order
- Add tests for sigset handling
- Restore the default handler after signal test
- make signal_check variable static
v1:
- Update architecture support (adding sh)
- Move sparc sys_rt_sigaction logic into its header
- Add sig_atomic_t
- Use new BITSET_* macros
- Move test into syscall suite
- Various other small changes
tools/include/nolibc/arch-arm.h | 7 ++ tools/include/nolibc/arch-arm64.h | 3 + tools/include/nolibc/arch-loongarch.h | 3 + tools/include/nolibc/arch-m68k.h | 10 ++ tools/include/nolibc/arch-mips.h | 3 + tools/include/nolibc/arch-powerpc.h | 8 ++ tools/include/nolibc/arch-riscv.h | 3 + tools/include/nolibc/arch-s390.h | 8 +- tools/include/nolibc/arch-sh.h | 5 + tools/include/nolibc/arch-sparc.h | 47 ++++++++ tools/include/nolibc/arch-x86.h | 13 +++ tools/include/nolibc/signal.h | 103 +++++++++++++++++ tools/include/nolibc/sys.h | 2 +- tools/include/nolibc/time.h | 3 +- tools/include/nolibc/types.h | 9 ++ tools/testing/selftests/nolibc/nolibc-test.c | 115 +++++++++++++++++++ 16 files changed, 338 insertions(+), 4 deletions(-)
(...)
diff --git a/tools/include/nolibc/signal.h b/tools/include/nolibc/signal.h index ac13e53ac31d..16b8b17496bc 100644 --- a/tools/include/nolibc/signal.h +++ b/tools/include/nolibc/signal.h @@ -14,6 +14,14 @@ #include "arch.h" #include "types.h" #include "sys.h" +#include "string.h"
Unnecessary now as memset() is not used anymore.
+/* other signal definitions are included by arch.h */
+/* The kernel headers do not provide a sig_atomic_t definition */ +#ifndef __sig_atomic_t_defined +#define __sig_atomic_t_defined 1 +typedef int sig_atomic_t; +#endif
(...)
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index d612150d2ea3..fcd44b27cd5e 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1293,6 +1293,120 @@ int test_namespace(void) return ret; } +sig_atomic_t signal_check;
Still not static :-)
Eeks.
+static void sighandler(int signum) +{
- if (signum == SIGUSR1) {
kill(getpid(), SIGUSR2);
/* The second step has not run because SIGUSR2 is
masked */
MARK_STEP_DONE(signal_check, 0);
- } else {
MARK_STEP_DONE(signal_check, 1);
- }
+}
+int test_signals(int test_idx) +{
- struct sigaction sa = {
.sa_flags = 0,
.sa_handler = sighandler,
- };
- struct sigaction sa_old = {
/* Anything other than SIG_DFL */
.sa_handler = sighandler,
- };
- int llen; /* line length */
- int ret = 0;
- int res;
- signal_check = 0;
+#ifdef NOLIBC
- /* Do some checks on sa_mask handling */
- sigfillset(&sa.sa_mask);
- llen = printf(" sa_mask.sig[0] (full): ");
- EXPECT_EQ(1, sa.sa_mask.sig[0],
~(__typeof__(sa.sa_mask.sig[0]))0);
- llen = printf(" sa_mask.sig[%d] (full): ",
(int)_NSIG_WORDS - 1);
- EXPECT_EQ(1, sa.sa_mask.sig[_NSIG_WORDS - 1],
~(__typeof__(sa.sa_mask.sig[0]))0);
- sigemptyset(&sa.sa_mask);
- llen = printf(" sa_mask.sig[0] (empty): ");
- EXPECT_EQ(1, sa.sa_mask.sig[0], 0);
- llen = printf(" sa_mask.sig[%d] (empty): ",
(int)_NSIG_WORDS - 1);
- EXPECT_EQ(1, sa.sa_mask.sig[_NSIG_WORDS - 1], 0);
- /* SIGUSR2 is always in the first word */
- sigaddset(&sa.sa_mask, SIGUSR2);
- llen = printf(" sa_mask.sig[0] (SIGUSR2 set): ");
- EXPECT_EQ(1, sa.sa_mask.sig[0], 1 << (SIGUSR2 - 1));
- llen = printf(" sa_mask.sig[0] (test SIGUSR2): ");
- EXPECT_NZ(1, sigismember(&sa.sa_mask, SIGUSR2));
- sigdelset(&sa.sa_mask, SIGUSR2);
- llen = printf(" sa_mask.sig[0] (SIGUSR2 unset): ");
- EXPECT_ZR(1, sigismember(&sa.sa_mask, SIGUSR2));
- /* _NSIG is the highest valid number and may not be in the
first word */
- sigaddset(&sa.sa_mask, _NSIG);
- llen = printf(" sa_mask.sig[%d] (_NSIG set): ",
(int)_NSIG_WORDS - 1);
- EXPECT_EQ(1, sa.sa_mask.sig[_NSIG_WORDS - 1],
1UL << (_NSIG - (_NSIG_WORDS - 1) * _NSIG_BPW
- 1));
- llen = printf(" sa_mask.sig[%d] (test _NSIG): ",
(int)_NSIG_WORDS - 1);
- EXPECT_NZ(1, sigismember(&sa.sa_mask, _NSIG));
- sigdelset(&sa.sa_mask, _NSIG);
- llen = printf(" sa_mask.sig[%d] (_NSIG unset): ",
(int)_NSIG_WORDS - 1);
- EXPECT_ZR(1, sigismember(&sa.sa_mask, _NSIG));
+#endif
This is more testing than expected, but that's good. Could you move it to a dedicated test function, gated on "is_nolibc" and the #ifdef.
It did find the bit shift type bug in the macros :-)
Benjamin
- /* sa_mask is empty at this point, set SIGUSR2 to verify
masking */
- sigaddset(&sa.sa_mask, SIGUSR2);
- res = sigaction(SIGUSR1, &sa, &sa_old);
- llen = printf(" register SIGUSR1: %d", res);
- EXPECT_SYSZR(1, res);
- if (res)
goto out;
- llen = printf(" sa_old.sa_handler: SIG_DFL (%p)",
SIG_DFL);
- EXPECT_PTREQ(1, SIG_DFL, sa_old.sa_handler);
- if (res)
goto out;
- res = sigaction(SIGUSR2, &sa, NULL);
- llen = printf(" register SIGUSR2: %d", res);
- EXPECT_SYSZR(1, res);
- if (res)
goto out;
- /* Trigger the first signal. */
- kill(getpid(), SIGUSR1);
- /* Check the two signal handlers ran in the expected order
*/
- llen = printf(" signal emission: ");
- EXPECT_STEPS(1, signal_check, 2);
+out:
- sa.sa_handler = SIG_DFL;
- res = sigaction(SIGUSR1, &sa, NULL);
- llen = printf(" restore SIGUSR1: %d", res);
- EXPECT_SYSZR(1, res);
- res = sigaction(SIGUSR1, &sa, NULL);
SIGUSR2?
- llen = printf(" restore SIGUSR2: %d", res);
- EXPECT_SYSZR(1, res);
- llen = printf("%d %s", test_idx, "sigaction");
- EXPECT_EQ(1, res, 0);
- return ret;
+}
/* Run syscall tests between IDs <min> and <max>. * Return 0 on success, non-zero on failure. */ @@ -1421,6 +1535,7 @@ int run_syscall(int min, int max) CASE_TEST(syscall_noargs); EXPECT_SYSEQ(1, syscall(__NR_getpid), getpid()); break; CASE_TEST(syscall_args); EXPECT_SYSER(1, syscall(__NR_statx, 0, NULL, 0, 0, NULL), -1, EFAULT); break; CASE_TEST(namespace); EXPECT_SYSZR(euid0 && proc, test_namespace()); break;
case __LINE__: ret +=
test_signals(test); break; case __LINE__: return ret; /* must be last */ /* note: do not set any defaults so as to permit holes above */ -- 2.50.0
On 2025-07-11 09:25:26+0200, Benjamin Berg wrote:
On Fri, 2025-07-11 at 07:40 +0200, Thomas Weißschuh wrote:
On 2025-07-10 12:39:50+0200, Benjamin Berg wrote:
From: Benjamin Berg benjamin.berg@intel.com
Add support for sigaction() and implement the normal sa_mask helpers.
On many architectures, linux/signal.h pulls in compatibility definitions for the old sigaction syscall instead of rt_sigaction. However, the kernel can be compiled without support for this compatibility syscall and it also results in sa_mask to be too small for realtime signals.
To work around this, the includes are handled separately for each architecture. This way either linux/signal.h or the asm-generic headers can be used to get the correct definition for the rt_sigaction syscall including sigset_t.
I checked this against my WIP alpha support and there this scheme breaks. linux/signal.h provides the old compat types but the asm-generic variant provides an incorrect SIGCHLD.
Any ideas?
I had a quick look, and I don't have a good idea really. For sparc there was at least a #define that permitted to get the correct version. But here, there is nothing.
Probably the best is to just copy in a modified version of signal.h with the correct definitions present. i.e. replace the "ifndef __KERNEL__" sections with something reasonable.
But then we can just have our own copy for every architecture, no? To me that looks like the best solution right now. These UAPI headers and APIs are a mess...
Thomas
On Sun, Jul 13, 2025 at 05:02:27PM +0200, Thomas Weißschuh wrote:
On 2025-07-11 09:25:26+0200, Benjamin Berg wrote:
On Fri, 2025-07-11 at 07:40 +0200, Thomas Weißschuh wrote:
On 2025-07-10 12:39:50+0200, Benjamin Berg wrote:
From: Benjamin Berg benjamin.berg@intel.com
Add support for sigaction() and implement the normal sa_mask helpers.
On many architectures, linux/signal.h pulls in compatibility definitions for the old sigaction syscall instead of rt_sigaction. However, the kernel can be compiled without support for this compatibility syscall and it also results in sa_mask to be too small for realtime signals.
To work around this, the includes are handled separately for each architecture. This way either linux/signal.h or the asm-generic headers can be used to get the correct definition for the rt_sigaction syscall including sigset_t.
I checked this against my WIP alpha support and there this scheme breaks. linux/signal.h provides the old compat types but the asm-generic variant provides an incorrect SIGCHLD.
Any ideas?
I had a quick look, and I don't have a good idea really. For sparc there was at least a #define that permitted to get the correct version. But here, there is nothing.
Probably the best is to just copy in a modified version of signal.h with the correct definitions present. i.e. replace the "ifndef __KERNEL__" sections with something reasonable.
But then we can just have our own copy for every architecture, no? To me that looks like the best solution right now.
I generally agree. Originally in nolibc, lots of stuff was copied and we only used very little of UAPI. As new architectures were supported, we took this opportunity for cleaning this up and relying more on UAPI, but that doesn't mean we need to get rid of our local definitions all the time, so if that helps, yeah clearly we can have our local copies and definitions.
Cheers, Willy
On Sun, 2025-07-13 at 17:50 +0200, Willy Tarreau wrote:
On Sun, Jul 13, 2025 at 05:02:27PM +0200, Thomas Weißschuh wrote:
On 2025-07-11 09:25:26+0200, Benjamin Berg wrote:
On Fri, 2025-07-11 at 07:40 +0200, Thomas Weißschuh wrote:
On 2025-07-10 12:39:50+0200, Benjamin Berg wrote:
From: Benjamin Berg benjamin.berg@intel.com
Add support for sigaction() and implement the normal sa_mask helpers.
On many architectures, linux/signal.h pulls in compatibility definitions for the old sigaction syscall instead of rt_sigaction. However, the kernel can be compiled without support for this compatibility syscall and it also results in sa_mask to be too small for realtime signals.
To work around this, the includes are handled separately for each architecture. This way either linux/signal.h or the asm-generic headers can be used to get the correct definition for the rt_sigaction syscall including sigset_t.
I checked this against my WIP alpha support and there this scheme breaks. linux/signal.h provides the old compat types but the asm-generic variant provides an incorrect SIGCHLD.
Any ideas?
I had a quick look, and I don't have a good idea really. For sparc there was at least a #define that permitted to get the correct version. But here, there is nothing.
Probably the best is to just copy in a modified version of signal.h with the correct definitions present. i.e. replace the "ifndef __KERNEL__" sections with something reasonable.
But then we can just have our own copy for every architecture, no? To me that looks like the best solution right now.
I generally agree. Originally in nolibc, lots of stuff was copied and we only used very little of UAPI. As new architectures were supported, we took this opportunity for cleaning this up and relying more on UAPI, but that doesn't mean we need to get rid of our local definitions all the time, so if that helps, yeah clearly we can have our local copies and definitions.
Not sure our own copy for every architecture is the best solution. Many of them work fine with the asm-generic headers, and using those where they apply seems pretty reasonable to me.
So, maybe that would be a good compromise? i.e. use asm-generic where possible and copy in a file otherwise. That seems slightly less convoluted and still avoids having to actually fix the architecture headers.
Benjamin
On 2025-07-14 14:52:23+0200, Benjamin Berg wrote:
On Sun, 2025-07-13 at 17:50 +0200, Willy Tarreau wrote:
On Sun, Jul 13, 2025 at 05:02:27PM +0200, Thomas Weißschuh wrote:
On 2025-07-11 09:25:26+0200, Benjamin Berg wrote:
On Fri, 2025-07-11 at 07:40 +0200, Thomas Weißschuh wrote:
On 2025-07-10 12:39:50+0200, Benjamin Berg wrote:
From: Benjamin Berg benjamin.berg@intel.com
Add support for sigaction() and implement the normal sa_mask helpers.
On many architectures, linux/signal.h pulls in compatibility definitions for the old sigaction syscall instead of rt_sigaction. However, the kernel can be compiled without support for this compatibility syscall and it also results in sa_mask to be too small for realtime signals.
To work around this, the includes are handled separately for each architecture. This way either linux/signal.h or the asm-generic headers can be used to get the correct definition for the rt_sigaction syscall including sigset_t.
I checked this against my WIP alpha support and there this scheme breaks. linux/signal.h provides the old compat types but the asm-generic variant provides an incorrect SIGCHLD.
Any ideas?
I had a quick look, and I don't have a good idea really. For sparc there was at least a #define that permitted to get the correct version. But here, there is nothing.
Probably the best is to just copy in a modified version of signal.h with the correct definitions present. i.e. replace the "ifndef __KERNEL__" sections with something reasonable.
But then we can just have our own copy for every architecture, no? To me that looks like the best solution right now.
I generally agree. Originally in nolibc, lots of stuff was copied and we only used very little of UAPI. As new architectures were supported, we took this opportunity for cleaning this up and relying more on UAPI, but that doesn't mean we need to get rid of our local definitions all the time, so if that helps, yeah clearly we can have our local copies and definitions.
Not sure our own copy for every architecture is the best solution. Many of them work fine with the asm-generic headers, and using those where they apply seems pretty reasonable to me.
I meant *one shared* copy for all architectures. It would essentially be the asm-generic definitions with some ifdeffery.
So, maybe that would be a good compromise? i.e. use asm-generic where possible and copy in a file otherwise. That seems slightly less convoluted and still avoids having to actually fix the architecture headers.
Does my proposal from above satisfy this?
Thomas
linux-kselftest-mirror@lists.linaro.org