Reland "sigchain: add wrappers for sigaction64/sigprocmask64."

This reverts commit bed29657743f64612be4969f036c324df4baf7a9.

"Fix" the mac build by using sigchain_dummy on it unconditionally.

Also, fix a test breakage caused by use of memcmp on a struct with
padding bytes that went from being assigned via memcpy to manual
assignment of each of the fields.

Test: test/testrunner/testrunner.py -b --target -t 004-SignalTest
Change-Id: I332d633a06abbf189d1e51fa8d031c2745f8f1a7
diff --git a/sigchainlib/Android.bp b/sigchainlib/Android.bp
index 7aab726..ac5a907 100644
--- a/sigchainlib/Android.bp
+++ b/sigchainlib/Android.bp
@@ -16,15 +16,24 @@
 
 cc_library {
     name: "libsigchain",
+    cpp_std: "gnu++17",
+
     host_supported: true,
     defaults: ["art_defaults"],
-    shared: {
-        srcs: ["sigchain_dummy.cc"],
-    },
-    static: {
-        srcs: ["sigchain.cc"],
-    },
     target: {
+        linux: {
+            shared: {
+                srcs: ["sigchain_dummy.cc"],
+            },
+            static: {
+                srcs: ["sigchain.cc"],
+            },
+        },
+
+        darwin: {
+            srcs: ["sigchain_dummy.cc"],
+        },
+
         android: {
             shared_libs: ["liblog"],
         },
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
index b8ab51b..85482e3 100644
--- a/sigchainlib/sigchain.cc
+++ b/sigchainlib/sigchain.cc
@@ -29,8 +29,10 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <algorithm>
 #include <initializer_list>
 #include <mutex>
+#include <type_traits>
 #include <utility>
 
 #include "sigchain.h"
@@ -78,9 +80,28 @@
 
 #define fatal(...) log(__VA_ARGS__); abort()
 
-static int sigorset(sigset_t* dest, sigset_t* left, sigset_t* right) {
+#if defined(__BIONIC__) && !defined(__LP64__)
+static int sigismember(const sigset64_t* sigset, int signum) {
+  return sigismember64(sigset, signum);
+}
+
+static int sigemptyset(sigset64_t* sigset) {
+  return sigemptyset64(sigset);
+}
+
+static int sigaddset(sigset64_t* sigset, int signum) {
+  return sigaddset64(sigset, signum);
+}
+
+static int sigdelset(sigset64_t* sigset, int signum) {
+  return sigdelset64(sigset, signum);
+}
+#endif
+
+template<typename SigsetType>
+static int sigorset(SigsetType* dest, SigsetType* left, SigsetType* right) {
   sigemptyset(dest);
-  for (size_t i = 0; i < sizeof(sigset_t) * CHAR_BIT; ++i) {
+  for (size_t i = 0; i < sizeof(SigsetType) * CHAR_BIT; ++i) {
     if (sigismember(left, i) == 1 || sigismember(right, i) == 1) {
       sigaddset(dest, i);
     }
@@ -93,35 +114,36 @@
 static decltype(&sigaction) linked_sigaction;
 static decltype(&sigprocmask) linked_sigprocmask;
 
+#if defined(__BIONIC__)
+static decltype(&sigaction64) linked_sigaction64;
+static decltype(&sigprocmask64) linked_sigprocmask64;
+#endif
+
+template<typename T>
+static void lookup_next_symbol(T* output, T wrapper, const char* name) {
+  void* sym = dlsym(RTLD_NEXT, name);
+  if (sym == nullptr) {
+    sym = dlsym(RTLD_DEFAULT, name);
+    if (sym == wrapper || sym == sigaction) {
+      fatal("Unable to find next %s in signal chain", name);
+    }
+  }
+  *output = reinterpret_cast<T>(sym);
+}
+
 __attribute__((constructor)) static void InitializeSignalChain() {
   static std::once_flag once;
   std::call_once(once, []() {
-    void* linked_sigaction_sym = dlsym(RTLD_NEXT, "sigaction");
-    if (linked_sigaction_sym == nullptr) {
-      linked_sigaction_sym = dlsym(RTLD_DEFAULT, "sigaction");
-      if (linked_sigaction_sym == nullptr ||
-          linked_sigaction_sym == reinterpret_cast<void*>(sigaction)) {
-        fatal("Unable to find next sigaction in signal chain");
-      }
-    }
+    lookup_next_symbol(&linked_sigaction, sigaction, "sigaction");
+    lookup_next_symbol(&linked_sigprocmask, sigprocmask, "sigprocmask");
 
-    void* linked_sigprocmask_sym = dlsym(RTLD_NEXT, "sigprocmask");
-    if (linked_sigprocmask_sym == nullptr) {
-      linked_sigprocmask_sym = dlsym(RTLD_DEFAULT, "sigprocmask");
-      if (linked_sigprocmask_sym == nullptr ||
-          linked_sigprocmask_sym == reinterpret_cast<void*>(sigprocmask)) {
-        fatal("Unable to find next sigprocmask in signal chain");
-      }
-    }
-
-    linked_sigaction =
-        reinterpret_cast<decltype(linked_sigaction)>(linked_sigaction_sym);
-    linked_sigprocmask =
-        reinterpret_cast<decltype(linked_sigprocmask)>(linked_sigprocmask_sym);
+#if defined(__BIONIC__)
+    lookup_next_symbol(&linked_sigaction64, sigaction64, "sigaction64");
+    lookup_next_symbol(&linked_sigprocmask64, sigprocmask64, "sigprocmask64");
+#endif
   });
 }
 
-
 static pthread_key_t GetHandlingSignalKey() {
   static pthread_key_t key;
   static std::once_flag once;
@@ -175,19 +197,51 @@
 
   // Register the signal chain with the kernel if needed.
   void Register(int signo) {
+#if defined(__BIONIC__)
+    struct sigaction64 handler_action = {};
+    sigfillset64(&handler_action.sa_mask);
+#else
     struct sigaction handler_action = {};
+    sigfillset(&handler_action.sa_mask);
+#endif
+
     handler_action.sa_sigaction = SignalChain::Handler;
     handler_action.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
-    sigfillset(&handler_action.sa_mask);
+
+#if defined(__BIONIC__)
+    linked_sigaction64(signo, &handler_action, &action_);
+#else
     linked_sigaction(signo, &handler_action, &action_);
+#endif
   }
 
-  void SetAction(const struct sigaction* action) {
-    action_ = *action;
+  template <typename SigactionType>
+  SigactionType GetAction() {
+    if constexpr (std::is_same_v<decltype(action_), SigactionType>) {
+      return action_;
+    } else {
+      SigactionType result;
+      result.sa_flags = action_.sa_flags;
+      result.sa_handler = action_.sa_handler;
+      result.sa_restorer = action_.sa_restorer;
+      memcpy(&result.sa_mask, &action_.sa_mask,
+             std::min(sizeof(action_.sa_mask), sizeof(result.sa_mask)));
+      return result;
+    }
   }
 
-  struct sigaction GetAction() {
-    return action_;
+  template <typename SigactionType>
+  void SetAction(const SigactionType* new_action) {
+    if constexpr (std::is_same_v<decltype(action_), SigactionType>) {
+      action_ = *new_action;
+    } else {
+      action_.sa_flags = new_action->sa_flags;
+      action_.sa_handler = new_action->sa_handler;
+      action_.sa_restorer = new_action->sa_restorer;
+      sigemptyset(&action_.sa_mask);
+      memcpy(&action_.sa_mask, &new_action->sa_mask,
+             std::min(sizeof(action_.sa_mask), sizeof(new_action->sa_mask)));
+    }
   }
 
   void AddSpecialHandler(SigchainAction* sa) {
@@ -222,7 +276,11 @@
 
  private:
   bool claimed_;
+#if defined(__BIONIC__)
+  struct sigaction64 action_;
+#else
   struct sigaction action_;
+#endif
   SigchainAction special_handlers_[2];
 };
 
@@ -260,12 +318,22 @@
   // Forward to the user's signal handler.
   int handler_flags = chains[signo].action_.sa_flags;
   ucontext_t* ucontext = static_cast<ucontext_t*>(ucontext_raw);
+#if defined(__BIONIC__)
+  sigset64_t mask;
+  sigorset(&mask, &ucontext->uc_sigmask64, &chains[signo].action_.sa_mask);
+#else
   sigset_t mask;
   sigorset(&mask, &ucontext->uc_sigmask, &chains[signo].action_.sa_mask);
+#endif
   if (!(handler_flags & SA_NODEFER)) {
     sigaddset(&mask, signo);
   }
+
+#if defined(__BIONIC__)
+  linked_sigprocmask64(SIG_SETMASK, &mask, nullptr);
+#else
   linked_sigprocmask(SIG_SETMASK, &mask, nullptr);
+#endif
 
   if ((handler_flags & SA_SIGINFO)) {
     chains[signo].action_.sa_sigaction(signo, siginfo, ucontext_raw);
@@ -281,9 +349,11 @@
   }
 }
 
-extern "C" int sigaction(int signal, const struct sigaction* new_action, struct sigaction* old_action) {
-  InitializeSignalChain();
-
+template <typename SigactionType>
+static int __sigaction(int signal, const SigactionType* new_action,
+                       SigactionType* old_action,
+                       int (*linked)(int, const SigactionType*,
+                                     SigactionType*)) {
   // If this signal has been claimed as a signal chain, record the user's
   // action but don't pass it on to the kernel.
   // Note that we check that the signal number is in range here.  An out of range signal
@@ -294,7 +364,7 @@
   }
 
   if (chains[signal].IsClaimed()) {
-    struct sigaction saved_action = chains[signal].GetAction();
+    SigactionType saved_action = chains[signal].GetAction<SigactionType>();
     if (new_action != nullptr) {
       chains[signal].SetAction(new_action);
     }
@@ -306,9 +376,23 @@
 
   // Will only get here if the signal chain has not been claimed.  We want
   // to pass the sigaction on to the kernel via the real sigaction in libc.
-  return linked_sigaction(signal, new_action, old_action);
+  return linked(signal, new_action, old_action);
 }
 
+extern "C" int sigaction(int signal, const struct sigaction* new_action,
+                         struct sigaction* old_action) {
+  InitializeSignalChain();
+  return __sigaction(signal, new_action, old_action, linked_sigaction);
+}
+
+#if defined(__BIONIC__)
+extern "C" int sigaction64(int signal, const struct sigaction64* new_action,
+                           struct sigaction64* old_action) {
+  InitializeSignalChain();
+  return __sigaction(signal, new_action, old_action, linked_sigaction64);
+}
+#endif
+
 extern "C" sighandler_t signal(int signo, sighandler_t handler) {
   InitializeSignalChain();
 
@@ -326,7 +410,8 @@
   // If this signal has been claimed as a signal chain, record the user's
   // action but don't pass it on to the kernel.
   if (chains[signo].IsClaimed()) {
-    oldhandler = reinterpret_cast<sighandler_t>(chains[signo].GetAction().sa_handler);
+    oldhandler = reinterpret_cast<sighandler_t>(
+        chains[signo].GetAction<struct sigaction>().sa_handler);
     chains[signo].SetAction(&sa);
     return oldhandler;
   }
@@ -348,18 +433,18 @@
 }
 #endif
 
-extern "C" int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_set) {
-  InitializeSignalChain();
-
+template <typename SigsetType>
+int __sigprocmask(int how, const SigsetType* new_set, SigsetType* old_set,
+                  int (*linked)(int, const SigsetType*, SigsetType*)) {
   // When inside a signal handler, forward directly to the actual sigprocmask.
   if (GetHandlingSignal()) {
-    return linked_sigprocmask(how, bionic_new_set, bionic_old_set);
+    return linked(how, new_set, old_set);
   }
 
-  const sigset_t* new_set_ptr = bionic_new_set;
-  sigset_t tmpset;
-  if (bionic_new_set != nullptr) {
-    tmpset = *bionic_new_set;
+  const SigsetType* new_set_ptr = new_set;
+  SigsetType tmpset;
+  if (new_set != nullptr) {
+    tmpset = *new_set;
 
     if (how == SIG_BLOCK) {
       // Don't allow claimed signals in the mask.  If a signal chain has been claimed
@@ -373,9 +458,23 @@
     new_set_ptr = &tmpset;
   }
 
-  return linked_sigprocmask(how, new_set_ptr, bionic_old_set);
+  return linked(how, new_set_ptr, old_set);
 }
 
+extern "C" int sigprocmask(int how, const sigset_t* new_set,
+                           sigset_t* old_set) {
+  InitializeSignalChain();
+  return __sigprocmask(how, new_set, old_set, linked_sigprocmask);
+}
+
+#if defined(__BIONIC__)
+extern "C" int sigprocmask64(int how, const sigset64_t* new_set,
+                             sigset64_t* old_set) {
+  InitializeSignalChain();
+  return __sigprocmask(how, new_set, old_set, linked_sigprocmask64);
+}
+#endif
+
 extern "C" void AddSpecialSignalHandlerFn(int signal, SigchainAction* sa) {
   InitializeSignalChain();
 
diff --git a/sigchainlib/version-script32.txt b/sigchainlib/version-script32.txt
index 2340785..e8a18e7 100644
--- a/sigchainlib/version-script32.txt
+++ b/sigchainlib/version-script32.txt
@@ -5,8 +5,10 @@
   RemoveSpecialSignalHandlerFn;
   bsd_signal;
   sigaction;
+  sigaction64;
   signal;
   sigprocmask;
+  sigprocmask64;
 local:
   *;
 };
diff --git a/sigchainlib/version-script64.txt b/sigchainlib/version-script64.txt
index acf3630..72c86a1 100644
--- a/sigchainlib/version-script64.txt
+++ b/sigchainlib/version-script64.txt
@@ -4,8 +4,10 @@
   AddSpecialSignalHandlerFn;
   RemoveSpecialSignalHandlerFn;
   sigaction;
+  sigaction64;
   signal;
   sigprocmask;
+  sigprocmask64;
 local:
   *;
 };
diff --git a/test/004-SignalTest/signaltest.cc b/test/004-SignalTest/signaltest.cc
index a58a075..67118d5 100644
--- a/test/004-SignalTest/signaltest.cc
+++ b/test/004-SignalTest/signaltest.cc
@@ -99,6 +99,15 @@
 
 static struct sigaction oldaction;
 
+bool compare_sigaction(const struct sigaction* lhs, const struct sigaction* rhs) {
+  // bionic's definition of `struct sigaction` has internal padding bytes, so we can't just do a
+  // naive memcmp of the entire struct.
+  return memcmp(&lhs->sa_mask, &rhs->sa_mask, sizeof(lhs->sa_mask)) == 0 &&
+         lhs->sa_sigaction == rhs->sa_sigaction &&
+         lhs->sa_flags == rhs->sa_flags &&
+         lhs->sa_restorer == rhs->sa_restorer;
+}
+
 extern "C" JNIEXPORT void JNICALL Java_Main_initSignalTest(JNIEnv*, jclass) {
   struct sigaction action;
   action.sa_sigaction = signalhandler;
@@ -112,8 +121,15 @@
   sigaction(SIGSEGV, &action, &oldaction);
   struct sigaction check;
   sigaction(SIGSEGV, nullptr, &check);
-  if (memcmp(&action, &check, sizeof(action)) != 0) {
+  if (!compare_sigaction(&check, &action)) {
     printf("sigaction returned different value\n");
+    printf("action.sa_mask = %p, check.sa_mask = %p\n",
+           *reinterpret_cast<void**>(&action.sa_mask),
+           *reinterpret_cast<void**>(&check.sa_mask));
+    printf("action.sa_sigaction = %p, check.sa_sigaction = %p\n",
+           action.sa_sigaction, check.sa_sigaction);
+    printf("action.sa_flags = %x, check.sa_flags = %x\n",
+           action.sa_flags, check.sa_flags);
   }
   signal(BLOCKED_SIGNAL, blocked_signal);
   signal(UNBLOCKED_SIGNAL, unblocked_signal);
diff --git a/test/115-native-bridge/expected.txt b/test/115-native-bridge/expected.txt
index 343a762..06f5979 100644
--- a/test/115-native-bridge/expected.txt
+++ b/test/115-native-bridge/expected.txt
@@ -63,6 +63,8 @@
 Getting trampoline for Java_Main_testSignal with shorty I.
 NB signal handler with signal 11.
 NB signal handler with signal 4.
+NB signal handler with signal 11.
+NB signal handler with signal 4.
 Loading invalid library 'libinvalid.so' from Java, which will fail.
 Checking for support.
 Was to load 'libinvalid.so', force fail.
diff --git a/test/115-native-bridge/nativebridge.cc b/test/115-native-bridge/nativebridge.cc
index a049b97..3b35209 100644
--- a/test/115-native-bridge/nativebridge.cc
+++ b/test/115-native-bridge/nativebridge.cc
@@ -224,6 +224,22 @@
   sigaction(SIGILL, &tmp, nullptr);
   kill(getpid(), SIGILL);
 
+#if defined(__BIONIC__)
+  // Do the same again, but with sigaction64.
+  struct sigaction64 tmp2;
+  sigemptyset64(&tmp2.sa_mask);
+  tmp2.sa_sigaction = test_sigaction_handler;
+  tmp2.sa_restorer = nullptr;
+
+  sigaction64(SIGSEGV, &tmp2, nullptr);
+  sigaction64(SIGILL, &tmp2, nullptr);
+#endif
+
+  // Reraise SIGSEGV/SIGILL even on non-bionic, so that the expected output is
+  // the same.
+  raise_sigsegv();
+  kill(getpid(), SIGILL);
+
   return 1234;
 }