--- /dev/null
+/* exceptions.cc
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ 2005, 2006, 2007, 2008 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 "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
+ | FILE_OPEN_FOR_RECOVERY, 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)
+{
+ WCHAR dbg_cmd[sizeof debugger_command];
+
+ 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;
+ sys_mbstowcs (dbg_cmd, sizeof debugger_command, 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");
+
+extern "C" char *__progname;
+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 = (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->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;
+
+ if (si.si_signo == SIGKILL)
+ goto exit_sig;
+ if (si.si_signo == SIGSTOP)
+ {
+ sig_clear (SIGCONT);
+ if (!tls)
+ tls = _main_tls;
+ goto stop;
+ }
+
+ bool insigwait_mask;
+ if ((masked = ISSTATE (myself, PID_STOPPED)))
+ insigwait_mask = false;
+ else if (!tls)
+ insigwait_mask = !handler && (tls = _cygtls::find_tls (si.si_signo));
+ else
+ insigwait_mask = sigismember (&tls->sigwait_mask, si.si_signo);
+
+ 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 (!tls)
+ tls = _main_tls;
+
+ 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;
+
+ 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, 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);
+ tls->copy_context (&c);
+ si.si_signo |= 0x80;
+ }
+ sigproc_printf ("signal %d, about to call do_exit", si.si_signo);
+ 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);
+ }
+}