/* -- Dribbling Signals -------------------------------------------------------------------- */ /* for Darwin */ /* * We have this issue that CCL cannot cope with signal handler frames on the * Lisp stack at all. So we need to get it off that stack when we're asked * to handle a signal. * * Previously this was using Mach Exceptions which work by messages sent to * some other threaad and that other thread then mimicked UNIX signals while * caring for the stack. Then perhaps previously it worked by copying the * ucontext_t off the Lisp stack and then issuing a sigreturn() system call * when trying to go back, which of course is undocumented. And because we * then made libc unhappy it was using _sigaction() instead of _sigaction(2) * to get rid of the SA_VALIDATE_SIGRETURN_FROM_SIGTRAMP flag. * * Needless to say that this broke again. With Rosetta. Apparently there's * some bug with getting or setting the FPU state with Mach under Rosetta. * And that sigreturn syscall business broke as well. * * So we take a different approach, when a signal arrives while on the Lisp * stack it goes like this: * * 1. Get hold of the foreign stack rsp * * 2. Copy siginfo_t and ucontext_t to that foreign stack * * 3. Update signal handler's mcontext, so that we make a function call * to `signal_trampoline` * * 5. Update the sigmask of the ucontext. * * 4. Return from signal. libc is happy. Rosetta is happy. * * 5. We're in `signal_trampoline` now. * * 6. Invoke whatever is the real handler. * * 7. Real handler returned. Issue INT UUO_SIGRETURN * * 8. `arbstack_signal_handler` is invoked because of that INT. * * 9. Copy the mcontext of our copy of the orignal mcontext to * arbstack_signal_handler()'s mcontext. * * 10. Don't forget the sigmask. * * 11. Return from `arbstack_signal_handler`. * * 12. Kernel will get us back to where we came from. * * Steps 7ff could as well be just a setcontext(2). But that proved to * be unreliable. * * We believe that this is moderately safe as we're kind of supposed to be * able to update the mcontext. We don't need Mach, we don't need sigreturn * syscalls, we don't need _sigaction(), we don't need to drop that * SA_VALIDATE_SIGRETURN_FROM_SIGTRAMP. * * However, another idea would be to just copy the ucontext off the Lisp * stack to the foreign stack, handle the signal, copy the context back onto * that Lisp stack, and then return. I might attempt that. There's a red * zone even. * * In the end of day, I believe it would be saner to either allow for one * mixed Lisp/C stack. Or to not use %rsp for the Lisp stack pointer. There * should be only one "CPU" stack and it should not be switched. */ struct signal_trampoline_arg { ucontext_t context; struct __darwin_mcontext64 mcontext; int signum; siginfo_t info; void (*handler)(int, siginfo_t *, ExceptionInformation *); }; void signal_trampoline (struct signal_trampoline_arg *arg); void handle_signal_on_foreign_stack(TCR *tcr, void (*handler)(int, siginfo_t *, ExceptionInformation *), int signum, siginfo_t *info, ExceptionInformation *context, LispObj return_address) { uintptr_t rsp = (uintptr_t)find_foreign_rsp(xpGPR(context,Isp), tcr->cs_area, tcr); (void)return_address; /* Copy all what is needed to our foreign stack */ rsp &= ~((uintptr_t)15); rsp -= sizeof(struct signal_trampoline_arg); rsp &= ~((uintptr_t)15); struct signal_trampoline_arg *arg = (struct signal_trampoline_arg*)rsp; arg->handler = handler; arg->signum = signum; arg->info = *info; arg->context = *context; arg->context.uc_mcontext = (void*)&(arg->mcontext); arg->mcontext = *(context->uc_mcontext); pthread_sigmask(SIG_SETMASK, 0, &(context->uc_sigmask)); xpGPR(context, REG_RDI) = rsp; rsp &= ~((uintptr_t)15); rsp -= 8; /* When entering a function rsp must be odd. */ xpGPR(context, Iflags) &= ~(1ULL << X86_DIRECTION_FLAG_BIT); xpGPR(context, REG_RSP) = rsp; xpGPR(context, REG_RIP) = (uintptr_t)&signal_trampoline; } void signal_trampoline (struct signal_trampoline_arg *arg) { extern void do_uuo_sigreturn (int, siginfo_t *, ExceptionInformation *); sigset_t m; (arg->handler)(arg->signum, &(arg->info), &(arg->context)); sigfillset(&m); sigdelset(&m, SIGSEGV); pthread_sigmask(SIG_SETMASK, &m, 0); do_uuo_sigreturn(arg->signum, &(arg->info), &(arg->context)); } void uuo_sigreturn (int signum, siginfo_t *info, ExceptionInformation *context) { /* The arguments to the orignal (first) signal are in the context's rdi, rsi, rdx. */ ExceptionInformation *context_copy = (ExceptionInformation *)xpGPR(context, REG_RDX); (void)info; (void)signum; /* We just copy over the sigmask and the mcontext and hope for the best. */ context->uc_sigmask = context_copy->uc_sigmask; *context->uc_mcontext = *context_copy->uc_mcontext; }