diff options
Diffstat (limited to 'winsup/cygwin/exceptions.cc')
-rw-r--r-- | winsup/cygwin/exceptions.cc | 1435 |
1 files changed, 0 insertions, 1435 deletions
diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc deleted file mode 100644 index df0248947..000000000 --- a/winsup/cygwin/exceptions.cc +++ /dev/null @@ -1,1435 +0,0 @@ -/* exceptions.cc - - Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, - 2005, 2006, 2007, 2008, 2009 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. */ - -#define CYGTLS_HANDLE -#include "winsup.h" -#include "miscfuncs.h" -#include <wingdi.h> -#include <winuser.h> -#include <imagehlp.h> -#include <stdlib.h> -#include <syslog.h> -#include <wchar.h> - -#include "pinfo.h" -#include "cygtls.h" -#include "sigproc.h" -#include "shared_info.h" -#include "perprocess.h" -#include "path.h" -#include "fhandler.h" -#include "dtable.h" -#include "cygheap.h" -#include "child_info.h" -#include "ntdll.h" - -#define CALL_HANDLER_RETRY 20 - -char debugger_command[2 * NT_MAX_PATH + 20]; - -extern "C" { -extern void sigdelayed (); -}; - -extern child_info_spawn *chExeced; -int NO_COPY sigExeced; - -static BOOL WINAPI ctrl_c_handler (DWORD); -static WCHAR 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 muto NO_COPY mask_sync; - -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. */ - -void -init_console_handler (bool install_handler) -{ - BOOL res; - - SetConsoleCtrlHandler (ctrl_c_handler, FALSE); - SetConsoleCtrlHandler (NULL, FALSE); - if (install_handler) - res = SetConsoleCtrlHandler (ctrl_c_handler, TRUE); - else - res = SetConsoleCtrlHandler (NULL, TRUE); - if (!res) - system_printf ("SetConsoleCtrlHandler failed, %E"); -} - -extern "C" void -error_start_init (const char *buf) -{ - if (!buf || !*buf) - { - debugger_command[0] = '\0'; - return; - } - - char pgm[NT_MAX_PATH]; - if (!GetModuleFileName (NULL, pgm, NT_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; - - WCHAR corefile[strlen (p) + sizeof (".stackdump")]; - UNICODE_STRING ucore; - OBJECT_ATTRIBUTES attr; - /* Create the UNICODE variation of <progname>.stackdump. */ - RtlInitEmptyUnicodeString (&ucore, corefile, - sizeof corefile - sizeof (WCHAR)); - ucore.Length = sys_mbstowcs (ucore.Buffer, - ucore.MaximumLength / sizeof (WCHAR), - p, strlen (p)) * sizeof (WCHAR); - RtlAppendUnicodeToString (&ucore, L".stackdump"); - /* Create an object attribute which refers to <progname>.stackdump - in Cygwin's cwd. Stick to caseinsensitivity. */ - InitializeObjectAttributes (&attr, &ucore, OBJ_CASE_INSENSITIVE, - cygheap->cwd.get_handle (), NULL); - HANDLE h; - IO_STATUS_BLOCK io; - NTSTATUS status; - /* Try to open it to dump the stack in it. */ - status = NtCreateFile (&h, GENERIC_WRITE | SYNCHRONIZE, &attr, &io, - NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF, - FILE_SYNCHRONOUS_IO_NONALERT - | FILE_OPEN_FOR_BACKUP_INTENT, NULL, 0); - if (NT_SUCCESS (status)) - { - if (!myself->cygstarted) - system_printf ("Dumping stack trace to %S", &ucore); - else - debug_printf ("Dumping stack trace to %S", &ucore); - 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 ("Signal %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 -} - -extern "C" void _cygwin_exit_return (); - -/* Walk the stack by looking at successive stored 'bp' frames. - This is not foolproof. */ -int -stack_info::walk () -{ - char **ebp; - - if ((void (*) ()) sf.AddrPC.Offset == _cygwin_exit_return) - return 0; /* stack frames are exhausted */ - - if (((ebp = (char **) next_offset ()) == NULL) || (ebp >= (char **) cygwin_hmodule)) - return 0; - - sf.AddrFrame.Offset = (DWORD) ebp; - sf.AddrPC.Offset = sf.AddrReturn.Offset; - - /* The return address always follows the stack pointer */ - sf.AddrReturn.Offset = (DWORD) *++ebp; - - if (needargs) - { - unsigned nparams = NPARAMS; - - /* The arguments follow the return address */ - sf.Params[0] = (DWORD) *++ebp; - /* Hack for XP/2K3 WOW64. If the first stack param points to the - application entry point, we can only fetch one additional - parameter. Accessing anything beyond this address results in - a SEGV. This is fixed in Vista/2K8 WOW64. */ - if (wincap.has_restricted_stack_args () && sf.Params[0] == 0x401000) - nparams = 2; - for (unsigned i = 1; 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; - static bool already_dumped; - - if (rlim_core == 0UL || (open_file && already_dumped)) - return; - - if (open_file) - open_stackdumpfile (); - - already_dumped = true; - - 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)" : ""); -} - -bool -_cygtls::inside_kernel (CONTEXT *cx) -{ - int res; - MEMORY_BASIC_INFORMATION m; - - if (!isinitialized ()) - return true; - - memset (&m, 0, sizeof m); - if (!VirtualQuery ((LPCVOID) cx->Eip, &m, sizeof m)) - sigproc_printf ("couldn't get memory info, pc %p, %E", cx->Eip); - - size_t size = (windows_system_directory_length + 6) * sizeof (WCHAR); - PWCHAR checkdir = (PWCHAR) alloca (size); - memset (checkdir, 0, size); - -# 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 = true; - else if (h == user_data->hmodule) - res = false; - else if (!GetModuleFileNameW (h, checkdir, windows_system_directory_length + 6)) - res = false; - else - { - /* Skip potential long path prefix. */ - if (!wcsncmp (checkdir, L"\\\\?\\", 4)) - checkdir += 4; - res = !wcsncasecmp (windows_system_directory, checkdir, - windows_system_directory_length); - } - sigproc_printf ("pc %p, h %p, inside_kernel %d", cx->Eip, h, res); -# undef h - return res; -} - -/* 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') - return 0; - if (being_debugged ()) - { - extern void break_here (); - break_here (); - 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}; - - STARTUPINFOW 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 */ - - lock_ttys::release (); - - /* prevent recursive exception handling */ - PWCHAR rawenv = GetEnvironmentStringsW () ; - for (PWCHAR p = rawenv; *p != L'\0'; p = wcschr (p, L'\0') + 1) - { - if (wcsncmp (p, L"CYGWIN=", wcslen (L"CYGWIN=")) == 0) - { - PWCHAR q = wcsstr (p, L"error_start") ; - /* replace 'error_start=...' with '_rror_start=...' */ - if (q) - { - *q = L'_' ; - SetEnvironmentVariableW (L"CYGWIN", p + wcslen (L"CYGWIN=")) ; - } - break ; - } - } - - console_printf ("*** starting debugger for pid %u, tid %u\n", - cygwin_pid (GetCurrentProcessId ()), GetCurrentThreadId ()); - BOOL dbg; - WCHAR dbg_cmd[strlen(debugger_command)]; - sys_mbstowcs (dbg_cmd, strlen(debugger_command) + 1, debugger_command); - dbg = CreateProcessW (NULL, - dbg_cmd, - 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 ()) - low_priority_sleep (0); - Sleep (2000); - } - - console_printf ("*** continuing pid %u from debugger call (%d)\n", - cygwin_pid (GetCurrentProcessId ()), dbg); - - SetThreadPriority (GetCurrentThread (), prio); - return dbg; -} - -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) -{ - __asm__ ("\n\ - pushl %%ebx \n\ - pushl %%edi \n\ - pushl %%esi \n\ - pushl $0 \n\ - pushl %1 \n\ - pushl $1f \n\ - pushl %0 \n\ - call _RtlUnwind@16 \n\ -1: \n\ - popl %%esi \n\ - popl %%edi \n\ - popl %%ebx \n\ -": : "r" (frame), "r" (e)); -} - -/* Main exception handler. */ - -extern exception_list *_except_list asm ("%fs:0"); - -int -_cygtls::handle_exceptions (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in, void *) -{ - static bool NO_COPY debugging; - static int NO_COPY recursed; - _cygtls& me = _my_tls; - - 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 || e->ExceptionFlags) - return 1; - - siginfo_t si = {0}; - si.si_code = SI_KERNEL; - /* 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_code = FPE_FLTSUB; - break; - case STATUS_FLOAT_INEXACT_RESULT: - si.si_signo = SIGFPE; - si.si_code = FPE_FLTRES; - break; - case STATUS_FLOAT_OVERFLOW: - si.si_signo = SIGFPE; - si.si_code = FPE_FLTOVF; - break; - case STATUS_FLOAT_UNDERFLOW: - si.si_signo = SIGFPE; - si.si_code = FPE_FLTUND; - break; - case STATUS_INTEGER_DIVIDE_BY_ZERO: - si.si_signo = SIGFPE; - si.si_code = FPE_INTDIV; - break; - case STATUS_INTEGER_OVERFLOW: - si.si_signo = SIGFPE; - si.si_code = FPE_INTOVF; - break; - - case STATUS_ILLEGAL_INSTRUCTION: - si.si_signo = SIGILL; - si.si_code = ILL_ILLOPC; - break; - - case STATUS_PRIVILEGED_INSTRUCTION: - si.si_signo = SIGILL; - si.si_code = ILL_PRVOPC; - break; - - case STATUS_NONCONTINUABLE_EXCEPTION: - si.si_signo = SIGILL; - si.si_code = ILL_ILLADR; - break; - - case STATUS_TIMEOUT: - si.si_signo = SIGALRM; - break; - - case STATUS_GUARD_PAGE_VIOLATION: - si.si_signo = SIGBUS; - si.si_code = BUS_OBJERR; - break; - - case STATUS_DATATYPE_MISALIGNMENT: - si.si_signo = SIGBUS; - si.si_code = BUS_ADRALN; - break; - - case STATUS_ACCESS_VIOLATION: - switch (mmap_is_attached_or_noreserve ((void *)e->ExceptionInformation[1], - 1)) - { - case MMAP_NORESERVE_COMMITED: - return 0; - case MMAP_RAISE_SIGBUS: /* MAP_NORESERVE page, commit failed, or - access to mmap page beyond EOF. */ - si.si_signo = SIGBUS; - si.si_code = BUS_OBJERR; - break; - default: - MEMORY_BASIC_INFORMATION m; - VirtualQuery ((PVOID) e->ExceptionInformation[1], &m, sizeof m); - si.si_signo = SIGSEGV; - si.si_code = m.State == MEM_FREE ? SEGV_MAPERR : SEGV_ACCERR; - break; - } - break; - - case STATUS_ARRAY_BOUNDS_EXCEEDED: - case STATUS_IN_PAGE_ERROR: - case STATUS_NO_MEMORY: - case STATUS_INVALID_DISPOSITION: - case STATUS_STACK_OVERFLOW: - si.si_signo = SIGSEGV; - si.si_code = SEGV_MAPERR; - break; - - case STATUS_CONTROL_C_EXIT: - si.si_signo = SIGINT; - 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); - - bool masked = !!(me.sigmask & SIGTOMASK (si.si_signo)); - if (masked) - 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 (me.fault_guarded ()) - me.return_from_fault (); - - me.copy_context (in); - - /* Temporarily replace windows top level SEH with our own handler. - We don't want any Windows magic kicking in. This top level frame - will be removed automatically after our exception handler returns. */ - _except_list->handler = _cygtls::handle_exceptions; - - if (masked - || &me == _sig_tls - || !cygwin_finished_initializing - || (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->cygstarted) - 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; - } - - rtl_unwind (frame, e); - open_stackdumpfile (); - exception (e, in); - stackdump ((DWORD) ebp, 0, 1); - } - - if (e->ExceptionCode == STATUS_ACCESS_VIOLATION) - { - int error_code = 0; - if (si.si_code == SEGV_ACCERR) /* Address present */ - error_code |= 1; - if (e->ExceptionInformation[0]) /* Write access */ - error_code |= 2; - if (!me.inside_kernel (in)) /* User space */ - error_code |= 4; - klog (LOG_INFO, "%s[%d]: segfault at %08x rip %08x rsp %08x error %d", - __progname, myself->pid, - e->ExceptionInformation[1], in->Eip, in->Esp, - ((in->Eip >= 0x61000000 && in->Eip < 0x61200000) - ? 0 : 4) | (e->ExceptionInformation[0] << 1)); - } - - me.signal_exit (0x80 | si.si_signo); // Flag signal + core dump - } - - si.si_addr = (si.si_signo == SIGSEGV || si.si_signo == SIGBUS - ? (void *) e->ExceptionInformation[1] - : (void *) in->Eip); - si.si_errno = si.si_pid = si.si_uid = 0; - me.incyg++; - sig_send (NULL, si, &me); // Signal myself - me.incyg--; - e->ExceptionFlags = 0; - return 0; -} - -/* 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) -{ - if (&_my_tls != _main_tls) - { - cancelable_wait (signal_arrived, INFINITE, cw_cancel_self); - return -1; - } - - sigset_t oldmask = _my_tls.sigmask; // Remember for restoration - - set_signal_mask (tempmask, _my_tls.sigmask); - sigproc_printf ("oldmask %p, newmask %p", oldmask, tempmask); - - pthread_testcancel (); - 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) -{ - _my_tls.incyg = 1; - /* 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; - 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: - reset_signal_arrived (); - myself->stopsig = SIGCONT; - myself->alert_parent (SIGCONT); - break; - default: - api_fatal ("WaitSingleObject failed, %E"); - break; - } - _my_tls.incyg = 0; -} -} - -bool -_cygtls::interrupt_now (CONTEXT *cx, int sig, void *handler, - struct sigaction& siga) -{ - bool interrupted; - - if (incyg || spinning || locked () || inside_kernel (cx)) - interrupted = false; - else - { - push ((__stack_t) cx->Eip); - interrupt_setup (sig, handler, siga); - cx->Eip = pop (); - SetThreadContext (*this, cx); /* Restart the thread in a new location */ - interrupted = true; - } - return interrupted; -} - -void __stdcall -_cygtls::interrupt_setup (int sig, void *handler, struct sigaction& siga) -{ - push ((__stack_t) sigdelayed); - deltamask = siga.sa_mask & ~SIG_NONMASKABLE; - sa_flags = siga.sa_flags; - func = (void (*) (int)) handler; - if (siga.sa_flags & SA_RESETHAND) - siga.sa_handler = SIG_DFL; - 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 - - 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, sig %d, res %d", signal_arrived, - sig, res); -} - -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) - { - sigproc_printf ("controlled interrupt. stackptr %p, stack %p, stackptr[-1] %p", - tls->stackptr, tls->stack, tls->stackptr[-1]); - 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) - { - ResumeThread (hth); - break; - } - cx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; - if (!GetThreadContext (hth, &cx)) - system_printf ("couldn't get context of main thread, %E"); - else - 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: - sigproc_printf ("signal %d %sdelivered", sig, interrupted ? "" : "not "); - return interrupted; -} - -static inline bool -has_visible_window_station () -{ - HWINSTA station_hdl; - USEROBJECTFLAGS uof; - DWORD len; - - /* Check if the process is associated with a visible window station. - These are processes running on the local desktop as well as processes - running in terminal server sessions. - Processes running in a service session not explicitely associated - with the desktop (using the "Allow service to interact with desktop" - property) are running in an invisible window station. */ - if ((station_hdl = GetProcessWindowStation ()) - && GetUserObjectInformationW (station_hdl, UOI_FLAGS, &uof, - sizeof uof, &len) - && (uof.dwFlags & WSF_VISIBLE)) - return true; - return false; -} - -/* Keyboard interrupt handler. */ -static BOOL WINAPI -ctrl_c_handler (DWORD type) -{ - static bool saw_close; - lock_process now; - - if (!cygwin_finished_initializing) - { - if (myself->cygstarted) /* Was this process created by a cygwin process? */ - return TRUE; /* Yes. Let the parent eventually handle CTRL-C issues. */ - debug_printf ("exiting with status %p", STATUS_CONTROL_C_EXIT); - ExitProcess (STATUS_CONTROL_C_EXIT); - } - - _my_tls.remove (INFINITE); - -#if 0 - if (type == CTRL_C_EVENT || type == CTRL_BREAK_EVENT) - proc_subproc (PROC_KILLFORKED, 0); -#endif - - /* 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) - { - /* The CTRL_LOGOFF_EVENT is sent when *any* user logs off. - The below code sends a SIGHUP only if it is not performing the - default activity for SIGHUP. Note that it is possible for two - SIGHUP signals to arrive if a process group leader is exiting - too. Getting this 100% right is saved for a future cygwin mailing - list goad. */ - if (global_sigs[SIGHUP].sa_handler != SIG_DFL) - { - sig_send (myself_nowait, SIGHUP); - return TRUE; - } - return FALSE; - } - } - - if (chExeced) - { - chExeced->set_saw_ctrl_c (); - 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. */ - { - int sig = SIGINT; - /* If intr and quit are both mapped to ^C, send SIGQUIT on ^BREAK */ - if (type == CTRL_BREAK_EVENT - && t->ti.c_cc[VINTR] == 3 && t->ti.c_cc[VQUIT] == 3) - sig = SIGQUIT; - t->last_ctrl_c = GetTickCount (); - killsys (-myself->pid, sig); - 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, _my_tls.sigmask); -} - -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 = _my_tls.sigmask; - sigaddset (&mask, sig); - set_signal_mask (mask, _my_tls.sigmask); - mask_sync.release (); - return 0; -} - -extern "C" int -sigrelse (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 = _my_tls.sigmask; - sigdelset (&mask, sig); - set_signal_mask (mask, _my_tls.sigmask); - mask_sync.release (); - return 0; -} - -extern "C" _sig_func_ptr -sigset (int sig, _sig_func_ptr func) -{ - sig_dispatch_pending (); - _sig_func_ptr prev; - - /* check that sig is in right range */ - if (sig < 0 || sig >= NSIG || sig == SIGKILL || sig == SIGSTOP) - { - set_errno (EINVAL); - syscall_printf ("SIG_ERR = sigset (%d, %p)", sig, 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. */ - if (sigismember (&mask, sig)) - prev = SIG_HOLD; - else - prev = global_sigs[sig].sa_handler; - /* If func is SIG_HOLD, add sig to the signal mask, otherwise set the - disposition to func and remove sig from the signal mask. */ - if (func == SIG_HOLD) - sigaddset (&mask, sig); - else - { - /* No error checking. The test which could return SIG_ERR has already - been made above. */ - signal (sig, func); - sigdelset (&mask, sig); - } - set_signal_mask (mask, _my_tls.sigmask); - mask_sync.release (); - return prev; -} - -extern "C" int -sigignore (int sig) -{ - return sigset (sig, SIG_IGN) == SIG_ERR ? -1 : 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 = _my_tls.sigmask; - newmask = (oldmask | _my_tls.deltamask) & ~SIG_NONMASKABLE; - 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. */ -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 = oldmask & ~newmask; - sigproc_printf ("oldmask %p, newmask %p, mask_bits %p", oldmask, newmask, - mask_bits); - oldmask = newmask; - if (mask_bits) - sig_dispatch_pending (true); - else - sigproc_printf ("not calling sig_dispatch_pending"); - mask_sync.release (); -} - -int __stdcall -sigpacket::process () -{ - DWORD continue_now; - struct sigaction dummy = global_sigs[SIGSTOP]; - - 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++; - - bool masked; - void *handler; - if (!hExeced || (void *) thissig.sa_handler == (void *) SIG_IGN) - handler = (void *) thissig.sa_handler; - else if (tls) - return 1; - else - handler = NULL; - - _cygtls *use_tls = tls ?: _main_tls; - - if (si.si_signo == SIGKILL) - goto exit_sig; - if (si.si_signo == SIGSTOP) - { - sig_clear (SIGCONT); - goto stop; - } - - 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 - { - insigwait_mask = !handler && (tls = _cygtls::find_tls (si.si_signo)); - if (tls) - use_tls = tls; - } - - 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; - goto done; - } - - /* Clear pending SIGCONT on stop signals */ - if (si.si_signo == SIGTSTP || si.si_signo == SIGTTIN || si.si_signo == SIGTTOU) - sig_clear (SIGCONT); - -#ifdef CGF - if (being_debugged ()) - { - char sigmsg[sizeof (_CYGWIN_SIGNAL_STRING " 0xffffffff")]; - __small_sprintf (sigmsg, _CYGWIN_SIGNAL_STRING " %p", 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; - - 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: - /* 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 (sigCONT); - sigproc_printf ("returning %d", rc); - return rc; - -thread_specific: - use_tls->sig = si.si_signo; - use_tls->set_siginfo (this); - sigproc_printf ("releasing sigwait for thread"); - SetEvent (use_tls->event); - goto done; - -exit_sig: - if (si.si_signo == SIGQUIT || si.si_signo == SIGABRT) - { - CONTEXT c; - c.ContextFlags = CONTEXT_FULL; - GetThreadContext (hMainThread, &c); - use_tls->copy_context (&c); - si.si_signo |= 0x80; - } - sigproc_printf ("signal %d, about to call do_exit", si.si_signo); - use_tls->signal_exit (si.si_signo); /* never returns */ -} - -/* 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. */ -void -_cygtls::signal_exit (int rc) -{ - if (hExeced) - { - sigproc_printf ("terminating captive process"); - TerminateProcess (hExeced, sigExeced = rc); - } - - signal_debugger (rc & 0x7f); - if ((rc & 0x80) && !try_to_debug ()) - stackdump (thread_context.ebp, 1, 1); - - lock_process until_exit (true); - if (hExeced || exit_state > ES_PROCESS_LOCKED) - myself.exit (rc); - - /* Starve other threads in a vain attempt to stop them from doing something - stupid. */ - SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL); - - sigproc_printf ("about to call do_exit (%x)", rc); - SetEvent (signal_arrived); - do_exit (rc); -} - -void -events_init () -{ - mask_sync.init ("mask_sync"); - windows_system_directory[0] = L'\0'; - GetSystemDirectoryW (windows_system_directory, sizeof (windows_system_directory) / sizeof (WCHAR) - 2); - PWCHAR end = wcschr (windows_system_directory, L'\0'); - if (end == windows_system_directory) - api_fatal ("can't find windows system directory"); - if (end[-1] != L'\\') - { - *end++ = L'\\'; - *end = L'\0'; - } - windows_system_directory_length = end - windows_system_directory; - debug_printf ("windows_system_directory '%W', windows_system_directory_length %d", - windows_system_directory, windows_system_directory_length); -} - -void -events_terminate () -{ - exit_already = 1; -} - -int -_cygtls::call_signal_handler () -{ - int this_sa_flags = 0; - /* Call signal handler. */ - while (sig) - { - lock (); - this_sa_flags = sa_flags; - int thissig = sig; - - pop (); - reset_signal_arrived (); - sigset_t this_oldmask = set_process_mask_delta (); - int this_errno = saved_errno; - sig = 0; - unlock (); // make sure synchronized - incyg = 0; - if (!(this_sa_flags & SA_SIGINFO)) - { - void (*sigfunc) (int) = func; - sigfunc (thissig); - } - else - { - siginfo_t thissi = infodata; - void (*sigact) (int, siginfo_t *, void *) = (void (*) (int, siginfo_t *, void *)) func; - /* no ucontext_t information provided yet */ - sigact (thissig, &thissi, NULL); - } - incyg = 1; - set_signal_mask (this_oldmask, _my_tls.sigmask); - 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); - ResetEvent (signal_arrived); - sigproc_printf ("reset signal_arrived"); - if (_my_tls.stackptr > _my_tls.stack) - debug_printf ("stackptr[-1] %p", _my_tls.stackptr[-1]); -} - -void -_cygtls::copy_context (CONTEXT *c) -{ - memcpy (&thread_context, c, (&thread_context._internal - (unsigned char *) &thread_context)); -} - -void -_cygtls::signal_debugger (int sig) -{ - if (isinitialized () && being_debugged ()) - { - char sigmsg[2 * sizeof (_CYGWIN_SIGNAL_STRING " ffffffff ffffffff")]; - __small_sprintf (sigmsg, _CYGWIN_SIGNAL_STRING " %d %p %p", sig, thread_id, &thread_context); - OutputDebugString (sigmsg); - } -} |