From: Benjamin Berg <[email protected]>

In preparation to add tests that use it.

Note that some architectures do not have a usable linux/signal.h include
file. However, in those cases we can use asm-generic/signal.h instead.

Signed-off-by: Benjamin Berg <[email protected]>

---

Another attempt at signal handling for nolibc which should actually be
working. Some trickery is needed to get the right definition, but I feel
it is sufficiently clean this way.

Submitting this as RFC mostly because I do not yet have a proper patch
to add a test that uses the feature.

Benjamin
---
 tools/include/nolibc/arch-aarch64.h          |  3 +
 tools/include/nolibc/arch-arm.h              |  7 ++
 tools/include/nolibc/arch-i386.h             | 13 +++
 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-sparc.h            | 43 +++++++++
 tools/include/nolibc/arch-x86_64.h           | 10 ++
 tools/include/nolibc/signal.h                | 97 ++++++++++++++++++++
 tools/include/nolibc/sys.h                   |  2 +-
 tools/include/nolibc/time.h                  |  3 +-
 tools/testing/selftests/nolibc/nolibc-test.c | 52 +++++++++++
 15 files changed, 261 insertions(+), 4 deletions(-)

diff --git a/tools/include/nolibc/arch-aarch64.h 
b/tools/include/nolibc/arch-aarch64.h
index 937a348da42e..736aae6dbd47 100644
--- a/tools/include/nolibc/arch-aarch64.h
+++ b/tools/include/nolibc/arch-aarch64.h
@@ -10,6 +10,9 @@
 #include "compiler.h"
 #include "crt.h"
 
+/* Architecture has a usable linux/signal.h */
+#include <linux/signal.h>
+
 /* Syscalls for AARCH64 :
  *   - registers are 64-bit
  *   - stack is 16-byte aligned
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-i386.h b/tools/include/nolibc/arch-i386.h
index 7c9b38e96418..fbec7490a92c 100644
--- a/tools/include/nolibc/arch-i386.h
+++ b/tools/include/nolibc/arch-i386.h
@@ -10,6 +10,19 @@
 #include "compiler.h"
 #include "crt.h"
 
+/* Needed to get the correct struct sigaction definition */
+#define SA_RESTORER    0x04000000
+
+/* Restorer must be set on i386 */
+#define _NOLIBC_ARCH_NEEDS_SA_RESTORER
+
+/* Otherwise we would need to use sigreturn instead of rt_sigreturn */
+#define _NOLIBC_ARCH_FORCE_SIG_FLAGS SA_SIGINFO
+
+/* Avoid linux/signal.h, it has an incorrect _NSIG and sigset_t */
+#include <asm-generic/signal.h>
+#include <asm-generic/siginfo.h>
+
 /* Syscalls for i386 :
  *   - mostly similar to x86_64
  *   - registers are 32-bit
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 753a8ed2cf69..a8837452e744 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-sparc.h 
b/tools/include/nolibc/arch-sparc.h
index 1435172f3dfe..303291d4b8fb 100644
--- a/tools/include/nolibc/arch-sparc.h
+++ b/tools/include/nolibc/arch-sparc.h
@@ -12,6 +12,19 @@
 #include "compiler.h"
 #include "crt.h"
 
+/* Otherwise we would need to use sigreturn instead of rt_sigreturn */
+#define _NOLIBC_ARCH_FORCE_SIG_FLAGS SA_SIGINFO
+
+/* The includes are sane, if one sets __WANT_POSIX1B_SIGNALS__ */
+#define __WANT_POSIX1B_SIGNALS__
+#include <linux/signal.h>
+
+/*
+ * sparc has ODD_RT_SIGACTION, we always pass our restorer as an argument
+ * to rt_sigaction. The restorer is implemented in this file.
+ */
+#define _NOLIBC_RT_SIGACTION_PASSES_RESTORER
+
 /*
  * Syscalls for SPARC:
  *   - registers are native word size
@@ -188,4 +201,34 @@ pid_t sys_fork(void)
 }
 #define sys_fork sys_fork
 
+#define __nolibc_stringify_1(x...)     #x
+#define __nolibc_stringify(x...)       __stringify_1(x)
+
+/* The compiler insists on adding a SAVE call to the start of every function */
+#define __nolibc_sa_restorer __nolibc_sa_restorer
+void __nolibc_sa_restorer (void);
+#ifdef __arch64__
+__asm__(                                                        \
+       ".section .text\n"                                      \
+       ".align  4 \n"                                          \
+       "__nolibc_sa_restorer:\n"                               \
+       "nop\n"                                                 \
+       "nop\n"                                                 \
+       "mov     " __stringify(__NR_rt_sigreturn) ", %g1 \n"    \
+       "t       0x6d \n");
+#else
+__asm__(                                                        \
+       ".section .text\n"                                      \
+       ".align  4 \n"                                          \
+       "__nolibc_sa_restorer:\n"                               \
+       "nop\n"                                                 \
+       "nop\n"                                                 \
+       "mov     " __stringify(__NR_rt_sigreturn) ", %g1 \n"    \
+       "t       0x10 \n"                                       \
+       );
+#endif
+
+#undef __nolibc_stringify_1(x...)
+#undef __nolibc_stringify
+
 #endif /* _NOLIBC_ARCH_SPARC_H */
diff --git a/tools/include/nolibc/arch-x86_64.h 
b/tools/include/nolibc/arch-x86_64.h
index 67305e24dbef..9f13a2205876 100644
--- a/tools/include/nolibc/arch-x86_64.h
+++ b/tools/include/nolibc/arch-x86_64.h
@@ -10,6 +10,16 @@
 #include "compiler.h"
 #include "crt.h"
 
+/* Needed to get the correct struct sigaction definition */
+#define SA_RESTORER    0x04000000
+
+/* Restorer must be set on i386 */
+#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 x86_64 :
  *   - registers are 64-bit
  *   - syscall number is passed in rax
diff --git a/tools/include/nolibc/signal.h b/tools/include/nolibc/signal.h
index ac13e53ac31d..829672250ede 100644
--- a/tools/include/nolibc/signal.h
+++ b/tools/include/nolibc/signal.h
@@ -14,6 +14,8 @@
 #include "arch.h"
 #include "types.h"
 #include "sys.h"
+#include "string.h"
+/* signal definitions are included by arch.h */
 
 /* This one is not marked static as it's needed by libgcc for divide by zero */
 int raise(int signal);
@@ -23,4 +25,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 __attribute__((noreturn)) __nolibc_entrypoint __no_stack_protector
+void __nolibc_sa_restorer(void)
+{
+       my_syscall0(__NR_rt_sigreturn);
+       __nolibc_entrypoint_epilogue();
+}
+#endif
+
+static __attribute__((unused))
+int sys_rt_sigaction(int signum, const struct sigaction *act, struct sigaction 
*oldact)
+{
+       struct sigaction real_act = *act;
+#if defined(SA_RESTORER) && 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
+
+#ifndef _NOLIBC_RT_SIGACTION_PASSES_RESTORER
+       return my_syscall4(__NR_rt_sigaction, signum, &real_act, oldact,
+                          sizeof(act->sa_mask));
+#else
+       return my_syscall5(__NR_rt_sigaction, signum, &real_act, oldact,
+                          __nolibc_sa_restorer, 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)
+{
+       memset(set, 0, sizeof(*set));
+       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)
+{
+       set->sig[(signum - 1) / (8 * sizeof(set->sig[0]))] |=
+               1UL << ((signum - 1) % (8 * sizeof(set->sig[0])));
+       return 0;
+}
+
+/*
+ * int sigdelset(sigset_t *set, int signum)
+ */
+static __attribute__((unused))
+int sigdelset(sigset_t *set, int signum)
+{
+       set->sig[(signum - 1) / (8 * sizeof(set->sig[0]))] &=
+               ~(1UL << ((signum - 1) % (8 * sizeof(set->sig[0]))));
+       return 0;
+}
+
+/*
+ * int sigismember(sigset_t *set, int signum)
+ */
+static __attribute__((unused))
+int sigismember(sigset_t *set, int signum)
+{
+       unsigned long res =
+               set->sig[(signum - 1) / (8 * sizeof(set->sig[0]))] &
+                       (1UL << ((signum - 1) % (8 * sizeof(set->sig[0]))));
+       return !!res;
+}
+
 #endif /* _NOLIBC_SIGNAL_H */
diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h
index 9556c69a6ae1..fb9a1ccf2440 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>
@@ -23,6 +22,7 @@
 #include <linux/auxvec.h>
 #include <linux/fcntl.h> /* for O_* and AT_* */
 #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 fc387940d51f..70ef31d4117f 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 dbe13000fb1a..af66b739ea18 100644
--- a/tools/testing/selftests/nolibc/nolibc-test.c
+++ b/tools/testing/selftests/nolibc/nolibc-test.c
@@ -1750,6 +1750,57 @@ static int run_protection(int min 
__attribute__((unused)),
        }
 }
 
+volatile int signal_check;
+
+void test_sighandler(int signum)
+{
+       if (signum == SIGUSR1) {
+               kill(getpid(), SIGUSR2);
+               signal_check = 1;
+       } else {
+               signal_check++;
+       }
+}
+
+int run_signal(int min, int max)
+{
+       struct sigaction sa = {
+               .sa_flags = 0,
+               .sa_handler = test_sighandler,
+       };
+       int llen; /* line length */
+       int ret = 0;
+       int res;
+
+       (void)min;
+       (void)max;
+
+       signal_check = 0;
+
+       sigemptyset(&sa.sa_mask);
+       sigaddset(&sa.sa_mask, SIGUSR2);
+
+       res = sigaction(SIGUSR1, &sa, NULL);
+       llen = printf("register SIGUSR1: %d", res);
+       EXPECT_EQ(1, 0, res);
+       res = sigaction(SIGUSR2, &sa, NULL);
+       llen = printf("register SIGUSR2: %d", res);
+       EXPECT_EQ(1, 0, res);
+
+       /* 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);
+
+       return ret;
+}
+
 /* prepare what needs to be prepared for pid 1 (stdio, /dev, /proc, etc) */
 int prepare(void)
 {
@@ -1815,6 +1866,7 @@ static const struct test test_names[] = {
        { .name = "stdlib",     .func = run_stdlib     },
        { .name = "printf",     .func = run_printf     },
        { .name = "protection", .func = run_protection },
+       { .name = "signal",     .func = run_signal },
        { 0 }
 };
 
-- 
2.50.0


Reply via email to