#include "globals.h"
#include "lispregs.h"
#include "validate.h"
-#include "monitor.h"
#include "gc.h"
#include "alloc.h"
#include "dynbind.h"
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
{
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
}
void
check_blockables_blocked_or_lose()
{
-#ifndef LISP_FEATURE_WIN32
+#if !defined(LISP_FEATURE_WIN32)
/* Get the current sigmask, by blocking the empty set. */
sigset_t empty,current;
int i;
#endif
}
+void
+block_deferrable_signals(void)
+{
+#ifndef LISP_FEATURE_WIN32
+ thread_sigmask(SIG_BLOCK, &deferrable_sigset, 0);
+#endif
+}
+
\f
/*
* utility routines used by various signal handlers
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\n", 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
}
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
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)) {
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
* 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);
}
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
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);
{
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);
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;
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;
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",
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
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)
{
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*/
*(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;
#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);
(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;
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));
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);
}
* 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, ¤t);
+ thread_sigmask(SIG_BLOCK, &empty, ¤t);
+ /* 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(¤t, i) != ((i == SIGABRT) ? 1 : 0)) {
+ if (sigismember(¤t, 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;
}
sa.sa_flags = SA_SIGINFO | SA_NODEFER;
sa.sa_sigaction = sigaction_nodefer_test_handler;
sigemptyset(&sa.sa_mask);
- sigaddset(&sa.sa_mask, SIGABRT);
- sigaction(SIGUSR1, &sa, &old_sa);
+ 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(), SIGUSR1);
+ kill(getpid(), SA_NODEFER_TEST_KILL_SIGNAL);
while (sigaction_nodefer_works == -1);
- sigaction(SIGUSR1, &old_sa, NULL);
+ 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);
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);
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
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