/* exceptions.cc

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 <imagehlp.h>
#include <stdlib.h>
#include <stdarg.h>
#include <syslog.h>
#include <wchar.h>

#include "cygtls.h"
#include "pinfo.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"
#include "posix_timer.h"
#include "gcc_seh.h"

/* Define macros for CPU-agnostic register access.  The _CX_foo
   macros are for access into CONTEXT, the _MC_foo ones for access into
   mcontext. The idea is to access the registers in terms of their job,
   not in terms of their name on the given target. */
#ifdef __x86_64__
#define _CX_instPtr	Rip
#define _CX_stackPtr	Rsp
#define _CX_framePtr	Rbp
/* For special register access inside mcontext. */
#define _MC_retReg	rax
#define _MC_instPtr	rip
#define _MC_stackPtr	rsp
#define _MC_uclinkReg	rbx	/* MUST be callee-saved reg */
#else
#error unimplemented for this target
#endif

#define CALL_HANDLER_RETRY_OUTER 10
#define CALL_HANDLER_RETRY_INNER 10
#define DUMPSTACK_FRAME_LIMIT    32

PWCHAR debugger_command;
extern uint8_t _sigbe;
extern uint8_t _sigdelayed_end;

static BOOL ctrl_c_handler (DWORD);

static const struct
{
  NTSTATUS 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)
    return;
  if (!debugger_command &&
      !(debugger_command = (PWCHAR) malloc ((2 * NT_MAX_PATH + 20)
					    * sizeof (WCHAR))))
    return;

  PWCHAR cp = debugger_command
	      + sys_mbstowcs (debugger_command, NT_MAX_PATH, buf) - 1;
  cp = wcpcpy (cp, L" \"");
  wcpcpy (cp, global_progname);
  for (PWCHAR p = wcschr (cp, L'\\'); p; p = wcschr (p, L'\\'))
    *p = L'/';
  wcscat (cp, L"\"");
}

void
cygwin_exception::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);
      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.  */

void
cygwin_exception::dump_exception ()
{
  const char *exception_name = NULL;

  for (int i = 0; status_info[i].name; i++)
    {
      if (status_info[i].code == (NTSTATUS) e->ExceptionCode)
	{
	  exception_name = status_info[i].name;
	  break;
	}
    }

#ifdef __x86_64__
  if (exception_name)
    small_printf ("Exception: %s at rip=%012X\r\n", exception_name, ctx->Rip);
  else
    small_printf ("Signal %d at rip=%012X\r\n", e->ExceptionCode, ctx->Rip);
  small_printf ("rax=%016X rbx=%016X rcx=%016X\r\n",
		ctx->Rax, ctx->Rbx, ctx->Rcx);
  small_printf ("rdx=%016X rsi=%016X rdi=%016X\r\n",
		ctx->Rdx, ctx->Rsi, ctx->Rdi);
  small_printf ("r8 =%016X r9 =%016X r10=%016X\r\n",
		ctx->R8, ctx->R9, ctx->R10);
  small_printf ("r11=%016X r12=%016X r13=%016X\r\n",
		ctx->R11, ctx->R12, ctx->R13);
  small_printf ("r14=%016X r15=%016X\r\n", ctx->R14, ctx->R15);
  small_printf ("rbp=%016X rsp=%016X\r\n", ctx->Rbp, ctx->Rsp);
  small_printf ("program=%W, pid %u, thread %s\r\n",
		myself->progname, myself->pid, mythreadname ());
  small_printf ("cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x\r\n",
		ctx->SegCs, ctx->SegDs, ctx->SegEs, ctx->SegFs,
		ctx->SegGs, ctx->SegSs);
#else
#error unimplemented for this target
#endif
}

/* 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;
  PUINT_PTR dummy_frame;
  CONTEXT c;
  UNWIND_HISTORY_TABLE hist;
  __tlsstack_t *sigstackptr;
public:
  STACKFRAME sf;		 /* For storing the stack information */
  void init (PUINT_PTR, bool, PCONTEXT); /* 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 (PUINT_PTR framep, bool wantargs, PCONTEXT ctx)
{
  memset (&hist, 0, sizeof hist);
  if (ctx)
    memcpy (&c, ctx, sizeof c);
  else
    {
      memset (&c, 0, sizeof c);
      c.ContextFlags = CONTEXT_ALL;
    }
  sigstackptr = _my_tls.stackptr;
  memset (&sf, 0, sizeof (sf));
  if (ctx)
    sf.AddrFrame.Offset = (UINT_PTR) framep;
  else
    {
      dummy_frame = framep;
      sf.AddrFrame.Offset = (UINT_PTR) &dummy_frame;
    }
  if (framep)
    sf.AddrReturn.Offset = framep[1];
  sf.AddrFrame.Mode = AddrModeFlat;
  needargs = wantargs;
}

extern "C" void _cygwin_exit_return ();

static inline void
__unwind_single_frame (PCONTEXT ctx)
{
  PRUNTIME_FUNCTION f;
  ULONG64 imagebase;
  UNWIND_HISTORY_TABLE hist = {0};
  DWORD64 establisher;
  PVOID hdl;

  f = RtlLookupFunctionEntry (ctx->_CX_instPtr, &imagebase, &hist);
  if (f)
    RtlVirtualUnwind (0, imagebase, ctx->_CX_instPtr, f, ctx, &hdl,
		      &establisher, NULL);
  else
    {
      ctx->_CX_instPtr = *(ULONG_PTR *) ctx->_CX_stackPtr;
      ctx->_CX_stackPtr += 8;
    }
}

/* Walk the stack.

   On 32 bit we're doing this by looking at successive stored 'ebp' frames.
   This is not foolproof. */
int
stack_info::walk ()
{
  if (!c._CX_instPtr)
    return 0;

  sf.AddrPC.Offset = c._CX_instPtr;
  sf.AddrStack.Offset = c._CX_stackPtr;
  sf.AddrFrame.Offset = c._CX_framePtr;

  if ((c._CX_instPtr >= (DWORD64)&_sigbe)
      && (c._CX_instPtr < (DWORD64)&_sigdelayed_end))
    {
      /* _sigbe and sigdelayed don't have SEH unwinding data, so virtually
         unwind the tls sigstack */
      c._CX_instPtr = sigstackptr[-1];
      sigstackptr--;
      return 1;
    }
  __unwind_single_frame (&c);
  if (needargs && c._CX_instPtr)
    {
      PULONG_PTR p = (PULONG_PTR) c._CX_stackPtr;
      for (unsigned i = 0; i < NPARAMS; ++i)
	sf.Params[i] = p[i + 1];
    }
  return 1;
}

/*
  Walk the list of modules in the current process to find the one containing
   'func_va'.

   This implementation requires no allocation of memory and minimal system
   calls, so it should be safe in the context of an exception handler.
*/
static char *
prettyprint_va (PVOID func_va)
{
  static char buf[256];
  buf[0] = '\0';

  PLIST_ENTRY head = &NtCurrentTeb()->Peb->Ldr->InMemoryOrderModuleList;
  for (PLIST_ENTRY x = head->Flink; x != head; x = x->Flink)
    {
      PLDR_DATA_TABLE_ENTRY mod = CONTAINING_RECORD (x, LDR_DATA_TABLE_ENTRY,
						     InMemoryOrderLinks);
      if ((func_va < mod->DllBase) ||
	  (func_va > (PVOID)((DWORD_PTR)mod->DllBase + mod->SizeOfImage)))
	continue;

      __small_sprintf (buf, "%S+0x%x", &mod->BaseDllName,
		       (DWORD_PTR)func_va - (DWORD_PTR)mod->DllBase);
      break;
    }

  return buf;
}

void
cygwin_exception::dumpstack ()
{
  static bool already_dumped;

  __try
    {
      if (already_dumped || cygheap->rlim_core == 0Ul)
	return;
      already_dumped = true;
      open_stackdumpfile ();

      if (e)
	dump_exception ();

      int i;

      thestack.init (framep, 1, ctx);	/* Initialize from the input CONTEXT */
      small_printf ("Stack trace:\r\nFrame         Function      Args\r\n");
      for (i = 0; i < DUMPSTACK_FRAME_LIMIT && thestack++; i++)
	{
	  small_printf ("%012X  %012X", thestack.sf.AddrFrame.Offset,
			thestack.sf.AddrPC.Offset);
	  for (unsigned j = 0; j < NPARAMS; j++)
	    small_printf ("%s%012X", j == 0 ? " (" : ", ",
			  thestack.sf.Params[j]);
	  small_printf (") %s\r\n", prettyprint_va((PVOID)thestack.sf.AddrPC.Offset));
	}
      small_printf ("End of stack trace%s\r\n",
		    i == DUMPSTACK_FRAME_LIMIT ?
		    " (more stack frames may be present)" : "");

      small_printf ("Loaded modules:\r\n");
      PLIST_ENTRY head = &NtCurrentTeb()->Peb->Ldr->InMemoryOrderModuleList;
      for (PLIST_ENTRY x = head->Flink; x != head; x = x->Flink)
	{
	  PLDR_DATA_TABLE_ENTRY mod = CONTAINING_RECORD (x, LDR_DATA_TABLE_ENTRY,
							 InMemoryOrderLinks);
	  small_printf ("%012X %S\r\n", mod->DllBase, &mod->BaseDllName);
	}

      if (h)
	NtClose (h);
    }
  __except (NO_ERROR) {}
  __endtry
}

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->_CX_instPtr, &m, sizeof m))
    sigproc_printf ("couldn't get memory info, pc %p, %E", cx->_CX_instPtr);

  size_t size = (windows_system_directory_length + 6) * sizeof (WCHAR);
  PWCHAR checkdir = (PWCHAR) alloca (size);
  memset (checkdir, 0, size);

# define h ((HMODULE) m.AllocationBase)
  if (!h || m.State != MEM_COMMIT)	/* Be defensive */
    res = true;
  else if (h == hntdll)
    res = true;				/* Calling GetModuleFilename on ntdll.dll
					   can hang */
  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) == 0;
    }
  sigproc_printf ("pc %p, h %p, inside_kernel %d", cx->_CX_instPtr, 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;
  RtlCaptureContext (&c);
  cygwin_exception exc ((PUINT_PTR) c._CX_framePtr, &c);
  exc.dumpstack ();
}

extern "C" int
try_to_debug ()
{
  if (!debugger_command)
    return 0;
  debug_printf ("debugger_command '%W'", debugger_command);
  if (being_debugged ())
    {
      extern void break_here ();
      break_here ();
      return 0;
    }

  PWCHAR dbg_end = wcschr (debugger_command, L'\0');
  __small_swprintf (dbg_end, L" %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;
	}
    }
  FreeEnvironmentStringsW (rawenv);

  console_printf ("*** starting debugger for pid %u, tid %u\n",
		  cygwin_pid (GetCurrentProcessId ()), GetCurrentThreadId ());
  BOOL dbg;
  dbg = CreateProcessW (NULL,
			debugger_command,
			NULL,
			NULL,
			FALSE,
			CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP,
			NULL,
			NULL,
			&si,
			&pi);

  /* We want to stop here and wait until the error_start process attaches.  But
     we can't wait here for the error_start process to exit, as if it's a
     debugger, it might want to continue this thread.  So we busy wait until a
     debugger attaches, which stops this process, after which it can decide if
     we continue or not. */

  *dbg_end = L'\0';
  if (!dbg)
    system_printf ("Failed to start debugger, %E");
  else
    {
      SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_IDLE);
      while (!being_debugged ())
	Sleep (1);
      Sleep (2000);
    }

  console_printf ("*** continuing pid %u from debugger call (%d)\n",
		  cygwin_pid (GetCurrentProcessId ()), dbg);

  SetThreadPriority (GetCurrentThread (), prio);
  return dbg;
}

/* myfault exception handler. */
EXCEPTION_DISPOSITION
exception::myfault (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in,
		    PDISPATCHER_CONTEXT dispatch)
{
  PSCOPE_TABLE table = (PSCOPE_TABLE) dispatch->HandlerData;
  RtlUnwindEx (frame,
	       (char *) dispatch->ImageBase + table->ScopeRecord[0].JumpTarget,
	       e, 0, in, dispatch->HistoryTable);
  /* NOTREACHED, make gcc happy. */
  return ExceptionContinueSearch;
}

/* If another exception occurs while running a signal handler on an alternate
   signal stack, the normal SEH handlers are skipped, because the OS exception
   handling considers the current (alternate) stack "broken".  However, it
   still calls vectored exception handlers.

   TODO: What we do here is to handle only __try/__except blocks in Cygwin.
         "Normal" exceptions will simply exit the process.  Still, better
	 than nothing... */
LONG
myfault_altstack_handler (EXCEPTION_POINTERS *exc)
{
  _cygtls& me = _my_tls;

  if (me.andreas)
    {
      CONTEXT *c = exc->ContextRecord;

      /* Unwind the stack manually and call RtlRestoreContext.  This
	 is necessary because RtlUnwindEx checks the stack for validity,
	 which, as outlined above, fails for the alternate stack. */
      while (c->_CX_stackPtr < me.andreas->frame)
	__unwind_single_frame (c);
      c->_CX_instPtr = me.andreas->ret;
      RtlRestoreContext (c, NULL);
    }
  return EXCEPTION_CONTINUE_SEARCH;
}

/* Main exception handler. */
EXCEPTION_DISPOSITION
exception::handle (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in,
		   PDISPATCHER_CONTEXT dispatch)
{
  static int NO_COPY debugging = 0;
  _cygtls& me = _my_tls;

  if (debugging && ++debugging < 500000)
    {
      SetThreadPriority (hMainThread, THREAD_PRIORITY_NORMAL);
      return ExceptionContinueExecution;
    }

  /* If we're exiting, tell Windows to keep looking for an
     exception handler.  */
  if (exit_state || e->ExceptionFlags)
    return ExceptionContinueSearch;

  siginfo_t si = {};
  si.si_code = SI_KERNEL;
  /* Coerce win32 value to posix value.  */
  switch (e->ExceptionCode)
    {
    case STATUS_FLOAT_DIVIDE_BY_ZERO:
      si.si_signo = SIGFPE;
      si.si_code = FPE_FLTDIV;
      break;
    case STATUS_FLOAT_DENORMAL_OPERAND:
    case STATUS_FLOAT_INVALID_OPERATION:
      si.si_signo = SIGFPE;
      si.si_code = FPE_FLTINV;
      break;
    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 ExceptionContinueExecution;
	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_STACK_OVERFLOW:
      /* If we encounter a stack overflow, and if the thread has no alternate
         stack, don't even try to call a signal handler.  This is in line with
	 Linux behaviour and also makes a lot of sense on Windows. */
      if (me.altstack.ss_flags)
	global_sigs[SIGSEGV].sa_handler = SIG_DFL;
      fallthrough;
    case STATUS_ARRAY_BOUNDS_EXCEEDED:
    case STATUS_IN_PAGE_ERROR:
    case STATUS_NO_MEMORY:
    case STATUS_INVALID_DISPOSITION:
      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 ExceptionContinueExecution;

    case STATUS_GCC_THROW:
    case STATUS_GCC_UNWIND:
    case STATUS_GCC_FORCED:
      /* According to a comment in the GCC function
	 _Unwind_RaiseException(), GCC expects us to continue all the
	 (continuable) GCC exceptions that reach us. */
      return ExceptionContinueExecution;

    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 ExceptionContinueSearch;
    }

  debug_printf ("In cygwin_except_handler exception %y at %p sp %p",
		e->ExceptionCode, in->_CX_instPtr, in->_CX_stackPtr);
  debug_printf ("In cygwin_except_handler signal %d at %p",
		si.si_signo, in->_CX_instPtr);

  PUINT_PTR framep = (PUINT_PTR) in->_CX_framePtr;
  /* Sometimes, when a stack is screwed up, the frame pointer tends to be NULL.
     In that case, base the stacktrace on the stack pointer.  In most cases,
     it allows to generate useful stack trace. */
  if (!framep)
    framep = (PUINT_PTR) in->_CX_stackPtr;

  if (exit_state >= ES_SIGNAL_EXIT
      && (NTSTATUS) e->ExceptionCode != STATUS_CONTROL_C_EXIT)
    api_fatal ("Exception during process exit");
  else if (try_to_debug ())
    {
      debugging = 1;
      return ExceptionContinueExecution;
    }

  cygwin_exception exc (framep, in, e);
  si.si_cyg = (void *) &exc;
  /* POSIX requires that for SIGSEGV and SIGBUS, si_addr should be set to the
     address of faulting memory reference.  For SIGILL and SIGFPE these should
     be the address of the faulting instruction.  Other signals are apparently
     undefined so we just set those to the faulting instruction too.  */
  si.si_addr = (si.si_signo == SIGSEGV || si.si_signo == SIGBUS)
	       ? (void *) e->ExceptionInformation[1] : (void *) in->_CX_instPtr;
  me.incyg++;
  sig_send (NULL, si, &me);	/* Signal myself */
  if ((NTSTATUS) e->ExceptionCode == STATUS_STACK_OVERFLOW)
    {
      /* If we catched a stack overflow, and if the signal handler didn't exit
	 or longjmp, we're back here and about to continue, supposed to run the
	 offending instruction again.  That works on Linux, but not on Windows.
	 In case of a stack overflow we're not immediately returning to the
	 system exception handler, but to NTDLL::__stkchk.  __stkchk will then
	 terminate the applicaton.  So what we do here is to signal our current
	 process again, but this time with SIG_DFL action.  This creates a
	 stackdump and then exits through our own means. */
      global_sigs[SIGSEGV].sa_handler = SIG_DFL;
      sig_send (NULL, si, &me);
    }
  me.incyg--;
  e->ExceptionFlags = 0;
  return ExceptionContinueExecution;
}

/* 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
handle_sigsuspend (sigset_t tempmask)
{
  sigset_t oldmask = _my_tls.sigmask;	// Remember for restoration

  set_signal_mask (_my_tls.sigmask, tempmask);
  sigproc_printf ("oldmask %ly, newmask %ly", oldmask, tempmask);

  pthread_testcancel ();
  cygwait (NULL, cw_infinite, cw_cancel | cw_cancel_self | cw_sig_eintr);

  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, siginfo_t *, void *)
{
  /* 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;
  else
    {
      _my_tls.incyg = 1;
      myself->stopsig = sig;
      myself->alert_parent (sig);
      sigproc_printf ("process %d stopped by signal %d", myself->pid, sig);
      /* FIXME! This does nothing to suspend anything other than the main
	 thread. */
      /* Use special cygwait parameter to handle SIGCONT.  _main_tls.sig will
	 be cleared under lock when SIGCONT is detected.  */
      pthread::suspend_all_except_self ();
      DWORD res = cygwait (NULL, cw_infinite, cw_sig_cont);
      pthread::resume_all ();
      switch (res)
	{
	case WAIT_SIGNALED:
	  myself->stopsig = SIGCONT;
	  myself->alert_parent (SIGCONT);
	  break;
	default:
	  api_fatal ("WaitSingleObject returned %d", res);
	  break;
	}
      _my_tls.incyg = 0;
    }
}
} /* end extern "C" */

bool
_cygtls::interrupt_now (CONTEXT *cx, siginfo_t& si, 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
    {
      DWORD64 &ip = cx->_CX_instPtr;
      push (ip);
      interrupt_setup (si, handler, siga);
      ip = pop ();
      SetThreadContext (*this, cx); /* Restart the thread in a new location */
      interrupted = true;
    }
  return interrupted;
}

void
_cygtls::interrupt_setup (siginfo_t& si, void *handler, struct sigaction& siga)
{
  push ((__tlsstack_t) sigdelayed);
  deltamask = siga.sa_mask & ~SIG_NONMASKABLE;
  sa_flags = siga.sa_flags;
  func = (void (*) (int, siginfo_t *, void *)) 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;
    }

  infodata = si;
  this->sig = si.si_signo; /* Should always be last thing set to avoid race */

  if (incyg)
    set_signal_arrived ();

  if (!have_execed && !(myself->exec_sendsig && !ch_spawn.iscygwin ()))
    proc_subproc (PROC_CLEARWAIT, 1);
  sigproc_printf ("armed signal_arrived %p, signal %d",
		  signal_arrived, si.si_signo);
}

extern "C" void
set_sig_errno (int e)
{
  *_my_tls.errno_addr = e;
  _my_tls.saved_errno = e;
}

int
sigpacket::setup_handler (void *handler, struct sigaction& siga, _cygtls *tls)
{
  CONTEXT cx;
  bool interrupted = false;

  if (tls->sig)
    {
      sigproc_printf ("trying to send signal %d but signal %d already armed",
		      si.si_signo, 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 (si, handler, siga);
	      interrupted = true;
	      tls->unlock ();
	      goto out;
	    }

	  DWORD res;
	  HANDLE hth = (HANDLE) *tls;
	  if (!hth)
	    {
	      tls->unlock ();
	      sigproc_printf ("thread handle NULL, not set up yet?");
	    }
	  else
	    {
	      /* 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, tls %p, _main_tls %p",
			      tls, _main_tls);
	      res = SuspendThread (hth);
	      /* Just set pending if thread is already suspended */
	      if (res)
		{
		  tls->unlock ();
		  ResumeThread (hth);
		  goto out;
		}
	      cx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
	      if (!GetThreadContext (hth, &cx))
		sigproc_printf ("couldn't get context of thread, %E");
	      else
		interrupted = tls->interrupt_now (&cx, si, handler, siga);

	      tls->unlock ();
	      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", si.si_signo,
		  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
ctrl_c_handler (DWORD type)
{
  static bool saw_close;

  /* Remove early or we could overthrow the threadlist in cygheap.
     Deleting this line causes ash to SEGV if CTRL-C is hit repeatedly.
     I am not exactly sure why that is.  Maybe it's just because this
     adds some early serialization to ctrl_c_handler which prevents
     multiple simultaneous calls? */
  _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 != CTTY_UNINITIALIZED)
    {
      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, SIGHUP);
	      return TRUE;
	    }
	  return FALSE;
	}
    }

  if (ch_spawn.set_saw_ctrl_c ())
    {
      if (myself->process_state & PID_NOTCYGWIN)
	sigExeced = SIGINT;
      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;

  if (type == CTRL_C_EVENT && ::cygheap->ctty
      && !cygheap->ctty->need_console_handler ())
    /* Ctrl-C is handled in fhandler_console::cons_master_thread(). */
    return TRUE;

  tty_min *t = cygwin_shared->tty.get_cttyp ();

  if (!t)
    return TRUE;

  /* If process group leader is non-cygwin process or not exist,
     send signal to myself. */
  pinfo pi (t->getpgid ());
  if ((!pi || (pi->process_state & PID_NOTCYGWIN))
      && (!have_execed || have_execed_cygwin)
      && t->getpgid () == myself->pgid
      && type == CTRL_C_EVENT)
    {
      t->output_stopped = false;
      sig_send(myself, SIGINT);
    }

  /* Ignore this if we're not the process group leader since it should be
     handled *by* the process group leader. */
  if (t && (!have_execed || have_execed_cygwin)
      && t->getpgid () == myself->pid &&
      (GetTickCount64 () - 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 = GetTickCount64 ();
      t->kill_pgrp (sig);
      t->output_stopped = false;
      t->last_ctrl_c = GetTickCount64 ();
      return TRUE;
    }

  return TRUE;
}

/* Function used by low level sig wrappers. */
extern "C" void
set_process_mask (sigset_t newmask)
{
  set_signal_mask (_my_tls.sigmask, 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;
    }
  sigset_t mask = _my_tls.sigmask;
  sigaddset (&mask, sig);
  set_signal_mask (_my_tls.sigmask, mask);
  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;
    }
  sigset_t mask = _my_tls.sigmask;
  sigdelset (&mask, sig);
  set_signal_mask (_my_tls.sigmask, mask);
  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;
    }

  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 (_my_tls.sigmask, mask);
  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 call_signal_handler */
extern "C" sigset_t
set_process_mask_delta ()
{
  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 %lx, newmask %lx, deltamask %lx", oldmask, newmask,
		  _my_tls.deltamask);
  _my_tls.sigmask = newmask;
  return oldmask;
}

/* Set the signal mask for this process.
   Note that some signals are unmaskable, as in UNIX.  */

void
set_signal_mask (sigset_t& setmask, sigset_t newmask)
{
  newmask &= ~SIG_NONMASKABLE;
  sigset_t mask_bits = setmask & ~newmask;
  sigproc_printf ("setmask %lx, newmask %lx, mask_bits %lx", setmask, newmask,
		  mask_bits);
  setmask = newmask;
  if (mask_bits)
    sig_dispatch_pending (true);
}


DWORD
dumpstack_overflow_wrapper (PVOID arg)
{
  cygwin_exception *exc = (cygwin_exception *) arg;
  SetThreadName (GetCurrentThreadId (), "__dumpstack_overflow");
  exc->dumpstack ();
  return 0;
}

/* Exit due to a signal.  Should only be called from the signal thread.  */
extern "C" {
static void
signal_exit (int sig, siginfo_t *si, void *)
{
  debug_printf ("exiting due to signal %d", sig);
  exit_state = ES_SIGNAL_EXIT;

  if (cygheap->rlim_core > 0UL)
    switch (sig)
      {
      case SIGABRT:
      case SIGBUS:
      case SIGFPE:
      case SIGILL:
      case SIGQUIT:
      case SIGSEGV:
      case SIGSYS:
      case SIGTRAP:
      case SIGXCPU:
      case SIGXFSZ:
	sig |= 0x80;		/* Flag that we've "dumped core" */
	if (try_to_debug ())
	  break;
	if (si->si_code != SI_USER && si->si_cyg)
	  {
	    cygwin_exception *exc = (cygwin_exception *) si->si_cyg;
	    if ((NTSTATUS) exc->exception_record ()->ExceptionCode
		== STATUS_STACK_OVERFLOW)
	      {
		/* We're handling a stack overflow so we're running low
		   on stack (surprise!)  The dumpstack method needs lots
		   of stack for buffers.  So what we do here is to run
		   dumpstack in another thread with its own stack. */
		HANDLE thread = CreateThread (&sec_none_nih, 0,
					      dumpstack_overflow_wrapper,
					      exc, 0, NULL);
		if (thread)
		  {
		    WaitForSingleObject (thread, INFINITE);
		    CloseHandle (thread);
		  }
	      }
	    else
	      ((cygwin_exception *) si->si_cyg)->dumpstack ();
	  }
	else
	  {
	    CONTEXT c;
	    c.ContextFlags = CONTEXT_FULL;
	    RtlCaptureContext (&c);
	    cygwin_exception exc ((PUINT_PTR) __builtin_frame_address (0), &c);
	    exc.dumpstack ();
	  }
	break;
      }

  lock_process until_exit (true);

  if (have_execed || exit_state > ES_PROCESS_LOCKED)
    {
      debug_printf ("recursive exit?");
      myself.exit (sig);
    }

  /* 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)", sig);
  do_exit (sig);
}
} /* extern "C" */

/* Attempt to carefully handle SIGCONT when we are stopped. */
void
_cygtls::handle_SIGCONT ()
{
  if (NOTSTATE (myself, PID_STOPPED))
    return;

  myself->stopsig = 0;
  myself->process_state &= ~PID_STOPPED;
  /* Carefully tell sig_handle_tty_stop to wake up.
     Make sure that any pending signal is handled before trying to
     send a new one.  Then make sure that SIGCONT has been recognized
     before exiting the loop.  */
  bool sigsent = false;
  while (1)
    if (sig)		/* Assume that it's ok to just test sig outside of a
			   lock since setup_handler does it this way.  */
      yield ();		/* Attempt to schedule another thread.  */
    else if (sigsent)
      break;		/* SIGCONT has been recognized by other thread */
    else
      {
	sig = SIGCONT;
	set_signal_arrived (); /* alert sig_handle_tty_stop */
	sigsent = true;
      }
  /* Clear pending stop signals */
  sig_clear (SIGSTOP);
  sig_clear (SIGTSTP);
  sig_clear (SIGTTIN);
  sig_clear (SIGTTOU);
}

int
sigpacket::process ()
{
  int rc = 1;
  bool issig_wait = false;
  struct sigaction& thissig = global_sigs[si.si_signo];
  void *handler = have_execed ? NULL : (void *) thissig.sa_handler;

  threadlist_t *tl_entry = NULL;
  _cygtls *tls = NULL;

  /* Don't try to send signals if we're just starting up since signal masks
     may not be available.  */
  if (!cygwin_finished_initializing)
    {
      rc = -1;
      goto done;
    }

  sigproc_printf ("signal %d processing", si.si_signo);

  myself->rusage_self.ru_nsignals++;

  if (si.si_signo == SIGCONT)
    {
      tl_entry = cygheap->find_tls (_main_tls);
      _main_tls->handle_SIGCONT ();
      cygheap->unlock_tls (tl_entry);
    }

  /* SIGKILL is special.  It always goes through.  */
  if (si.si_signo == SIGKILL)
    {
      tl_entry = cygheap->find_tls (_main_tls);
      tls = _main_tls;
    }
  else if (ISSTATE (myself, PID_STOPPED))
    {
      rc = -1;		/* Don't send signals when stopped */
      goto done;
    }
  else if (!sigtls)
    {
      tl_entry = cygheap->find_tls (si.si_signo, issig_wait);
      if (tl_entry)
	{
	  tls = tl_entry->thread;
	  sigproc_printf ("using tls %p", tls);
	}
    }
  else
    {
      tl_entry = cygheap->find_tls (sigtls);
      if (tl_entry)
	{
	  tls = tl_entry->thread;
	  if (sigismember (&tls->sigwait_mask, si.si_signo))
	    issig_wait = true;
	  else if (!sigismember (&tls->sigmask, si.si_signo))
	    issig_wait = false;
	  else
	    tls = NULL;
	}
    }

  /* !tls means no threads available to catch a signal. */
  if (!tls)
    {
      sigproc_printf ("signal %d blocked", si.si_signo);
      rc = -1;
      goto done;
    }

  /* Do stuff for gdb */
  if ((HANDLE) *tls)
    tls->signal_debugger (si);

  if (issig_wait)
    {
      tls->sigwait_mask = 0;
      goto dosig;
    }

  if (handler == SIG_IGN)
    {
      if (si.si_code == SI_TIMER)
	((timer_tracker *) si.si_tid)->disarm_overrun_event ();
      sigproc_printf ("signal %d ignored", si.si_signo);
      goto done;
    }

  if (si.si_signo == SIGKILL)
    goto exit_sig;
  if (si.si_signo == SIGSTOP)
    {
      sig_clear (SIGCONT);
      goto stop;
    }

  /* Clear pending SIGCONT on stop signals */
  if (si.si_signo == SIGTSTP || si.si_signo == SIGTTIN
      || si.si_signo == SIGTTOU)
    sig_clear (SIGCONT);

  if (handler == (void *) SIG_DFL)
    {
      if (si.si_signo == SIGCHLD || si.si_signo == SIGIO
	  || si.si_signo == SIGCONT || si.si_signo == SIGWINCH
	  || si.si_signo == SIGURG)
	{
	  if (si.si_code == SI_TIMER)
	    ((timer_tracker *) si.si_tid)->disarm_overrun_event ();
	  sigproc_printf ("signal %d default is currently ignore", si.si_signo);
	  goto done;
	}

      if (si.si_signo == SIGTSTP || si.si_signo == SIGTTIN
	  || si.si_signo == SIGTTOU)
	goto stop;

      goto exit_sig;
    }

  if (handler == (void *) SIG_ERR)
    goto exit_sig;

  goto dosig;

stop:
  if (tls != _main_tls)
    {
      cygheap->unlock_tls (tl_entry);
      tl_entry = cygheap->find_tls (_main_tls);
      tls = _main_tls;
    }
  handler = (void *) sig_handle_tty_stop;
  thissig = global_sigs[SIGSTOP];
  goto dosig;

exit_sig:
  handler = (void *) signal_exit;
  thissig.sa_flags |= SA_SIGINFO;
  /* Don't run signal_exit on alternate stack. */
  thissig.sa_flags &= ~SA_ONSTACK;

dosig:
  if (have_execed)
    {
      sigproc_printf ("terminating captive process");
      if (::cygheap->ctty)
	::cygheap->ctty->cleanup_before_exit ();
      TerminateProcess (ch_spawn, sigExeced = si.si_signo);
    }
  /* Dispatch to the appropriate function. */
  sigproc_printf ("signal %d, signal handler %p", si.si_signo, handler);
  rc = setup_handler (handler, thissig, tls);

done:
  cygheap->unlock_tls (tl_entry);
  sigproc_printf ("returning %d", rc);
  return rc;

}

static void
altstack_wrapper (int sig, siginfo_t *siginfo, ucontext_t *sigctx,
		  void (*handler) (int, siginfo_t *, void *))
{
  siginfo_t si = *siginfo;
  ULONG guard_size = 0;
  DWORD old_prot = (DWORD) -1;
  PTEB teb = NtCurrentTeb ();
  PVOID old_limit = NULL;

  /* Check if we're just handling a stack overflow.  If so... */
  if (sig == SIGSEGV && si.si_cyg
      && ((cygwin_exception *) si.si_cyg)->exception_record ()->ExceptionCode
	  == (DWORD) STATUS_STACK_OVERFLOW)
    {
      /* ...restore guard pages in original stack as if MSVCRT::_resetstkovlw
	 has been called.

	 Compute size of guard pages.  If SetThreadStackGuarantee returns 0,
	 use the default guard page size. */
      SetThreadStackGuarantee (&guard_size);
      if (!guard_size)
	guard_size = wincap.def_guard_page_size ();
      else
	guard_size += wincap.page_size ();
      old_limit = teb->Tib.StackLimit;
      /* Amazing but true: This VirtualProtect call automatically fixes the
	 value of teb->Tib.StackLimit on some systems.*/
      if (VirtualProtect (teb->Tib.StackLimit, guard_size,
			  PAGE_READWRITE | PAGE_GUARD, &old_prot)
	  && old_limit == teb->Tib.StackLimit)
	teb->Tib.StackLimit = (caddr_t) old_limit + guard_size;
    }
  handler (sig, &si, sigctx);
  if (old_prot != (DWORD) -1)
    {
      /* Typically the handler would exit or at least perform a siglongjmp
	 trying to overcome a SEGV condition.  However, if we return from a
	 segv handler after a stack overflow, we're dead.  While on Linux the
	 process returns to the offending code and thus the handler is called
	 ad infinitum, on Windows the NTDLL::__stkchk function will simply kill
	 the process.  So what we do here is to remove the guard pages again so
	 we can return to exception::handle.  exception::handle will then call
	 sig_send again, this time with SIG_DFL action, so at least we get a
	 stackdump. */
      if (VirtualProtect ((caddr_t) teb->Tib.StackLimit - guard_size,
			  guard_size, old_prot, &old_prot))
	teb->Tib.StackLimit = old_limit;
    }
}

int
_cygtls::call_signal_handler ()
{
  int this_sa_flags = SA_RESTART;
  while (1)
    {
      lock ();
      if (!sig)
	{
	  unlock ();
	  break;
	}

      /* Pop the stack if the next "return address" is sigdelayed, since
	 this function is doing what sigdelayed would have done anyway. */
      if (retaddr () == (__tlsstack_t) sigdelayed)
	pop ();

      debug_only_printf ("dealing with signal %d", sig);
      this_sa_flags = sa_flags;

      sigset_t this_oldmask = set_process_mask_delta ();

      if (infodata.si_code == SI_TIMER)
	{
	  timer_tracker *tt = (timer_tracker *)
			      infodata.si_tid;
	  infodata.si_overrun = tt->disarm_overrun_event ();
	}

      /* Save information locally on stack to pass to handler. */
      int thissig = sig;
      siginfo_t thissi = infodata;
      void (*thisfunc) (int, siginfo_t *, void *) = func;

      ucontext_t *thiscontext = NULL, context_copy;

      /* Only make a context for SA_SIGINFO handlers */
      if (this_sa_flags & SA_SIGINFO)
	{
	  context.uc_link = 0;
	  context.uc_flags = 0;
	  if (thissi.si_cyg)
	    memcpy (&context.uc_mcontext,
		    ((cygwin_exception *) thissi.si_cyg)->context (),
		    sizeof (CONTEXT));
	  else
	    {
	      /* Software-generated signal.  We're fetching the current
		 context, unwind to the caller and in case we're called
		 from sigdelayed, fix the instruction pointer accordingly. */
	      context.uc_mcontext.ctxflags = CONTEXT_FULL;
	      RtlCaptureContext ((PCONTEXT) &context.uc_mcontext);
	      __unwind_single_frame ((PCONTEXT) &context.uc_mcontext);
	      if (stackptr > stack)
		{
#ifdef __x86_64__
		  context.uc_mcontext.rip = retaddr ();
#else
#error unimplemented for this target
#endif
		}
	    }

	  if (this_sa_flags & SA_ONSTACK
	      && !_my_tls.altstack.ss_flags
	      && _my_tls.altstack.ss_sp)
	    {
	      context.uc_stack = _my_tls.altstack;
	      context.uc_stack.ss_flags = SS_ONSTACK;
	    }
	  else
	    {
	      context.uc_stack.ss_sp = NtCurrentTeb ()->Tib.StackBase;
	      context.uc_stack.ss_flags = 0;
	      if (!NtCurrentTeb ()->DeallocationStack)
		context.uc_stack.ss_size
		  = (uintptr_t) NtCurrentTeb ()->Tib.StackLimit
		    - (uintptr_t) NtCurrentTeb ()->Tib.StackBase;
	      else
		context.uc_stack.ss_size
		  = (uintptr_t) NtCurrentTeb ()->DeallocationStack
		    - (uintptr_t) NtCurrentTeb ()->Tib.StackBase;
	    }
	  context.uc_sigmask = context.uc_mcontext.oldmask = this_oldmask;

	  context.uc_mcontext.cr2 = (thissi.si_signo == SIGSEGV
				     || thissi.si_signo == SIGBUS)
				    ? (uintptr_t) thissi.si_addr : 0;

	  thiscontext = &context;
	  context_copy = context;
	}

      int this_errno = saved_errno;
      reset_signal_arrived ();
      incyg = false;
      sig = 0;		/* Flag that we can accept another signal */
      unlock ();	/* unlock signal stack */

      /* Alternate signal stack requested for this signal and alternate signal
	 stack set up for this thread? */
      if (this_sa_flags & SA_ONSTACK
	  && !_my_tls.altstack.ss_flags
	  && _my_tls.altstack.ss_sp)
	{
	  /* Yes, use alternate signal stack.

	    NOTE:

	    We DO NOT change the TEB's stack addresses and we DO NOT move the
	    _cygtls area to the alternate stack.  This seems to work fine,
	    but there may be Windows functions not working correctly under
	    these circumstances.

	    On the other hand, if a Windows function crashed and we're handling
	    this here, moving the TEB stack addresses may be fatal.

	    If the current code does not work as expected in the "usual"
	    POSIX circumstances, this problem must be revisited. */

	  /* Compute new stackbase.  We start from the high address, aligned
	     to 16 byte. */
	  uintptr_t new_sp = ((uintptr_t) _my_tls.altstack.ss_sp
			      + _my_tls.altstack.ss_size) & ~0xf;
	  /* In assembler: Save regs on new stack, move to alternate stack,
	     call thisfunc, revert stack regs. */
#ifdef __x86_64__
	  /* Clobbered regs: rcx, rdx, r8, r9, r10, r11, rbp, rsp */
	  __asm__ ("\n\
		   movq  %[NEW_SP], %%rax  # Load alt stack into rax	\n\
		   subq  $0x60, %%rax      # Make room on alt stack	\n\
					   # for clobbered regs and	\n\
					   # required shadow space	\n\
		   movq  %%rcx, 0x20(%%rax)# Save clobbered regs	\n\
		   movq  %%rdx, 0x28(%%rax)				\n\
		   movq  %%r8,  0x30(%%rax)				\n\
		   movq  %%r9,  0x38(%%rax)				\n\
		   movq  %%r10, 0x40(%%rax)				\n\
		   movq  %%r11, 0x48(%%rax)				\n\
		   movq  %%rbp, 0x50(%%rax)				\n\
		   movq  %%rsp, 0x58(%%rax)				\n\
		   movl  %[SIG], %%ecx     # thissig to 1st arg reg	\n\
		   leaq  %[SI], %%rdx      # &thissi to 2nd arg reg	\n\
		   movq  %[CTX], %%r8      # thiscontext to 3rd arg reg	\n\
		   movq  %[FUNC], %%r9     # thisfunc to r9		\n\
		   leaq  %[WRAPPER], %%r10 # wrapper address to r10	\n\
		   movq  %%rax, %%rsp      # Move alt stack into rsp	\n\
		   call  *%%r10            # Call wrapper		\n\
		   movq  %%rsp, %%rax      # Restore clobbered regs	\n\
		   movq  0x58(%%rax), %%rsp				\n\
		   movq  0x50(%%rax), %%rbp				\n\
		   movq  0x48(%%rax), %%r11				\n\
		   movq  0x40(%%rax), %%r10				\n\
		   movq  0x38(%%rax), %%r9				\n\
		   movq  0x30(%%rax), %%r8				\n\
		   movq  0x28(%%rax), %%rdx				\n\
		   movq  0x20(%%rax), %%rcx				\n"
		   : : [NEW_SP]	"o" (new_sp),
		       [SIG]	"o" (thissig),
		       [SI]	"o" (thissi),
		       [CTX]	"o" (thiscontext),
		       [FUNC]	"o" (thisfunc),
		       [WRAPPER] "o" (altstack_wrapper)
		   : "memory");
#else
#error unimplemented for this target
#endif
	}
      else
	/* No alternate signal stack requested or available, just call
	   signal handler. */
	thisfunc (thissig, &thissi, thiscontext);

      incyg = true;

      set_signal_mask (_my_tls.sigmask, (this_sa_flags & SA_SIGINFO)
					? context.uc_sigmask : this_oldmask);
      if (this_errno >= 0)
	set_errno (this_errno);
      if (this_sa_flags & SA_SIGINFO)
	{
	  /* If more than just the sigmask in the context has been changed by
	     the signal handler, call setcontext. */
	  context_copy.uc_sigmask = context.uc_sigmask;
	  if (memcmp (&context, &context_copy, sizeof context) != 0)
	    setcontext (&context);
	}
    }

  /* FIXME: Since 2011 this return statement always returned 1 (meaning
     SA_RESTART is effective) if the thread we're running in is not the
     main thread.  We're disabling this check to enable EINTR behaviour
     on system calls not running in the main thread.  It's not quite clear
     if that has undesired side-effects, therefore this comment. */
  return this_sa_flags & SA_RESTART;
}

void
_cygtls::signal_debugger (siginfo_t& si)
{
  HANDLE th;
  /* If si.si_cyg is set then the signal was already sent to the debugger. */
  if (isinitialized () && !si.si_cyg && (th = (HANDLE) *this)
      && being_debugged () && SuspendThread (th) >= 0)
    {
      CONTEXT c;
      c.ContextFlags = CONTEXT_FULL;
      if (GetThreadContext (th, &c))
	{
	  if (incyg)
	    c._CX_instPtr = retaddr ();
	  memcpy (&context.uc_mcontext, &c, sizeof (CONTEXT));
	  /* Enough space for 64 bit addresses */
	  char sigmsg[2 * sizeof (_CYGWIN_SIGNAL_STRING
				  " ffffffff ffffffffffffffff")];
	  __small_sprintf (sigmsg, _CYGWIN_SIGNAL_STRING " %d %y %p",
			   si.si_signo, thread_id, &context.uc_mcontext);
	  OutputDebugString (sigmsg);
	}
      ResumeThread (th);
    }
}

extern "C" int
setcontext (const ucontext_t *ucp)
{
  PCONTEXT ctx = (PCONTEXT) &ucp->uc_mcontext;
  set_signal_mask (_my_tls.sigmask, ucp->uc_sigmask);
  _my_tls.incyg = true;
  RtlRestoreContext (ctx, NULL);
  /* If we got here, something was wrong. */
  set_errno (EINVAL);
  return -1;
}

extern "C" int
getcontext (ucontext_t *ucp)
{
  PCONTEXT ctx = (PCONTEXT) &ucp->uc_mcontext;
  ctx->ContextFlags = CONTEXT_FULL;
  RtlCaptureContext (ctx);
  __unwind_single_frame (ctx);
  /* Successful getcontext is supposed to return 0.  If we don't set the
     return register to 0 here, there's a chance that code like this:

       if (getcontext (&ctx) != 0)

     assumes that getcontext failed after calling setcontext (&ctx). */
  ucp->uc_mcontext._MC_retReg = 0;
  ucp->uc_sigmask = ucp->uc_mcontext.oldmask = _my_tls.sigmask;
  /* Do not touch any other member of ucontext_t. */
  return 0;
}

extern "C" int
swapcontext (ucontext_t *oucp, const ucontext_t *ucp)
{
  PCONTEXT ctx = (PCONTEXT) &oucp->uc_mcontext;
  ctx->ContextFlags = CONTEXT_FULL;
  RtlCaptureContext (ctx);
  __unwind_single_frame (ctx);
  /* See comment in getcontext. */
  oucp->uc_mcontext._MC_retReg = 0;
  oucp->uc_sigmask = oucp->uc_mcontext.oldmask = _my_tls.sigmask;
  return setcontext (ucp);
}

/* Trampoline function to set the context to uc_link.  The pointer to the
   address of uc_link is stored in a callee-saved register, referenced by
   _MC_uclinkReg from the C code.  If uc_link is NULL, call exit. */
#ifdef __x86_64__
/* _MC_uclinkReg == %rbx */
__asm__ ("				\n\
	.global	__cont_link_context	\n\
	.seh_proc __cont_link_context	\n\
__cont_link_context:			\n\
	.seh_endprologue		\n\
	movq	%rbx, %rsp		\n\
	movq	(%rsp), %rcx		\n\
	# align stack and subtract shadow space	\n\
	andq	$~0xf, %rsp		\n\
	subq	$0x20, %rsp		\n\
	testq	%rcx, %rcx		\n\
	je	1f			\n\
	call	setcontext		\n\
	movq	$0xff, %rcx		\n\
1:					\n\
	call	cygwin_exit		\n\
	nop				\n\
	.seh_endproc			\n\
	");

#else
#error unimplemented for this target
#endif

/* makecontext is modelled after GLibc's makecontext.  The stack from uc_stack
   is prepared so that it starts with a pointer to the linked context uc_link,
   followed by the arguments to func, and finally at the bottom the "return"
   address set to __cont_link_context.  In the ucp context, rbx/ebx is set to
   point to the stack address where the pointer to uc_link is stored.  The
   requirement to make this work is that rbx/ebx are callee-saved registers
   per the ABI.  If any function is called which doesn't follow the ABI
   conventions, e.g. assembler code, this method will break.  But that's ok. */
extern "C" void
makecontext (ucontext_t *ucp, void (*func) (void), int argc, ...)
{
  extern void __cont_link_context (void);
  uintptr_t *sp;
  va_list ap;

  /* Initialize sp to the top of the stack. */
  sp = (uintptr_t *) ((uintptr_t) ucp->uc_stack.ss_sp + ucp->uc_stack.ss_size);
  /* Subtract slots required for arguments and the pointer to uc_link. */
  sp -= (argc + 1);
  /* Align. */
  sp = (uintptr_t *) ((uintptr_t) sp & ~0xf);
  /* Subtract one slot for setting the return address. */
  --sp;
  /* Set return address to the trampolin function __cont_link_context. */
  sp[0] = (uintptr_t) __cont_link_context;
  /* Fetch arguments and store them on the stack.

     x86_64:

     - Store first four args in the AMD64 ABI arg registers.

     - Note that the stack is not short by these four register args.  The
       reason is the shadow space for these regs required by the AMD64 ABI.

     - The definition of makecontext only allows for "int" sized arguments to
       func, 32 bit, likely for historical reasons.  However, the argument
       slots on x86_64 are 64 bit anyway, so we can fetch and store the args
       as 64 bit values, and func can request 64 bit args without violating
       the definition.  This potentially allows porting 32 bit applications
       providing pointer values to func without additional porting effort. */
  va_start (ap, argc);
  for (int i = 0; i < argc; ++i)
#ifdef __x86_64__
    switch (i)
      {
      case 0:
	ucp->uc_mcontext.rcx = va_arg (ap, uintptr_t);
	break;
      case 1:
	ucp->uc_mcontext.rdx = va_arg (ap, uintptr_t);
	break;
      case 2:
	ucp->uc_mcontext.r8 = va_arg (ap, uintptr_t);
	break;
      case 3:
	ucp->uc_mcontext.r9 = va_arg (ap, uintptr_t);
	break;
      default:
	sp[i + 1] = va_arg (ap, uintptr_t);
	break;
      }
#else
#error unimplemented for this target
#endif
  va_end (ap);
  /* Store pointer to uc_link at the top of the stack. */
  sp[argc + 1] = (uintptr_t) ucp->uc_link;
  /* Last but not least set the register in the context at ucp so that a
     subsequent setcontext or swapcontext picks up the right values:
     - Set instruction pointer to the target function.
     - Set stack pointer to the just computed stack pointer value.
     - Set Cygwin-specific uclink register to the address of the pointer
       to uc_link. */
  ucp->uc_mcontext._MC_instPtr = (uint64_t) func;
  ucp->uc_mcontext._MC_stackPtr = (uint64_t) sp;
  ucp->uc_mcontext._MC_uclinkReg = (uint64_t) (sp + argc + 1);
}
