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.
Benjamin
Benjamin Berg (3): tools/nolibc: show failed run if test process crashes tools/nolibc: add more generic BITSET_* 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 | 67 ++++++------ .../testing/selftests/nolibc/Makefile.nolibc | 3 +- tools/testing/selftests/nolibc/nolibc-test.c | 67 ++++++++++++ 17 files changed, 319 insertions(+), 36 deletions(-)
From: Benjamin Berg benjamin.berg@intel.com
The logic would not catch if the test process crashes and would incorrectly report a "success" state. Fix this by looking for the final "Total number of errors:" message and printing "failure" if it was not seen.
Signed-off-by: Benjamin Berg benjamin.berg@intel.com --- tools/testing/selftests/nolibc/Makefile.nolibc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/nolibc/Makefile.nolibc b/tools/testing/selftests/nolibc/Makefile.nolibc index 8cb65392fa10..51ba853dd97e 100644 --- a/tools/testing/selftests/nolibc/Makefile.nolibc +++ b/tools/testing/selftests/nolibc/Makefile.nolibc @@ -245,8 +245,9 @@ endif include $(srctree)/tools/scripts/Makefile.include
REPORT ?= awk '/[OK][\r]*$$/{p++} /[FAIL][\r]*$$/{if (!f) printf("\n"); f++; print;} /[SKIPPED][\r]*$$/{s++} \ + /^Total number of errors:/{done++} \ END{ printf("\n%3d test(s): %3d passed, %3d skipped, %3d failed => status: ", p+s+f, p, s, f); \ - if (f || !p) printf("failure\n"); else if (s) printf("warning\n"); else printf("success\n");; \ + if (f || !p || !done) printf("failure\n"); else if (s) printf("warning\n"); else printf("success\n");; \ printf("\nSee all results in %s\n", ARGV[1]); }'
help:
On 2025-07-09 17:55:10+0200, Benjamin Berg wrote:
From: Benjamin Berg benjamin.berg@intel.com
The logic would not catch if the test process crashes and would incorrectly report a "success" state. Fix this by looking for the final "Total number of errors:" message and printing "failure" if it was not seen.
Nice, I'll pick this up directly.
Signed-off-by: Benjamin Berg benjamin.berg@intel.com
tools/testing/selftests/nolibc/Makefile.nolibc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/nolibc/Makefile.nolibc b/tools/testing/selftests/nolibc/Makefile.nolibc index 8cb65392fa10..51ba853dd97e 100644 --- a/tools/testing/selftests/nolibc/Makefile.nolibc +++ b/tools/testing/selftests/nolibc/Makefile.nolibc @@ -245,8 +245,9 @@ endif include $(srctree)/tools/scripts/Makefile.include REPORT ?= awk '/[OK][\r]*$$/{p++} /[FAIL][\r]*$$/{if (!f) printf("\n"); f++; print;} /[SKIPPED][\r]*$$/{s++} \
END{ printf("\n%3d test(s): %3d passed, %3d skipped, %3d failed => status: ", p+s+f, p, s, f); \/^Total number of errors:/{done++} \
if (f || !p) printf("failure\n"); else if (s) printf("warning\n"); else printf("success\n");; \
printf("\nSee all results in %s\n", ARGV[1]); }'if (f || !p || !done) printf("failure\n"); else if (s) printf("warning\n"); else printf("success\n");; \
help: -- 2.50.0
From: Benjamin Berg benjamin.berg@intel.com
The FD_* macros are assuming a specific type for the bitset. Add new macros that introspect the type of the passed variable in order to know the size of the bitset. This way the same macros can be used for other purposes.
Signed-off-by: Benjamin Berg benjamin.berg@intel.com --- tools/include/nolibc/types.h | 67 +++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 31 deletions(-)
diff --git a/tools/include/nolibc/types.h b/tools/include/nolibc/types.h index 16c6e9ec9451..3100771e21ad 100644 --- a/tools/include/nolibc/types.h +++ b/tools/include/nolibc/types.h @@ -115,48 +115,53 @@ #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 BITSET_CLR(num, set) do { \ + __typeof__(set) *__set = &(set); \ + int __num = (num); \ + if (__num >= 0 && __num < 8 * (ssize_t)sizeof(*__set)) \ + (*__set)[__num / (8 * sizeof(set[0]))] &= \ + ~(1U << (__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 BITSET_SET(num, set) do { \ + __typeof__(set) *__set = &(set); \ + int __num = (num); \ + if (__num >= 0 && __num < 8 * (ssize_t)sizeof(*__set)) \ + (*__set)[__num / (8 * sizeof(set[0]))] |= \ + 1U << (__num % (8 * sizeof(**__set))); \ } while (0)
-#define FD_ISSET(fd, set) ({ \ - fd_set *__set = (set); \ - int __fd = (fd); \ +#define BITSET_ISSET(num, set) ({ \ + __typeof__(set) *__set = &(set); \ + int __num = (num); \ int __r = 0; \ - if (__fd >= 0) \ - __r = !!(__set->fds[__fd / FD_SETIDXMASK] & \ -1U << (__fd & FD_SETBITMASK)); \ - __r; \ + if (__num >= 0 && __num < 8 * (ssize_t)sizeof(*__set)) \ + __r = (*__set)[__num / (8 * sizeof(set[0]))] & \ + (1U << (__num % (8 * sizeof(**__set)))); \ + !!__r; \ })
-#define FD_ZERO(set) do { \ - fd_set *__set = (set); \ +#define BITSET_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) BITSET_CLR(fd, (set)->fds) +#define FD_SET(fd, set) BITSET_SET(fd, (set)->fds) +#define FD_ISSET(fd, set) BITSET_ISSET(fd, (set)->fds) +#define FD_ZERO(set) BITSET_ZERO((set)->fds) + /* for getdents64() */ struct linux_dirent64 { uint64_t d_ino;
On 2025-07-09 17:55:11+0200, Benjamin Berg wrote:
From: Benjamin Berg benjamin.berg@intel.com
The FD_* macros are assuming a specific type for the bitset. Add new macros that introspect the type of the passed variable in order to know the size of the bitset. This way the same macros can be used for other purposes.
Signed-off-by: Benjamin Berg benjamin.berg@intel.com
tools/include/nolibc/types.h | 67 +++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 31 deletions(-)
diff --git a/tools/include/nolibc/types.h b/tools/include/nolibc/types.h index 16c6e9ec9451..3100771e21ad 100644 --- a/tools/include/nolibc/types.h +++ b/tools/include/nolibc/types.h @@ -115,48 +115,53 @@ #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 BITSET_CLR(num, set) do { \
__NOLIBC_BITMASK_CLEAR()
To avoid conflicts with user code, being clear about the intent and having some constency with the kernel code.
__typeof__(set) *__set = &(set); \
int __num = (num); \
if (__num >= 0 && __num < 8 * (ssize_t)sizeof(*__set)) \
(*__set)[__num / (8 * sizeof(set[0]))] &= \
} while (0)~(1U << (__num % (8 * sizeof(**__set)))); \
-#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 BITSET_SET(num, set) do { \
__typeof__(set) *__set = &(set); \
int __num = (num); \
if (__num >= 0 && __num < 8 * (ssize_t)sizeof(*__set)) \
(*__set)[__num / (8 * sizeof(set[0]))] |= \
} while (0)1U << (__num % (8 * sizeof(**__set))); \
-#define FD_ISSET(fd, set) ({ \
fd_set *__set = (set); \
int __fd = (fd); \
+#define BITSET_ISSET(num, set) ({ \
__NOLIBC_BITMASK_TEST()
__typeof__(set) *__set = &(set); \
int __r = 0; \int __num = (num); \
if (__fd >= 0) \
__r = !!(__set->fds[__fd / FD_SETIDXMASK] & \
-1U << (__fd & FD_SETBITMASK)); \
__r; \
if (__num >= 0 && __num < 8 * (ssize_t)sizeof(*__set)) \
__r = (*__set)[__num / (8 * sizeof(set[0]))] & \
(1U << (__num % (8 * sizeof(**__set)))); \
})!!__r; \
-#define FD_ZERO(set) do { \
fd_set *__set = (set); \
+#define BITSET_ZERO(set) do { \
int __idx; \__typeof__(set) *__set = &(set); \
int __size = (FD_SETSIZE+FD_SETBITMASK) / FD_SETIDXMASK;\
for (__idx = 0; __idx < __size; __idx++) \int __size = sizeof(*__set) / sizeof(**__set); \
__set->fds[__idx] = 0; \
} while (0)(*__set)[__idx] = 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) BITSET_CLR(fd, (set)->fds) +#define FD_SET(fd, set) BITSET_SET(fd, (set)->fds) +#define FD_ISSET(fd, set) BITSET_ISSET(fd, (set)->fds) +#define FD_ZERO(set) BITSET_ZERO((set)->fds)
These could be inline function I think.
/* for getdents64() */ struct linux_dirent64 { uint64_t d_ino; -- 2.50.0
Hi Thomas,
On Wed, Jul 09, 2025 at 08:36:49PM +0200, Thomas Weißschuh wrote:
+#define FD_CLR(fd, set) BITSET_CLR(fd, (set)->fds) +#define FD_SET(fd, set) BITSET_SET(fd, (set)->fds) +#define FD_ISSET(fd, set) BITSET_ISSET(fd, (set)->fds) +#define FD_ZERO(set) BITSET_ZERO((set)->fds)
These could be inline function I think.
It's better not do, because man says they are macros, and I've seen programs using ifdef on them (not to mean that it's common but it's supposed to be possible according to the man).
Cheers, Willy
On 2025-07-09 20:44:43+0200, Willy Tarreau wrote:
Hi Thomas,
On Wed, Jul 09, 2025 at 08:36:49PM +0200, Thomas Weißschuh wrote:
+#define FD_CLR(fd, set) BITSET_CLR(fd, (set)->fds) +#define FD_SET(fd, set) BITSET_SET(fd, (set)->fds) +#define FD_ISSET(fd, set) BITSET_ISSET(fd, (set)->fds) +#define FD_ZERO(set) BITSET_ZERO((set)->fds)
These could be inline function I think.
It's better not do, because man says they are macros, and I've seen programs using ifdef on them (not to mean that it's common but it's supposed to be possible according to the man).
Ack. OTOH, we could define a noop-macro for the functions:
static __inline__ void FD_SET(...) { } #define FD_SET FD_SET
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
---
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
Signed-off-by: Benjamin Berg benjamin.berg@intel.com --- 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/testing/selftests/nolibc/nolibc-test.c | 67 ++++++++++++ 15 files changed, 281 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..cbba57012767 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) +{ + BITSET_ZERO(set->sig); + return 0; +} + +/* + * int sigfillset(sigset_t *set) + */ +static __attribute__((unused)) +int sigfillset(sigset_t *set) +{ + memset(set, 0xff, sizeof(*set)); + 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); + + BITSET_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); + + BITSET_CLR(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 BITSET_ISSET(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/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index a297ee0d6d07..0bed3270a867 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1270,6 +1270,72 @@ int test_namespace(void) return ret; }
+sig_atomic_t signal_check; + +static void sighandler(int signum) +{ + if (signum == SIGUSR1) { + kill(getpid(), SIGUSR2); + signal_check = 1; + } else { + signal_check++; + } +} + +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; + + sigemptyset(&sa.sa_mask); + 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); + + /* If signal_check is 1 or higher, then signal emission worked */ + llen = printf(" signal emission: 1 <= signal_check"); + EXPECT_GE(1, signal_check, 1); + + /* If it is 2, then signal masking worked */ + llen = printf(" signal masking: 2 == signal_check"); + EXPECT_EQ(1, signal_check, 2); + +out: + 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. */ @@ -1398,6 +1464,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-09 17:55:12+0200, Benjamin Berg wrote: (...)
--- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1270,6 +1270,72 @@ int test_namespace(void) return ret; } +sig_atomic_t signal_check;
static?
+static void sighandler(int signum) +{
- if (signum == SIGUSR1) {
kill(getpid(), SIGUSR2);
signal_check = 1;
- } else {
signal_check++;
- }
This could use some operations which make it clearer which steps occurred in which order. See the constructor tests.
+}
+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;
- sigemptyset(&sa.sa_mask);
- sigaddset(&sa.sa_mask, SIGUSR2);
I'd like to see some tests for the sigset functions.
- 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);
- /* If signal_check is 1 or higher, then signal emission worked */
- llen = printf(" signal emission: 1 <= signal_check");
- EXPECT_GE(1, signal_check, 1);
- /* If it is 2, then signal masking worked */
- llen = printf(" signal masking: 2 == signal_check");
- EXPECT_EQ(1, signal_check, 2);
+out:
- llen = printf("%d %s", test_idx, "sigaction");
- EXPECT_EQ(1, res, 0);
Restore the default handler afterwards?
- return ret;
+}
/* Run syscall tests between IDs <min> and <max>.
- Return 0 on success, non-zero on failure.
*/ @@ -1398,6 +1464,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 Wed, 2025-07-09 at 23:21 +0200, Thomas Weißschuh wrote:
On 2025-07-09 17:55:12+0200, Benjamin Berg wrote: (...)
--- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1270,6 +1270,72 @@ int test_namespace(void) return ret; } +sig_atomic_t signal_check;
static?
Oops.
+static void sighandler(int signum) +{
- if (signum == SIGUSR1) {
kill(getpid(), SIGUSR2);
signal_check = 1;
- } else {
signal_check++;
- }
This could use some operations which make it clearer which steps occurred in which order. See the constructor tests.
Could it be that the constructor test logic is buggy? It seems to simply OR the bottom two bits, but doing that will not verify the order.
Maybe something more generic like the below macro? i.e. use the bottom bit to store whether the order has been correct.
/* Set BIT(step + 1), BIT(0) shows whether all steps ran in order */ #define mark_step_done(v, step) do { \ v |= v & (1 << ((step) + 1)); \ if (v == 0 && (step) == 0) \ v |= 0x1; \ else if (!(v & (1 << (step))) \ v &= ~0x1; \ } while (0)
+}
+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;
- sigemptyset(&sa.sa_mask);
- sigaddset(&sa.sa_mask, SIGUSR2);
I'd like to see some tests for the sigset functions.
Heh, yeah. sigaddset is kind of tested indirectly when verifying the emission order. But that just makes it unclear where the test failed.
Benjamin
- 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);
- /* If signal_check is 1 or higher, then signal emission
worked */
- llen = printf(" signal emission: 1 <= signal_check");
- EXPECT_GE(1, signal_check, 1);
- /* If it is 2, then signal masking worked */
- llen = printf(" signal masking: 2 == signal_check");
- EXPECT_EQ(1, signal_check, 2);
+out:
- llen = printf("%d %s", test_idx, "sigaction");
- EXPECT_EQ(1, res, 0);
Restore the default handler afterwards?
- return ret;
+}
/* Run syscall tests between IDs <min> and <max>. * Return 0 on success, non-zero on failure. */ @@ -1398,6 +1464,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
linux-kselftest-mirror@lists.linaro.org