diff options
Diffstat (limited to 'winsup/cygwin/exceptions.cc')
-rw-r--r-- | winsup/cygwin/exceptions.cc | 1195 |
1 files changed, 0 insertions, 1195 deletions
diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc deleted file mode 100644 index 3b7be7042..000000000 --- a/winsup/cygwin/exceptions.cc +++ /dev/null @@ -1,1195 +0,0 @@ -/* exceptions.cc - - Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc. - -This file is part of Cygwin. - -This software is a copyrighted work licensed under the terms of the -Cygwin license. Please consult the file "CYGWIN_LICENSE" for -details. */ - -#include "winsup.h" -#include <imagehlp.h> -#include <stdlib.h> -#include <setjmp.h> -#include <assert.h> - -#include "exceptions.h" -#include "sync.h" -#include "pinfo.h" -#include "cygtls.h" -#include "sigproc.h" -#include "cygerrno.h" -#define NEED_VFORK -#include "perthread.h" -#include "shared_info.h" -#include "perprocess.h" -#include "security.h" -#include "cygthread.h" - -#define CALL_HANDLER_RETRY 20 - -char debugger_command[2 * CYG_MAX_PATH + 20]; - -extern "C" { -static int handle_exceptions (EXCEPTION_RECORD *, void *, CONTEXT *, void *); -extern void sigdelayed (); -}; - -extern DWORD sigtid; - -extern HANDLE hExeced; -extern DWORD dwExeced; - -static BOOL WINAPI ctrl_c_handler (DWORD); -static void signal_exit (int) __attribute__ ((noreturn)); -static char windows_system_directory[1024]; -static size_t windows_system_directory_length; - -/* This is set to indicate that we have already exited. */ - -static NO_COPY int exit_already = 0; -static NO_COPY muto *mask_sync = NULL; - -NO_COPY static struct -{ - unsigned int code; - const char *name; -} status_info[] = -{ -#define X(s) s, #s - { X (STATUS_ABANDONED_WAIT_0) }, - { X (STATUS_ACCESS_VIOLATION) }, - { X (STATUS_ARRAY_BOUNDS_EXCEEDED) }, - { X (STATUS_BREAKPOINT) }, - { X (STATUS_CONTROL_C_EXIT) }, - { X (STATUS_DATATYPE_MISALIGNMENT) }, - { X (STATUS_FLOAT_DENORMAL_OPERAND) }, - { X (STATUS_FLOAT_DIVIDE_BY_ZERO) }, - { X (STATUS_FLOAT_INEXACT_RESULT) }, - { X (STATUS_FLOAT_INVALID_OPERATION) }, - { X (STATUS_FLOAT_OVERFLOW) }, - { X (STATUS_FLOAT_STACK_CHECK) }, - { X (STATUS_FLOAT_UNDERFLOW) }, - { X (STATUS_GUARD_PAGE_VIOLATION) }, - { X (STATUS_ILLEGAL_INSTRUCTION) }, - { X (STATUS_INTEGER_DIVIDE_BY_ZERO) }, - { X (STATUS_INTEGER_OVERFLOW) }, - { X (STATUS_INVALID_DISPOSITION) }, - { X (STATUS_IN_PAGE_ERROR) }, - { X (STATUS_NONCONTINUABLE_EXCEPTION) }, - { X (STATUS_NO_MEMORY) }, - { X (STATUS_PENDING) }, - { X (STATUS_PRIVILEGED_INSTRUCTION) }, - { X (STATUS_SINGLE_STEP) }, - { X (STATUS_STACK_OVERFLOW) }, - { X (STATUS_TIMEOUT) }, - { X (STATUS_USER_APC) }, - { X (STATUS_WAIT_0) }, - { 0, 0 } -#undef X -}; - -/* Initialization code. */ - -// Set up the exception handler for the current thread. The PowerPC & Mips -// use compiler generated tables to set up the exception handlers for each -// region of code, and the kernel walks the call list until it finds a region -// of code that handles exceptions. The x86 on the other hand uses segment -// register fs, offset 0 to point to the current exception handler. - -extern exception_list *_except_list asm ("%fs:0"); - -void -init_exception_handler (exception_list *el, exception_handler *eh) -{ - el->handler = eh; - el->prev = _except_list; - _except_list = el; -} - -extern "C" void -init_exceptions (exception_list *el) -{ - init_exception_handler (el, handle_exceptions); -} - -void -init_console_handler () -{ - (void) SetConsoleCtrlHandler (ctrl_c_handler, FALSE); - if (!SetConsoleCtrlHandler (ctrl_c_handler, TRUE)) - system_printf ("SetConsoleCtrlHandler failed, %E"); -} - -extern "C" void -error_start_init (const char *buf) -{ - if (!buf || !*buf) - { - debugger_command[0] = '\0'; - return; - } - - char pgm[CYG_MAX_PATH + 1]; - if (!GetModuleFileName (NULL, pgm, CYG_MAX_PATH)) - strcpy (pgm, "cygwin1.dll"); - for (char *p = strchr (pgm, '\\'); p; p = strchr (p, '\\')) - *p = '/'; - - __small_sprintf (debugger_command, "%s \"%s\"", buf, pgm); -} - -static void -open_stackdumpfile () -{ - if (myself->progname[0]) - { - const char *p; - /* write to progname.stackdump if possible */ - if (!myself->progname[0]) - p = "unknown"; - else if ((p = strrchr (myself->progname, '\\'))) - p++; - else - p = myself->progname; - char corefile[strlen (p) + sizeof (".stackdump")]; - __small_sprintf (corefile, "%s.stackdump", p); - HANDLE h = CreateFile (corefile, GENERIC_WRITE, 0, &sec_none_nih, - CREATE_ALWAYS, 0, 0); - if (h != INVALID_HANDLE_VALUE) - { - if (!myself->ppid_handle) - system_printf ("Dumping stack trace to %s", corefile); - else - debug_printf ("Dumping stack trace to %s", corefile); - SetStdHandle (STD_ERROR_HANDLE, h); - } - } -} - -/* Utilities for dumping the stack, etc. */ - -static void -exception (EXCEPTION_RECORD *e, CONTEXT *in) -{ - const char *exception_name = NULL; - - if (e) - { - for (int i = 0; status_info[i].name; i++) - { - if (status_info[i].code == e->ExceptionCode) - { - exception_name = status_info[i].name; - break; - } - } - } - - if (exception_name) - small_printf ("Exception: %s at eip=%08x\r\n", exception_name, in->Eip); - else - small_printf ("Exception %d at eip=%08x\r\n", e->ExceptionCode, in->Eip); - small_printf ("eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x\r\n", - in->Eax, in->Ebx, in->Ecx, in->Edx, in->Esi, in->Edi); - small_printf ("ebp=%08x esp=%08x program=%s, pid %u, thread %s\r\n", - in->Ebp, in->Esp, myself->progname, myself->pid, cygthread::name ()); - small_printf ("cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x\r\n", - in->SegCs, in->SegDs, in->SegEs, in->SegFs, in->SegGs, in->SegSs); -} - -/* A class for manipulating the stack. */ -class stack_info -{ - int walk (); /* Uses the "old" method */ - char *next_offset () {return *((char **) sf.AddrFrame.Offset);} - bool needargs; - DWORD dummy_frame; -public: - STACKFRAME sf; /* For storing the stack information */ - void init (DWORD, bool, bool); /* Called the first time that stack info is needed */ - - /* Postfix ++ iterates over the stack, returning zero when nothing is left. */ - int operator ++(int) { return walk (); } -}; - -/* The number of parameters used in STACKFRAME */ -#define NPARAMS (sizeof (thestack.sf.Params) / sizeof (thestack.sf.Params[0])) - -/* This is the main stack frame info for this process. */ -static NO_COPY stack_info thestack; - -/* Initialize everything needed to start iterating. */ -void -stack_info::init (DWORD ebp, bool wantargs, bool goodframe) -{ -# define debp ((DWORD *) ebp) - memset (&sf, 0, sizeof (sf)); - if (!goodframe) - sf.AddrFrame.Offset = ebp; - else - { - dummy_frame = ebp; - sf.AddrFrame.Offset = (DWORD) &dummy_frame; - } - sf.AddrReturn.Offset = debp[1]; - sf.AddrFrame.Mode = AddrModeFlat; - needargs = wantargs; -# undef debp -} - -/* Walk the stack by looking at successive stored 'bp' frames. - This is not foolproof. */ -int -stack_info::walk () -{ - char **ebp; - if ((ebp = (char **) next_offset ()) == NULL) - return 0; - - sf.AddrFrame.Offset = (DWORD) ebp; - sf.AddrPC.Offset = sf.AddrReturn.Offset; - - if (!sf.AddrPC.Offset) - return 0; /* stack frames are exhausted */ - - /* The return address always follows the stack pointer */ - sf.AddrReturn.Offset = (DWORD) *++ebp; - - if (needargs) - /* The arguments follow the return address */ - for (unsigned i = 0; i < NPARAMS; i++) - sf.Params[i] = (DWORD) *++ebp; - - return 1; -} - -static void -stackdump (DWORD ebp, int open_file, bool isexception) -{ - extern unsigned long rlim_core; - - if (rlim_core == 0UL) - return; - - if (open_file) - open_stackdumpfile (); - - int i; - - thestack.init (ebp, 1, !isexception); /* Initialize from the input CONTEXT */ - small_printf ("Stack trace:\r\nFrame Function Args\r\n"); - for (i = 0; i < 16 && thestack++; i++) - { - small_printf ("%08x %08x ", thestack.sf.AddrFrame.Offset, - thestack.sf.AddrPC.Offset); - for (unsigned j = 0; j < NPARAMS; j++) - small_printf ("%s%08x", j == 0 ? " (" : ", ", thestack.sf.Params[j]); - small_printf (")\r\n"); - } - small_printf ("End of stack trace%s\n", - i == 16 ? " (more stack frames may be present)" : ""); -} - -/* Temporary (?) function for external callers to get a stack dump */ -extern "C" void -cygwin_stackdump () -{ - CONTEXT c; - c.ContextFlags = CONTEXT_FULL; - GetThreadContext (GetCurrentThread (), &c); - stackdump (c.Ebp, 0, 0); -} - -#define TIME_TO_WAIT_FOR_DEBUGGER 10000 - -extern "C" int -try_to_debug (bool waitloop) -{ - debug_printf ("debugger_command '%s'", debugger_command); - if (*debugger_command == '\0' || being_debugged ()) - return 0; - - __small_sprintf (strchr (debugger_command, '\0'), " %u", GetCurrentProcessId ()); - - LONG prio = GetThreadPriority (GetCurrentThread ()); - SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST); - PROCESS_INFORMATION pi = {NULL, 0, 0, 0}; - - STARTUPINFO si = {0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL}; - si.lpReserved = NULL; - si.lpDesktop = NULL; - si.dwFlags = 0; - si.cb = sizeof (si); - - /* FIXME: need to know handles of all running threads to - suspend_all_threads_except (current_thread_id); - */ - - /* if any of these mutexes is owned, we will fail to start any cygwin app - until trapped app exits */ - - ReleaseMutex (title_mutex); - - /* prevent recursive exception handling */ - char* rawenv = GetEnvironmentStrings () ; - for (char* p = rawenv; *p != '\0'; p = strchr (p, '\0') + 1) - { - if (strncmp (p, "CYGWIN=", strlen ("CYGWIN=")) == 0) - { - char* q = strstr (p, "error_start") ; - /* replace 'error_start=...' with '_rror_start=...' */ - if (q) - { - *q = '_' ; - SetEnvironmentVariable ("CYGWIN", p + strlen ("CYGWIN=")) ; - } - break ; - } - } - - console_printf ("*** starting debugger for pid %u, tid %u\n", - cygwin_pid (GetCurrentProcessId ()), GetCurrentThreadId ()); - BOOL dbg; - dbg = CreateProcess (NULL, - debugger_command, - NULL, - NULL, - FALSE, - CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP, - NULL, - NULL, - &si, - &pi); - - if (!dbg) - system_printf ("Failed to start debugger: %E"); - else - { - if (!waitloop) - return dbg; - SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_IDLE); - while (!being_debugged ()) - Sleep (0); - Sleep (2000); - } - - console_printf ("*** continuing pid %u from debugger call (%d)\n", - cygwin_pid (GetCurrentProcessId ()), dbg); - - SetThreadPriority (GetCurrentThread (), prio); - return dbg; -} - -/* Main exception handler. */ - -extern "C" DWORD __stdcall RtlUnwind (void *, void *, void *, DWORD); -static int -handle_exceptions (EXCEPTION_RECORD *e0, void *frame, CONTEXT *in0, void *) -{ - static bool NO_COPY debugging = false; - static int NO_COPY recursed = 0; - - if (debugging && ++debugging < 500000) - { - SetThreadPriority (hMainThread, THREAD_PRIORITY_NORMAL); - return 0; - } - - /* If we've already exited, don't do anything here. Returning 1 - tells Windows to keep looking for an exception handler. */ - if (exit_already) - return 1; - - EXCEPTION_RECORD e = *e0; - CONTEXT in = *in0; - - siginfo_t si; - /* Coerce win32 value to posix value. */ - switch (e.ExceptionCode) - { - case STATUS_FLOAT_DENORMAL_OPERAND: - case STATUS_FLOAT_DIVIDE_BY_ZERO: - case STATUS_FLOAT_INVALID_OPERATION: - case STATUS_FLOAT_STACK_CHECK: - si.si_signo = SIGFPE; - si.si_sigval.sival_int = FPE_FLTSUB; - break; - case STATUS_FLOAT_INEXACT_RESULT: - si.si_signo = SIGFPE; - si.si_sigval.sival_int = FPE_FLTRES; - break; - case STATUS_FLOAT_OVERFLOW: - si.si_signo = SIGFPE; - si.si_sigval.sival_int = FPE_FLTOVF; - break; - case STATUS_FLOAT_UNDERFLOW: - si.si_signo = SIGFPE; - si.si_sigval.sival_int = FPE_FLTUND; - break; - case STATUS_INTEGER_DIVIDE_BY_ZERO: - si.si_signo = SIGFPE; - si.si_sigval.sival_int = FPE_INTDIV; - break; - case STATUS_INTEGER_OVERFLOW: - si.si_signo = SIGFPE; - si.si_sigval.sival_int = FPE_INTOVF; - break; - - case STATUS_ILLEGAL_INSTRUCTION: - si.si_signo = SIGILL; - si.si_sigval.sival_int = ILL_ILLOPC; - break; - - case STATUS_PRIVILEGED_INSTRUCTION: - si.si_signo = SIGILL; - si.si_sigval.sival_int = ILL_PRVOPC; - break; - - case STATUS_NONCONTINUABLE_EXCEPTION: - si.si_signo = SIGILL; - si.si_sigval.sival_int = ILL_ILLADR; - break; - - case STATUS_TIMEOUT: - si.si_signo = SIGALRM; - si.si_sigval.sival_int = 0; - break; - - case STATUS_ACCESS_VIOLATION: - case STATUS_DATATYPE_MISALIGNMENT: - case STATUS_ARRAY_BOUNDS_EXCEEDED: - case STATUS_GUARD_PAGE_VIOLATION: - case STATUS_IN_PAGE_ERROR: - case STATUS_NO_MEMORY: - case STATUS_INVALID_DISPOSITION: - case STATUS_STACK_OVERFLOW: - si.si_signo = SIGSEGV; - si.si_sigval.sival_int = SEGV_MAPERR; - break; - - case STATUS_CONTROL_C_EXIT: - si.si_signo = SIGINT; - si.si_sigval.sival_int = 0; - break; - - case STATUS_INVALID_HANDLE: - /* CloseHandle will throw this exception if it is given an - invalid handle. We don't care about the exception; we just - want CloseHandle to return an error. This can be revisited - if gcc ever supports Windows style structured exception - handling. */ - return 0; - - default: - /* If we don't recognize the exception, we have to assume that - we are doing structured exception handling, and we let - something else handle it. */ - return 1; - } - - debug_printf ("In cygwin_except_handler exc %p at %p sp %p", e.ExceptionCode, in.Eip, in.Esp); - debug_printf ("In cygwin_except_handler sig = %d at %p", si.si_signo, in.Eip); - - if (global_sigs[si.si_signo].sa_mask & SIGTOMASK (si.si_signo)) - syscall_printf ("signal %d, masked %p", si.si_signo, - global_sigs[si.si_signo].sa_mask); - - debug_printf ("In cygwin_except_handler calling %p", - global_sigs[si.si_signo].sa_handler); - - DWORD *ebp = (DWORD *)in.Esp; - for (DWORD *bpend = (DWORD *) __builtin_frame_address (0); ebp > bpend; ebp--) - if (*ebp == in.SegCs && ebp[-1] == in.Eip) - { - ebp -= 2; - break; - } - - if (!cygwin_finished_initializing - || GetCurrentThreadId () == sigtid - || (void *) global_sigs[si.si_signo].sa_handler == (void *) SIG_DFL - || (void *) global_sigs[si.si_signo].sa_handler == (void *) SIG_IGN - || (void *) global_sigs[si.si_signo].sa_handler == (void *) SIG_ERR) - { - /* Print the exception to the console */ - if (!myself->ppid_handle) - for (int i = 0; status_info[i].name; i++) - if (status_info[i].code == e.ExceptionCode) - { - system_printf ("Exception: %s", status_info[i].name); - break; - } - - /* Another exception could happen while tracing or while exiting. - Only do this once. */ - if (recursed++) - system_printf ("Error while dumping state (probably corrupted stack)"); - else - { - if (try_to_debug (0)) - { - debugging = true; - return 0; - } - - open_stackdumpfile (); - exception (&e, &in); - stackdump ((DWORD) ebp, 0, 1); - } - - signal_exit (0x80 | si.si_signo); // Flag signal + core dump - } - - extern DWORD ret_here[]; - RtlUnwind (frame, ret_here, e0, 0); - __asm__ volatile (".equ _ret_here,."); - - si.si_addr = ebp; - si.si_code = SI_KERNEL; - si.si_errno = si.si_pid = si.si_uid = 0; - _my_tls.push ((__stack_t) ebp, true); - sig_send (NULL, si, &_my_tls); // Signal myself - return 1; -} - -/* Utilities to call a user supplied exception handler. */ - -#define SIG_NONMASKABLE (SIGTOMASK (SIGKILL) | SIGTOMASK (SIGSTOP)) - -/* Non-raceable sigsuspend - * Note: This implementation is based on the Single UNIX Specification - * man page. This indicates that sigsuspend always returns -1 and that - * attempts to block unblockable signals will be silently ignored. - * This is counter to what appears to be documented in some UNIX - * man pages, e.g. Linux. - */ -int __stdcall -handle_sigsuspend (sigset_t tempmask) -{ - sigset_t oldmask = myself->getsigmask (); // Remember for restoration - - set_signal_mask (tempmask, oldmask); - sigproc_printf ("oldmask %p, newmask %p", oldmask, tempmask); - - pthread_testcancel (); - pthread::cancelable_wait (signal_arrived, INFINITE); - - set_sig_errno (EINTR); // Per POSIX - - /* A signal dispatch function will have been added to our stack and will - be hit eventually. Set the old mask to be restored when the signal - handler returns and indicate its presence by modifying deltamask. */ - - _my_tls.deltamask |= SIG_NONMASKABLE; - _my_tls.oldmask = oldmask; // Will be restored by signal handler - return -1; -} - -extern DWORD exec_exit; // Possible exit value for exec - -extern "C" { -static void -sig_handle_tty_stop (int sig) -{ - /* Silently ignore attempts to suspend if there is no accomodating - cygwin parent to deal with this behavior. */ - if (!myself->ppid_handle) - { - myself->process_state &= ~PID_STOPPED; - return; - } - - myself->stopsig = sig; - /* See if we have a living parent. If so, send it a special signal. - It will figure out exactly which pid has stopped by scanning - its list of subprocesses. */ - if (my_parent_is_alive ()) - { - pinfo parent (myself->ppid); - if (NOTSTATE (parent, PID_NOCLDSTOP)) - { - siginfo_t si; - si.si_signo = SIGCHLD; - si.si_code = SI_KERNEL; - si.si_sigval.sival_int = CLD_STOPPED; - si.si_errno = si.si_pid = si.si_uid = si.si_errno = 0; - sig_send (parent, si); - } - } - sigproc_printf ("process %d stopped by signal %d, myself->ppid_handle %p", - myself->pid, sig, myself->ppid_handle); - 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: - reset_signal_arrived (); - break; - default: - api_fatal ("WaitSingleObject failed, %E"); - break; - } - return; -} -} - -bool -interruptible (DWORD pc) -{ - int res; - MEMORY_BASIC_INFORMATION m; - - memset (&m, 0, sizeof m); - if (!VirtualQuery ((LPCVOID) pc, &m, sizeof m)) - sigproc_printf ("couldn't get memory info, pc %p, %E", pc); - - char *checkdir = (char *) alloca (windows_system_directory_length + 4); - memset (checkdir, 0, sizeof (checkdir)); - -# define h ((HMODULE) m.AllocationBase) - /* Apparently Windows 95 can sometimes return bogus addresses from - GetThreadContext. These resolve to a strange allocation base. - These should *never* be treated as interruptible. */ - if (!h || m.State != MEM_COMMIT) - res = false; - else if (h == user_data->hmodule) - res = true; - else if (!GetModuleFileName (h, checkdir, windows_system_directory_length + 2)) - res = false; - else - res = !strncasematch (windows_system_directory, checkdir, - windows_system_directory_length); - sigproc_printf ("pc %p, h %p, interruptible %d", pc, h, res); -# undef h - return res; -} -void __stdcall -_cygtls::interrupt_setup (int sig, void *handler, struct sigaction& siga) -{ - push ((__stack_t) sigdelayed, false); - deltamask = (siga.sa_mask | SIGTOMASK (sig)) & ~SIG_NONMASKABLE; - sa_flags = siga.sa_flags; - func = (void (*) (int)) handler; - saved_errno = -1; // Flag: no errno to save - if (handler == sig_handle_tty_stop) - { - myself->stopsig = 0; - myself->process_state |= PID_STOPPED; - } - - this->sig = sig; // Should always be last thing set to avoid a race - - /* 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, sig %d, res %d", signal_arrived, - sig, res); -} - -bool -_cygtls::interrupt_now (CONTEXT *ctx, int sig, void *handler, - struct sigaction& siga) -{ - push ((__stack_t) ctx->Eip, false); - interrupt_setup (sig, handler, siga); - ctx->Eip = pop (); - SetThreadContext (*this, ctx); /* Restart the thread in a new location */ - return 1; -} - -extern "C" void __stdcall -set_sig_errno (int e) -{ - *_my_tls.errno_addr = e; - _my_tls.saved_errno = e; - // sigproc_printf ("errno %d", e); -} - -static int setup_handler (int, void *, struct sigaction&, _cygtls *tls) - __attribute__((regparm(3))); -static int -setup_handler (int sig, void *handler, struct sigaction& siga, _cygtls *tls) -{ - CONTEXT cx; - bool interrupted = false; - - if (tls->sig) - { - sigproc_printf ("trying to send sig %d but signal %d already armed", - sig, tls->sig); - goto out; - } - - for (int i = 0; i < CALL_HANDLER_RETRY; i++) - { - tls->lock (); - if (tls->incyg || tls->in_exception ()) - { - sigproc_printf ("controlled interrupt. incyg %d, exception %d, stackptr %p, stack %p, stackptr[-1] %p", - tls->incyg, tls->in_exception (), tls->stackptr, tls->stack, tls->stackptr[-1]); - tls->reset_exception (); - tls->interrupt_setup (sig, handler, siga); - interrupted = true; - tls->unlock (); - break; - } - - tls->unlock (); - DWORD res; - HANDLE hth = (HANDLE) *tls; - - /* Suspend the thread which will receive the signal. - For Windows 95, we also have to ensure that the addresses returned by - GetThreadContext are valid. - If one of these conditions is not true we loop for a fixed number of times - since we don't want to stall the signal handler. FIXME: Will this result in - noticeable delays? - If the thread is already suspended (which can occur when a program has called - SuspendThread on itself) then just queue the signal. */ - -#ifndef DEBUGGING - sigproc_printf ("suspending mainthread"); -#else - cx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; - if (!GetThreadContext (hth, &cx)) - memset (&cx, 0, sizeof cx); - sigproc_printf ("suspending mainthread PC %p", cx.Eip); -#endif - res = SuspendThread (hth); - /* Just set pending if thread is already suspended */ - if (res) - { - (void) ResumeThread (hth); - break; - } - if (tls->incyg || tls->in_exception () || tls->spinning || tls->locked ()) - sigproc_printf ("incyg %d, in_exception %d, spinning %d, locked %d\n", - tls->incyg, tls->in_exception (), tls->spinning, tls->locked ()); - else - { - cx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; - if (!GetThreadContext (hth, &cx)) - system_printf ("couldn't get context of main thread, %E"); - else if (interruptible (cx.Eip)) - interrupted = tls->interrupt_now (&cx, sig, handler, siga); - } - - res = ResumeThread (hth); - if (interrupted) - break; - - sigproc_printf ("couldn't interrupt. trying again."); - low_priority_sleep (0); - } - -out: - if (interrupted && tls->event) - { - HANDLE h = tls->event; - tls->event = NULL; - SetEvent (h); - } - sigproc_printf ("signal %d %sdelivered", sig, interrupted ? "" : "not "); - return interrupted; -} - -/* Keyboard interrupt handler. */ -static BOOL WINAPI -ctrl_c_handler (DWORD type) -{ - static bool saw_close; - - if (!cygwin_finished_initializing) - { - debug_printf ("exiting with status %p", STATUS_CONTROL_C_EXIT); - ExitProcess (STATUS_CONTROL_C_EXIT); - } - - _my_tls.remove (INFINITE); - - /* Return FALSE to prevent an "End task" dialog box from appearing - for each Cygwin process window that's open when the computer - is shut down or console window is closed. */ - - if (type == CTRL_SHUTDOWN_EVENT) - { -#if 0 - /* Don't send a signal. Only NT service applications and their child - processes will receive this event and the services typically already - handle the shutdown action when getting the SERVICE_CONTROL_SHUTDOWN - control message. */ - sig_send (NULL, SIGTERM); -#endif - return FALSE; - } - - if (myself->ctty != -1) - { - if (type == CTRL_CLOSE_EVENT) - { - sig_send (NULL, SIGHUP); - saw_close = true; - return FALSE; - } - if (!saw_close && type == CTRL_LOGOFF_EVENT) - { - /* Check if the process is actually associated with a visible - window station, one which actually represents a visible desktop. - If not, the CTRL_LOGOFF_EVENT doesn't concern this process. */ - if (has_visible_window_station ()) - sig_send (myself_nowait, SIGHUP); - return FALSE; - } - } - - /* If we are a stub and the new process has a pinfo structure, let it - handle this signal. */ - if (dwExeced && pinfo (dwExeced)) - return TRUE; - - /* We're only the process group leader when we have a valid pinfo structure. - If we don't have one, then the parent "stub" will handle the signal. */ - if (!pinfo (cygwin_pid (GetCurrentProcessId ()))) - return TRUE; - - tty_min *t = cygwin_shared->tty.get_tty (myself->ctty); - /* Ignore this if we're not the process group leader since it should be handled - *by* the process group leader. */ - if (myself->ctty != -1 && t->getpgid () == myself->pid && - (GetTickCount () - t->last_ctrl_c) >= MIN_CTRL_C_SLOP) - /* Otherwise we just send a SIGINT to the process group and return TRUE (to indicate - that we have handled the signal). At this point, type should be - a CTRL_C_EVENT or CTRL_BREAK_EVENT. */ - { - t->last_ctrl_c = GetTickCount (); - killsys (-myself->pid, SIGINT); - t->last_ctrl_c = GetTickCount (); - return TRUE; - } - - return TRUE; -} - -/* Function used by low level sig wrappers. */ -extern "C" void __stdcall -set_process_mask (sigset_t newmask) -{ - set_signal_mask (newmask); -} - -extern "C" int -sighold (int sig) -{ - /* check that sig is in right range */ - if (sig < 0 || sig >= NSIG) - { - set_errno (EINVAL); - syscall_printf ("signal %d out of range", sig); - return -1; - } - mask_sync->acquire (INFINITE); - sigset_t mask = myself->getsigmask (); - sigaddset (&mask, sig); - set_signal_mask (mask); - mask_sync->release (); - return 0; -} - -/* Update the signal mask for this process - and return the old mask. - 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) - oldmask = _my_tls.oldmask; /* from handle_sigsuspend */ - else - oldmask = myself->getsigmask (); - newmask = (oldmask | _my_tls.deltamask) & ~SIG_NONMASKABLE; - sigproc_printf ("oldmask %p, newmask %p, deltamask %p", oldmask, newmask, - _my_tls.deltamask); - myself->setsigmask (newmask); - mask_sync->release (); - return oldmask; -} - -/* Set the signal mask for this process. - Note that some signals are unmaskable, as in UNIX. */ -extern "C" void __stdcall -set_signal_mask (sigset_t newmask, sigset_t oldmask) -{ - mask_sync->acquire (INFINITE); - newmask &= ~SIG_NONMASKABLE; - sigset_t mask_bits = oldmask & ~newmask; - sigproc_printf ("oldmask %p, newmask %p, mask_bits %p", oldmask, newmask, - mask_bits); - myself->setsigmask (newmask); // Set a new mask - if (mask_bits) - sig_dispatch_pending (true); - else - sigproc_printf ("not calling sig_dispatch_pending"); - mask_sync->release (); - return; -} - -int __stdcall -sigpacket::process () -{ - DWORD continue_now; - if (si.si_signo != SIGCONT) - continue_now = false; - else - { - continue_now = myself->process_state & PID_STOPPED; - myself->stopsig = 0; - myself->process_state &= ~PID_STOPPED; - /* Clear pending stop signals */ - sig_clear (SIGSTOP); - sig_clear (SIGTSTP); - sig_clear (SIGTTIN); - sig_clear (SIGTTOU); - } - - int rc = 1; - - sigproc_printf ("signal %d processing", si.si_signo); - struct sigaction thissig = global_sigs[si.si_signo]; - - myself->rusage_self.ru_nsignals++; - - if (si.si_signo == SIGKILL) - goto exit_sig; - if ( si.si_signo == SIGSTOP) - { - sig_clear (SIGCONT); - if (!tls) - tls = _main_tls; - goto stop; - } - - bool masked; - bool special_case; - bool insigwait_mask; - insigwait_mask = masked = false; - if (special_case = (VFORKPID || ISSTATE (myself, PID_STOPPED))) - /* nothing to do */; - else if (tls && sigismember (&tls->sigwait_mask, si.si_signo)) - insigwait_mask = true; - else if (!tls && (tls = _cygtls::find_tls (si.si_signo))) - insigwait_mask = true; - else if (!(masked = sigismember (mask, si.si_signo)) && tls) - masked = sigismember (&tls->sigmask, si.si_signo); - - if (insigwait_mask) - goto thread_specific; - - if (!tls) - tls = _main_tls; - - if (special_case || masked) - { - sigproc_printf ("signal %d blocked", si.si_signo); - rc = -1; - goto done; - } - - void *handler; - handler = (void *) thissig.sa_handler; - - /* Clear pending SIGCONT on stop signals */ - if (si.si_signo == SIGTSTP || si.si_signo == SIGTTIN || si.si_signo == SIGTTOU) - sig_clear (SIGCONT); - -#if 0 - char sigmsg[24]; - __small_sprintf (sigmsg, "cygwin: signal %d\n", si.si_signo); - OutputDebugString (sigmsg); -#endif - - 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 ("default signal %d ignored", si.si_signo); - if (continue_now) - SetEvent (signal_arrived); - goto done; - } - - if (si.si_signo == SIGTSTP || si.si_signo == SIGTTIN || si.si_signo == SIGTTOU) - goto stop; - - 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; - - 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 = global_sigs[SIGSTOP]; - -dosig: - /* 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, tls); - -done: - if (continue_now) - SetEvent (sigCONT); - sigproc_printf ("returning %d", rc); - return rc; - -thread_specific: - tls->sig = si.si_signo; - tls->set_siginfo (this); - sigproc_printf ("releasing sigwait for thread"); - SetEvent (tls->event); - goto done; - -exit_sig: - if (si.si_signo == SIGQUIT || si.si_signo == SIGABRT) - { - CONTEXT c; - c.ContextFlags = CONTEXT_FULL; - GetThreadContext (hMainThread, &c); - if (!try_to_debug ()) - stackdump (c.Ebp, 1, 1); - si.si_signo |= 0x80; - } - sigproc_printf ("signal %d, about to call do_exit", si.si_signo); - signal_exit (si.si_signo); - /* Never returns */ -} - -CRITICAL_SECTION NO_COPY exit_lock; - -/* Cover function to `do_exit' to handle exiting even in presence of more - exceptions. We used to call exit, but a SIGSEGV shouldn't cause atexit - routines to run. */ -static void -signal_exit (int rc) -{ - EnterCriticalSection (&exit_lock); - rc = EXIT_SIGNAL | (rc << 8); - if (exit_already++) - myself->exit (rc); - - /* We'd like to stop the main thread from executing but when we do that it - causes random, inexplicable hangs. So, instead, we set up the priority - of this thread really high so that it should do its thing and then exit. */ - (void) SetThreadPriority (hMainThread, THREAD_PRIORITY_IDLE); - (void) SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL); - - user_data->resourcelocks->Delete (); - user_data->resourcelocks->Init (); - - if (hExeced) - { - sigproc_printf ("terminating captive process"); - TerminateProcess (hExeced, rc); - } - - sigproc_printf ("about to call do_exit (%x)", rc); - (void) SetEvent (signal_arrived); - do_exit (rc); -} - -HANDLE NO_COPY title_mutex = NULL; - -void -events_init (void) -{ - char *name; - char mutex_name[CYG_MAX_PATH]; - /* title_mutex protects modification of console title. It's necessary - while finding console window handle */ - - if (!(title_mutex = CreateMutex (&sec_all_nih, FALSE, - name = shared_name (mutex_name, - "title_mutex", 0)))) - api_fatal ("can't create title mutex '%s', %E", name); - - ProtectHandle (title_mutex); - new_muto (mask_sync); - windows_system_directory[0] = '\0'; - (void) GetSystemDirectory (windows_system_directory, sizeof (windows_system_directory) - 2); - char *end = strchr (windows_system_directory, '\0'); - if (end == windows_system_directory) - api_fatal ("can't find windows system directory"); - if (end[-1] != '\\') - { - *end++ = '\\'; - *end = '\0'; - } - windows_system_directory_length = end - windows_system_directory; - debug_printf ("windows_system_directory '%s', windows_system_directory_length %d", - windows_system_directory, windows_system_directory_length); - InitializeCriticalSection (&exit_lock); -} - -void -events_terminate (void) -{ - exit_already = 1; -} - -int -_cygtls::call_signal_handler () -{ - int this_sa_flags = 0; - /* Call signal handler. */ - while (sig) - { - lock (); unlock (); // make sure synchronized - this_sa_flags = sa_flags; - int thissig = sig; - void (*sigfunc) (int) = func; - - (void) pop (); - reset_signal_arrived (); - sigset_t this_oldmask = set_process_mask_delta (); - int this_errno = saved_errno; - incyg--; - sig = 0; - sigfunc (thissig); - incyg++; - set_signal_mask (this_oldmask); - if (this_errno >= 0) - set_errno (this_errno); - } - - return this_sa_flags & SA_RESTART; -} - -extern "C" void __stdcall -reset_signal_arrived () -{ - // NEEDED? WaitForSingleObject (signal_arrived, 10); - (void) ResetEvent (signal_arrived); - sigproc_printf ("reset signal_arrived"); - if (_my_tls.stackptr > _my_tls.stack) - debug_printf ("stackptr[-1] %p", _my_tls.stackptr[-1]); -} |