1.0.25.57: fix compilation on win32
[sbcl.git] / src / runtime / interrupt.c
index 22ea827..b6c5c0d 100644 (file)
 #include "genesis/simple-fun.h"
 #include "genesis/cons.h"
 
-static void run_deferred_handler(struct interrupt_data *data, void *v_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. */
+#if defined(RESTORE_FP_CONTROL_FROM_CONTEXT)
+#define RESTORE_FP_CONTROL_WORD(context,void_context)           \
+    os_context_t *context = arch_os_get_context(&void_context); \
+    os_restore_fp_control(context);
+#else
+#define RESTORE_FP_CONTROL_WORD(context,void_context)           \
+    os_context_t *context = arch_os_get_context(&void_context);
+#endif
+
+/* These are to be used in signal handlers. Currently all handlers are
+ * called from one of:
+ *
+ * interrupt_handle_now_handler
+ * maybe_now_maybe_later
+ * unblock_me_trampoline
+ * low_level_handle_now_handler
+ * low_level_maybe_now_maybe_later
+ * low_level_unblock_me_trampoline
+ *
+ * This gives us a single point of control (or six) over errno, fp
+ * control word, and fixing up signal context on sparc.
+ *
+ * The SPARC/Linux platform doesn't quite do signals the way we want
+ * them done. The third argument in the handler isn't filled in by the
+ * kernel properly, so we fix it up ourselves in the
+ * arch_os_get_context(..) function. -- CSR, 2002-07-23
+ */
+#define SAVE_ERRNO(context,void_context)                        \
+    {                                                           \
+        int _saved_errno = errno;                               \
+        RESTORE_FP_CONTROL_WORD(context,void_context);          \
+        {
+
+#define RESTORE_ERRNO                                           \
+        }                                                       \
+        errno = _saved_errno;                                   \
+    }
+
+static void run_deferred_handler(struct interrupt_data *data,
+                                 os_context_t *context);
 #ifndef LISP_FEATURE_WIN32
 static void store_signal_data_for_later (struct interrupt_data *data,
                                          void *handler, int signal,
@@ -104,10 +146,6 @@ sigaddset_deferrable(sigset_t *s)
     sigaddset(s, SIGVTALRM);
     sigaddset(s, SIGPROF);
     sigaddset(s, SIGWINCH);
-
-#ifdef LISP_FEATURE_SB_THREAD
-    sigaddset(s, SIG_INTERRUPT_THREAD);
-#endif
 }
 
 void
@@ -129,10 +167,6 @@ sigdelset_deferrable(sigset_t *s)
     sigdelset(s, SIGVTALRM);
     sigdelset(s, SIGPROF);
     sigdelset(s, SIGWINCH);
-
-#ifdef LISP_FEATURE_SB_THREAD
-    sigdelset(s, SIG_INTERRUPT_THREAD);
-#endif
 }
 
 void
@@ -202,6 +236,16 @@ check_deferrables_blocked_in_sigset_or_lose(sigset_t *sigset)
 }
 
 void
+check_deferrables_unblocked_or_lose(void)
+{
+#if !defined(LISP_FEATURE_WIN32)
+    sigset_t current;
+    fill_current_sigmask(&current);
+    check_deferrables_unblocked_in_sigset_or_lose(&current);
+#endif
+}
+
+void
 check_deferrables_blocked_or_lose(void)
 {
 #if !defined(LISP_FEATURE_WIN32)
@@ -265,6 +309,7 @@ check_interrupts_enabled_or_lose(os_context_t *context)
 void
 maybe_save_gc_mask_and_block_deferrables(sigset_t *sigset)
 {
+#ifndef LISP_FEATURE_WIN32
     struct thread *thread = arch_os_get_current_thread();
     struct interrupt_data *data = thread->interrupt_data;
     sigset_t oldset;
@@ -297,6 +342,7 @@ maybe_save_gc_mask_and_block_deferrables(sigset_t *sigset)
         }
     }
     thread_sigmask(SIG_SETMASK,&oldset,0);
+#endif
 }
 
 /* Are we leaving WITH-GCING and already running with interrupts
@@ -319,6 +365,7 @@ in_leaving_without_gcing_race_p(struct thread *thread)
 void
 check_interrupt_context_or_lose(os_context_t *context)
 {
+#ifndef LISP_FEATURE_WIN32
     struct thread *thread = arch_os_get_current_thread();
     struct interrupt_data *data = thread->interrupt_data;
     int interrupt_deferred_p = (data->pending_handler != 0);
@@ -367,6 +414,7 @@ check_interrupt_context_or_lose(os_context_t *context)
          * that run lisp code. */
         check_gc_signals_unblocked_in_sigset_or_lose(sigset);
     }
+#endif
 }
 
 /* When we catch an internal error, should we pass it back to Lisp to
@@ -377,7 +425,8 @@ check_interrupt_context_or_lose(os_context_t *context)
 boolean internal_errors_enabled = 0;
 
 #ifndef LISP_FEATURE_WIN32
-static void (*interrupt_low_level_handlers[NSIG]) (int, siginfo_t*, void*);
+static
+void (*interrupt_low_level_handlers[NSIG]) (int, siginfo_t*, os_context_t*);
 #endif
 union interrupt_handler interrupt_handlers[NSIG];
 
@@ -700,7 +749,9 @@ interrupt_handle_pending(os_context_t *context)
          * the os_context for the signal we're currently in the
          * handler for. This should ensure that when we return from
          * the handler the blocked signals are unblocked. */
+#ifndef LISP_FEATURE_WIN32
         sigcopyset(os_context_sigmask_addr(context), &data->pending_mask);
+#endif
         data->gc_blocked_deferrables = 0;
     }
 
@@ -791,9 +842,7 @@ interrupt_handle_pending(os_context_t *context)
         arch_clear_pseudo_atomic_interrupted(context);
         /* Restore the sigmask in the context. */
         sigcopyset(os_context_sigmask_addr(context), &data->pending_mask);
-        /* This will break on sparc linux: the deferred handler really
-         * wants to be called with a void_context */
-        run_deferred_handler(data,(void *)context);
+        run_deferred_handler(data, context);
     }
     /* It is possible that the end of this function was reached
      * without never actually doing anything, the tests in Lisp for
@@ -802,20 +851,6 @@ interrupt_handle_pending(os_context_t *context)
 #endif
 }
 \f
-/*
- * the two main signal handlers:
- *   interrupt_handle_now(..)
- *   maybe_now_maybe_later(..)
- *
- * to which we have added interrupt_handle_now_handler(..).  Why?
- * Well, mostly because the SPARC/Linux platform doesn't quite do
- * signals the way we want them done.  The third argument in the
- * handler isn't filled in by the kernel properly, so we fix it up
- * ourselves in the arch_os_get_context(..) function; however, we only
- * want to do this when we first hit the handler, and not when
- * interrupt_handle_now(..) is being called from some other handler
- * (when the fixup will already have been done). -- CSR, 2002-07-23
- */
 
 void
 interrupt_handle_now(int signal, siginfo_t *info, os_context_t *context)
@@ -832,13 +867,6 @@ interrupt_handle_now(int signal, siginfo_t *info, os_context_t *context)
         check_interrupts_enabled_or_lose(context);
 #endif
 
-#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)) {
@@ -923,17 +951,18 @@ interrupt_handle_now(int signal, siginfo_t *info, os_context_t *context)
  * already; we're just doing the Lisp-level processing now that we
  * put off then */
 static void
-run_deferred_handler(struct interrupt_data *data, void *v_context)
+run_deferred_handler(struct interrupt_data *data, os_context_t *context)
 {
     /* The pending_handler may enable interrupts and then another
      * interrupt may hit, overwrite interrupt_data, so reset the
      * 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;
+    void (*pending_handler) (int, siginfo_t*, os_context_t*) =
+        data->pending_handler;
 
     data->pending_handler=0;
     FSHOW_SIGNAL((stderr, "/running deferred handler %p\n", pending_handler));
-    (*pending_handler)(data->pending_signal,&(data->pending_info), v_context);
+    (*pending_handler)(data->pending_signal,&(data->pending_info), context);
 }
 
 #ifndef LISP_FEATURE_WIN32
@@ -1022,16 +1051,13 @@ store_signal_data_for_later (struct interrupt_data *data, void *handler,
 static void
 maybe_now_maybe_later(int signal, siginfo_t *info, void *void_context)
 {
-    os_context_t *context = arch_os_get_context(&void_context);
+    SAVE_ERRNO(context,void_context);
     struct thread *thread = arch_os_get_current_thread();
     struct interrupt_data *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))
         interrupt_handle_now(signal, info, context);
+    RESTORE_ERRNO;
 }
 
 static void
@@ -1048,17 +1074,14 @@ low_level_interrupt_handle_now(int signal, siginfo_t *info,
 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);
+    SAVE_ERRNO(context,void_context);
     struct thread *thread = arch_os_get_current_thread();
     struct interrupt_data *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))
         low_level_interrupt_handle_now(signal, info, context);
+    RESTORE_ERRNO;
 }
 #endif
 
@@ -1066,10 +1089,8 @@ low_level_maybe_now_maybe_later(int signal, siginfo_t *info, void *void_context)
 
 /* This function must not cons, because that may trigger a GC. */
 void
-sig_stop_for_gc_handler(int signal, siginfo_t *info, void *void_context)
+sig_stop_for_gc_handler(int signal, siginfo_t *info, os_context_t *context)
 {
-    os_context_t *context = arch_os_get_context(&void_context);
-
     struct thread *thread=arch_os_get_current_thread();
     sigset_t ss;
 
@@ -1127,9 +1148,7 @@ sig_stop_for_gc_handler(int signal, siginfo_t *info, void *void_context)
 void
 interrupt_handle_now_handler(int signal, siginfo_t *info, void *void_context)
 {
-    os_context_t *context = arch_os_get_context(&void_context);
-#if defined(LISP_FEATURE_LINUX) || defined(RESTORE_FP_CONTROL_FROM_CONTEXT)
-    os_restore_fp_control(context);
+    SAVE_ERRNO(context,void_context);
 #ifndef LISP_FEATURE_WIN32
     if ((signal == SIGILL) || (signal == SIGBUS)
 #ifndef LISP_FEATURE_LINUX
@@ -1138,8 +1157,8 @@ interrupt_handle_now_handler(int signal, siginfo_t *info, void *void_context)
         )
         corruption_warning_and_maybe_lose("Signal %d recieved", signal);
 #endif
-#endif
     interrupt_handle_now(signal, info, context);
+    RESTORE_ERRNO;
 }
 
 /* manipulate the signal context and stack such that when the handler
@@ -1157,8 +1176,10 @@ extern void call_into_lisp_tramp(void);
 void
 arrange_return_to_lisp_function(os_context_t *context, lispobj function)
 {
+#ifndef LISP_FEATURE_WIN32
     check_gc_signals_unblocked_in_sigset_or_lose
         (os_context_sigmask_addr(context));
+#endif
 #if !(defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64))
     void * fun=native_pointer(function);
     void *code = &(((struct simple_fun *) fun)->code);
@@ -1167,8 +1188,14 @@ arrange_return_to_lisp_function(os_context_t *context, lispobj function)
     /* Build a stack frame showing `interrupted' so that the
      * user's backtrace makes (as much) sense (as usual) */
 
-    /* FIXME: what about restoring fp state? */
-    /* FIXME: what about restoring errno? */
+    /* fp state is saved and restored by call_into_lisp */
+    /* FIXME: errno is not restored, but since current uses of this
+     * function only call Lisp code that signals an error, it's not
+     * much of a problem. In other words, running out of the control
+     * stack between a syscall and (GET-ERRNO) may clobber errno if
+     * something fails during signalling or in the handler. But I
+     * can't see what can go wrong as long as there is no CONTINUE
+     * like restart on them. */
 #ifdef LISP_FEATURE_X86
     /* Suppose the existence of some function that saved all
      * registers, called call_into_lisp, then restored GP registers and
@@ -1330,26 +1357,6 @@ arrange_return_to_lisp_function(os_context_t *context, lispobj function)
            (long)function));
 }
 
-#ifdef LISP_FEATURE_SB_THREAD
-
-/* FIXME: this function can go away when all lisp handlers are invoked
- * via arrange_return_to_lisp_function. */
-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);
-
-    FSHOW_SIGNAL((stderr,"/interrupt_thread_handler\n"));
-    check_blockables_blocked_or_lose();
-
-    /* let the handler enable interrupts again when it sees fit */
-    sigaddset_deferrable(os_context_sigmask_addr(context));
-    arrange_return_to_lisp_function(context,
-                                    StaticSymbolFunction(RUN_INTERRUPTION));
-}
-
-#endif
-
 /* KLUDGE: Theoretically the approach we use for undefined alien
  * variables should work for functions as well, but on PPC/Darwin
  * we get bus error at bogus addresses instead, hence this workaround,
@@ -1376,8 +1383,8 @@ handle_guard_page_triggered(os_context_t *context,os_vm_address_t addr)
          * previous page so that we can catch returns from the guard page
          * and restore it. */
         corruption_warning_and_maybe_lose("Control stack exhausted");
-        protect_control_stack_guard_page(0);
-        protect_control_stack_return_guard_page(1);
+        protect_control_stack_guard_page(0, NULL);
+        protect_control_stack_return_guard_page(1, NULL);
 
 #ifdef LISP_FEATURE_C_STACK_IS_CONTROL_STACK
         /* For the unfortunate case, when the control stack is
@@ -1395,8 +1402,48 @@ handle_guard_page_triggered(os_context_t *context,os_vm_address_t addr)
          * the return-guard-page, and hit it on our way to new
          * exhaustion instead. */
         fprintf(stderr, "INFO: Control stack guard page reprotected\n");
-        protect_control_stack_guard_page(1);
-        protect_control_stack_return_guard_page(0);
+        protect_control_stack_guard_page(1, NULL);
+        protect_control_stack_return_guard_page(0, NULL);
+        return 1;
+    }
+    else if(addr >= BINDING_STACK_GUARD_PAGE(th) &&
+            addr < BINDING_STACK_GUARD_PAGE(th) + os_vm_page_size) {
+        corruption_warning_and_maybe_lose("Binding stack exhausted");
+        protect_binding_stack_guard_page(0, NULL);
+        protect_binding_stack_return_guard_page(1, NULL);
+
+        /* For the unfortunate case, when the binding stack is
+         * exhausted in a signal handler. */
+        unblock_signals_in_context_and_maybe_warn(context);
+        arrange_return_to_lisp_function
+            (context, StaticSymbolFunction(BINDING_STACK_EXHAUSTED_ERROR));
+        return 1;
+    }
+    else if(addr >= BINDING_STACK_RETURN_GUARD_PAGE(th) &&
+            addr < BINDING_STACK_RETURN_GUARD_PAGE(th) + os_vm_page_size) {
+        fprintf(stderr, "INFO: Binding stack guard page reprotected\n");
+        protect_binding_stack_guard_page(1, NULL);
+        protect_binding_stack_return_guard_page(0, NULL);
+        return 1;
+    }
+    else if(addr >= ALIEN_STACK_GUARD_PAGE(th) &&
+            addr < ALIEN_STACK_GUARD_PAGE(th) + os_vm_page_size) {
+        corruption_warning_and_maybe_lose("Alien stack exhausted");
+        protect_alien_stack_guard_page(0, NULL);
+        protect_alien_stack_return_guard_page(1, NULL);
+
+        /* For the unfortunate case, when the alien stack is
+         * exhausted in a signal handler. */
+        unblock_signals_in_context_and_maybe_warn(context);
+        arrange_return_to_lisp_function
+            (context, StaticSymbolFunction(ALIEN_STACK_EXHAUSTED_ERROR));
+        return 1;
+    }
+    else if(addr >= ALIEN_STACK_RETURN_GUARD_PAGE(th) &&
+            addr < ALIEN_STACK_RETURN_GUARD_PAGE(th) + os_vm_page_size) {
+        fprintf(stderr, "INFO: Alien stack guard page reprotected\n");
+        protect_alien_stack_guard_page(1, NULL);
+        protect_alien_stack_return_guard_page(0, NULL);
         return 1;
     }
     else if (addr >= undefined_alien_address &&
@@ -1481,23 +1528,35 @@ see_if_sigaction_nodefer_works(void)
 static void
 unblock_me_trampoline(int signal, siginfo_t *info, void *void_context)
 {
+    SAVE_ERRNO(context,void_context);
     sigset_t unblock;
 
     sigemptyset(&unblock);
     sigaddset(&unblock, signal);
     thread_sigmask(SIG_UNBLOCK, &unblock, 0);
-    interrupt_handle_now_handler(signal, info, void_context);
+    interrupt_handle_now(signal, info, context);
+    RESTORE_ERRNO;
 }
 
 static void
 low_level_unblock_me_trampoline(int signal, siginfo_t *info, void *void_context)
 {
+    SAVE_ERRNO(context,void_context);
     sigset_t unblock;
 
     sigemptyset(&unblock);
     sigaddset(&unblock, signal);
     thread_sigmask(SIG_UNBLOCK, &unblock, 0);
-    (*interrupt_low_level_handlers[signal])(signal, info, void_context);
+    (*interrupt_low_level_handlers[signal])(signal, info, context);
+    RESTORE_ERRNO;
+}
+
+static void
+low_level_handle_now_handler(int signal, siginfo_t *info, void *void_context)
+{
+    SAVE_ERRNO(context,void_context);
+    (*interrupt_low_level_handlers[signal])(signal, info, context);
+    RESTORE_ERRNO;
 }
 
 void
@@ -1511,30 +1570,20 @@ undoably_install_low_level_interrupt_handler (int signal,
     }
 
     if (ARE_SAME_HANDLER(handler, SIG_DFL))
-        sa.sa_sigaction = handler;
+        sa.sa_sigaction = (void (*)(int, siginfo_t*, void*))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;
+        sa.sa_sigaction = low_level_handle_now_handler;
 
     sigcopyset(&sa.sa_mask, &blockable_sigset);
     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_INTERRUPT_THREAD
-       || (signal==SIG_INTERRUPT_THREAD)
-#endif
-       )
+    if((signal==SIG_MEMORY_FAULT))
         sa.sa_flags |= SA_ONSTACK;
 #endif
 
@@ -1546,7 +1595,7 @@ undoably_install_low_level_interrupt_handler (int signal,
 
 /* This is called from Lisp. */
 unsigned long
-install_handler(int signal, void handler(int, siginfo_t*, void*))
+install_handler(int signal, void handler(int, siginfo_t*, os_context_t*))
 {
 #ifndef LISP_FEATURE_WIN32
     struct sigaction sa;
@@ -1564,7 +1613,7 @@ install_handler(int signal, void handler(int, siginfo_t*, void*))
     if (interrupt_low_level_handlers[signal]==0) {
         if (ARE_SAME_HANDLER(handler, SIG_DFL) ||
             ARE_SAME_HANDLER(handler, SIG_IGN))
-            sa.sa_sigaction = handler;
+            sa.sa_sigaction = (void (*)(int, siginfo_t*, void*))handler;
         else if (sigismember(&deferrable_sigset, signal))
             sa.sa_sigaction = maybe_now_maybe_later;
         else if (!sigaction_nodefer_works &&
@@ -1596,7 +1645,7 @@ install_handler(int signal, void handler(int, siginfo_t*, void*))
 /* This must not go through lisp as it's allowed anytime, even when on
  * the altstack. */
 void
-sigabrt_handler(int signal, siginfo_t *info, void *void_context)
+sigabrt_handler(int signal, siginfo_t *info, os_context_t *context)
 {
     lose("SIGABRT received.\n");
 }
@@ -1623,7 +1672,7 @@ interrupt_init(void)
              * signal(..)-style one-argument handlers, which is OK
              * because it works to call the 1-argument form where the
              * 3-argument form is expected.) */
-            (void (*)(int, siginfo_t*, void*))SIG_DFL;
+            (void (*)(int, siginfo_t*, os_context_t*))SIG_DFL;
     }
     undoably_install_low_level_interrupt_handler(SIGABRT, sigabrt_handler);
     SHOW("returning from interrupt_init()");
@@ -1648,10 +1697,14 @@ lisp_memory_fault_error(os_context_t *context, os_vm_address_t addr)
     */
     current_memory_fault_address = addr;
     /* To allow debugging memory faults in signal handlers and such. */
-    corruption_warning_and_maybe_lose("Memory fault");
+    corruption_warning_and_maybe_lose("Memory fault at %x", addr);
     unblock_signals_in_context_and_maybe_warn(context);
+#ifdef LISP_FEATURE_C_STACK_IS_CONTROL_STACK
     arrange_return_to_lisp_function(context,
                                     StaticSymbolFunction(MEMORY_FAULT_ERROR));
+#else
+    funcall0(StaticSymbolFunction(MEMORY_FAULT_ERROR));
+#endif
 }
 #endif