673a691372
due to a signal.
1435 lines
38 KiB
C++
1435 lines
38 KiB
C++
/* exceptions.cc
|
|
|
|
Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
|
|
2005, 2006, 2007, 2008, 2009, 2010, 2011 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"
|
|
#include "exception.h"
|
|
|
|
#define CALL_HANDLER_RETRY_OUTER 10
|
|
#define CALL_HANDLER_RETRY_INNER 10
|
|
|
|
char debugger_command[2 * NT_MAX_PATH + 20];
|
|
|
|
extern "C" {
|
|
extern void sigdelayed ();
|
|
};
|
|
|
|
extern child_info_spawn *chExeced;
|
|
|
|
static BOOL WINAPI ctrl_c_handler (DWORD);
|
|
|
|
/* This is set to indicate that we have already exited. */
|
|
|
|
static NO_COPY int exit_already = 0;
|
|
static muto NO_COPY mask_sync;
|
|
|
|
NO_COPY static struct
|
|
{
|
|
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 we have no executable name, or if the CWD handle is NULL,
|
|
which means, the CWD is a virtual path, don't even try to open
|
|
a stackdump file. */
|
|
if (myself->progname[0] && cygheap->cwd.get_handle ())
|
|
{
|
|
const WCHAR *p;
|
|
/* write to progname.stackdump if possible */
|
|
if (!myself->progname[0])
|
|
p = L"unknown";
|
|
else if ((p = wcsrchr (myself->progname, L'\\')))
|
|
p++;
|
|
else
|
|
p = myself->progname;
|
|
|
|
WCHAR corefile[wcslen (p) + sizeof (".stackdump")];
|
|
wcpcpy (wcpcpy(corefile, p), L".stackdump");
|
|
UNICODE_STRING ucore;
|
|
OBJECT_ATTRIBUTES attr;
|
|
/* Create the UNICODE variation of <progname>.stackdump. */
|
|
RtlInitUnicodeString (&ucore, corefile);
|
|
/* 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
|
|
dump_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=%W, 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)
|
|
{
|
|
static bool already_dumped;
|
|
|
|
if (cygheap->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 the tty mutex is owned, we will fail to start any cygwin app
|
|
until the trapped app exits. However, this will only release any
|
|
the mutex if it is owned by this thread so that may be problematic. */
|
|
|
|
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 ())
|
|
yield ();
|
|
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
|
|
exception::handle (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.andreas)
|
|
me.andreas->leave (); /* Return from a "san" caught 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 = handle;
|
|
|
|
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);
|
|
if (cygheap->rlim_core > 0UL)
|
|
{
|
|
open_stackdumpfile ();
|
|
dump_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));
|
|
}
|
|
|
|
/* Flag signal + core dump */
|
|
me.signal_exit ((cygheap->rlim_core > 0UL ? 0x80 : 0) | si.si_signo);
|
|
}
|
|
|
|
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)
|
|
{
|
|
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);
|
|
|
|
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;
|
|
|
|
/* Delay the interrupt if we are
|
|
1) somehow inside the DLL
|
|
2) in _sigfe (spinning is true) and about to enter cygwin DLL
|
|
3) in a Windows DLL. */
|
|
if (incyg || spinning || 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 n = 0; n < CALL_HANDLER_RETRY_OUTER; n++)
|
|
{
|
|
for (int i = 0; i < CALL_HANDLER_RETRY_INNER; 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 ();
|
|
goto out;
|
|
}
|
|
|
|
DWORD res;
|
|
HANDLE hth = (HANDLE) *tls;
|
|
|
|
/* Suspend the thread which will receive the signal.
|
|
If one of these conditions is not true we loop.
|
|
If the thread is already suspended (which can occur when a program
|
|
has called SuspendThread on itself) then just queue the signal. */
|
|
|
|
sigproc_printf ("suspending thread");
|
|
res = SuspendThread (hth);
|
|
/* Just set pending if thread is already suspended */
|
|
if (res)
|
|
{
|
|
ResumeThread (hth);
|
|
goto out;
|
|
}
|
|
cx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
|
|
if (!GetThreadContext (hth, &cx))
|
|
system_printf ("couldn't get context of thread, %E");
|
|
else
|
|
interrupted = tls->interrupt_now (&cx, sig, handler, siga);
|
|
|
|
tls->unlock ();
|
|
res = ResumeThread (hth);
|
|
if (interrupted)
|
|
goto out;
|
|
|
|
sigproc_printf ("couldn't interrupt. trying again.");
|
|
yield ();
|
|
}
|
|
/* Hit here if we couldn't deliver the signal. Take a more drastic
|
|
action before trying again. */
|
|
Sleep (1);
|
|
}
|
|
|
|
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_cttyp ();
|
|
/* Ignore this if we're not the process group leader since it should be handled
|
|
*by* the process group leader. */
|
|
if (t && 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);
|
|
}
|
|
|
|
switch (si.si_signo)
|
|
{
|
|
case SIGINT:
|
|
case SIGQUIT:
|
|
case SIGSTOP:
|
|
case SIGTSTP:
|
|
if (cygheap->ctty)
|
|
cygheap->ctty->sigflush ();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
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 if (!(tls = _cygtls::find_tls (si.si_signo)))
|
|
insigwait_mask = false;
|
|
else
|
|
{
|
|
use_tls = tls;
|
|
insigwait_mask = true;
|
|
}
|
|
|
|
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);
|
|
use_tls->func = NULL;
|
|
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);
|
|
if (cygheap->rlim_core > 0UL)
|
|
si.si_signo |= 0x80;
|
|
}
|
|
SetEvent (signal_arrived); // To avoid a potential deadlock with proc_lock
|
|
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);
|
|
do_exit (rc);
|
|
}
|
|
|
|
void
|
|
events_init ()
|
|
{
|
|
mask_sync.init ("mask_sync");
|
|
}
|
|
|
|
void
|
|
events_terminate ()
|
|
{
|
|
exit_already = 1;
|
|
}
|
|
|
|
int
|
|
_cygtls::call_signal_handler ()
|
|
{
|
|
int this_sa_flags = 0;
|
|
/* Call signal handler. */
|
|
while (sig && func)
|
|
{
|
|
lock ();
|
|
this_sa_flags = sa_flags;
|
|
int thissig = sig;
|
|
void (*thisfunc) (int) = func;
|
|
|
|
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) = thisfunc;
|
|
sigfunc (thissig);
|
|
}
|
|
else
|
|
{
|
|
siginfo_t thissi = infodata;
|
|
void (*sigact) (int, siginfo_t *, void *) = (void (*) (int, siginfo_t *, void *)) thisfunc;
|
|
/* 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);
|
|
}
|
|
}
|