1.0.3.16: experimental x86-64/darwin suport
[sbcl.git] / src / runtime / interrupt.c
index f24c454..00c920e 100644 (file)
  *
  * - WHN 20000728, dan 20010128 */
 
+#include "sbcl.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <signal.h>
 #include <sys/types.h>
+#ifndef LISP_FEATURE_WIN32
 #include <sys/wait.h>
+#endif
 #include <errno.h>
 
-#include "sbcl.h"
 #include "runtime.h"
 #include "arch.h"
 #include "os.h"
@@ -57,7 +59,6 @@
 #include "globals.h"
 #include "lispregs.h"
 #include "validate.h"
-#include "monitor.h"
 #include "gc.h"
 #include "alloc.h"
 #include "dynbind.h"
@@ -69,6 +70,7 @@
 
 
 static void run_deferred_handler(struct interrupt_data *data, void *v_context);
+#ifndef LISP_FEATURE_WIN32
 static void store_signal_data_for_later (struct interrupt_data *data,
                                          void *handler, int signal,
                                          siginfo_t *info,
@@ -92,8 +94,12 @@ sigaddset_deferrable(sigset_t *s)
     sigaddset(s, SIGVTALRM);
     sigaddset(s, SIGPROF);
     sigaddset(s, SIGWINCH);
+
+#if !((defined(LISP_FEATURE_DARWIN) || defined(LISP_FEATURE_FREEBSD)) && defined(LISP_FEATURE_SB_THREAD))
     sigaddset(s, SIGUSR1);
     sigaddset(s, SIGUSR2);
+#endif
+
 #ifdef LISP_FEATURE_SB_THREAD
     sigaddset(s, SIG_INTERRUPT_THREAD);
 #endif
@@ -104,6 +110,9 @@ sigaddset_blockable(sigset_t *s)
 {
     sigaddset_deferrable(s);
 #ifdef LISP_FEATURE_SB_THREAD
+#ifdef SIG_RESUME_FROM_GC
+    sigaddset(s, SIG_RESUME_FROM_GC);
+#endif
     sigaddset(s, SIG_STOP_FOR_GC);
 #endif
 }
@@ -111,10 +120,12 @@ sigaddset_blockable(sigset_t *s)
 /* initialized in interrupt_init */
 static sigset_t deferrable_sigset;
 static sigset_t blockable_sigset;
+#endif
 
 void
 check_blockables_blocked_or_lose()
 {
+#if !defined(LISP_FEATURE_WIN32)
     /* Get the current sigmask, by blocking the empty set. */
     sigset_t empty,current;
     int i;
@@ -122,8 +133,9 @@ check_blockables_blocked_or_lose()
     thread_sigmask(SIG_BLOCK, &empty, &current);
     for(i = 1; i < NSIG; i++) {
         if (sigismember(&blockable_sigset, i) && !sigismember(&current, i))
-            lose("blockable signal %d not blocked",i);
+            lose("blockable signal %d not blocked\n",i);
     }
+#endif
 }
 
 inline static void
@@ -131,13 +143,13 @@ check_interrupts_enabled_or_lose(os_context_t *context)
 {
     struct thread *thread=arch_os_get_current_thread();
     if (SymbolValue(INTERRUPTS_ENABLED,thread) == NIL)
-        lose("interrupts not enabled");
+        lose("interrupts not enabled\n");
     if (
 #if !defined(LISP_FEATURE_X86) && !defined(LISP_FEATURE_X86_64)
         (!foreign_function_call_active) &&
 #endif
         arch_pseudo_atomic_atomic(context))
-        lose ("in pseudo atomic section");
+        lose ("in pseudo atomic section\n");
 }
 
 /* When we catch an internal error, should we pass it back to Lisp to
@@ -147,7 +159,9 @@ check_interrupts_enabled_or_lose(os_context_t *context)
  * becomes 'yes'.) */
 boolean internal_errors_enabled = 0;
 
+#ifndef LISP_FEATURE_WIN32
 static void (*interrupt_low_level_handlers[NSIG]) (int, siginfo_t*, void*);
+#endif
 union interrupt_handler interrupt_handlers[NSIG];
 
 /* At the toplevel repl we routinely call this function.  The signal
@@ -157,15 +171,27 @@ union interrupt_handler interrupt_handlers[NSIG];
 void
 reset_signal_mask(void)
 {
+#ifndef LISP_FEATURE_WIN32
     sigset_t new;
     sigemptyset(&new);
     thread_sigmask(SIG_SETMASK,&new,0);
+#endif
 }
 
 void
 block_blockable_signals(void)
 {
+#ifndef LISP_FEATURE_WIN32
     thread_sigmask(SIG_BLOCK, &blockable_sigset, 0);
+#endif
+}
+
+void
+block_deferrable_signals(void)
+{
+#ifndef LISP_FEATURE_WIN32
+    thread_sigmask(SIG_BLOCK, &deferrable_sigset, 0);
+#endif
 }
 
 \f
@@ -242,9 +268,10 @@ fake_foreign_function_call(os_context_t *context)
     dynamic_space_free_pointer =
         (lispobj *)(unsigned long)
             (*os_context_register_addr(context, reg_ALLOC));
+    /* fprintf(stderr,"dynamic_space_free_pointer: %p\n", dynamic_space_free_pointer); */
 #if defined(LISP_FEATURE_ALPHA)
     if ((long)dynamic_space_free_pointer & 1) {
-        lose("dead in fake_foreign_function_call, context = %x", context);
+        lose("dead in fake_foreign_function_call, context = %x\n", context);
     }
 #endif
 #endif
@@ -262,7 +289,7 @@ fake_foreign_function_call(os_context_t *context)
         fixnum_value(SymbolValue(FREE_INTERRUPT_CONTEXT_INDEX,thread));
 
     if (context_index >= MAX_INTERRUPTS) {
-        lose("maximum interrupt nesting depth (%d) exceeded", MAX_INTERRUPTS);
+        lose("maximum interrupt nesting depth (%d) exceeded\n", MAX_INTERRUPTS);
     }
 
     bind_variable(FREE_INTERRUPT_CONTEXT_INDEX,
@@ -293,7 +320,13 @@ undo_fake_foreign_function_call(os_context_t *context)
 #ifdef reg_ALLOC
     /* Put the dynamic space free pointer back into the context. */
     *os_context_register_addr(context, reg_ALLOC) =
-        (unsigned long) dynamic_space_free_pointer;
+        (unsigned long) dynamic_space_free_pointer
+        | (*os_context_register_addr(context, reg_ALLOC)
+           & LOWTAG_MASK);
+    /*
+      ((unsigned long)(*os_context_register_addr(context, reg_ALLOC)) & ~LOWTAG_MASK)
+      | ((unsigned long) dynamic_space_free_pointer & LOWTAG_MASK);
+    */
 #endif
 }
 
@@ -311,14 +344,16 @@ interrupt_internal_error(int signal, siginfo_t *info, os_context_t *context,
         describe_internal_error(context);
         /* There's no good way to recover from an internal error
          * before the Lisp error handling mechanism is set up. */
-        lose("internal error too early in init, can't recover");
+        lose("internal error too early in init, can't recover\n");
     }
 
     /* Allocate the SAP object while the interrupts are still
      * disabled. */
     context_sap = alloc_sap(context);
 
+#ifndef LISP_FEATURE_WIN32
     thread_sigmask(SIG_SETMASK, os_context_sigmask_addr(context), 0);
+#endif
 
     SHOW("in interrupt_internal_error");
 #ifdef QSHOW
@@ -341,16 +376,15 @@ interrupt_handle_pending(os_context_t *context)
     struct thread *thread;
     struct interrupt_data *data;
 
-    check_blockables_blocked_or_lose();
+    FSHOW_SIGNAL((stderr, "/entering interrupt_handle_pending\n"));
 
+    check_blockables_blocked_or_lose();
     thread=arch_os_get_current_thread();
     data=thread->interrupt_data;
 
-#if defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64)
     /* If pseudo_atomic_interrupted is set then the interrupt is going
      * to be handled now, ergo it's safe to clear it. */
     arch_clear_pseudo_atomic_interrupted(context);
-#endif
 
     if (SymbolValue(GC_INHIBIT,thread)==NIL) {
 #ifdef LISP_FEATURE_SB_THREAD
@@ -394,6 +428,7 @@ interrupt_handle_pending(os_context_t *context)
              * PSEUDO_ATOMIC_INTERRUPTED only if interrupts are enabled.*/
             SetSymbolValue(INTERRUPT_PENDING, NIL,thread);
 
+#ifndef LISP_FEATURE_WIN32
             /* restore the saved signal mask from the original signal (the
              * one that interrupted us during the critical section) into the
              * os_context for the signal we're currently in the handler for.
@@ -402,6 +437,7 @@ interrupt_handle_pending(os_context_t *context)
             sigcopyset(os_context_sigmask_addr(context), &data->pending_mask);
 
             sigemptyset(&data->pending_mask);
+#endif
             /* This will break on sparc linux: the deferred handler really wants
              * to be called with a void_context */
             run_deferred_handler(data,(void *)context);
@@ -432,16 +468,23 @@ interrupt_handle_now(int signal, siginfo_t *info, void *void_context)
     boolean were_in_lisp;
 #endif
     union interrupt_handler handler;
+
     check_blockables_blocked_or_lose();
+
+
+#ifndef LISP_FEATURE_WIN32
     if (sigismember(&deferrable_sigset,signal))
         check_interrupts_enabled_or_lose(context);
+#endif
 
-#ifdef LISP_FEATURE_LINUX
+#if defined(LISP_FEATURE_LINUX) || defined(RESTORE_FP_CONTROL_FROM_CONTEXT)
     /* Under Linux on some architectures, we appear to have to restore
        the FPU control word from the context, as after the signal is
        delivered we appear to have a null FPU control word. */
     os_restore_fp_control(context);
 #endif
+
+
     handler = interrupt_handlers[signal];
 
     if (ARE_SAME_HANDLER(handler.c, SIG_IGN)) {
@@ -465,7 +508,7 @@ interrupt_handle_now(int signal, siginfo_t *info, void *void_context)
         /* This can happen if someone tries to ignore or default one
          * of the signals we need for runtime support, and the runtime
          * support decides to pass on it. */
-        lose("no handler for signal %d in interrupt_handle_now(..)", signal);
+        lose("no handler for signal %d in interrupt_handle_now(..)\n", signal);
 
     } else if (lowtag_of(handler.lisp) == FUN_POINTER_LOWTAG) {
         /* Once we've decided what to do about contexts in a
@@ -492,6 +535,9 @@ interrupt_handle_now(int signal, siginfo_t *info, void *void_context)
             sigset_t unblock;
             sigemptyset(&unblock);
             sigaddset(&unblock, SIG_STOP_FOR_GC);
+#ifdef SIG_RESUME_FROM_GC
+            sigaddset(&unblock, SIG_RESUME_FROM_GC);
+#endif
             thread_sigmask(SIG_UNBLOCK, &unblock, 0);
         }
 #endif
@@ -506,8 +552,10 @@ interrupt_handle_now(int signal, siginfo_t *info, void *void_context)
 
         FSHOW_SIGNAL((stderr,"/calling C-level handler\n"));
 
+#ifndef LISP_FEATURE_WIN32
         /* Allow signals again. */
         thread_sigmask(SIG_SETMASK, os_context_sigmask_addr(context), 0);
+#endif
 
         (*handler.c)(signal, info, void_context);
     }
@@ -536,10 +584,12 @@ run_deferred_handler(struct interrupt_data *data, void *v_context) {
      * pending handler before calling it. Trust the handler to finish
      * with the siginfo before enabling interrupts. */
     void (*pending_handler) (int, siginfo_t*, void*)=data->pending_handler;
+
     data->pending_handler=0;
     (*pending_handler)(data->pending_signal,&(data->pending_info), v_context);
 }
 
+#ifndef LISP_FEATURE_WIN32
 boolean
 maybe_defer_handler(void *handler, struct interrupt_data *data,
                     int signal, siginfo_t *info, os_context_t *context)
@@ -549,7 +599,7 @@ maybe_defer_handler(void *handler, struct interrupt_data *data,
     check_blockables_blocked_or_lose();
 
     if (SymbolValue(INTERRUPT_PENDING,thread) != NIL)
-        lose("interrupt already pending");
+        lose("interrupt already pending\n");
     /* If interrupts are disabled then INTERRUPT_PENDING is set and
      * not PSEDUO_ATOMIC_INTERRUPTED. This is important for a pseudo
      * atomic section inside a WITHOUT-INTERRUPTS.
@@ -606,6 +656,9 @@ store_signal_data_for_later (struct interrupt_data *data, void *handler,
     data->pending_signal = signal;
     if(info)
         memcpy(&(data->pending_info), info, sizeof(siginfo_t));
+
+    FSHOW_SIGNAL((stderr, "/store_signal_data_for_later: signal: %d\n", signal));
+
     if(context) {
         /* the signal mask in the context (from before we were
          * interrupted) is copied to be restored when
@@ -621,11 +674,17 @@ static void
 maybe_now_maybe_later(int signal, siginfo_t *info, void *void_context)
 {
     os_context_t *context = arch_os_get_context(&void_context);
-    struct thread *thread=arch_os_get_current_thread();
-    struct interrupt_data *data=thread->interrupt_data;
-#ifdef LISP_FEATURE_LINUX
+
+    struct thread *thread;
+    struct interrupt_data *data;
+
+    thread=arch_os_get_current_thread();
+    data=thread->interrupt_data;
+
+#if defined(LISP_FEATURE_LINUX) || defined(RESTORE_FP_CONTROL_FROM_CONTEXT)
     os_restore_fp_control(context);
 #endif
+
     if(maybe_defer_handler(interrupt_handle_now,data,signal,info,context))
         return;
     interrupt_handle_now(signal, info, context);
@@ -640,9 +699,10 @@ low_level_interrupt_handle_now(int signal, siginfo_t *info, void *void_context)
 {
     os_context_t *context = (os_context_t*)void_context;
 
-#ifdef LISP_FEATURE_LINUX
+#if defined(LISP_FEATURE_LINUX) || defined(RESTORE_FP_CONTROL_FROM_CONTEXT)
     os_restore_fp_control(context);
 #endif
+
     check_blockables_blocked_or_lose();
     check_interrupts_enabled_or_lose(context);
     interrupt_low_level_handlers[signal](signal, info, void_context);
@@ -656,11 +716,16 @@ static void
 low_level_maybe_now_maybe_later(int signal, siginfo_t *info, void *void_context)
 {
     os_context_t *context = arch_os_get_context(&void_context);
-    struct thread *thread=arch_os_get_current_thread();
-    struct interrupt_data *data=thread->interrupt_data;
-#ifdef LISP_FEATURE_LINUX
+    struct thread *thread;
+    struct interrupt_data *data;
+
+    thread=arch_os_get_current_thread();
+    data=thread->interrupt_data;
+
+#if defined(LISP_FEATURE_LINUX) || defined(RESTORE_FP_CONTROL_FROM_CONTEXT)
     os_restore_fp_control(context);
 #endif
+
     if(maybe_defer_handler(low_level_interrupt_handle_now,data,
                            signal,info,context))
         return;
@@ -670,6 +735,7 @@ low_level_maybe_now_maybe_later(int signal, siginfo_t *info, void *void_context)
     DARWIN_FIX_CONTEXT(context);
 #endif
 }
+#endif
 
 #ifdef LISP_FEATURE_SB_THREAD
 
@@ -677,6 +743,7 @@ void
 sig_stop_for_gc_handler(int signal, siginfo_t *info, void *void_context)
 {
     os_context_t *context = arch_os_get_context(&void_context);
+
     struct thread *thread=arch_os_get_current_thread();
     sigset_t ss;
 
@@ -701,10 +768,24 @@ sig_stop_for_gc_handler(int signal, siginfo_t *info, void *void_context)
         thread->state=STATE_SUSPENDED;
         FSHOW_SIGNAL((stderr,"thread=%lu suspended\n",thread->os_thread));
 
+#if defined(SIG_RESUME_FROM_GC)
+        sigemptyset(&ss); sigaddset(&ss,SIG_RESUME_FROM_GC);
+#else
         sigemptyset(&ss); sigaddset(&ss,SIG_STOP_FOR_GC);
+#endif
+
         /* It is possible to get SIGCONT (and probably other
          * non-blockable signals) here. */
+#ifdef SIG_RESUME_FROM_GC
+        {
+            int sigret;
+            do { sigwait(&ss, &sigret); }
+            while (sigret != SIG_RESUME_FROM_GC);
+        }
+#else
         while (sigwaitinfo(&ss,0) != SIG_STOP_FOR_GC);
+#endif
+
         FSHOW_SIGNAL((stderr,"thread=%lu resumed\n",thread->os_thread));
         if(thread->state!=STATE_RUNNING) {
             lose("sig_stop_for_gc_handler: wrong thread state on wakeup: %ld\n",
@@ -740,7 +821,7 @@ gc_trigger_hit(int signal, siginfo_t *info, os_context_t *context)
     else{
         void *badaddr=arch_get_bad_addr(signal,info,context);
         return (badaddr >= (void *)current_auto_gc_trigger &&
-                badaddr <((void *)current_dynamic_space + DYNAMIC_SPACE_SIZE));
+                badaddr <((void *)current_dynamic_space + dynamic_space_size));
     }
 }
 #endif
@@ -756,6 +837,7 @@ extern int *context_eflags_addr(os_context_t *context);
 
 extern lispobj call_into_lisp(lispobj fun, lispobj *args, int nargs);
 extern void post_signal_tramp(void);
+extern void call_into_lisp_tramp(void);
 void
 arrange_return_to_lisp_function(os_context_t *context, lispobj function)
 {
@@ -801,6 +883,35 @@ arrange_return_to_lisp_function(os_context_t *context, lispobj function)
 
     u32 *sp=(u32 *)*os_context_register_addr(context,reg_ESP);
 
+#if defined(LISP_FEATURE_DARWIN)
+    u32 *register_save_area = (u32 *)os_validate(0, 0x40);
+
+    FSHOW_SIGNAL((stderr, "/arrange_return_to_lisp_function: preparing to go to function %x, sp: %x\n", function, sp));
+    FSHOW_SIGNAL((stderr, "/arrange_return_to_lisp_function: context: %x, &context %x\n", context, &context));
+
+    /* 1. os_validate (malloc/mmap) register_save_block
+     * 2. copy register state into register_save_block
+     * 3. put a pointer to register_save_block in a register in the context
+     * 4. set the context's EIP to point to a trampoline which:
+     *    a. builds the fake stack frame from the block
+     *    b. frees the block
+     *    c. calls the function
+     */
+
+    *register_save_area = *os_context_pc_addr(context);
+    *(register_save_area + 1) = function;
+    *(register_save_area + 2) = *os_context_register_addr(context,reg_EDI);
+    *(register_save_area + 3) = *os_context_register_addr(context,reg_ESI);
+    *(register_save_area + 4) = *os_context_register_addr(context,reg_EDX);
+    *(register_save_area + 5) = *os_context_register_addr(context,reg_ECX);
+    *(register_save_area + 6) = *os_context_register_addr(context,reg_EBX);
+    *(register_save_area + 7) = *os_context_register_addr(context,reg_EAX);
+    *(register_save_area + 8) = *context_eflags_addr(context);
+
+    *os_context_pc_addr(context) = call_into_lisp_tramp;
+    *os_context_register_addr(context,reg_ECX) = register_save_area;
+#else
+
     /* return address for call_into_lisp: */
     *(sp-15) = (u32)post_signal_tramp;
     *(sp-14) = function;        /* args for call_into_lisp : function*/
@@ -822,8 +933,11 @@ arrange_return_to_lisp_function(os_context_t *context, lispobj function)
     *(sp-2)=*os_context_register_addr(context,reg_EBP);
     *(sp-1)=*os_context_pc_addr(context);
 
+#endif
+
 #elif defined(LISP_FEATURE_X86_64)
     u64 *sp=(u64 *)*os_context_register_addr(context,reg_RSP);
+
     /* return address for call_into_lisp: */
     *(sp-18) = (u64)post_signal_tramp;
 
@@ -856,6 +970,8 @@ arrange_return_to_lisp_function(os_context_t *context, lispobj function)
 #endif
 
 #ifdef LISP_FEATURE_X86
+
+#if !defined(LISP_FEATURE_DARWIN)
     *os_context_pc_addr(context) = (os_context_register_t)call_into_lisp;
     *os_context_register_addr(context,reg_ECX) = 0;
     *os_context_register_addr(context,reg_EBP) = (os_context_register_t)(sp-2);
@@ -864,7 +980,9 @@ arrange_return_to_lisp_function(os_context_t *context, lispobj function)
         (os_context_register_t)(sp-15);
 #else
     *os_context_register_addr(context,reg_ESP) = (os_context_register_t)(sp-15);
-#endif
+#endif /* __NETBSD__ */
+#endif /* LISP_FEATURE_DARWIN */
+
 #elif defined(LISP_FEATURE_X86_64)
     *os_context_pc_addr(context) = (os_context_register_t)call_into_lisp;
     *os_context_register_addr(context,reg_RCX) = 0;
@@ -898,6 +1016,7 @@ void
 interrupt_thread_handler(int num, siginfo_t *info, void *v_context)
 {
     os_context_t *context = (os_context_t*)arch_os_get_context(&v_context);
+
     /* let the handler enable interrupts again when it sees fit */
     sigaddset_deferrable(os_context_sigmask_addr(context));
     arrange_return_to_lisp_function(context, SymbolFunction(RUN_INTERRUPTION));
@@ -999,7 +1118,9 @@ boolean
 interrupt_maybe_gc_int(int signal, siginfo_t *info, void *void_context)
 {
     os_context_t *context=(os_context_t *) void_context;
+#ifndef LISP_FEATURE_WIN32
     struct thread *thread=arch_os_get_current_thread();
+#endif
 
     fake_foreign_function_call(context);
 
@@ -1025,16 +1146,21 @@ interrupt_maybe_gc_int(int signal, siginfo_t *info, void *void_context)
      * A kludgy alternative is to propagate the sigmask change to the
      * outer context.
      */
+#ifndef LISP_FEATURE_WIN32
     if(SymbolValue(INTERRUPTS_ENABLED,thread)!=NIL)
         thread_sigmask(SIG_SETMASK, os_context_sigmask_addr(context), 0);
 #ifdef LISP_FEATURE_SB_THREAD
     else {
         sigset_t new;
         sigemptyset(&new);
+#if defined(SIG_RESUME_FROM_GC)
+        sigaddset(&new,SIG_RESUME_FROM_GC);
+#endif
         sigaddset(&new,SIG_STOP_FOR_GC);
         thread_sigmask(SIG_UNBLOCK,&new,0);
     }
 #endif
+#endif
     funcall0(SymbolFunction(SUB_GC));
 
     undo_fake_foreign_function_call(context);
@@ -1046,6 +1172,7 @@ interrupt_maybe_gc_int(int signal, siginfo_t *info, void *void_context)
  * noise to install handlers
  */
 
+#ifndef LISP_FEATURE_WIN32
 /* In Linux 2.4 synchronous signals (sigtrap & co) can be delivered if
  * they are blocked, in Linux 2.6 the default handler is invoked
  * instead that usually coredumps. One might hastily think that adding
@@ -1053,18 +1180,32 @@ interrupt_maybe_gc_int(int signal, siginfo_t *info, void *void_context)
  * the whole sa_mask is ignored and instead of not adding the signal
  * in question to the mask. That means if it's not blockable the
  * signal must be unblocked at the beginning of signal handlers.
+ *
+ * It turns out that NetBSD's SA_NODEFER doesn't DTRT in a different
+ * way: if SA_NODEFER is set and the signal is in sa_mask, the signal
+ * will be unblocked in the sigmask during the signal handler.  -- RMK
+ * X-mas day, 2005
  */
 static volatile int sigaction_nodefer_works = -1;
 
+#define SA_NODEFER_TEST_BLOCK_SIGNAL SIGABRT
+#define SA_NODEFER_TEST_KILL_SIGNAL SIGUSR1
+
 static void
 sigaction_nodefer_test_handler(int signal, siginfo_t *info, void *void_context)
 {
     sigset_t empty, current;
     int i;
     sigemptyset(&empty);
-    sigprocmask(SIG_BLOCK, &empty, &current);
+    thread_sigmask(SIG_BLOCK, &empty, &current);
+    /* There should be exactly two blocked signals: the two we added
+     * to sa_mask when setting up the handler.  NetBSD doesn't block
+     * the signal we're handling when SA_NODEFER is set; Linux before
+     * 2.6.13 or so also doesn't block the other signal when
+     * SA_NODEFER is set. */
     for(i = 1; i < NSIG; i++)
-        if (sigismember(&current, i) != ((i == SIGABRT) ? 1 : 0)) {
+        if (sigismember(&current, i) !=
+            (((i == SA_NODEFER_TEST_BLOCK_SIGNAL) || (i == signal)) ? 1 : 0)) {
             FSHOW_SIGNAL((stderr, "SA_NODEFER doesn't work, signal %d\n", i));
             sigaction_nodefer_works = 0;
         }
@@ -1075,30 +1216,33 @@ sigaction_nodefer_test_handler(int signal, siginfo_t *info, void *void_context)
 static void
 see_if_sigaction_nodefer_works()
 {
-    struct sigaction sa;
+    struct sigaction sa, old_sa;
 
     sa.sa_flags = SA_SIGINFO | SA_NODEFER;
     sa.sa_sigaction = sigaction_nodefer_test_handler;
     sigemptyset(&sa.sa_mask);
-    sigaddset(&sa.sa_mask, SIGABRT);
-    /* We can use any signal for which a handler will be installed
-     * later. Let's go with SIGINT because gdb barfs on SIGTRAP on
-     * Darwin. */
-    sigaction(SIGINT, &sa, NULL);
+    sigaddset(&sa.sa_mask, SA_NODEFER_TEST_BLOCK_SIGNAL);
+    sigaddset(&sa.sa_mask, SA_NODEFER_TEST_KILL_SIGNAL);
+    sigaction(SA_NODEFER_TEST_KILL_SIGNAL, &sa, &old_sa);
     /* Make sure no signals are blocked. */
     {
         sigset_t empty;
         sigemptyset(&empty);
-        sigprocmask(SIG_SETMASK, &empty, 0);
+        thread_sigmask(SIG_SETMASK, &empty, 0);
     }
-    kill(getpid(), SIGINT);
+    kill(getpid(), SA_NODEFER_TEST_KILL_SIGNAL);
     while (sigaction_nodefer_works == -1);
+    sigaction(SA_NODEFER_TEST_KILL_SIGNAL, &old_sa, NULL);
 }
 
+#undef SA_NODEFER_TEST_BLOCK_SIGNAL
+#undef SA_NODEFER_TEST_KILL_SIGNAL
+
 static void
 unblock_me_trampoline(int signal, siginfo_t *info, void *void_context)
 {
     sigset_t unblock;
+
     sigemptyset(&unblock);
     sigaddset(&unblock, signal);
     thread_sigmask(SIG_UNBLOCK, &unblock, 0);
@@ -1109,6 +1253,7 @@ static void
 low_level_unblock_me_trampoline(int signal, siginfo_t *info, void *void_context)
 {
     sigset_t unblock;
+
     sigemptyset(&unblock);
     sigaddset(&unblock, signal);
     thread_sigmask(SIG_UNBLOCK, &unblock, 0);
@@ -1124,24 +1269,33 @@ undoably_install_low_level_interrupt_handler (int signal,
     struct sigaction sa;
 
     if (0 > signal || signal >= NSIG) {
-        lose("bad signal number %d", signal);
+        lose("bad signal number %d\n", signal);
     }
 
     if (ARE_SAME_HANDLER(handler, SIG_DFL))
         sa.sa_sigaction = handler;
     else if (sigismember(&deferrable_sigset,signal))
         sa.sa_sigaction = low_level_maybe_now_maybe_later;
+    /* The use of a trampoline appears to break the
+       arch_os_get_context() workaround for SPARC/Linux.  For now,
+       don't use the trampoline (and so be vulnerable to the problems
+       that SA_NODEFER is meant to solve. */
+#if !(defined(LISP_FEATURE_SPARC) && defined(LISP_FEATURE_LINUX))
     else if (!sigaction_nodefer_works &&
              !sigismember(&blockable_sigset, signal))
         sa.sa_sigaction = low_level_unblock_me_trampoline;
+#endif
     else
         sa.sa_sigaction = handler;
 
     sigcopyset(&sa.sa_mask, &blockable_sigset);
-    sa.sa_flags = SA_SIGINFO | SA_RESTART |
-        (sigaction_nodefer_works ? SA_NODEFER : 0);
+    sa.sa_flags = SA_SIGINFO | SA_RESTART
+        | (sigaction_nodefer_works ? SA_NODEFER : 0);
 #ifdef LISP_FEATURE_C_STACK_IS_CONTROL_STACK
     if((signal==SIG_MEMORY_FAULT)
+#ifdef SIG_MEMORY_FAULT2
+       || (signal==SIG_MEMORY_FAULT2)
+#endif
 #ifdef SIG_INTERRUPT_THREAD
        || (signal==SIG_INTERRUPT_THREAD)
 #endif
@@ -1153,11 +1307,13 @@ undoably_install_low_level_interrupt_handler (int signal,
     interrupt_low_level_handlers[signal] =
         (ARE_SAME_HANDLER(handler, SIG_DFL) ? 0 : handler);
 }
+#endif
 
 /* This is called from Lisp. */
 unsigned long
 install_handler(int signal, void handler(int, siginfo_t*, void*))
 {
+#ifndef LISP_FEATURE_WIN32
     struct sigaction sa;
     sigset_t old, new;
     union interrupt_handler oldhandler;
@@ -1196,11 +1352,16 @@ install_handler(int signal, void handler(int, siginfo_t*, void*))
     FSHOW((stderr, "/leaving POSIX install_handler(%d, ..)\n", signal));
 
     return (unsigned long)oldhandler.lisp;
+#else
+    /* Probably-wrong Win32 hack */
+    return 0;
+#endif
 }
 
 void
 interrupt_init()
 {
+#ifndef LISP_FEATURE_WIN32
     int i;
     SHOW("entering interrupt_init()");
     see_if_sigaction_nodefer_works();
@@ -1221,4 +1382,26 @@ interrupt_init()
     }
 
     SHOW("returning from interrupt_init()");
+#endif
+}
+
+#ifndef LISP_FEATURE_WIN32
+int
+siginfo_code(siginfo_t *info)
+{
+    return info->si_code;
+}
+os_vm_address_t current_memory_fault_address;
+
+void
+lisp_memory_fault_error(os_context_t *context, os_vm_address_t addr)
+{
+   /* FIXME: This is lossy: if we get another memory fault (eg. from
+    * another thread) before lisp has read this, we the information.
+    * However, since this is mostly informative, we'll live with that for
+    * now -- some address is better then no address in this case.
+    */
+    current_memory_fault_address = addr;
+    arrange_return_to_lisp_function(context, SymbolFunction(MEMORY_FAULT_ERROR));
 }
+#endif