aboutsummaryrefslogtreecommitdiff
path: root/winsup/cygwin/exceptions.cc
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/exceptions.cc')
-rw-r--r--winsup/cygwin/exceptions.cc84
1 files changed, 78 insertions, 6 deletions
diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc
index 2e25aa2..f79978f 100644
--- a/winsup/cygwin/exceptions.cc
+++ b/winsup/cygwin/exceptions.cc
@@ -440,7 +440,7 @@ cygwin_exception::dumpstack ()
}
bool
-_cygtls::inside_kernel (CONTEXT *cx)
+_cygtls::inside_kernel (CONTEXT *cx, bool inside_cygwin)
{
int res;
MEMORY_BASIC_INFORMATION m;
@@ -462,6 +462,8 @@ _cygtls::inside_kernel (CONTEXT *cx)
else if (h == hntdll)
res = true; /* Calling GetModuleFilename on ntdll.dll
can hang */
+ else if (h == cygwin_hmodule && inside_cygwin)
+ res = true;
else if (h == user_data->hmodule)
res = false;
else if (!GetModuleFileNameW (h, checkdir,
@@ -595,7 +597,7 @@ try_to_debug ()
{
extern void break_here ();
break_here ();
- return 1;
+ return 0;
}
/* Otherwise, invoke the JIT debugger, if set */
@@ -659,7 +661,7 @@ exception::handle (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in,
/* If we're exiting, tell Windows to keep looking for an
exception handler. */
- if (exit_state || e->ExceptionFlags)
+ if (exit_state || (e->ExceptionFlags & ~EXCEPTION_SOFTWARE_ORIGINATE))
return ExceptionContinueSearch;
siginfo_t si = {};
@@ -810,6 +812,8 @@ exception::handle (EXCEPTION_RECORD *e, exception_list *frame, CONTEXT *in,
else if (try_to_debug ())
{
debugging = 1;
+ /* If a JIT debugger just attached, replay the exception for the benefit
+ of that */
return ExceptionContinueExecution;
}
@@ -912,6 +916,24 @@ sig_handle_tty_stop (int sig, siginfo_t *, void *)
}
} /* end extern "C" */
+#ifdef __x86_64__
+static LONG CALLBACK
+singlestep_handler (EXCEPTION_POINTERS *ep)
+{
+ if (_my_tls.suspend_on_exception)
+ {
+ _my_tls.in_singlestep_handler = true;
+ RtlWakeAddressSingle ((void *) &_my_tls.in_singlestep_handler);
+ while (_my_tls.suspend_on_exception)
+ ; /* Don't call yield() to prevent the thread
+ from being suspended in the kernel. */
+ if (ep->ExceptionRecord->ExceptionCode == (DWORD) STATUS_SINGLE_STEP)
+ return EXCEPTION_CONTINUE_EXECUTION;
+ }
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+#endif
+
bool
_cygtls::interrupt_now (CONTEXT *cx, siginfo_t& si, void *handler,
struct sigaction& siga)
@@ -925,6 +947,44 @@ _cygtls::interrupt_now (CONTEXT *cx, siginfo_t& si, void *handler,
interrupted = false;
else
{
+#ifdef __x86_64__
+ /* When the Rip points to an instruction that causes an exception,
+ modifying Rip and calling ResumeThread() may sometimes result in
+ a crash. To prevent this, advance execution by a single instruction
+ by setting the trap flag (TF) before calling ResumeThread(). This
+ will trigger either STATUS_SINGLE_STEP or the exception caused by
+ the instruction that Rip originally pointed to. By suspending the
+ targeted thread within singlestep_handler(), Rip no longer points
+ to the problematic instruction, allowing safe handling of the
+ interrupt. As a result, Rip can be adjusted appropriately,
+ and the thread can resume execution without unexpected crashes. */
+ if (!inside_kernel (cx, true))
+ {
+ HANDLE h_veh = AddVectoredExceptionHandler (0, singlestep_handler);
+ cx->EFlags |= 0x100; /* Set TF (setup single step execution) */
+ SetThreadContext (*this, cx);
+ suspend_on_exception = true;
+ in_singlestep_handler = false;
+ bool bool_false = false;
+ NTSTATUS status = STATUS_SUCCESS;
+ ResumeThread (*this);
+ while (!in_singlestep_handler && NT_SUCCESS (status))
+ {
+ LARGE_INTEGER timeout;
+ timeout.QuadPart = -100000ULL; /* 10ms */
+ status = RtlWaitOnAddress (&in_singlestep_handler, &bool_false,
+ sizeof (bool), &timeout);
+ if (status == STATUS_TIMEOUT)
+ break;
+ }
+ SuspendThread (*this);
+ GetThreadContext (*this, cx);
+ RemoveVectoredExceptionHandler (h_veh);
+ suspend_on_exception = false;
+ if (!NT_SUCCESS (status) || status == STATUS_TIMEOUT)
+ return false; /* Not interrupted */
+ }
+#endif
DWORD64 &ip = cx->_CX_instPtr;
push (ip);
interrupt_setup (si, handler, siga);
@@ -1281,6 +1341,7 @@ set_process_mask_delta ()
else
oldmask = _my_tls.sigmask;
newmask = (oldmask | _my_tls.deltamask) & ~SIG_NONMASKABLE;
+ _my_tls.deltamask = 0;
sigproc_printf ("oldmask %lx, newmask %lx, deltamask %lx", oldmask, newmask,
_my_tls.deltamask);
_my_tls.sigmask = newmask;
@@ -1503,12 +1564,15 @@ sigpacket::process ()
if (tl_entry)
{
tls = tl_entry->thread;
+ tl_entry->thread->lock ();
if (sigismember (&tls->sigwait_mask, si.si_signo))
issig_wait = true;
- else if (!sigismember (&tls->sigmask, si.si_signo))
+ else if (!sigismember (&tls->sigmask, si.si_signo)
+ && !sigismember (&tls->deltamask, si.si_signo))
issig_wait = false;
else
tls = NULL;
+ tl_entry->thread->unlock ();
}
}
@@ -1756,7 +1820,7 @@ _cygtls::call_signal_handler ()
int this_errno = saved_errno;
reset_signal_arrived ();
- incyg = false;
+ incyg = 0;
current_sig = 0; /* Flag that we can accept another signal */
/* We have to fetch the original return address from the signal stack
@@ -1791,6 +1855,13 @@ _cygtls::call_signal_handler ()
to 16 byte. */
uintptr_t new_sp = ((uintptr_t) _my_tls.altstack.ss_sp
+ _my_tls.altstack.ss_size) & ~0xf;
+ /* Copy context1 to the alternate signal stack area, because the
+ context1 allocated in the normal stack area is not accessible
+ from the signal handler that uses alternate signal stack. */
+ thiscontext = (ucontext_t *) ((new_sp - sizeof (ucontext_t)) & ~0xf);
+ memcpy (thiscontext, &context1, sizeof (ucontext_t));
+ new_sp = (uintptr_t) thiscontext;
+
/* In assembler: Save regs on new stack, move to alternate stack,
call thisfunc, revert stack regs. */
#ifdef __x86_64__
@@ -1834,6 +1905,7 @@ _cygtls::call_signal_handler ()
#else
#error unimplemented for this target
#endif
+ memcpy (&context1, thiscontext, sizeof (ucontext_t));
}
else
/* No alternate signal stack requested or available, just call
@@ -1861,7 +1933,7 @@ _cygtls::call_signal_handler ()
}
unlock ();
- incyg = true;
+ incyg = 1;
set_signal_mask (_my_tls.sigmask, (this_sa_flags & SA_SIGINFO)
? context1.uc_sigmask : this_oldmask);