summaryrefslogtreecommitdiff
path: root/winsup/cygwin/exceptions.cc
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/exceptions.cc')
-rw-r--r--winsup/cygwin/exceptions.cc1115
1 files changed, 1115 insertions, 0 deletions
diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc
new file mode 100644
index 00000000000..e2877edcfa6
--- /dev/null
+++ b/winsup/cygwin/exceptions.cc
@@ -0,0 +1,1115 @@
+/* exceptions.cc
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 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;
+ }
+ }
+ }
+
+#ifdef __i386__
+#define HAVE_STATUS
+ 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\r\n",
+ in->Ebp, in->Esp, myself->progname);
+ 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);
+#endif
+
+#ifndef HAVE_STATUS
+ system_printf ("Had an exception");
+#endif
+}
+
+#ifdef __i386__
+/* Print a stack backtrace. */
+
+#define HAVE_STACK_TRACE
+
+/* 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",
+ 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 ;
+ }
+ }
+
+ small_printf ("*** starting debugger for pid %u\n",
+ cygwin_pid (GetCurrentProcessId ()));
+ 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 1;
+ SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_IDLE);
+ while (!being_debugged ())
+ low_priority_sleep (0);
+ small_printf ("*** continuing pid %u from debugger call\n",
+ cygwin_pid (GetCurrentProcessId ()));
+ }
+
+ SetThreadPriority (GetCurrentThread (), prio);
+ return 0;
+}
+
+/* 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;
+
+ extern DWORD ret_here[];
+ RtlUnwind (frame, ret_here, e0, 0);
+ __asm__ volatile (".equ _ret_here,.");
+
+ int sig;
+ /* Coerce win32 value to posix value. */
+ switch (e.ExceptionCode)
+ {
+ case STATUS_FLOAT_DENORMAL_OPERAND:
+ case STATUS_FLOAT_DIVIDE_BY_ZERO:
+ case STATUS_FLOAT_INEXACT_RESULT:
+ case STATUS_FLOAT_INVALID_OPERATION:
+ case STATUS_FLOAT_OVERFLOW:
+ case STATUS_FLOAT_STACK_CHECK:
+ case STATUS_FLOAT_UNDERFLOW:
+ case STATUS_INTEGER_DIVIDE_BY_ZERO:
+ case STATUS_INTEGER_OVERFLOW:
+ sig = SIGFPE;
+ break;
+
+ case STATUS_ILLEGAL_INSTRUCTION:
+ case STATUS_PRIVILEGED_INSTRUCTION:
+ case STATUS_NONCONTINUABLE_EXCEPTION:
+ sig = SIGILL;
+ break;
+
+ case STATUS_TIMEOUT:
+ sig = SIGALRM;
+ 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:
+ sig = SIGSEGV;
+ break;
+
+ case STATUS_CONTROL_C_EXIT:
+ sig = 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", sig, in.Eip);
+
+ if (global_sigs[sig].sa_mask & SIGTOMASK (sig))
+ syscall_printf ("signal %d, masked %p", sig, global_sigs[sig].sa_mask);
+
+ debug_printf ("In cygwin_except_handler calling %p",
+ global_sigs[sig].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 (!myself->progname[0]
+ || GetCurrentThreadId () == sigtid
+ || (void *) global_sigs[sig].sa_handler == (void *) SIG_DFL
+ || (void *) global_sigs[sig].sa_handler == (void *) SIG_IGN
+ || (void *) global_sigs[sig].sa_handler == (void *) SIG_ERR)
+ {
+ /* Print the exception to the console */
+ if (1)
+ {
+ for (int i = 0; status_info[i].name; i++)
+ {
+ if (status_info[i].code == e.ExceptionCode)
+ {
+ if (!myself->ppid_handle)
+ 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 | sig); // Flag signal + core dump
+ }
+
+ _my_tls.push ((__stack_t) ebp, true);
+ sig_send (NULL, sig, &_my_tls); // Signal myself
+ return 1;
+}
+#endif /* __i386__ */
+
+#ifndef HAVE_STACK_TRACE
+void
+stack (void)
+{
+ system_printf ("Stack trace not yet supported on this machine.");
+}
+#endif
+
+/* Utilities to call a user supplied exception handler. */
+
+#define SIG_NONMASKABLE (SIGTOMASK (SIGKILL) | SIGTOMASK (SIGSTOP))
+
+#ifdef __i386__
+#define HAVE_CALL_HANDLER
+
+/* 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)
+{
+ sig_dispatch_pending ();
+ sigset_t oldmask = myself->getsigmask (); // Remember for restoration
+
+ set_signal_mask (tempmask &= ~SIG_NONMASKABLE, oldmask);// Let signals we're
+ // interested in through.
+ 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. */
+
+ _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 (ISSTATE (parent, PID_NOCLDSTOP))
+ sig_send (parent, SIGCHLD);
+ }
+ sigproc_printf ("process %d stopped by signal %d, myself->ppid_handle %p",
+ myself->pid, sig, myself->ppid_handle);
+ if (WaitForSingleObject (sigCONT, INFINITE) != WAIT_OBJECT_0)
+ api_fatal ("WaitSingleObject failed, %E");
+ 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
+_threadinfo::interrupt_setup (int sig, void *handler,
+ struct sigaction& siga, __stack_t retaddr)
+{
+ __stack_t *retaddr_in_tls = stackptr - 1;
+ push ((__stack_t) sigdelayed);
+ oldmask = myself->getsigmask ();
+ newmask = oldmask | siga.sa_mask | SIGTOMASK (sig);
+ 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 second to last setting set to avoid a race
+ *retaddr_in_tls = retaddr;
+ /* 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
+_threadinfo::interrupt_now (CONTEXT *ctx, int sig, void *handler,
+ struct sigaction& siga)
+{
+ push (0);
+ interrupt_setup (sig, handler, siga, (__stack_t) ctx->Eip);
+ ctx->Eip = pop ();
+ SetThreadContext (*this, ctx); /* Restart the thread in a new location */
+ return 1;
+}
+
+void __stdcall
+signal_fixup_after_fork ()
+{
+ if (_my_tls.sig)
+ {
+ _my_tls.sig = 0;
+ _my_tls.stackptr = _my_tls.stack + 1; // FIXME?
+ set_signal_mask (_my_tls.oldmask);
+ }
+ sigproc_init ();
+}
+
+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&, _threadinfo *tls)
+ __attribute__((regparm(3)));
+static int
+setup_handler (int sig, void *handler, struct sigaction& siga, _threadinfo *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++)
+ {
+ __stack_t *retaddr_on_stack = tls->stackptr - 1;
+ if (retaddr_on_stack >= tls->stack)
+ {
+ __stack_t retaddr = InterlockedExchange ((LONG *) retaddr_on_stack, 0);
+ if (!retaddr)
+ continue;
+ tls->reset_exception ();
+ tls->interrupt_setup (sig, handler, siga, retaddr);
+ sigproc_printf ("interrupted known cygwin routine");
+ interrupted = true;
+ break;
+ }
+
+ DWORD res;
+ HANDLE hth = (HANDLE) *tls;
+
+ /* Suspend the thread which will receive the signal. But first ensure that
+ this thread doesn't have any mutos. (FIXME: Someday we should just grab
+ all of the mutos rather than checking for them)
+ 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 release the lock now since we hav suspended the main thread and it
+ definitely can't be grabbing it now. This will have to change, of course,
+ if/when we can send signals to other than the main thread. */
+
+ /* Just set pending if thread is already suspended */
+ if (res)
+ {
+ (void) ResumeThread (hth);
+ break;
+ }
+
+ // FIXME - add check for reentering of DLL here
+
+ 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:
+ sigproc_printf ("signal %d %sdelivered", sig, interrupted ? "" : "not ");
+ return interrupted;
+}
+#endif /* i386 */
+
+#ifndef HAVE_CALL_HANDLER
+#error "Need to supply machine dependent setup_handler"
+#endif
+
+/* Keyboard interrupt handler. */
+static BOOL WINAPI
+ctrl_c_handler (DWORD type)
+{
+ static bool saw_close;
+ _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)
+ {
+ saw_close = true;
+ sig_send (NULL, SIGHUP);
+ 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 ();
+ kill (-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);
+}
+
+/* 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
+ mask_sync->release ();
+ if (mask_bits)
+ sig_dispatch_pending ();
+ else
+ sigproc_printf ("not calling sig_dispatch_pending");
+ return;
+}
+
+int __stdcall
+sig_handle (int sig, sigset_t mask, int pid, _threadinfo *tls)
+{
+ if (sig == SIGCONT)
+ {
+ DWORD stopped = 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);
+ if (stopped)
+ SetEvent (sigCONT);
+ }
+
+ int rc = 1;
+ bool insigwait_mask = tls ? sigismember (&tls->sigwait_mask, sig) : false;
+ bool special_case = ISSTATE (myself, PID_STOPPED) || main_vfork->pid;
+ bool masked = sigismember (&mask, sig);
+ if (sig != SIGKILL && sig != SIGSTOP
+ && (special_case || main_vfork->pid || masked || insigwait_mask
+ || (tls && sigismember (&tls->sigmask, sig))))
+ {
+ sigproc_printf ("signal %d blocked", sig);
+ if ((!special_case && !masked)
+ && (insigwait_mask || (tls = _threadinfo::find_tls (sig)) != NULL))
+ goto thread_specific;
+ rc = -1;
+ goto done;
+ }
+
+ /* Clear pending SIGCONT on stop signals */
+ if (sig == SIGSTOP || sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU)
+ sig_clear (SIGCONT);
+
+ sigproc_printf ("signal %d processing", sig);
+ struct sigaction thissig = global_sigs[sig];
+ void *handler;
+ handler = (void *) thissig.sa_handler;
+
+ myself->rusage_self.ru_nsignals++;
+
+ if (sig == SIGKILL)
+ goto exit_sig;
+
+ if (sig == SIGSTOP)
+ goto stop;
+
+#if 0
+ char sigmsg[24];
+ __small_sprintf (sigmsg, "cygwin: signal %d\n", sig);
+ OutputDebugString (sigmsg);
+#endif
+
+ if (handler == (void *) SIG_DFL)
+ {
+ if (insigwait_mask)
+ goto thread_specific;
+ if (sig == SIGCHLD || sig == SIGIO || sig == SIGCONT || sig == SIGWINCH
+ || sig == SIGURG)
+ {
+ sigproc_printf ("default signal %d ignored", sig);
+ goto done;
+ }
+
+ if (sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU)
+ goto stop;
+
+ goto exit_sig;
+ }
+
+ if (handler == (void *) SIG_IGN)
+ {
+ sigproc_printf ("signal %d ignored", sig);
+ goto done;
+ }
+
+ if (handler == (void *) SIG_ERR)
+ goto exit_sig;
+
+ 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", sig, handler);
+ rc = setup_handler (sig, handler, thissig, tls ?: _main_tls);
+
+done:
+ sigproc_printf ("returning %d", rc);
+ return rc;
+
+thread_specific:
+ tls->sig = sig;
+ sigproc_printf ("releasing sigwait for thread");
+ SetEvent (tls->event);
+ goto done;
+
+exit_sig:
+ if (sig == SIGQUIT || sig == SIGABRT)
+ {
+ CONTEXT c;
+ c.ContextFlags = CONTEXT_FULL;
+ GetThreadContext (hMainThread, &c);
+ if (!try_to_debug ())
+ stackdump (c.Ebp, 1, 1);
+ sig |= 0x80;
+ }
+ sigproc_printf ("signal %d, about to call do_exit", sig);
+ signal_exit (sig);
+ /* 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;
+}
+
+extern "C" {
+int __stdcall
+call_signal_handler_now ()
+{
+ int sa_flags = 0;
+ while (_my_tls.sig && _my_tls.stackptr > _my_tls.stack)
+ {
+ sa_flags = _my_tls.sa_flags;
+ int sig = _my_tls.sig;
+ void (*sigfunc) (int) = _my_tls.func;
+
+ (void) _my_tls.pop ();
+ reset_signal_arrived ();
+ sigset_t oldmask = _my_tls.oldmask;
+ int this_errno = _my_tls.saved_errno;
+ set_process_mask (_my_tls.newmask);
+ _my_tls.sig = 0;
+ sigfunc (sig);
+ set_process_mask (oldmask);
+ if (this_errno >= 0)
+ set_errno (this_errno);
+ }
+
+ return sa_flags & SA_RESTART;
+}
+
+void __stdcall
+reset_signal_arrived ()
+{
+ (void) ResetEvent (signal_arrived);
+ sigproc_printf ("reset signal_arrived");
+}
+}