/* exceptions.cc
Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
- 2005, 2006, 2007, 2008 Red Hat, Inc.
+ 2005, 2006, 2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
This file is part of Cygwin.
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
+#define CYGTLS_HANDLE
#include "winsup.h"
#include "miscfuncs.h"
#include <wingdi.h>
#include "cygheap.h"
#include "child_info.h"
#include "ntdll.h"
+#include "exception.h"
-#define CALL_HANDLER_RETRY 20
+#define CALL_HANDLER_RETRY_OUTER 10
+#define CALL_HANDLER_RETRY_INNER 10
char debugger_command[2 * NT_MAX_PATH + 20];
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 void
open_stackdumpfile ()
{
- if (myself->progname[0])
+ /* 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 char *p;
+ const WCHAR *p;
/* write to progname.stackdump if possible */
if (!myself->progname[0])
- p = "unknown";
- else if ((p = strrchr (myself->progname, '\\')))
+ p = L"unknown";
+ else if ((p = wcsrchr (myself->progname, L'\\')))
p++;
else
p = myself->progname;
- WCHAR corefile[strlen (p) + sizeof (".stackdump")];
+ 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. */
- 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");
+ 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,
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);
+ | FILE_OPEN_FOR_BACKUP_INTENT, NULL, 0);
if (NT_SUCCESS (status))
{
if (!myself->cygstarted)
/* Utilities for dumping the stack, etc. */
static void
-exception (EXCEPTION_RECORD *e, CONTEXT *in)
+dump_exception (EXCEPTION_RECORD *e, CONTEXT *in)
{
const char *exception_name = NULL;
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",
+ 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);
return 1;
}
-static void
+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))
+ if (cygheap->rlim_core == 0UL || (open_file && already_dumped))
return;
if (open_file)
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;
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 */
+ /* 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 ();
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);
+ WCHAR dbg_cmd[strlen(debugger_command)];
+ sys_mbstowcs (dbg_cmd, strlen(debugger_command) + 1, debugger_command);
dbg = CreateProcessW (NULL,
dbg_cmd,
NULL,
return dbg;
SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_IDLE);
while (!being_debugged ())
- low_priority_sleep (0);
+ yield ();
Sleep (2000);
}
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 *)
+exception::handle (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in, void *)
{
static bool NO_COPY debugging;
static int NO_COPY recursed;
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);
+ debug_printf ("In cygwin_except_handler exception %p at %p sp %p", e->ExceptionCode, in->Eip, in->Esp);
+ debug_printf ("In cygwin_except_handler signal %d at %p", si.si_signo, in->Eip);
bool masked = !!(me.sigmask & SIGTOMASK (si.si_signo));
if (masked)
break;
}
- if (me.fault_guarded ())
- me.return_from_fault ();
+ 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 = _cygtls::handle_exceptions;
+ _except_list->handler = handle;
if (masked
|| &me == _sig_tls
}
rtl_unwind (frame, e);
- open_stackdumpfile ();
- exception (e, in);
- stackdump ((DWORD) ebp, 0, 1);
+ if (cygheap->rlim_core > 0UL)
+ {
+ open_stackdumpfile ();
+ dump_exception (e, in);
+ stackdump ((DWORD) ebp, 0, 1);
+ }
}
if (e->ExceptionCode == STATUS_ACCESS_VIOLATION)
? 0 : 4) | (e->ExceptionInformation[0] << 1));
}
- me.signal_exit (0x80 | si.si_signo); // Flag signal + core dump
+ /* Flag signal + core dump */
+ me.signal_exit ((cygheap->rlim_core > 0UL ? 0x80 : 0) | si.si_signo);
}
- si.si_addr = (void *) in->Eip;
+ 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
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);
+ cancelable_wait (signal_arrived);
set_sig_errno (EINTR); // Per POSIX
{
case WAIT_OBJECT_0:
case WAIT_OBJECT_0 + 1:
- reset_signal_arrived ();
+ myself->stopsig = SIGCONT;
myself->alert_parent (SIGCONT);
break;
default:
{
bool interrupted;
- if (incyg || spinning || locked () || inside_kernel (cx))
+ /* 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
{
/* 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,
+ sigproc_printf ("armed signal_arrived %p, signal %d, res %d", signal_arrived,
sig, res);
}
if (tls->sig)
{
- sigproc_printf ("trying to send sig %d but signal %d already armed",
+ sigproc_printf ("trying to send signal %d but signal %d already armed",
sig, tls->sig);
goto out;
}
- for (int i = 0; i < CALL_HANDLER_RETRY; i++)
+ for (int n = 0; n < CALL_HANDLER_RETRY_OUTER; n++)
{
- tls->lock ();
- if (tls->incyg)
+ for (int i = 0; i < CALL_HANDLER_RETRY_INNER; i++)
{
- 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->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;
+ }
- 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);
+ DWORD res;
+ HANDLE hth = (HANDLE) *tls;
- res = ResumeThread (hth);
- if (interrupted)
- break;
+ /* 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);
- sigproc_printf ("couldn't interrupt. trying again.");
- low_priority_sleep (0);
+ 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:
ctrl_c_handler (DWORD type)
{
static bool saw_close;
- lock_process now;
if (!cygwin_finished_initializing)
{
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);
}
}
- if (chExeced)
- {
- chExeced->set_saw_ctrl_c ();
- return TRUE;
- }
+ if (ch_spawn.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);
+ 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 (myself->ctty != -1 && t->getpgid () == myself->pid &&
+ 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
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);
bool masked;
void *handler;
- if (!hExeced || (void *) thissig.sa_handler == (void *) SIG_IGN)
+ if (!have_execed || (void *) thissig.sa_handler == (void *) SIG_IGN)
handler = (void *) thissig.sa_handler;
else if (tls)
return 1;
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
{
- insigwait_mask = !handler && (tls = _cygtls::find_tls (si.si_signo));
- if (tls)
- use_tls = tls;
+ use_tls = tls;
+ insigwait_mask = true;
}
if (insigwait_mask)
rc = setup_handler (si.si_signo, handler, thissig, use_tls);
done:
+ tls = use_tls;
if (continue_now)
SetEvent (sigCONT);
sigproc_printf ("returning %d", 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);
- si.si_signo |= 0x80;
- }
- sigproc_printf ("signal %d, about to call do_exit", si.si_signo);
use_tls->signal_exit (si.si_signo); /* never returns */
}
-/* Cover function to `do_exit' to handle exiting even in presence of more
- exceptions. We used to call exit, but a SIGSEGV shouldn't cause atexit
- routines to run. */
-void
-_cygtls::signal_exit (int rc)
-{
- if (hExeced)
- {
- sigproc_printf ("terminating captive process");
- TerminateProcess (hExeced, sigExeced = rc);
- }
-
- signal_debugger (rc & 0x7f);
- if ((rc & 0x80) && !try_to_debug ())
- stackdump (thread_context.ebp, 1, 1);
-
- lock_process until_exit (true);
- if (hExeced || exit_state > ES_PROCESS_LOCKED)
- myself.exit (rc);
-
- /* Starve other threads in a vain attempt to stop them from doing something
- stupid. */
- SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL);
-
- sigproc_printf ("about to call do_exit (%x)", rc);
- SetEvent (signal_arrived);
- do_exit (rc);
-}
-
void
events_init ()
{
mask_sync.init ("mask_sync");
- windows_system_directory[0] = L'\0';
- GetSystemDirectoryW (windows_system_directory, sizeof (windows_system_directory) / sizeof (WCHAR) - 2);
- PWCHAR end = wcschr (windows_system_directory, L'\0');
- if (end == windows_system_directory)
- api_fatal ("can't find windows system directory");
- if (end[-1] != L'\\')
- {
- *end++ = L'\\';
- *end = L'\0';
- }
- windows_system_directory_length = end - windows_system_directory;
- debug_printf ("windows_system_directory '%W', windows_system_directory_length %d",
- windows_system_directory, windows_system_directory_length);
}
void
int
_cygtls::call_signal_handler ()
{
- int this_sa_flags = 0;
- /* Call signal handler. */
- while (sig)
+ int this_sa_flags = SA_RESTART;
+ while (1)
{
lock ();
+ if (sig)
+ pop ();
+ else if (this != _main_tls)
+ {
+ _main_tls->lock ();
+ if (_main_tls->sig)
+ {
+ sig = _main_tls->sig;
+ sa_flags = _main_tls->sa_flags;
+ func = _main_tls->func;
+ infodata = _main_tls->infodata;
+ _main_tls->pop ();
+ _main_tls->sig = 0;
+
+ }
+ _main_tls->unlock ();
+ }
+ if (!sig)
+ break;
+
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) = func;
+ void (*sigfunc) (int) = thisfunc;
+ incyg = false;
sigfunc (thissig);
}
else
{
siginfo_t thissi = infodata;
- void (*sigact) (int, siginfo_t *, void *) = (void (*) (int, siginfo_t *, void *)) func;
+ void (*sigact) (int, siginfo_t *, void *) = (void (*) (int, siginfo_t *, void *)) thisfunc;
/* no ucontext_t information provided yet */
+ incyg = false;
sigact (thissig, &thissi, NULL);
}
- incyg = 1;
+ incyg = true;
set_signal_mask (this_oldmask, _my_tls.sigmask);
if (this_errno >= 0)
set_errno (this_errno);
}
+ unlock ();
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)
{