Better handling of sigset_t on LP32.

The main motivation here is that the sigprocmask in pthread_exit wasn't
actually blocking the real-time signals, and debuggerd (amongst other
things) is using them. I wasn't able to write a test that actually won
that race but I did write an equivalent one for posix_spawn.

This also fixes all the uses of sigset_t where the sigset_t isn't
exposed to the outside (which we can't easily fix because it would be
an ABI change).

Bug: https://issuetracker.google.com/72291624
Test: ran tests
Change-Id: Ib6eebebc5a7b0150079f1cb79593247917dcf750
diff --git a/libc/private/kernel_sigset_t.h b/libc/private/kernel_sigset_t.h
index 9415fcf..bdfb729 100644
--- a/libc/private/kernel_sigset_t.h
+++ b/libc/private/kernel_sigset_t.h
@@ -17,18 +17,27 @@
 #ifndef LIBC_PRIVATE_KERNEL_SIGSET_T_H_
 #define LIBC_PRIVATE_KERNEL_SIGSET_T_H_
 
+#include <errno.h>
 #include <signal.h>
 
+#include <async_safe/log.h>
+
 // Our sigset_t is wrong for ARM and x86. It's 32-bit but the kernel expects 64 bits.
-// This means we can't support real-time signals correctly until we can change the ABI.
+// This means we can't support real-time signals correctly without breaking the ABI.
 // In the meantime, we can use this union to pass an appropriately-sized block of memory
-// to the kernel, at the cost of not being able to refer to real-time signals.
+// to the kernel, at the cost of not being able to refer to real-time signals when
+// initializing from a sigset_t on LP32.
 union kernel_sigset_t {
+ public:
   kernel_sigset_t() {
-    clear();
   }
 
-  kernel_sigset_t(const sigset_t* value) {
+  explicit kernel_sigset_t(int signal_number) {
+    clear();
+    if (!set(signal_number)) async_safe_fatal("kernel_sigset_t(%d)", signal_number);
+  }
+
+  explicit kernel_sigset_t(const sigset_t* value) {
     clear();
     set(value);
   }
@@ -37,7 +46,32 @@
     __builtin_memset(this, 0, sizeof(*this));
   }
 
+  bool clear(int signal_number) {
+    int bit = bit_of(signal_number);
+    if (bit == -1) return false;
+    bits[bit / LONG_BIT] &= ~(1UL << (bit % LONG_BIT));
+    return true;
+  }
+
+  void fill() {
+    __builtin_memset(this, 0xff, sizeof(*this));
+  }
+
+  bool is_set(int signal_number) {
+    int bit = bit_of(signal_number);
+    if (bit == -1) return false;
+    return ((bits[bit / LONG_BIT] >> (bit % LONG_BIT)) & 1) == 1;
+  }
+
+  bool set(int signal_number) {
+    int bit = bit_of(signal_number);
+    if (bit == -1) return false;
+    bits[bit / LONG_BIT] |= 1UL << (bit % LONG_BIT);
+    return true;
+  }
+
   void set(const sigset_t* value) {
+    clear();
     bionic = *value;
   }
 
@@ -46,9 +80,21 @@
   }
 
   sigset_t bionic;
-#ifndef __mips__
-  uint32_t kernel[2];
-#endif
+  unsigned long bits[_KERNEL__NSIG/LONG_BIT];
+
+ private:
+  int bit_of(int signal_number) {
+    int bit = signal_number - 1; // Signal numbers start at 1, but bit positions start at 0.
+    if (bit < 0 || bit >= static_cast<int>(8*sizeof(*this))) {
+      errno = EINVAL;
+      return -1;
+    }
+    return bit;
+  }
 };
 
+extern "C" int __rt_sigpending(const kernel_sigset_t*, size_t);
+extern "C" int __rt_sigprocmask(int, const kernel_sigset_t*, kernel_sigset_t*, size_t);
+extern "C" int __rt_sigsuspend(const kernel_sigset_t*, size_t);
+
 #endif