1196 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1196 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* exceptions.cc
 | 
						|
 | 
						|
   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
 | 
						|
 | 
						|
This file is part of Cygwin.
 | 
						|
 | 
						|
This software is a copyrighted work licensed under the terms of the
 | 
						|
Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 | 
						|
details. */
 | 
						|
 | 
						|
#include "winsup.h"
 | 
						|
#include <imagehlp.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <setjmp.h>
 | 
						|
#include <assert.h>
 | 
						|
 | 
						|
#include "exceptions.h"
 | 
						|
#include "sync.h"
 | 
						|
#include "pinfo.h"
 | 
						|
#include "cygtls.h"
 | 
						|
#include "sigproc.h"
 | 
						|
#include "cygerrno.h"
 | 
						|
#define NEED_VFORK
 | 
						|
#include "perthread.h"
 | 
						|
#include "shared_info.h"
 | 
						|
#include "perprocess.h"
 | 
						|
#include "security.h"
 | 
						|
#include "cygthread.h"
 | 
						|
 | 
						|
#define CALL_HANDLER_RETRY 20
 | 
						|
 | 
						|
char debugger_command[2 * CYG_MAX_PATH + 20];
 | 
						|
 | 
						|
extern "C" {
 | 
						|
static int handle_exceptions (EXCEPTION_RECORD *, void *, CONTEXT *, void *);
 | 
						|
extern void sigdelayed ();
 | 
						|
};
 | 
						|
 | 
						|
extern DWORD sigtid;
 | 
						|
 | 
						|
extern HANDLE hExeced;
 | 
						|
extern DWORD dwExeced;
 | 
						|
 | 
						|
static BOOL WINAPI ctrl_c_handler (DWORD);
 | 
						|
static void signal_exit (int) __attribute__ ((noreturn));
 | 
						|
static char windows_system_directory[1024];
 | 
						|
static size_t windows_system_directory_length;
 | 
						|
 | 
						|
/* This is set to indicate that we have already exited.  */
 | 
						|
 | 
						|
static NO_COPY int exit_already = 0;
 | 
						|
static NO_COPY muto *mask_sync = NULL;
 | 
						|
 | 
						|
NO_COPY static struct
 | 
						|
{
 | 
						|
  unsigned int code;
 | 
						|
  const char *name;
 | 
						|
} status_info[] =
 | 
						|
{
 | 
						|
#define X(s) s, #s
 | 
						|
  { X (STATUS_ABANDONED_WAIT_0) },
 | 
						|
  { X (STATUS_ACCESS_VIOLATION) },
 | 
						|
  { X (STATUS_ARRAY_BOUNDS_EXCEEDED) },
 | 
						|
  { X (STATUS_BREAKPOINT) },
 | 
						|
  { X (STATUS_CONTROL_C_EXIT) },
 | 
						|
  { X (STATUS_DATATYPE_MISALIGNMENT) },
 | 
						|
  { X (STATUS_FLOAT_DENORMAL_OPERAND) },
 | 
						|
  { X (STATUS_FLOAT_DIVIDE_BY_ZERO) },
 | 
						|
  { X (STATUS_FLOAT_INEXACT_RESULT) },
 | 
						|
  { X (STATUS_FLOAT_INVALID_OPERATION) },
 | 
						|
  { X (STATUS_FLOAT_OVERFLOW) },
 | 
						|
  { X (STATUS_FLOAT_STACK_CHECK) },
 | 
						|
  { X (STATUS_FLOAT_UNDERFLOW) },
 | 
						|
  { X (STATUS_GUARD_PAGE_VIOLATION) },
 | 
						|
  { X (STATUS_ILLEGAL_INSTRUCTION) },
 | 
						|
  { X (STATUS_INTEGER_DIVIDE_BY_ZERO) },
 | 
						|
  { X (STATUS_INTEGER_OVERFLOW) },
 | 
						|
  { X (STATUS_INVALID_DISPOSITION) },
 | 
						|
  { X (STATUS_IN_PAGE_ERROR) },
 | 
						|
  { X (STATUS_NONCONTINUABLE_EXCEPTION) },
 | 
						|
  { X (STATUS_NO_MEMORY) },
 | 
						|
  { X (STATUS_PENDING) },
 | 
						|
  { X (STATUS_PRIVILEGED_INSTRUCTION) },
 | 
						|
  { X (STATUS_SINGLE_STEP) },
 | 
						|
  { X (STATUS_STACK_OVERFLOW) },
 | 
						|
  { X (STATUS_TIMEOUT) },
 | 
						|
  { X (STATUS_USER_APC) },
 | 
						|
  { X (STATUS_WAIT_0) },
 | 
						|
  { 0, 0 }
 | 
						|
#undef X
 | 
						|
};
 | 
						|
 | 
						|
/* Initialization code.  */
 | 
						|
 | 
						|
// Set up the exception handler for the current thread.  The PowerPC & Mips
 | 
						|
// use compiler generated tables to set up the exception handlers for each
 | 
						|
// region of code, and the kernel walks the call list until it finds a region
 | 
						|
// of code that handles exceptions.  The x86 on the other hand uses segment
 | 
						|
// register fs, offset 0 to point to the current exception handler.
 | 
						|
 | 
						|
extern exception_list *_except_list asm ("%fs:0");
 | 
						|
 | 
						|
void
 | 
						|
init_exception_handler (exception_list *el, exception_handler *eh)
 | 
						|
{
 | 
						|
  el->handler = eh;
 | 
						|
  el->prev = _except_list;
 | 
						|
  _except_list = el;
 | 
						|
}
 | 
						|
 | 
						|
extern "C" void
 | 
						|
init_exceptions (exception_list *el)
 | 
						|
{
 | 
						|
  init_exception_handler (el, handle_exceptions);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
init_console_handler ()
 | 
						|
{
 | 
						|
  (void) SetConsoleCtrlHandler (ctrl_c_handler, FALSE);
 | 
						|
  if (!SetConsoleCtrlHandler (ctrl_c_handler, TRUE))
 | 
						|
    system_printf ("SetConsoleCtrlHandler failed, %E");
 | 
						|
}
 | 
						|
 | 
						|
extern "C" void
 | 
						|
error_start_init (const char *buf)
 | 
						|
{
 | 
						|
  if (!buf || !*buf)
 | 
						|
    {
 | 
						|
      debugger_command[0] = '\0';
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
  char pgm[CYG_MAX_PATH + 1];
 | 
						|
  if (!GetModuleFileName (NULL, pgm, CYG_MAX_PATH))
 | 
						|
    strcpy (pgm, "cygwin1.dll");
 | 
						|
  for (char *p = strchr (pgm, '\\'); p; p = strchr (p, '\\'))
 | 
						|
    *p = '/';
 | 
						|
 | 
						|
  __small_sprintf (debugger_command, "%s \"%s\"", buf, pgm);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
open_stackdumpfile ()
 | 
						|
{
 | 
						|
  if (myself->progname[0])
 | 
						|
    {
 | 
						|
      const char *p;
 | 
						|
      /* write to progname.stackdump if possible */
 | 
						|
      if (!myself->progname[0])
 | 
						|
	p = "unknown";
 | 
						|
      else if ((p = strrchr (myself->progname, '\\')))
 | 
						|
	p++;
 | 
						|
      else
 | 
						|
	p = myself->progname;
 | 
						|
      char corefile[strlen (p) + sizeof (".stackdump")];
 | 
						|
      __small_sprintf (corefile, "%s.stackdump", p);
 | 
						|
      HANDLE h = CreateFile (corefile, GENERIC_WRITE, 0, &sec_none_nih,
 | 
						|
			     CREATE_ALWAYS, 0, 0);
 | 
						|
      if (h != INVALID_HANDLE_VALUE)
 | 
						|
	{
 | 
						|
	  if (!myself->ppid_handle)
 | 
						|
	    system_printf ("Dumping stack trace to %s", corefile);
 | 
						|
	  else
 | 
						|
	    debug_printf ("Dumping stack trace to %s", corefile);
 | 
						|
	  SetStdHandle (STD_ERROR_HANDLE, h);
 | 
						|
	}
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* Utilities for dumping the stack, etc.  */
 | 
						|
 | 
						|
static void
 | 
						|
exception (EXCEPTION_RECORD *e,  CONTEXT *in)
 | 
						|
{
 | 
						|
  const char *exception_name = NULL;
 | 
						|
 | 
						|
  if (e)
 | 
						|
    {
 | 
						|
      for (int i = 0; status_info[i].name; i++)
 | 
						|
	{
 | 
						|
	  if (status_info[i].code == e->ExceptionCode)
 | 
						|
	    {
 | 
						|
	      exception_name = status_info[i].name;
 | 
						|
	      break;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  if (exception_name)
 | 
						|
    small_printf ("Exception: %s at eip=%08x\r\n", exception_name, in->Eip);
 | 
						|
  else
 | 
						|
    small_printf ("Exception %d at eip=%08x\r\n", e->ExceptionCode, in->Eip);
 | 
						|
  small_printf ("eax=%08x ebx=%08x ecx=%08x edx=%08x esi=%08x edi=%08x\r\n",
 | 
						|
		in->Eax, in->Ebx, in->Ecx, in->Edx, in->Esi, in->Edi);
 | 
						|
  small_printf ("ebp=%08x esp=%08x program=%s, 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
 | 
						|
}
 | 
						|
 | 
						|
/* Walk the stack by looking at successive stored 'bp' frames.
 | 
						|
   This is not foolproof. */
 | 
						|
int
 | 
						|
stack_info::walk ()
 | 
						|
{
 | 
						|
  char **ebp;
 | 
						|
  if ((ebp = (char **) next_offset ()) == NULL)
 | 
						|
    return 0;
 | 
						|
 | 
						|
  sf.AddrFrame.Offset = (DWORD) ebp;
 | 
						|
  sf.AddrPC.Offset = sf.AddrReturn.Offset;
 | 
						|
 | 
						|
  if (!sf.AddrPC.Offset)
 | 
						|
    return 0;		/* stack frames are exhausted */
 | 
						|
 | 
						|
  /* The return address always follows the stack pointer */
 | 
						|
  sf.AddrReturn.Offset = (DWORD) *++ebp;
 | 
						|
 | 
						|
  if (needargs)
 | 
						|
    /* The arguments follow the return address */
 | 
						|
    for (unsigned i = 0; i < NPARAMS; i++)
 | 
						|
      sf.Params[i] = (DWORD) *++ebp;
 | 
						|
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
stackdump (DWORD ebp, int open_file, bool isexception)
 | 
						|
{
 | 
						|
  extern unsigned long rlim_core;
 | 
						|
 | 
						|
  if (rlim_core == 0UL)
 | 
						|
    return;
 | 
						|
 | 
						|
  if (open_file)
 | 
						|
    open_stackdumpfile ();
 | 
						|
 | 
						|
  int i;
 | 
						|
 | 
						|
  thestack.init (ebp, 1, !isexception);	/* Initialize from the input CONTEXT */
 | 
						|
  small_printf ("Stack trace:\r\nFrame     Function  Args\r\n");
 | 
						|
  for (i = 0; i < 16 && thestack++; i++)
 | 
						|
    {
 | 
						|
      small_printf ("%08x  %08x ", thestack.sf.AddrFrame.Offset,
 | 
						|
		    thestack.sf.AddrPC.Offset);
 | 
						|
      for (unsigned j = 0; j < NPARAMS; j++)
 | 
						|
	small_printf ("%s%08x", j == 0 ? " (" : ", ", thestack.sf.Params[j]);
 | 
						|
      small_printf (")\r\n");
 | 
						|
    }
 | 
						|
  small_printf ("End of stack trace%s\n",
 | 
						|
	      i == 16 ? " (more stack frames may be present)" : "");
 | 
						|
}
 | 
						|
 | 
						|
/* Temporary (?) function for external callers to get a stack dump */
 | 
						|
extern "C" void
 | 
						|
cygwin_stackdump ()
 | 
						|
{
 | 
						|
  CONTEXT c;
 | 
						|
  c.ContextFlags = CONTEXT_FULL;
 | 
						|
  GetThreadContext (GetCurrentThread (), &c);
 | 
						|
  stackdump (c.Ebp, 0, 0);
 | 
						|
}
 | 
						|
 | 
						|
#define TIME_TO_WAIT_FOR_DEBUGGER 10000
 | 
						|
 | 
						|
extern "C" int
 | 
						|
try_to_debug (bool waitloop)
 | 
						|
{
 | 
						|
  debug_printf ("debugger_command '%s'", debugger_command);
 | 
						|
  if (*debugger_command == '\0' || being_debugged ())
 | 
						|
    return 0;
 | 
						|
 | 
						|
  __small_sprintf (strchr (debugger_command, '\0'), " %u", GetCurrentProcessId ());
 | 
						|
 | 
						|
  LONG prio = GetThreadPriority (GetCurrentThread ());
 | 
						|
  SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST);
 | 
						|
  PROCESS_INFORMATION pi = {NULL, 0, 0, 0};
 | 
						|
 | 
						|
  STARTUPINFO si = {0, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL};
 | 
						|
  si.lpReserved = NULL;
 | 
						|
  si.lpDesktop = NULL;
 | 
						|
  si.dwFlags = 0;
 | 
						|
  si.cb = sizeof (si);
 | 
						|
 | 
						|
  /* FIXME: need to know handles of all running threads to
 | 
						|
     suspend_all_threads_except (current_thread_id);
 | 
						|
  */
 | 
						|
 | 
						|
  /* if any of these mutexes is owned, we will fail to start any cygwin app
 | 
						|
     until trapped app exits */
 | 
						|
 | 
						|
  ReleaseMutex (tty_mutex);
 | 
						|
 | 
						|
  /* prevent recursive exception handling */
 | 
						|
  char* rawenv = GetEnvironmentStrings () ;
 | 
						|
  for (char* p = rawenv; *p != '\0'; p = strchr (p, '\0') + 1)
 | 
						|
    {
 | 
						|
      if (strncmp (p, "CYGWIN=", strlen ("CYGWIN=")) == 0)
 | 
						|
	{
 | 
						|
	  char* q = strstr (p, "error_start") ;
 | 
						|
	  /* replace 'error_start=...' with '_rror_start=...' */
 | 
						|
	  if (q)
 | 
						|
	    {
 | 
						|
	      *q = '_' ;
 | 
						|
	      SetEnvironmentVariable ("CYGWIN", p + strlen ("CYGWIN=")) ;
 | 
						|
	    }
 | 
						|
	  break ;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  console_printf ("*** starting debugger for pid %u, tid %u\n",
 | 
						|
		  cygwin_pid (GetCurrentProcessId ()), GetCurrentThreadId ());
 | 
						|
  BOOL dbg;
 | 
						|
  dbg = CreateProcess (NULL,
 | 
						|
		       debugger_command,
 | 
						|
		       NULL,
 | 
						|
		       NULL,
 | 
						|
		       FALSE,
 | 
						|
		       CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP,
 | 
						|
		       NULL,
 | 
						|
		       NULL,
 | 
						|
		       &si,
 | 
						|
		       &pi);
 | 
						|
 | 
						|
  if (!dbg)
 | 
						|
    system_printf ("Failed to start debugger: %E");
 | 
						|
  else
 | 
						|
    {
 | 
						|
      if (!waitloop)
 | 
						|
	return dbg;
 | 
						|
      SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_IDLE);
 | 
						|
      while (!being_debugged ())
 | 
						|
	Sleep (0);
 | 
						|
      Sleep (2000);
 | 
						|
    }
 | 
						|
 | 
						|
  console_printf ("*** continuing pid %u from debugger call (%d)\n",
 | 
						|
		  cygwin_pid (GetCurrentProcessId ()), dbg);
 | 
						|
 | 
						|
  SetThreadPriority (GetCurrentThread (), prio);
 | 
						|
  return dbg;
 | 
						|
}
 | 
						|
 | 
						|
/* Main exception handler. */
 | 
						|
 | 
						|
extern "C" DWORD __stdcall RtlUnwind (void *, void *, void *, DWORD);
 | 
						|
static int
 | 
						|
handle_exceptions (EXCEPTION_RECORD *e0, void *frame, CONTEXT *in0, void *)
 | 
						|
{
 | 
						|
  static bool NO_COPY debugging = false;
 | 
						|
  static int NO_COPY recursed = 0;
 | 
						|
 | 
						|
  if (debugging && ++debugging < 500000)
 | 
						|
    {
 | 
						|
      SetThreadPriority (hMainThread, THREAD_PRIORITY_NORMAL);
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
 | 
						|
  /* If we've already exited, don't do anything here.  Returning 1
 | 
						|
     tells Windows to keep looking for an exception handler.  */
 | 
						|
  if (exit_already)
 | 
						|
    return 1;
 | 
						|
 | 
						|
  EXCEPTION_RECORD e = *e0;
 | 
						|
  CONTEXT in = *in0;
 | 
						|
 | 
						|
  siginfo_t si;
 | 
						|
  /* 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_sigval.sival_int = FPE_FLTSUB;
 | 
						|
      break;
 | 
						|
    case STATUS_FLOAT_INEXACT_RESULT:
 | 
						|
      si.si_signo = SIGFPE;
 | 
						|
      si.si_sigval.sival_int = FPE_FLTRES;
 | 
						|
      break;
 | 
						|
    case STATUS_FLOAT_OVERFLOW:
 | 
						|
      si.si_signo = SIGFPE;
 | 
						|
      si.si_sigval.sival_int = FPE_FLTOVF;
 | 
						|
      break;
 | 
						|
    case STATUS_FLOAT_UNDERFLOW:
 | 
						|
      si.si_signo = SIGFPE;
 | 
						|
      si.si_sigval.sival_int = FPE_FLTUND;
 | 
						|
      break;
 | 
						|
    case STATUS_INTEGER_DIVIDE_BY_ZERO:
 | 
						|
      si.si_signo = SIGFPE;
 | 
						|
      si.si_sigval.sival_int = FPE_INTDIV;
 | 
						|
      break;
 | 
						|
    case STATUS_INTEGER_OVERFLOW:
 | 
						|
      si.si_signo = SIGFPE;
 | 
						|
      si.si_sigval.sival_int = FPE_INTOVF;
 | 
						|
      break;
 | 
						|
 | 
						|
    case STATUS_ILLEGAL_INSTRUCTION:
 | 
						|
      si.si_signo = SIGILL;
 | 
						|
      si.si_sigval.sival_int = ILL_ILLOPC;
 | 
						|
      break;
 | 
						|
 | 
						|
    case STATUS_PRIVILEGED_INSTRUCTION:
 | 
						|
      si.si_signo = SIGILL;
 | 
						|
      si.si_sigval.sival_int = ILL_PRVOPC;
 | 
						|
      break;
 | 
						|
 | 
						|
    case STATUS_NONCONTINUABLE_EXCEPTION:
 | 
						|
      si.si_signo = SIGILL;
 | 
						|
      si.si_sigval.sival_int = ILL_ILLADR;
 | 
						|
      break;
 | 
						|
 | 
						|
    case STATUS_TIMEOUT:
 | 
						|
      si.si_signo = SIGALRM;
 | 
						|
      si.si_sigval.sival_int = 0;
 | 
						|
      break;
 | 
						|
 | 
						|
    case STATUS_ACCESS_VIOLATION:
 | 
						|
    case STATUS_DATATYPE_MISALIGNMENT:
 | 
						|
    case STATUS_ARRAY_BOUNDS_EXCEEDED:
 | 
						|
    case STATUS_GUARD_PAGE_VIOLATION:
 | 
						|
    case STATUS_IN_PAGE_ERROR:
 | 
						|
    case STATUS_NO_MEMORY:
 | 
						|
    case STATUS_INVALID_DISPOSITION:
 | 
						|
    case STATUS_STACK_OVERFLOW:
 | 
						|
      si.si_signo = SIGSEGV;
 | 
						|
      si.si_sigval.sival_int = SEGV_MAPERR;
 | 
						|
      break;
 | 
						|
 | 
						|
    case STATUS_CONTROL_C_EXIT:
 | 
						|
      si.si_signo = SIGINT;
 | 
						|
      si.si_sigval.sival_int = 0;
 | 
						|
      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);
 | 
						|
 | 
						|
  if (global_sigs[si.si_signo].sa_mask & SIGTOMASK (si.si_signo))
 | 
						|
    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 (!cygwin_finished_initializing
 | 
						|
      || GetCurrentThreadId () == sigtid
 | 
						|
      || (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->ppid_handle)
 | 
						|
	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;
 | 
						|
	    }
 | 
						|
 | 
						|
	  open_stackdumpfile ();
 | 
						|
	  exception (&e, &in);
 | 
						|
	  stackdump ((DWORD) ebp, 0, 1);
 | 
						|
	}
 | 
						|
 | 
						|
      signal_exit (0x80 | si.si_signo);	// Flag signal + core dump
 | 
						|
    }
 | 
						|
 | 
						|
  extern DWORD ret_here[];
 | 
						|
  RtlUnwind (frame, ret_here, e0, 0);
 | 
						|
  __asm__ volatile (".equ _ret_here,.");
 | 
						|
 | 
						|
  si.si_addr = ebp;
 | 
						|
  si.si_code = SI_KERNEL;
 | 
						|
  si.si_errno = si.si_pid = si.si_uid = 0;
 | 
						|
  _my_tls.push ((__stack_t) ebp, true);
 | 
						|
  sig_send (NULL, si, &_my_tls);	// Signal myself
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
/* 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 = myself->getsigmask ();	// Remember for restoration
 | 
						|
 | 
						|
  set_signal_mask (tempmask, oldmask);
 | 
						|
  sigproc_printf ("oldmask %p, newmask %p", oldmask, tempmask);
 | 
						|
 | 
						|
  pthread_testcancel ();
 | 
						|
  pthread::cancelable_wait (signal_arrived, INFINITE);
 | 
						|
 | 
						|
  set_sig_errno (EINTR);	// Per POSIX
 | 
						|
 | 
						|
  /* A signal dispatch function will have been added to our stack and will
 | 
						|
     be hit eventually.  Set the old mask to be restored when the signal
 | 
						|
     handler returns 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)
 | 
						|
{
 | 
						|
  /* Silently ignore attempts to suspend if there is no accomodating
 | 
						|
     cygwin parent to deal with this behavior. */
 | 
						|
  if (!myself->ppid_handle)
 | 
						|
    {
 | 
						|
      myself->process_state &= ~PID_STOPPED;
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
  myself->stopsig = sig;
 | 
						|
  /* See if we have a living parent.  If so, send it a special signal.
 | 
						|
     It will figure out exactly which pid has stopped by scanning
 | 
						|
     its list of subprocesses.  */
 | 
						|
  if (my_parent_is_alive ())
 | 
						|
    {
 | 
						|
      pinfo parent (myself->ppid);
 | 
						|
      if (NOTSTATE (parent, PID_NOCLDSTOP))
 | 
						|
	{
 | 
						|
	  siginfo_t si;
 | 
						|
	  si.si_signo = SIGCHLD;
 | 
						|
	  si.si_code = SI_KERNEL;
 | 
						|
	  si.si_sigval.sival_int = CLD_STOPPED;
 | 
						|
	  si.si_errno = si.si_pid = si.si_uid = si.si_errno = 0;
 | 
						|
	  sig_send (parent, si);
 | 
						|
	}
 | 
						|
    }
 | 
						|
  sigproc_printf ("process %d stopped by signal %d, myself->ppid_handle %p",
 | 
						|
		  myself->pid, sig, myself->ppid_handle);
 | 
						|
  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 ();
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      api_fatal ("WaitSingleObject failed, %E");
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  return;
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
interruptible (DWORD pc)
 | 
						|
{
 | 
						|
  int res;
 | 
						|
  MEMORY_BASIC_INFORMATION m;
 | 
						|
 | 
						|
  memset (&m, 0, sizeof m);
 | 
						|
  if (!VirtualQuery ((LPCVOID) pc, &m, sizeof m))
 | 
						|
    sigproc_printf ("couldn't get memory info, pc %p, %E", pc);
 | 
						|
 | 
						|
  char *checkdir = (char *) alloca (windows_system_directory_length + 4);
 | 
						|
  memset (checkdir, 0, sizeof (checkdir));
 | 
						|
 | 
						|
# define h ((HMODULE) m.AllocationBase)
 | 
						|
  /* Apparently Windows 95 can sometimes return bogus addresses from
 | 
						|
     GetThreadContext.  These resolve to a strange allocation base.
 | 
						|
     These should *never* be treated as interruptible. */
 | 
						|
  if (!h || m.State != MEM_COMMIT)
 | 
						|
    res = false;
 | 
						|
  else if (h == user_data->hmodule)
 | 
						|
    res = true;
 | 
						|
  else if (!GetModuleFileName (h, checkdir, windows_system_directory_length + 2))
 | 
						|
    res = false;
 | 
						|
  else
 | 
						|
    res = !strncasematch (windows_system_directory, checkdir,
 | 
						|
			  windows_system_directory_length);
 | 
						|
  sigproc_printf ("pc %p, h %p, interruptible %d", pc, h, res);
 | 
						|
# undef h
 | 
						|
  return res;
 | 
						|
}
 | 
						|
void __stdcall
 | 
						|
_cygtls::interrupt_setup (int sig, void *handler, struct sigaction& siga)
 | 
						|
{
 | 
						|
  push ((__stack_t) sigdelayed, false);
 | 
						|
  deltamask = (siga.sa_mask | SIGTOMASK (sig)) & ~SIG_NONMASKABLE;
 | 
						|
  sa_flags = siga.sa_flags;
 | 
						|
  func = (void (*) (int)) handler;
 | 
						|
  saved_errno = -1;		// Flag: no errno to save
 | 
						|
  if (handler == sig_handle_tty_stop)
 | 
						|
    {
 | 
						|
      myself->stopsig = 0;
 | 
						|
      myself->process_state |= PID_STOPPED;
 | 
						|
    }
 | 
						|
 | 
						|
  this->sig = sig;			// Should always be last thing set to avoid a race
 | 
						|
 | 
						|
  /* Clear any waiting threads prior to dispatching to handler function */
 | 
						|
  int res = SetEvent (signal_arrived);	// For an EINTR case
 | 
						|
  proc_subproc (PROC_CLEARWAIT, 1);
 | 
						|
  sigproc_printf ("armed signal_arrived %p, sig %d, res %d", signal_arrived,
 | 
						|
		  sig, res);
 | 
						|
}
 | 
						|
 | 
						|
bool
 | 
						|
_cygtls::interrupt_now (CONTEXT *ctx, int sig, void *handler,
 | 
						|
			struct sigaction& siga)
 | 
						|
{
 | 
						|
  push ((__stack_t) ctx->Eip, false);
 | 
						|
  interrupt_setup (sig, handler, siga);
 | 
						|
  ctx->Eip = pop ();
 | 
						|
  SetThreadContext (*this, ctx); /* Restart the thread in a new location */
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
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 || tls->in_exception ())
 | 
						|
	{
 | 
						|
	  sigproc_printf ("controlled interrupt. incyg %d, exception %d, stackptr %p, stack %p, stackptr[-1] %p",
 | 
						|
			  tls->incyg, tls->in_exception (), tls->stackptr, tls->stack, tls->stackptr[-1]);
 | 
						|
	  tls->reset_exception ();
 | 
						|
	  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)
 | 
						|
	{
 | 
						|
	  (void) ResumeThread (hth);
 | 
						|
	  break;
 | 
						|
	}
 | 
						|
      if (tls->incyg || tls->in_exception () || tls->spinning || tls->locked ())
 | 
						|
	sigproc_printf ("incyg %d, in_exception %d, spinning %d, locked %d\n",
 | 
						|
			tls->incyg, tls->in_exception (), tls->spinning, tls->locked ());
 | 
						|
      else
 | 
						|
	{
 | 
						|
	  cx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
 | 
						|
	  if (!GetThreadContext (hth, &cx))
 | 
						|
	    system_printf ("couldn't get context of main thread, %E");
 | 
						|
	  else if (interruptible (cx.Eip))
 | 
						|
	    interrupted = tls->interrupt_now (&cx, sig, handler, siga);
 | 
						|
	}
 | 
						|
 | 
						|
      res = ResumeThread (hth);
 | 
						|
      if (interrupted)
 | 
						|
	break;
 | 
						|
 | 
						|
      sigproc_printf ("couldn't interrupt.  trying again.");
 | 
						|
      low_priority_sleep (0);
 | 
						|
    }
 | 
						|
 | 
						|
out:
 | 
						|
  if (interrupted && tls->event)
 | 
						|
    {
 | 
						|
      HANDLE h = tls->event;
 | 
						|
      tls->event = NULL;
 | 
						|
      SetEvent (h);
 | 
						|
    }
 | 
						|
  sigproc_printf ("signal %d %sdelivered", sig, interrupted ? "" : "not ");
 | 
						|
  return interrupted;
 | 
						|
}
 | 
						|
 | 
						|
/* Keyboard interrupt handler.  */
 | 
						|
static BOOL WINAPI
 | 
						|
ctrl_c_handler (DWORD type)
 | 
						|
{
 | 
						|
  static bool saw_close;
 | 
						|
 | 
						|
  if (!cygwin_finished_initializing)
 | 
						|
    {
 | 
						|
      debug_printf ("exiting with status %p", STATUS_CONTROL_C_EXIT);
 | 
						|
      ExitProcess (STATUS_CONTROL_C_EXIT);
 | 
						|
    }
 | 
						|
 | 
						|
  _my_tls.remove (INFINITE);
 | 
						|
 | 
						|
  /* Return FALSE to prevent an "End task" dialog box from appearing
 | 
						|
     for each Cygwin process window that's open when the computer
 | 
						|
     is shut down or console window is closed. */
 | 
						|
 | 
						|
  if (type == CTRL_SHUTDOWN_EVENT)
 | 
						|
    {
 | 
						|
#if 0
 | 
						|
      /* Don't send a signal.  Only NT service applications and their child
 | 
						|
	 processes will receive this event and the services typically already
 | 
						|
	 handle the shutdown action when getting the SERVICE_CONTROL_SHUTDOWN
 | 
						|
	 control message. */
 | 
						|
      sig_send (NULL, SIGTERM);
 | 
						|
#endif
 | 
						|
      return FALSE;
 | 
						|
    }
 | 
						|
 | 
						|
  if (myself->ctty != -1)
 | 
						|
    {
 | 
						|
      if (type == CTRL_CLOSE_EVENT)
 | 
						|
	{
 | 
						|
	  sig_send (NULL, SIGHUP);
 | 
						|
	  saw_close = true;
 | 
						|
	  return FALSE;
 | 
						|
	}
 | 
						|
      if (!saw_close && type == CTRL_LOGOFF_EVENT)
 | 
						|
	{
 | 
						|
	  /* Check if the process is actually associated with a visible
 | 
						|
	     window station, one which actually represents a visible desktop.
 | 
						|
	     If not, the CTRL_LOGOFF_EVENT doesn't concern this process. */
 | 
						|
	  if (has_visible_window_station ())
 | 
						|
	    sig_send (myself_nowait, SIGHUP);
 | 
						|
	  return FALSE;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
  /* If we are a stub and the new process has a pinfo structure, let it
 | 
						|
     handle this signal. */
 | 
						|
  if (dwExeced && pinfo (dwExeced))
 | 
						|
    return TRUE;
 | 
						|
 | 
						|
  /* We're only the process group leader when we have a valid pinfo structure.
 | 
						|
     If we don't have one, then the parent "stub" will handle the signal. */
 | 
						|
  if (!pinfo (cygwin_pid (GetCurrentProcessId ())))
 | 
						|
    return TRUE;
 | 
						|
 | 
						|
  tty_min *t = cygwin_shared->tty.get_tty (myself->ctty);
 | 
						|
  /* Ignore this if we're not the process group leader since it should be handled
 | 
						|
     *by* the process group leader. */
 | 
						|
  if (myself->ctty != -1 && t->getpgid () == myself->pid &&
 | 
						|
       (GetTickCount () - t->last_ctrl_c) >= MIN_CTRL_C_SLOP)
 | 
						|
    /* Otherwise we just send a SIGINT to the process group and return TRUE (to indicate
 | 
						|
       that we have handled the signal).  At this point, type should be
 | 
						|
       a CTRL_C_EVENT or CTRL_BREAK_EVENT. */
 | 
						|
    {
 | 
						|
      t->last_ctrl_c = GetTickCount ();
 | 
						|
      killsys (-myself->pid, SIGINT);
 | 
						|
      t->last_ctrl_c = GetTickCount ();
 | 
						|
      return TRUE;
 | 
						|
    }
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
/* Function used by low level sig wrappers. */
 | 
						|
extern "C" void __stdcall
 | 
						|
set_process_mask (sigset_t newmask)
 | 
						|
{
 | 
						|
  set_signal_mask (newmask);
 | 
						|
}
 | 
						|
 | 
						|
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 = myself->getsigmask ();
 | 
						|
  sigaddset (&mask, sig);
 | 
						|
  set_signal_mask (mask);
 | 
						|
  mask_sync->release ();
 | 
						|
  return 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 = myself->getsigmask ();
 | 
						|
  newmask = (oldmask | _my_tls.deltamask) & ~SIG_NONMASKABLE;
 | 
						|
  sigproc_printf ("oldmask %p, newmask %p, deltamask %p", oldmask, newmask,
 | 
						|
		  _my_tls.deltamask);
 | 
						|
  myself->setsigmask (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)
 | 
						|
{
 | 
						|
  mask_sync->acquire (INFINITE);
 | 
						|
  newmask &= ~SIG_NONMASKABLE;
 | 
						|
  sigset_t mask_bits = oldmask & ~newmask;
 | 
						|
  sigproc_printf ("oldmask %p, newmask %p, mask_bits %p", oldmask, newmask,
 | 
						|
		  mask_bits);
 | 
						|
  myself->setsigmask (newmask);	// Set a new mask
 | 
						|
  if (mask_bits)
 | 
						|
    sig_dispatch_pending (true);
 | 
						|
  else
 | 
						|
    sigproc_printf ("not calling sig_dispatch_pending");
 | 
						|
  mask_sync->release ();
 | 
						|
  return;
 | 
						|
}
 | 
						|
 | 
						|
int __stdcall
 | 
						|
sigpacket::process ()
 | 
						|
{
 | 
						|
  DWORD continue_now;
 | 
						|
  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++;
 | 
						|
 | 
						|
  if (si.si_signo == SIGKILL)
 | 
						|
    goto exit_sig;
 | 
						|
  if ( si.si_signo == SIGSTOP)
 | 
						|
    {
 | 
						|
      sig_clear (SIGCONT);
 | 
						|
      if (!tls)
 | 
						|
	tls = _main_tls;
 | 
						|
      goto stop;
 | 
						|
    }
 | 
						|
 | 
						|
  bool masked;
 | 
						|
  bool special_case;
 | 
						|
  bool insigwait_mask;
 | 
						|
  insigwait_mask = masked = false;
 | 
						|
  if (special_case = (VFORKPID || ISSTATE (myself, PID_STOPPED)))
 | 
						|
    /* nothing to do */;
 | 
						|
  else if (tls && sigismember (&tls->sigwait_mask, si.si_signo))
 | 
						|
    insigwait_mask = true;
 | 
						|
  else if (!tls && (tls = _cygtls::find_tls (si.si_signo)))
 | 
						|
    insigwait_mask = true;
 | 
						|
  else if (!(masked = sigismember (mask, si.si_signo)) && tls)
 | 
						|
    masked  = sigismember (&tls->sigmask, si.si_signo);
 | 
						|
 | 
						|
  if (insigwait_mask)
 | 
						|
    goto thread_specific;
 | 
						|
 | 
						|
  if (!tls)
 | 
						|
    tls = _main_tls;
 | 
						|
 | 
						|
  if (special_case || masked)
 | 
						|
    {
 | 
						|
      sigproc_printf ("signal %d blocked", si.si_signo);
 | 
						|
      rc = -1;
 | 
						|
      goto done;
 | 
						|
    }
 | 
						|
 | 
						|
  void *handler;
 | 
						|
  handler = (void *) thissig.sa_handler;
 | 
						|
 | 
						|
  /* Clear pending SIGCONT on stop signals */
 | 
						|
  if (si.si_signo == SIGTSTP || si.si_signo == SIGTTIN || si.si_signo == SIGTTOU)
 | 
						|
    sig_clear (SIGCONT);
 | 
						|
 | 
						|
#if 0
 | 
						|
  char sigmsg[24];
 | 
						|
  __small_sprintf (sigmsg, "cygwin: signal %d\n", 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 = global_sigs[SIGSTOP];
 | 
						|
 | 
						|
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);
 | 
						|
      if (!try_to_debug ())
 | 
						|
	stackdump (c.Ebp, 1, 1);
 | 
						|
      si.si_signo |= 0x80;
 | 
						|
    }
 | 
						|
  sigproc_printf ("signal %d, about to call do_exit", si.si_signo);
 | 
						|
  signal_exit (si.si_signo);
 | 
						|
  /* Never returns */
 | 
						|
}
 | 
						|
 | 
						|
CRITICAL_SECTION NO_COPY exit_lock;
 | 
						|
 | 
						|
/* Cover function to `do_exit' to handle exiting even in presence of more
 | 
						|
   exceptions.  We used to call exit, but a SIGSEGV shouldn't cause atexit
 | 
						|
   routines to run.  */
 | 
						|
static void
 | 
						|
signal_exit (int rc)
 | 
						|
{
 | 
						|
  EnterCriticalSection (&exit_lock);
 | 
						|
  rc = EXIT_SIGNAL | (rc << 8);
 | 
						|
  if (exit_already++)
 | 
						|
    myself->exit (rc);
 | 
						|
 | 
						|
  /* We'd like to stop the main thread from executing but when we do that it
 | 
						|
     causes random, inexplicable hangs.  So, instead, we set up the priority
 | 
						|
     of this thread really high so that it should do its thing and then exit. */
 | 
						|
  (void) SetThreadPriority (hMainThread, THREAD_PRIORITY_IDLE);
 | 
						|
  (void) SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_TIME_CRITICAL);
 | 
						|
 | 
						|
  user_data->resourcelocks->Delete ();
 | 
						|
  user_data->resourcelocks->Init ();
 | 
						|
 | 
						|
  if (hExeced)
 | 
						|
    {
 | 
						|
      sigproc_printf ("terminating captive process");
 | 
						|
      TerminateProcess (hExeced, rc);
 | 
						|
    }
 | 
						|
 | 
						|
  sigproc_printf ("about to call do_exit (%x)", rc);
 | 
						|
  (void) SetEvent (signal_arrived);
 | 
						|
  do_exit (rc);
 | 
						|
}
 | 
						|
 | 
						|
HANDLE NO_COPY tty_mutex = NULL;
 | 
						|
 | 
						|
void
 | 
						|
events_init (void)
 | 
						|
{
 | 
						|
  char *name;
 | 
						|
  char mutex_name[CYG_MAX_PATH];
 | 
						|
  /* tty_mutex is on while searching for a tty slot. It's necessary
 | 
						|
     while finding console window handle */
 | 
						|
 | 
						|
  if (!(tty_mutex = CreateMutex (&sec_all_nih, FALSE,
 | 
						|
				   name = shared_name (mutex_name,
 | 
						|
						       "tty_mutex", 0))))
 | 
						|
    api_fatal ("can't create title mutex '%s', %E", name);
 | 
						|
 | 
						|
  ProtectHandle (tty_mutex);
 | 
						|
  new_muto (mask_sync);
 | 
						|
  windows_system_directory[0] = '\0';
 | 
						|
  (void) GetSystemDirectory (windows_system_directory, sizeof (windows_system_directory) - 2);
 | 
						|
  char *end = strchr (windows_system_directory, '\0');
 | 
						|
  if (end == windows_system_directory)
 | 
						|
    api_fatal ("can't find windows system directory");
 | 
						|
  if (end[-1] != '\\')
 | 
						|
    {
 | 
						|
      *end++ = '\\';
 | 
						|
      *end = '\0';
 | 
						|
    }
 | 
						|
  windows_system_directory_length = end - windows_system_directory;
 | 
						|
  debug_printf ("windows_system_directory '%s', windows_system_directory_length %d",
 | 
						|
		windows_system_directory, windows_system_directory_length);
 | 
						|
  InitializeCriticalSection (&exit_lock);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
events_terminate (void)
 | 
						|
{
 | 
						|
  exit_already = 1;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
_cygtls::call_signal_handler ()
 | 
						|
{
 | 
						|
  int this_sa_flags = 0;
 | 
						|
  /* Call signal handler.  */
 | 
						|
  while (sig)
 | 
						|
    {
 | 
						|
      lock (); unlock ();	// make sure synchronized
 | 
						|
      this_sa_flags = sa_flags;
 | 
						|
      int thissig = sig;
 | 
						|
      void (*sigfunc) (int) = func;
 | 
						|
 | 
						|
      (void) pop ();
 | 
						|
      reset_signal_arrived ();
 | 
						|
      sigset_t this_oldmask = set_process_mask_delta ();
 | 
						|
      int this_errno = saved_errno;
 | 
						|
      incyg--;
 | 
						|
      sig = 0;
 | 
						|
      sigfunc (thissig);
 | 
						|
      incyg++;
 | 
						|
      set_signal_mask (this_oldmask);
 | 
						|
      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);
 | 
						|
  (void) ResetEvent (signal_arrived);
 | 
						|
  sigproc_printf ("reset signal_arrived");
 | 
						|
  if (_my_tls.stackptr > _my_tls.stack)
 | 
						|
    debug_printf ("stackptr[-1] %p", _my_tls.stackptr[-1]);
 | 
						|
}
 |