diff options
Diffstat (limited to 'winsup/cygwin/exceptions.cc')
-rw-r--r-- | winsup/cygwin/exceptions.cc | 84 |
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); |