#include "dynbind.h"
#include "interr.h"
-
void sigaddset_blockable(sigset_t *s)
{
sigaddset(s, SIGHUP);
* utility routines used by various signal handlers
*/
-void
-fake_foreign_function_call(os_context_t *context)
+void
+build_fake_control_stack_frames(os_context_t *context)
{
- int context_index;
-#ifndef __i386__
+#ifndef LISP_FEATURE_X86
+
lispobj oldcont;
-#endif
- /* Get current Lisp state from context. */
-#ifdef reg_ALLOC
- dynamic_space_free_pointer =
- (lispobj *)(*os_context_register_addr(context, reg_ALLOC));
-#ifdef alpha
- if ((long)dynamic_space_free_pointer & 1) {
- lose("dead in fake_foreign_function_call, context = %x", context);
- }
-#endif
-#endif
-#ifdef reg_BSP
- current_binding_stack_pointer =
- (lispobj *)(*os_context_register_addr(context, reg_BSP));
-#endif
+ /* Build a fake stack frame or frames */
-#ifndef __i386__
- /* Build a fake stack frame. */
current_control_frame_pointer =
(lispobj *)(*os_context_register_addr(context, reg_CSP));
if ((lispobj *)(*os_context_register_addr(context, reg_CFP))
oldcont = (lispobj)(*os_context_register_addr(context, reg_OCFP));
}
}
- /* ### We can't tell whether we are still in the caller if it had
- * to reg_ALLOCate the stack frame due to stack arguments. */
- /* ### Can anything strange happen during return? */
+ /* We can't tell whether we are still in the caller if it had to
+ * allocate a stack frame due to stack arguments. */
+ /* This observation provoked some past CMUCL maintainer to ask
+ * "Can anything strange happen during return?" */
else {
/* normal case */
oldcont = (lispobj)(*os_context_register_addr(context, reg_CFP));
current_control_frame_pointer[2] =
(lispobj)(*os_context_register_addr(context, reg_CODE));
#endif
+}
+
+void
+fake_foreign_function_call(os_context_t *context)
+{
+ int context_index;
+
+ /* Get current Lisp state from context. */
+#ifdef reg_ALLOC
+ dynamic_space_free_pointer =
+ (lispobj *)(*os_context_register_addr(context, reg_ALLOC));
+#ifdef alpha
+ if ((long)dynamic_space_free_pointer & 1) {
+ lose("dead in fake_foreign_function_call, context = %x", context);
+ }
+#endif
+#endif
+#ifdef reg_BSP
+ current_binding_stack_pointer =
+ (lispobj *)(*os_context_register_addr(context, reg_BSP));
+#endif
+
+ build_fake_control_stack_frames(context);
/* Do dynamic binding of the active interrupt context index
* and save the context in the context array. */
* which do bare >> and << for fixnum_value and make_fixnum. */
if (context_index >= MAX_INTERRUPTS) {
- lose("maximum interrupt nesting depth (%d) exceeded",
- MAX_INTERRUPTS);
+ lose("maximum interrupt nesting depth (%d) exceeded", MAX_INTERRUPTS);
}
bind_variable(FREE_INTERRUPT_CONTEXT_INDEX,
* 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
#endif
union interrupt_handler handler;
- /* FIXME: The CMU CL we forked off of had this Linux-only
- * operation here. Newer CMU CLs (e.g. 18c) have hairier
- * Linux/i386-only logic here. SBCL seems to be more reliable
- * without anything here. However, if we start supporting code
- * which sets the rounding mode, then we may want to do something
- * special to force the rounding mode back to some standard value
- * here, so that ISRs can have a standard environment. (OTOH, if
- * rounding modes are under user control, then perhaps we should
- * leave this up to the user.)
- *
- * In the absence of a test case to show that this is really a
- * problem, we just suppress this code completely (just like the
- * parallel code in maybe_now_maybe_later).
- * #ifdef __linux__
- * SET_FPU_CONTROL_WORD(context->__fpregs_mem.cw);
- * #endif */
-
+#ifdef LISP_FEATURE_LINUX
+ /* 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)) {
static void
maybe_now_maybe_later(int signal, siginfo_t *info, void *void_context)
{
- os_context_t *context = (os_context_t*)void_context;
-
- /* FIXME: See Debian cmucl 2.4.17, and mail from DTC on the CMU CL
- * mailing list 23 Oct 1999, for changes in FPU handling at
- * interrupt time which should be ported into SBCL. Also see the
- * analogous logic at the head of interrupt_handle_now for
- * more related FIXME stuff.
- *
- * For now, we just suppress this code completely.
- * #ifdef __linux__
- * SET_FPU_CONTROL_WORD(context->__fpregs_mem.cw);
- * #endif */
+ os_context_t *context = arch_os_get_context(&void_context);
+#ifdef LISP_FEATURE_LINUX
+ os_restore_fp_control(context);
+#endif
+
/* see comments at top of code/signal.lisp for what's going on here
* with INTERRUPTS_ENABLED/INTERRUPT_HANDLE_NOW
*/
}
}
\f
+
+void
+interrupt_handle_now_handler(int signal, siginfo_t *info, void *void_context)
+{
+ os_context_t *context = arch_os_get_context(&void_context);
+ interrupt_handle_now(signal, info, context);
+}
+
/*
* stuff to detect and handle hitting the GC trigger
*/
-#ifndef GENCGC /* since GENCGC has its own way to record trigger */
+#ifndef LISP_FEATURE_GENCGC
+/* since GENCGC has its own way to record trigger */
static boolean
gc_trigger_hit(int signal, siginfo_t *info, os_context_t *context)
{
if (current_auto_gc_trigger == NULL)
return 0;
else{
- lispobj *badaddr=(lispobj *)arch_get_bad_addr(signal,
- info,
- context);
-
- return (badaddr >= current_auto_gc_trigger &&
- badaddr < current_dynamic_space + DYNAMIC_SPACE_SIZE);
+ void *badaddr=arch_get_bad_addr(signal,info,context);
+ return (badaddr >= (void *)current_auto_gc_trigger &&
+ badaddr <((void *)current_dynamic_space + DYNAMIC_SPACE_SIZE));
}
}
#endif
-#ifndef __i386__
+/* and similarly for the control stack guard page */
+
+boolean handle_control_stack_guard_triggered(os_context_t *context,void *addr)
+{
+ /* note the os_context hackery here. When the signal handler returns,
+ * it won't go back to what it was doing ... */
+ if(addr>=(void *)CONTROL_STACK_GUARD_PAGE &&
+ addr<(void *)(CONTROL_STACK_GUARD_PAGE+os_vm_page_size)) {
+ void *fun;
+ void *code;
+
+ /* we hit the end of the control stack. disable protection
+ * temporarily so the error handler has some headroom */
+ protect_control_stack_guard_page(0);
+
+ fun = (void *)
+ native_pointer((lispobj) SymbolFunction(CONTROL_STACK_EXHAUSTED_ERROR));
+ code = &(((struct simple_fun *) fun)->code);
+
+ /* Build a stack frame showing `interrupted' so that the
+ * user's backtrace makes (as much) sense (as usual) */
+ build_fake_control_stack_frames(context);
+ /* signal handler will "return" to this error-causing function */
+ *os_context_pc_addr(context) = code;
+#ifdef LISP_FEATURE_X86
+ *os_context_register_addr(context,reg_ECX) = 0;
+#else
+ /* this much of the calling convention is common to all
+ non-x86 ports */
+ *os_context_register_addr(context,reg_NARGS) = 0;
+ *os_context_register_addr(context,reg_LIP) = code;
+ *os_context_register_addr(context,reg_CFP) =
+ current_control_frame_pointer;
+#endif
+#ifdef ARCH_HAS_NPC_REGISTER
+ *os_context_npc_addr(context) =
+ 4 + *os_context_pc_addr(context);
+#endif
+#ifdef LISP_FEATURE_SPARC
+ /* Bletch. This is a feature of the SPARC calling convention,
+ which sadly I'm not going to go into in large detail here,
+ as I don't know it well enough. Suffice to say that if the
+ line
+
+ (INST MOVE CODE-TN FUNCTION)
+
+ in compiler/sparc/call.lisp is changed, then this bit can
+ probably go away. -- CSR, 2002-07-24 */
+ *os_context_register_addr(context,reg_CODE) =
+ fun + FUN_POINTER_LOWTAG;
+#endif
+ return 1;
+ }
+ else return 0;
+}
+
+#ifndef LISP_FEATURE_X86
/* This function gets called from the SIGSEGV (for e.g. Linux or
* OpenBSD) or SIGBUS (for e.g. FreeBSD) handler. Here we check
* whether the signal was due to treading on the mprotect()ed zone -
os_context_t *context=(os_context_t *) void_context;
if (!foreign_function_call_active
-#ifndef GENCGC /* since GENCGC has its own way to record trigger */
+#ifndef LISP_FEATURE_GENCGC
+ /* nb: GENCGC on non-x86? I really don't think so. This
+ * happens every time */
&& gc_trigger_hit(signal, info, context)
#endif
) {
-#ifndef GENCGC /* since GENCGC has its own way to record trigger */
+#ifndef LISP_FEATURE_GENCGC
clear_auto_gc_trigger();
#endif
if(current_dynamic_space==old_free_space)
/* MAYBE-GC (as the name suggest) might not. If it
* doesn't, it won't reset the GC trigger either, so we
- * have to do it ourselves. Add small amount of space
- * to tide us over while GC is inhibited
+ * have to do it ourselves. Put it near the end of
+ * dynamic space so we're not running into it continually
*/
- set_auto_gc_trigger((u32)dynamic_space_free_pointer
- -(u32)current_dynamic_space
- +(u32)os_vm_page_size);
+ set_auto_gc_trigger(DYNAMIC_SPACE_SIZE
+ -(u32)os_vm_page_size);
}
return 1;
} else {
sigemptyset(&sa.sa_mask);
sigaddset_blockable(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO | SA_RESTART;
-
+#ifdef LISP_FEATURE_C_STACK_IS_CONTROL_STACK
+ /* Signal handlers are run on the control stack, so if it is exhausted
+ * we had better use an alternate stack for whatever signal tells us
+ * we've exhausted it */
+ if(signal==SIG_MEMORY_FAULT) {
+ stack_t sigstack;
+ sigstack.ss_sp=(void *) ALTERNATE_SIGNAL_STACK_START;
+ sigstack.ss_flags=0;
+ sigstack.ss_size = SIGSTKSZ;
+ sigaltstack(&sigstack,0);
+ sa.sa_flags|=SA_ONSTACK;
+ }
+#endif
+
/* In the case of interrupt handlers which are modified more than
* once, we only save the original unmodified copy. */
if (!old_low_level_signal_handler_state->was_modified) {
} else if (sigismember(&new, signal)) {
sa.sa_sigaction = maybe_now_maybe_later;
} else {
- sa.sa_sigaction = interrupt_handle_now;
+ sa.sa_sigaction = interrupt_handle_now_handler;
}
sigemptyset(&sa.sa_mask);