#include "globals.h"
#include "lispregs.h"
#include "validate.h"
+#include "interr.h"
#include "gc.h"
#include "alloc.h"
#include "dynbind.h"
-#include "interr.h"
#include "pseudo-atomic.h"
#include "genesis/fdefn.h"
#include "genesis/simple-fun.h"
#include "genesis/cons.h"
+/* When we catch an internal error, should we pass it back to Lisp to
+ * be handled in a high-level way? (Early in cold init, the answer is
+ * 'no', because Lisp is still too brain-dead to handle anything.
+ * After sufficient initialization has been completed, the answer
+ * becomes 'yes'.) */
+boolean internal_errors_enabled = 0;
+
+#ifndef LISP_FEATURE_WIN32
+static
+void (*interrupt_low_level_handlers[NSIG]) (int, siginfo_t*, os_context_t*);
+#endif
+union interrupt_handler interrupt_handlers[NSIG];
+
/* 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_context_t *context = arch_os_get_context(&void_context);
#endif
+/* Foreign code may want to start some threads on its own.
+ * Non-targetted, truly asynchronous signals can be delivered to
+ * basically any thread, but invoking Lisp handlers in such foregign
+ * threads is really bad, so let's resignal it.
+ *
+ * This should at least bring attention to the problem, but it cannot
+ * work for SIGSEGV and similar. It is good enough for timers, and
+ * maybe all deferrables. */
+
+#ifdef LISP_FEATURE_SB_THREAD
+static void
+add_handled_signals(sigset_t *sigset)
+{
+ int i;
+ for(i = 1; i < NSIG; i++) {
+ if (!(ARE_SAME_HANDLER(interrupt_low_level_handlers[i], SIG_DFL)) ||
+ !(ARE_SAME_HANDLER(interrupt_handlers[i].c, SIG_DFL))) {
+ sigaddset(sigset, i);
+ }
+ }
+}
+
+void block_signals(sigset_t *what, sigset_t *where, sigset_t *old);
+#endif
+
+static boolean
+maybe_resignal_to_lisp_thread(int signal, os_context_t *context)
+{
+#ifdef LISP_FEATURE_SB_THREAD
+ if (!pthread_getspecific(lisp_thread)) {
+ if (!(sigismember(&deferrable_sigset,signal))) {
+ corruption_warning_and_maybe_lose
+ ("Received signal %d in non-lisp thread %lu, resignalling to a lisp thread.",
+ signal,
+ pthread_self());
+ }
+ {
+ sigset_t sigset;
+ sigemptyset(&sigset);
+ add_handled_signals(&sigset);
+ block_signals(&sigset, 0, 0);
+ block_signals(&sigset, os_context_sigmask_addr(context), 0);
+ kill(getpid(), signal);
+ }
+ return 1;
+ } else
+#endif
+ return 0;
+}
+
/* These are to be used in signal handlers. Currently all handlers are
* called from one of:
*
* 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) \
+#define SAVE_ERRNO(signal,context,void_context) \
{ \
int _saved_errno = errno; \
RESTORE_FP_CONTROL_WORD(context,void_context); \
+ if (!maybe_resignal_to_lisp_thread(signal, context)) \
{
#define RESTORE_ERRNO \
#endif
+#if !defined(LISP_FEATURE_WIN32)
boolean
deferrables_blocked_p(sigset_t *sigset)
{
return all_signals_blocked_p(sigset, &deferrable_sigset, "deferrable");
}
+#endif
void
check_deferrables_unblocked_or_lose(sigset_t *sigset)
#endif
}
+#if !defined(LISP_FEATURE_WIN32)
boolean
blockables_blocked_p(sigset_t *sigset)
{
return all_signals_blocked_p(sigset, &blockable_sigset, "blockable");
}
+#endif
void
check_blockables_unblocked_or_lose(sigset_t *sigset)
#endif
}
+#if !defined(LISP_FEATURE_WIN32)
boolean
gc_signals_blocked_p(sigset_t *sigset)
{
return all_signals_blocked_p(sigset, &gc_sigset, "gc");
}
+#endif
void
check_gc_signals_unblocked_or_lose(sigset_t *sigset)
}
#endif
}
-
-/* When we catch an internal error, should we pass it back to Lisp to
- * be handled in a high-level way? (Early in cold init, the answer is
- * 'no', because Lisp is still too brain-dead to handle anything.
- * After sufficient initialization has been completed, the answer
- * becomes 'yes'.) */
-boolean internal_errors_enabled = 0;
-
-#ifndef LISP_FEATURE_WIN32
-static
-void (*interrupt_low_level_handlers[NSIG]) (int, siginfo_t*, os_context_t*);
-#endif
-union interrupt_handler interrupt_handlers[NSIG];
-
\f
/*
* utility routines used by various signal handlers
/* Build a fake stack frame or frames */
- current_control_frame_pointer =
+ access_control_frame_pointer(th) =
(lispobj *)(unsigned long)
(*os_context_register_addr(context, reg_CSP));
if ((lispobj *)(unsigned long)
(*os_context_register_addr(context, reg_CFP))
- == current_control_frame_pointer) {
+ == access_control_frame_pointer(th)) {
/* There is a small window during call where the callee's
* frame isn't built yet. */
if (lowtag_of(*os_context_register_addr(context, reg_CODE))
== FUN_POINTER_LOWTAG) {
/* We have called, but not built the new frame, so
* build it for them. */
- current_control_frame_pointer[0] =
+ access_control_frame_pointer(th)[0] =
*os_context_register_addr(context, reg_OCFP);
- current_control_frame_pointer[1] =
+ access_control_frame_pointer(th)[1] =
*os_context_register_addr(context, reg_LRA);
- current_control_frame_pointer += 8;
+ access_control_frame_pointer(th) += 8;
/* Build our frame on top of it. */
oldcont = (lispobj)(*os_context_register_addr(context, reg_CFP));
}
oldcont = (lispobj)(*os_context_register_addr(context, reg_CFP));
}
- current_control_stack_pointer = current_control_frame_pointer + 8;
+ access_control_stack_pointer(th) = access_control_frame_pointer(th) + 8;
- current_control_frame_pointer[0] = oldcont;
- current_control_frame_pointer[1] = NIL;
- current_control_frame_pointer[2] =
+ access_control_frame_pointer(th)[0] = oldcont;
+ access_control_frame_pointer(th)[1] = NIL;
+ access_control_frame_pointer(th)[2] =
(lispobj)(*os_context_register_addr(context, reg_CODE));
#endif
}
/* Get current Lisp state from context. */
#ifdef reg_ALLOC
+#ifdef LISP_FEATURE_SB_THREAD
+ thread->pseudo_atomic_bits =
+#else
dynamic_space_free_pointer =
(lispobj *)(unsigned long)
+#endif
(*os_context_register_addr(context, reg_ALLOC));
/* fprintf(stderr,"dynamic_space_free_pointer: %p\n", */
/* dynamic_space_free_pointer); */
#endif
#endif
#ifdef reg_BSP
- current_binding_stack_pointer =
- (lispobj *)(unsigned long)
- (*os_context_register_addr(context, reg_BSP));
+ set_binding_stack_pointer(thread,
+ *os_context_register_addr(context, reg_BSP));
#endif
build_fake_control_stack_frames(thread,context);
thread->interrupt_contexts[context_index] = context;
-#ifdef FOREIGN_FUNCTION_CALL_FLAG
- foreign_function_call_active = 1;
+#if !defined(LISP_FEATURE_X86) && !defined(LISP_FEATURE_X86_64)
+ /* x86oid targets don't maintain the foreign function call flag at
+ * all, so leave them to believe that they are never in foreign
+ * code. */
+ foreign_function_call_active_p(thread) = 1;
#endif
}
/* Block all blockable signals. */
block_blockable_signals(0, 0);
-#ifdef FOREIGN_FUNCTION_CALL_FLAG
- foreign_function_call_active = 0;
-#endif
+ foreign_function_call_active_p(thread) = 0;
/* Undo dynamic binding of FREE_INTERRUPT_CONTEXT_INDEX */
unbind(thread);
-#ifdef reg_ALLOC
+#if defined(reg_ALLOC) && !defined(LISP_FEATURE_SB_THREAD)
/* 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 & LOWTAG_MASK);
*/
#endif
+#if defined(reg_ALLOC) && defined(LISP_FEATURE_SB_THREAD)
+ /* Put the pseudo-atomic bits and dynamic space free pointer back
+ * into the context (p-a-bits for p-a, and dynamic space free
+ * pointer for ROOM). */
+ *os_context_register_addr(context, reg_ALLOC) =
+ (unsigned long) dynamic_space_free_pointer
+ | (thread->pseudo_atomic_bits & LOWTAG_MASK);
+ /* And clear them so we don't get bit later by call-in/call-out
+ * not updating them. */
+ thread->pseudo_atomic_bits = 0;
+#endif
}
/* a handler for the signal caused by execution of a trap opcode
void
interrupt_handle_now(int signal, siginfo_t *info, os_context_t *context)
{
-#ifdef FOREIGN_FUNCTION_CALL_FLAG
boolean were_in_lisp;
-#endif
union interrupt_handler handler;
check_blockables_blocked_or_lose(0);
return;
}
-#ifdef FOREIGN_FUNCTION_CALL_FLAG
- were_in_lisp = !foreign_function_call_active;
+ were_in_lisp = !foreign_function_call_active_p(arch_os_get_current_thread());
if (were_in_lisp)
-#endif
{
fake_foreign_function_call(context);
}
(*handler.c)(signal, info, context);
}
-#ifdef FOREIGN_FUNCTION_CALL_FLAG
if (were_in_lisp)
-#endif
{
undo_fake_foreign_function_call(context); /* block signals again */
}
static void
maybe_now_maybe_later(int signal, siginfo_t *info, void *void_context)
{
- SAVE_ERRNO(context,void_context);
+ SAVE_ERRNO(signal,context,void_context);
struct thread *thread = arch_os_get_current_thread();
struct interrupt_data *data = thread->interrupt_data;
-
if(!maybe_defer_handler(interrupt_handle_now,data,signal,info,context))
interrupt_handle_now(signal, info, context);
RESTORE_ERRNO;
static void
low_level_maybe_now_maybe_later(int signal, siginfo_t *info, void *void_context)
{
- SAVE_ERRNO(context,void_context);
+ SAVE_ERRNO(signal,context,void_context);
struct thread *thread = arch_os_get_current_thread();
struct interrupt_data *data = thread->interrupt_data;
sig_stop_for_gc_handler(int signal, siginfo_t *info, os_context_t *context)
{
struct thread *thread=arch_os_get_current_thread();
+ boolean was_in_lisp;
/* Test for GC_INHIBIT _first_, else we'd trap on every single
* pseudo atomic until gc is finally allowed. */
/* Not PA and GC not inhibited -- we can stop now. */
- /* need the context stored so it can have registers scavenged */
- fake_foreign_function_call(context);
+ was_in_lisp = !foreign_function_call_active_p(arch_os_get_current_thread());
+
+ if (was_in_lisp) {
+ /* need the context stored so it can have registers scavenged */
+ fake_foreign_function_call(context);
+ }
/* Not pending anymore. */
SetSymbolValue(GC_PENDING,NIL,thread);
set_thread_state(thread,STATE_SUSPENDED);
FSHOW_SIGNAL((stderr,"suspended\n"));
+ /* While waiting for gc to finish occupy ourselves with zeroing
+ * the unused portion of the control stack to reduce conservatism.
+ * On hypothetic platforms with threads and exact gc it is
+ * actually a must. */
+ scrub_control_stack();
+
wait_for_thread_state_change(thread, STATE_SUSPENDED);
FSHOW_SIGNAL((stderr,"resumed\n"));
fixnum_value(thread_state(thread)));
}
- undo_fake_foreign_function_call(context);
+ if (was_in_lisp) {
+ undo_fake_foreign_function_call(context);
+ }
}
#endif
void
interrupt_handle_now_handler(int signal, siginfo_t *info, void *void_context)
{
- SAVE_ERRNO(context,void_context);
+ SAVE_ERRNO(signal,context,void_context);
#ifndef LISP_FEATURE_WIN32
if ((signal == SIGILL) || (signal == SIGBUS)
#ifndef LISP_FEATURE_LINUX
|| (signal == SIGEMT)
#endif
)
- corruption_warning_and_maybe_lose("Signal %d recieved", signal);
+ corruption_warning_and_maybe_lose("Signal %d received", signal);
#endif
interrupt_handle_now(signal, info, context);
RESTORE_ERRNO;
*os_context_register_addr(context,reg_LIP) =
(os_context_register_t)(unsigned long)code;
*os_context_register_addr(context,reg_CFP) =
- (os_context_register_t)(unsigned long)current_control_frame_pointer;
+ (os_context_register_t)(unsigned long)access_control_frame_pointer(th);
#endif
#ifdef ARCH_HAS_NPC_REGISTER
*os_context_npc_addr(context) =
funcall0(StaticSymbolFunction(UNDEFINED_ALIEN_FUNCTION_ERROR));
}
+void lower_thread_control_stack_guard_page(struct thread *th)
+{
+ protect_control_stack_guard_page(0, th);
+ protect_control_stack_return_guard_page(1, th);
+ th->control_stack_guard_page_protected = NIL;
+ fprintf(stderr, "INFO: Control stack guard page unprotected\n");
+}
+
+void reset_thread_control_stack_guard_page(struct thread *th)
+{
+ memset(CONTROL_STACK_GUARD_PAGE(th), 0, os_vm_page_size);
+ protect_control_stack_guard_page(1, th);
+ protect_control_stack_return_guard_page(0, th);
+ th->control_stack_guard_page_protected = T;
+ fprintf(stderr, "INFO: Control stack guard page reprotected\n");
+}
+
+/* Called from the REPL, too. */
+void reset_control_stack_guard_page(void)
+{
+ struct thread *th=arch_os_get_current_thread();
+ if (th->control_stack_guard_page_protected == NIL) {
+ reset_thread_control_stack_guard_page(th);
+ }
+}
+
+void lower_control_stack_guard_page(void)
+{
+ lower_thread_control_stack_guard_page(arch_os_get_current_thread());
+}
+
boolean
handle_guard_page_triggered(os_context_t *context,os_vm_address_t addr)
{
* protection so the error handler has some headroom, protect the
* previous page so that we can catch returns from the guard page
* and restore it. */
- protect_control_stack_guard_page(0, NULL);
- protect_control_stack_return_guard_page(1, NULL);
- fprintf(stderr, "INFO: Control stack guard page unprotected\n");
-
+ if (th->control_stack_guard_page_protected == NIL)
+ lose("control_stack_guard_page_protected NIL");
+ lower_control_stack_guard_page();
#ifdef LISP_FEATURE_C_STACK_IS_CONTROL_STACK
/* For the unfortunate case, when the control stack is
* exhausted in a signal handler. */
* unprotect this one. This works even if we somehow missed
* the return-guard-page, and hit it on our way to new
* exhaustion instead. */
- protect_control_stack_guard_page(1, NULL);
- protect_control_stack_return_guard_page(0, NULL);
- fprintf(stderr, "INFO: Control stack guard page reprotected\n");
+ if (th->control_stack_guard_page_protected != NIL)
+ lose("control_stack_guard_page_protected not NIL");
+ reset_control_stack_guard_page();
return 1;
}
else if(addr >= BINDING_STACK_HARD_GUARD_PAGE(th) &&
static void
unblock_me_trampoline(int signal, siginfo_t *info, void *void_context)
{
- SAVE_ERRNO(context,void_context);
+ SAVE_ERRNO(signal,context,void_context);
sigset_t unblock;
sigemptyset(&unblock);
static void
low_level_unblock_me_trampoline(int signal, siginfo_t *info, void *void_context)
{
- SAVE_ERRNO(context,void_context);
+ SAVE_ERRNO(signal,context,void_context);
sigset_t unblock;
sigemptyset(&unblock);
static void
low_level_handle_now_handler(int signal, siginfo_t *info, void *void_context)
{
- SAVE_ERRNO(context,void_context);
+ SAVE_ERRNO(signal,context,void_context);
(*interrupt_low_level_handlers[signal])(signal, info, context);
RESTORE_ERRNO;
}