diff options
Diffstat (limited to 'winsup/cygwin/exceptions.cc')
-rw-r--r-- | winsup/cygwin/exceptions.cc | 248 |
1 files changed, 160 insertions, 88 deletions
diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index 649574fc2..458029f35 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -1,7 +1,7 @@ /* exceptions.cc Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, - 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Red Hat, Inc. + 2005, 2006, 2007, 2008, 2009, 2010, 2011 Red Hat, Inc. This file is part of Cygwin. @@ -19,8 +19,8 @@ details. */ #include <syslog.h> #include <wchar.h> -#include "cygtls.h" #include "pinfo.h" +#include "cygtls.h" #include "sigproc.h" #include "shared_info.h" #include "perprocess.h" @@ -37,13 +37,16 @@ details. */ char debugger_command[2 * NT_MAX_PATH + 20]; -extern "C" void sigdelayed (); +extern "C" { +extern void sigdelayed (); +}; static BOOL WINAPI ctrl_c_handler (DWORD); /* This is set to indicate that we have already exited. */ static NO_COPY int exit_already = 0; +static muto NO_COPY mask_sync; NO_COPY static struct { @@ -445,7 +448,7 @@ try_to_debug (bool waitloop) return dbg; } -extern "C" void WINAPI RtlUnwind (void *, void *, PEXCEPTION_RECORD, void *); +extern "C" DWORD __stdcall RtlUnwind (void *, void *, void *, DWORD); static void __stdcall rtl_unwind (exception_list *, PEXCEPTION_RECORD) __attribute__ ((noinline, regparm (3))); void __stdcall rtl_unwind (exception_list *frame, PEXCEPTION_RECORD e) @@ -660,7 +663,7 @@ exception::handle (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in, void stackdump ((DWORD) ebp, in, e); } - if ((NTSTATUS) e->ExceptionCode == STATUS_ACCESS_VIOLATION) + if (e->ExceptionCode == STATUS_ACCESS_VIOLATION) { int error_code = 0; if (si.si_code == SEGV_ACCERR) /* Address present */ @@ -672,7 +675,8 @@ exception::handle (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in, void klog (LOG_INFO, "%s[%d]: segfault at %08x rip %08x rsp %08x error %d", __progname, myself->pid, e->ExceptionInformation[1], in->Eip, in->Esp, - error_code); + ((in->Eip >= 0x61000000 && in->Eip < 0x61200000) + ? 0 : 4) | (e->ExceptionInformation[0] << 1)); } /* Flag signal + core dump */ @@ -706,11 +710,11 @@ handle_sigsuspend (sigset_t tempmask) { sigset_t oldmask = _my_tls.sigmask; // Remember for restoration - set_signal_mask (_my_tls.sigmask, tempmask); + set_signal_mask (tempmask, _my_tls.sigmask); sigproc_printf ("oldmask %p, newmask %p", oldmask, tempmask); pthread_testcancel (); - cancelable_wait (NULL, cw_infinite, cw_cancel | cw_cancel_self | cw_sig_eintr); + cancelable_wait (signal_arrived); set_sig_errno (EINTR); // Per POSIX @@ -733,29 +737,31 @@ sig_handle_tty_stop (int sig) /* Silently ignore attempts to suspend if there is no accommodating cygwin parent to deal with this behavior. */ if (!myself->cygstarted) - myself->process_state &= ~PID_STOPPED; - else { - myself->stopsig = sig; - myself->alert_parent (sig); - sigproc_printf ("process %d stopped by signal %d", myself->pid, sig); - /* FIXME! This does nothing to suspend anything other than the main - thread. */ - DWORD res = cancelable_wait (NULL, cw_infinite, cw_sig_eintr); - switch (res) - { - case WAIT_SIGNALED: - myself->stopsig = SIGCONT; - myself->alert_parent (SIGCONT); - break; - default: - api_fatal ("WaitSingleObject returned %d", res); - break; - } + myself->process_state &= ~PID_STOPPED; + return; + } + + myself->stopsig = sig; + myself->alert_parent (sig); + sigproc_printf ("process %d stopped by signal %d", myself->pid, sig); + HANDLE w4[2]; + w4[0] = sigCONT; + w4[1] = signal_arrived; + switch (WaitForMultipleObjects (2, w4, TRUE, INFINITE)) + { + case WAIT_OBJECT_0: + case WAIT_OBJECT_0 + 1: + myself->stopsig = SIGCONT; + myself->alert_parent (SIGCONT); + break; + default: + api_fatal ("WaitSingleObject failed, %E"); + break; } _my_tls.incyg = 0; } -} /* end extern "C" */ +} bool _cygtls::interrupt_now (CONTEXT *cx, int sig, void *handler, @@ -798,11 +804,20 @@ _cygtls::interrupt_setup (int sig, void *handler, struct sigaction& siga) this->sig = sig; // Should always be last thing set to avoid a race - if (incyg && signal_arrived) - SetEvent (signal_arrived); + if (!event) + threadkill = false; + else + { + HANDLE h = event; + event = NULL; + SetEvent (h); + } + /* Clear any waiting threads prior to dispatching to handler function */ + int res = SetEvent (signal_arrived); // For an EINTR case proc_subproc (PROC_CLEARWAIT, 1); - sigproc_printf ("armed signal_arrived %p, signal %d", signal_arrived, sig); + sigproc_printf ("armed signal_arrived %p, signal %d, res %d", signal_arrived, + sig, res); } extern "C" void __stdcall @@ -850,7 +865,7 @@ setup_handler (int sig, void *handler, struct sigaction& siga, _cygtls *tls) If the thread is already suspended (which can occur when a program has called SuspendThread on itself) then just queue the signal. */ - sigproc_printf ("suspending thread, tls %p, _main_tls %p", tls, _main_tls); + sigproc_printf ("suspending thread"); res = SuspendThread (hth); /* Just set pending if thread is already suspended */ if (res) @@ -865,7 +880,7 @@ setup_handler (int sig, void *handler, struct sigaction& siga, _cygtls *tls) interrupted = tls->interrupt_now (&cx, sig, handler, siga); tls->unlock (); - ResumeThread (hth); + res = ResumeThread (hth); if (interrupted) goto out; @@ -1006,7 +1021,7 @@ ctrl_c_handler (DWORD type) extern "C" void __stdcall set_process_mask (sigset_t newmask) { - set_signal_mask (_my_tls.sigmask, newmask); + set_signal_mask (newmask, _my_tls.sigmask); } extern "C" int @@ -1019,9 +1034,11 @@ sighold (int sig) syscall_printf ("signal %d out of range", sig); return -1; } + mask_sync.acquire (INFINITE); sigset_t mask = _my_tls.sigmask; sigaddset (&mask, sig); - set_signal_mask (_my_tls.sigmask, mask); + set_signal_mask (mask, _my_tls.sigmask); + mask_sync.release (); return 0; } @@ -1035,9 +1052,11 @@ sigrelse (int sig) syscall_printf ("signal %d out of range", sig); return -1; } + mask_sync.acquire (INFINITE); sigset_t mask = _my_tls.sigmask; sigdelset (&mask, sig); - set_signal_mask (_my_tls.sigmask, mask); + set_signal_mask (mask, _my_tls.sigmask); + mask_sync.release (); return 0; } @@ -1055,6 +1074,7 @@ sigset (int sig, _sig_func_ptr func) return (_sig_func_ptr) SIG_ERR; } + mask_sync.acquire (INFINITE); sigset_t mask = _my_tls.sigmask; /* If sig was in the signal mask return SIG_HOLD, otherwise return the previous disposition. */ @@ -1073,7 +1093,8 @@ sigset (int sig, _sig_func_ptr func) signal (sig, func); sigdelset (&mask, sig); } - set_signal_mask (_my_tls.sigmask, mask); + set_signal_mask (mask, _my_tls.sigmask); + mask_sync.release (); return prev; } @@ -1084,10 +1105,11 @@ sigignore (int sig) } /* Update the signal mask for this process and return the old mask. - Called from call_signal_handler */ + Called from sigdelayed */ extern "C" sigset_t set_process_mask_delta () { + mask_sync.acquire (INFINITE); sigset_t newmask, oldmask; if (_my_tls.deltamask & SIG_NONMASKABLE) @@ -1098,35 +1120,41 @@ set_process_mask_delta () sigproc_printf ("oldmask %p, newmask %p, deltamask %p", oldmask, newmask, _my_tls.deltamask); _my_tls.sigmask = newmask; + mask_sync.release (); return oldmask; } /* Set the signal mask for this process. Note that some signals are unmaskable, as in UNIX. */ - -void -set_signal_mask (sigset_t& setmask, sigset_t newmask) +extern "C" void __stdcall +set_signal_mask (sigset_t newmask, sigset_t& oldmask) { +#ifdef CGF + if (&_my_tls == _sig_tls) + small_printf ("********* waiting in signal thread\n"); +#endif + mask_sync.acquire (INFINITE); newmask &= ~SIG_NONMASKABLE; - sigset_t mask_bits = setmask & ~newmask; - sigproc_printf ("setmask %p, newmask %p, mask_bits %p", setmask, newmask, + sigset_t mask_bits = oldmask & ~newmask; + sigproc_printf ("oldmask %p, newmask %p, mask_bits %p", oldmask, newmask, mask_bits); - setmask = newmask; + oldmask = newmask; if (mask_bits) sig_dispatch_pending (true); + mask_sync.release (); } int __stdcall sigpacket::process () { - bool continue_now; + DWORD continue_now; struct sigaction dummy = global_sigs[SIGSTOP]; if (si.si_signo != SIGCONT) continue_now = false; else { - continue_now = ISSTATE (myself, PID_STOPPED); + continue_now = myself->process_state & PID_STOPPED; myself->stopsig = 0; myself->process_state &= ~PID_STOPPED; /* Clear pending stop signals */ @@ -1156,23 +1184,16 @@ sigpacket::process () myself->rusage_self.ru_nsignals++; - void *handler = (void *) thissig.sa_handler; - if (handler == SIG_IGN) - { - sigproc_printf ("signal %d ignored", si.si_signo); - goto done; - } - - if (have_execed) + bool masked; + void *handler; + if (!have_execed || (void *) thissig.sa_handler == (void *) SIG_IGN) + handler = (void *) thissig.sa_handler; + else if (tls) + return 1; + else handler = NULL; - if (tls) - sigproc_printf ("using tls %p", tls); - else - { - tls = cygheap->find_tls (si.si_signo); - sigproc_printf ("using tls %p", tls); - } + _cygtls *use_tls = tls ?: _main_tls; if (si.si_signo == SIGKILL) goto exit_sig; @@ -1182,12 +1203,30 @@ sigpacket::process () goto stop; } - if (sigismember (&tls->sigwait_mask, si.si_signo)) + bool insigwait_mask; + if ((masked = ISSTATE (myself, PID_STOPPED))) + insigwait_mask = false; + else if (tls) + insigwait_mask = sigismember (&tls->sigwait_mask, si.si_signo); + else if (!(tls = _cygtls::find_tls (si.si_signo))) + insigwait_mask = false; + else { - tls->sigwait_mask = 0; - goto dosig; + use_tls = tls; + insigwait_mask = true; } - if (sigismember (&tls->sigmask, si.si_signo) || ISSTATE (myself, PID_STOPPED)) + + if (insigwait_mask) + goto thread_specific; + + if (masked) + /* nothing to do */; + else if (sigismember (mask, si.si_signo)) + masked = true; + else if (tls) + masked = sigismember (&tls->sigmask, si.si_signo); + + if (masked) { sigproc_printf ("signal %d blocked", si.si_signo); rc = -1; @@ -1200,10 +1239,14 @@ sigpacket::process () if (handler == (void *) SIG_DFL) { + if (insigwait_mask) + goto thread_specific; if (si.si_signo == SIGCHLD || si.si_signo == SIGIO || si.si_signo == SIGCONT || si.si_signo == SIGWINCH || si.si_signo == SIGURG) { - sigproc_printf ("signal %d default is currently ignore", si.si_signo); + sigproc_printf ("default signal %d ignored", si.si_signo); + if (continue_now) + SetEvent (signal_arrived); goto done; } @@ -1213,35 +1256,53 @@ sigpacket::process () goto exit_sig; } + if (handler == (void *) SIG_IGN) + { + sigproc_printf ("signal %d ignored", si.si_signo); + goto done; + } + if (handler == (void *) SIG_ERR) goto exit_sig; + use_tls->set_siginfo (this); goto dosig; stop: + /* Eat multiple attempts to STOP */ + if (ISSTATE (myself, PID_STOPPED)) + goto done; handler = (void *) sig_handle_tty_stop; thissig = dummy; dosig: - if (ISSTATE (myself, PID_STOPPED) && !continue_now) - rc = -1; /* No signals delivered if stopped */ - else - { - tls->set_siginfo (this); - /* Dispatch to the appropriate function. */ - sigproc_printf ("signal %d, signal handler %p", si.si_signo, handler); - rc = setup_handler (si.si_signo, handler, thissig, tls); - continue_now = false; - } + /* Dispatch to the appropriate function. */ + sigproc_printf ("signal %d, about to call %p", si.si_signo, handler); + rc = setup_handler (si.si_signo, handler, thissig, use_tls); done: + tls = use_tls; if (continue_now) - SetEvent (tls->signal_arrived); + SetEvent (sigCONT); sigproc_printf ("returning %d", rc); return rc; +thread_specific: + use_tls->sig = si.si_signo; + use_tls->set_siginfo (this); + use_tls->func = NULL; + sigproc_printf ("releasing sigwait for thread"); + SetEvent (use_tls->event); + goto done; + exit_sig: - tls->signal_exit (si.si_signo); /* never returns */ + use_tls->signal_exit (si.si_signo); /* never returns */ +} + +void +events_init () +{ + mask_sync.init ("mask_sync"); } void @@ -1257,16 +1318,26 @@ _cygtls::call_signal_handler () while (1) { lock (); - if (!sig) + if (sig) + pop (); + else if (this != _main_tls) { - unlock (); - break; - } + _main_tls->lock (); + if (_main_tls->sig && _main_tls->incyg) + { + paranoid_printf ("Redirecting to main_tls signal %d", _main_tls->sig); + sig = _main_tls->sig; + sa_flags = _main_tls->sa_flags; + func = _main_tls->func; + infodata = _main_tls->infodata; + _main_tls->pop (); + _main_tls->sig = 0; - /* Pop the stack if the next "return address" is sigdelayed, since - this function is doing what sigdelayed would have done anyway. */ - if (retaddr () == (__stack_t) sigdelayed) - pop (); + } + _main_tls->unlock (); + } + if (!sig) + break; debug_only_printf ("dealing with signal %d", sig); this_sa_flags = sa_flags; @@ -1276,12 +1347,12 @@ _cygtls::call_signal_handler () sigset_t this_oldmask = set_process_mask_delta (); int this_errno = saved_errno; sig = 0; - reset_signal_arrived (); unlock (); // make sure synchronized if (!(this_sa_flags & SA_SIGINFO)) { + void (*sigfunc) (int) = thisfunc; incyg = false; - thisfunc (thissig); + sigfunc (thissig); } else { @@ -1292,11 +1363,12 @@ _cygtls::call_signal_handler () sigact (thissig, &thissi, NULL); } incyg = true; - set_signal_mask (_my_tls.sigmask, this_oldmask); + set_signal_mask (this_oldmask, _my_tls.sigmask); if (this_errno >= 0) set_errno (this_errno); } + unlock (); return this_sa_flags & SA_RESTART || (this != _main_tls); } |