#include "pal_unwind.h"
+PALIMPORT
+VOID
+PALAPI
+PAL_FreeExceptionRecords(
+ IN EXCEPTION_RECORD *exceptionRecord,
+ IN CONTEXT *contextRecord);
+
#define EXCEPTION_CONTINUE_SEARCH 0
#define EXCEPTION_EXECUTE_HANDLER 1
#define EXCEPTION_CONTINUE_EXECUTION -1
{
private:
static const SIZE_T NoTargetFrameSp = SIZE_MAX;
+
+ void Move(PAL_SEHException& ex)
+ {
+ ExceptionPointers.ExceptionRecord = ex.ExceptionPointers.ExceptionRecord;
+ ExceptionPointers.ContextRecord = ex.ExceptionPointers.ContextRecord;
+ TargetFrameSp = ex.TargetFrameSp;
+
+ ex.Clear();
+ }
+
+ void FreeRecords()
+ {
+ if (ExceptionPointers.ExceptionRecord != NULL)
+ {
+ PAL_FreeExceptionRecords(ExceptionPointers.ExceptionRecord, ExceptionPointers.ContextRecord);
+ ExceptionPointers.ExceptionRecord = NULL;
+ ExceptionPointers.ContextRecord = NULL;
+ }
+ }
+
public:
- // Note that the following two are actually embedded in this heap-allocated
- // instance - in contrast to Win32, where the exception record would usually
- // be allocated on the stack. This is needed because foreign cleanup handlers
- // partially unwind the stack on the second pass.
EXCEPTION_POINTERS ExceptionPointers;
- EXCEPTION_RECORD ExceptionRecord;
- CONTEXT ContextRecord;
// Target frame stack pointer set before the 2nd pass.
SIZE_T TargetFrameSp;
PAL_SEHException(EXCEPTION_RECORD *pExceptionRecord, CONTEXT *pContextRecord)
{
- ExceptionPointers.ExceptionRecord = &ExceptionRecord;
- ExceptionPointers.ContextRecord = &ContextRecord;
- ExceptionRecord = *pExceptionRecord;
- ContextRecord = *pContextRecord;
+ ExceptionPointers.ExceptionRecord = pExceptionRecord;
+ ExceptionPointers.ContextRecord = pContextRecord;
TargetFrameSp = NoTargetFrameSp;
}
PAL_SEHException()
{
+ Clear();
}
- PAL_SEHException(const PAL_SEHException& ex)
+ // The copy constructor and copy assignment operators are deleted so that the PAL_SEHException
+ // can never be copied, only moved. This enables simple lifetime management of the exception and
+ // context records, since there is always just one PAL_SEHException instance referring to the same records.
+ PAL_SEHException(const PAL_SEHException& ex) = delete;
+ PAL_SEHException& operator=(const PAL_SEHException& ex) = delete;
+
+ PAL_SEHException(PAL_SEHException&& ex)
{
- *this = ex;
+ Move(ex);
}
- bool IsFirstPass()
+ PAL_SEHException& operator=(PAL_SEHException&& ex)
{
- return (TargetFrameSp == NoTargetFrameSp);
+ FreeRecords();
+ Move(ex);
+ return *this;
}
- void SecondPassDone()
+ ~PAL_SEHException()
{
+ FreeRecords();
+ }
+
+ void Clear()
+ {
+ ExceptionPointers.ExceptionRecord = NULL;
+ ExceptionPointers.ContextRecord = NULL;
TargetFrameSp = NoTargetFrameSp;
}
- PAL_SEHException& operator=(const PAL_SEHException& ex)
+ CONTEXT* GetContextRecord()
{
- ExceptionPointers.ExceptionRecord = &ExceptionRecord;
- ExceptionPointers.ContextRecord = &ContextRecord;
- ExceptionRecord = ex.ExceptionRecord;
- ContextRecord = ex.ContextRecord;
- TargetFrameSp = ex.TargetFrameSp;
+ return ExceptionPointers.ContextRecord;
+ }
- return *this;
+ EXCEPTION_RECORD* GetExceptionRecord()
+ {
+ return ExceptionPointers.ExceptionRecord;
+ }
+
+ bool IsFirstPass()
+ {
+ return (TargetFrameSp == NoTargetFrameSp);
+ }
+
+ void SecondPassDone()
+ {
+ TargetFrameSp = NoTargetFrameSp;
}
};
-typedef VOID (PALAPI *PHARDWARE_EXCEPTION_HANDLER)(PAL_SEHException* ex);
+typedef BOOL (PALAPI *PHARDWARE_EXCEPTION_HANDLER)(PAL_SEHException* ex);
typedef BOOL (PALAPI *PHARDWARE_EXCEPTION_SAFETY_CHECK_FUNCTION)(PCONTEXT contextRecord, PEXCEPTION_RECORD exceptionRecord);
typedef VOID (PALAPI *PTERMINATION_REQUEST_HANDLER)();
typedef DWORD (PALAPI *PGET_GCMARKER_EXCEPTION_CODE)(LPVOID ip);
}
#endif // FEATURE_PAL_SXS
- EXCEPTION_POINTERS pointers;
- pointers.ExceptionRecord = pExRecord;
- pointers.ContextRecord = pContext;
+ CONTEXT *contextRecord;
+ EXCEPTION_RECORD *exceptionRecord;
+ AllocateExceptionRecords(&exceptionRecord, &contextRecord);
- TRACE("PAL_DispatchException(EC %08x EA %p)\n", pExRecord->ExceptionCode, pExRecord->ExceptionAddress);
- SEHProcessException(&pointers);
+ *contextRecord = *pContext;
+ *exceptionRecord = *pExRecord;
+
+ contextRecord->ContextFlags |= CONTEXT_EXCEPTION_ACTIVE;
+ bool continueExecution;
+
+ {
+ // The exception object takes ownership of the exceptionRecord and contextRecord
+ PAL_SEHException exception(exceptionRecord, contextRecord);
+
+ TRACE("PAL_DispatchException(EC %08x EA %p)\n", pExRecord->ExceptionCode, pExRecord->ExceptionAddress);
+
+ continueExecution = SEHProcessException(&exception);
+ if (continueExecution)
+ {
+ // Make a copy of the exception records so that we can free them before restoring the context
+ *pContext = *contextRecord;
+ *pExRecord = *exceptionRecord;
+ }
+
+ // The exception records are destroyed by the PAL_SEHException destructor now.
+ }
+
+ if (continueExecution)
+ {
+ RtlRestoreContext(pContext, pExRecord);
+ }
// Send the forward request to the exception thread to process
MachMessage sSendMessage;
#endif // !HAVE_UNW_GET_ACCESSORS
#endif // _AMD64_
+struct ExceptionRecords
+{
+ CONTEXT ContextRecord;
+ EXCEPTION_RECORD ExceptionRecord;
+};
+
+// Max number of fallback contexts that are used when malloc fails to allocate ExceptionRecords structure
+static const int MaxFallbackContexts = sizeof(size_t) * 8;
+// Array of fallback contexts
+static ExceptionRecords s_fallbackContexts[MaxFallbackContexts];
+// Bitmap used for allocating fallback contexts - bits set to 1 represent already allocated context.
+static volatile size_t s_allocatedContextsBitmap = 0;
+
+/*++
+Function:
+ AllocateExceptionRecords
+
+ Allocate EXCEPTION_RECORD and CONTEXT structures for an exception.
+Parameters:
+ exceptionRecord - output pointer to the allocated exception record
+ contextRecord - output pointer to the allocated context record
+--*/
+VOID
+AllocateExceptionRecords(EXCEPTION_RECORD** exceptionRecord, CONTEXT** contextRecord)
+{
+ ExceptionRecords* records;
+ if (posix_memalign((void**)&records, alignof(ExceptionRecords), sizeof(ExceptionRecords)) != 0)
+ {
+ size_t bitmap;
+ size_t newBitmap;
+ int index;
+
+ do
+ {
+ bitmap = s_allocatedContextsBitmap;
+ index = __builtin_ffsl(~bitmap) - 1;
+ if (index < 0)
+ {
+ PROCAbort();
+ }
+
+ newBitmap = bitmap | ((size_t)1 << index);
+ }
+ while (__sync_val_compare_and_swap(&s_allocatedContextsBitmap, bitmap, newBitmap) != bitmap);
+
+ records = &s_fallbackContexts[index];
+ }
+
+ *contextRecord = &records->ContextRecord;
+ *exceptionRecord = &records->ExceptionRecord;
+}
+
+/*++
+Function:
+ PAL_FreeExceptionRecords
+
+ Free EXCEPTION_RECORD and CONTEXT structures of an exception that were allocated by the
+ AllocateExceptionRecords.
+Parameters:
+ exceptionRecord - exception record
+ contextRecord - context record
+--*/
+VOID
+PALAPI
+PAL_FreeExceptionRecords(IN EXCEPTION_RECORD *exceptionRecord, IN CONTEXT *contextRecord)
+{
+ // Both records are allocated at once and the allocated memory starts at the contextRecord
+ ExceptionRecords* records = (ExceptionRecords*)contextRecord;
+ if ((records >= &s_fallbackContexts[0]) && (records < &s_fallbackContexts[MaxFallbackContexts]))
+ {
+ int index = records - &s_fallbackContexts[0];
+ __sync_fetch_and_and(&s_allocatedContextsBitmap, ~((size_t)1 << index));
+ }
+ else
+ {
+ free(contextRecord);
+ }
+}
+
/*++
Function:
RtlpRaiseException
Note:
The name of this function and the name of the ExceptionRecord
parameter is used in the sos lldb plugin code to read the exception
- record. See coreclr\src\ToolBox\SOS\lldbplugin\debugclient.cpp.
+ record. See coreclr\src\ToolBox\SOS\lldbplugin\services.cpp.
This function must not be inlined or optimized so the below PAL_VirtualUnwind
calls end up with RaiseException caller's context and so the above debugger
__attribute__((noinline))
__attribute__((optnone))
static void
-RtlpRaiseException(EXCEPTION_RECORD *ExceptionRecord)
+RtlpRaiseException(EXCEPTION_RECORD *ExceptionRecord, CONTEXT *ContextRecord)
{
- // Capture the context of RtlpRaiseException.
- CONTEXT ContextRecord;
- ZeroMemory(&ContextRecord, sizeof(CONTEXT));
- ContextRecord.ContextFlags = CONTEXT_FULL;
- CONTEXT_CaptureContext(&ContextRecord);
-
- // Find the caller of RtlpRaiseException.
- PAL_VirtualUnwind(&ContextRecord, NULL);
-
- // The frame we're looking at now is RaiseException. We have to unwind one
- // level further to get the actual context user code could be resumed at.
- PAL_VirtualUnwind(&ContextRecord, NULL);
-
-#if defined(_X86_)
- ExceptionRecord->ExceptionAddress = (void *) ContextRecord.Eip;
-#elif defined(_AMD64_)
- ExceptionRecord->ExceptionAddress = (void *) ContextRecord.Rip;
-#elif defined(_ARM_) || defined(_ARM64_)
- ExceptionRecord->ExceptionAddress = (void *) ContextRecord.Pc;
-#else
-#error unsupported architecture
-#endif
-
- throw PAL_SEHException(ExceptionRecord, &ContextRecord);
+ throw PAL_SEHException(ExceptionRecord, ContextRecord);
}
/*++
See MSDN doc.
--*/
// no PAL_NORETURN, as callers must assume this can return for continuable exceptions.
+__attribute__((noinline))
VOID
PALAPI
RaiseException(IN DWORD dwExceptionCode,
nNumberOfArguments = EXCEPTION_MAXIMUM_PARAMETERS;
}
- EXCEPTION_RECORD exceptionRecord;
- ZeroMemory(&exceptionRecord, sizeof(EXCEPTION_RECORD));
+ CONTEXT *contextRecord;
+ EXCEPTION_RECORD *exceptionRecord;
+ AllocateExceptionRecords(&exceptionRecord, &contextRecord);
- exceptionRecord.ExceptionCode = dwExceptionCode;
- exceptionRecord.ExceptionFlags = dwExceptionFlags;
- exceptionRecord.ExceptionRecord = NULL;
- exceptionRecord.ExceptionAddress = NULL; // will be set by RtlpRaiseException
- exceptionRecord.NumberParameters = nNumberOfArguments;
+ ZeroMemory(exceptionRecord, sizeof(EXCEPTION_RECORD));
+
+ exceptionRecord->ExceptionCode = dwExceptionCode;
+ exceptionRecord->ExceptionFlags = dwExceptionFlags;
+ exceptionRecord->ExceptionRecord = NULL;
+ exceptionRecord->ExceptionAddress = NULL; // will be set by RtlpRaiseException
+ exceptionRecord->NumberParameters = nNumberOfArguments;
if (nNumberOfArguments)
{
- CopyMemory(exceptionRecord.ExceptionInformation, lpArguments,
+ CopyMemory(exceptionRecord->ExceptionInformation, lpArguments,
nNumberOfArguments * sizeof(ULONG_PTR));
}
- RtlpRaiseException(&exceptionRecord);
+
+ // Capture the context of RaiseException.
+ ZeroMemory(contextRecord, sizeof(CONTEXT));
+ contextRecord->ContextFlags = CONTEXT_FULL;
+ CONTEXT_CaptureContext(contextRecord);
+
+ // We have to unwind one level to get the actual context user code could be resumed at.
+ PAL_VirtualUnwind(contextRecord, NULL);
+
+ exceptionRecord->ExceptionAddress = (void *)CONTEXTGetPC(contextRecord);
+
+ RtlpRaiseException(exceptionRecord, contextRecord);
LOGEXIT("RaiseException returns\n");
}
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
+#include <utility>
using namespace CorUnix;
// frames that will become obsolete by the ThrowExceptionFromContextInternal and the ThrowExceptionHelper
// could overwrite the "ex" object by stack e.g. when allocating the low level exception object for "throw".
static __thread BYTE threadLocalExceptionStorage[sizeof(PAL_SEHException)];
- ThrowExceptionFromContextInternal(context, new (threadLocalExceptionStorage) PAL_SEHException(*ex));
+ ThrowExceptionFromContextInternal(context, new (threadLocalExceptionStorage) PAL_SEHException(std::move(*ex)));
}
/*++
extern "C"
void ThrowExceptionHelper(PAL_SEHException* ex)
{
- throw *ex;
+ throw std::move(*ex);
}
/*++
Function:
SEHProcessException
- Build the PAL exception and sent it to any handler registered.
+ Send the PAL exception to any handler registered.
Parameters:
- PEXCEPTION_POINTERS pointers
+ PAL_SEHException* exception
Return value:
- Returns only if the exception is unhandled
+ Returns TRUE if the exception happened in managed code and the execution should
+ continue (with possibly modified context).
+ Returns FALSE if the exception happened in managed code and it was not handled.
+ In case the exception was handled by calling a catch handler, it doesn't return at all.
--*/
-VOID
-SEHProcessException(PEXCEPTION_POINTERS pointers)
+BOOL
+SEHProcessException(PAL_SEHException* exception)
{
- pointers->ContextRecord->ContextFlags |= CONTEXT_EXCEPTION_ACTIVE;
+ CONTEXT* contextRecord = exception->GetContextRecord();
+ EXCEPTION_RECORD* exceptionRecord = exception->GetExceptionRecord();
- if (!IsInDebugBreak(pointers->ExceptionRecord->ExceptionAddress))
+ if (!IsInDebugBreak(exceptionRecord->ExceptionAddress))
{
- PAL_SEHException exception(pointers->ExceptionRecord, pointers->ContextRecord);
-
if (g_hardwareExceptionHandler != NULL)
{
_ASSERTE(g_safeExceptionCheckFunction != NULL);
// Check if it is safe to handle the hardware exception (the exception happened in managed code
// or in a jitter helper or it is a debugger breakpoint)
- if (g_safeExceptionCheckFunction(pointers->ContextRecord, pointers->ExceptionRecord))
+ if (g_safeExceptionCheckFunction(contextRecord, exceptionRecord))
{
- if (pointers->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
+ if (exceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
{
// Check if the failed access has hit a stack guard page. In such case, it
// was a stack probe that detected that there is not enough stack left.
void* stackLimit = CPalThread::GetStackLimit();
void* stackGuard = (void*)((size_t)stackLimit - getpagesize());
- void* violationAddr = (void*)pointers->ExceptionRecord->ExceptionInformation[1];
+ void* violationAddr = (void*)exceptionRecord->ExceptionInformation[1];
if ((violationAddr >= stackGuard) && (violationAddr < stackLimit))
{
// The exception happened in the page right below the stack limit,
}
}
- // The following callback returns only in case the exception was a single step or
- // a breakpoint and it was not handled by the debugger.
- g_hardwareExceptionHandler(&exception);
+ if (g_hardwareExceptionHandler(exception))
+ {
+ // The exception happened in managed code and the execution should continue.
+ return TRUE;
+ }
+
+ // The exception was a single step or a breakpoint and it was not handled by the debugger.
}
}
if (CatchHardwareExceptionHolder::IsEnabled())
{
- PAL_ThrowExceptionFromContext(&exception.ContextRecord, &exception);
+ PAL_ThrowExceptionFromContext(exception->GetContextRecord(), exception);
}
}
- // Unhandled hardware exception pointers->ExceptionRecord->ExceptionCode at pointers->ExceptionRecord->ExceptionAddress
+ // Unhandled hardware exception pointers->ExceptionRecord->ExceptionCode at pointers->ExceptionRecord->ExceptionAddress
+ return FALSE;
}
/*++
static void sigquit_handler(int code, siginfo_t *siginfo, void *context);
static void sigterm_handler(int code, siginfo_t *siginfo, void *context);
-static void common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext, int numParams, ...);
+static bool common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext, int numParams, ...);
#ifdef INJECT_ACTIVATION_SIGNAL
static void inject_activation_handler(int code, siginfo_t *siginfo, void *context);
{
if (PALIsInitialized())
{
- common_signal_handler(code, siginfo, context, 0);
+ if (common_signal_handler(code, siginfo, context, 0))
+ {
+ return;
+ }
}
if (g_previous_sigill.sa_sigaction != NULL)
{
if (PALIsInitialized())
{
- common_signal_handler(code, siginfo, context, 0);
+ if (common_signal_handler(code, siginfo, context, 0))
+ {
+ return;
+ }
}
if (g_previous_sigfpe.sa_sigaction != NULL)
// TODO: First variable parameter says whether a read (0) or write (non-0) caused the
// fault. We must disassemble the instruction at record.ExceptionAddress
// to correctly fill in this value.
- common_signal_handler(code, siginfo, context, 2, (size_t)0, (size_t)siginfo->si_addr);
+ if (common_signal_handler(code, siginfo, context, 2, (size_t)0, (size_t)siginfo->si_addr))
+ {
+ return;
+ }
}
if (g_previous_sigsegv.sa_sigaction != NULL)
{
if (PALIsInitialized())
{
- common_signal_handler(code, siginfo, context, 0);
+ if (common_signal_handler(code, siginfo, context, 0))
+ {
+ return;
+ }
}
if (g_previous_sigtrap.sa_sigaction != NULL)
// TODO: First variable parameter says whether a read (0) or write (non-0) caused the
// fault. We must disassemble the instruction at record.ExceptionAddress
// to correctly fill in this value.
- common_signal_handler(code, siginfo, context, 2, (size_t)0, (size_t)siginfo->si_addr);
+ if (common_signal_handler(code, siginfo, context, 2, (size_t)0, (size_t)siginfo->si_addr))
+ {
+ return;
+ }
}
if (g_previous_sigbus.sa_sigaction != NULL)
int numParams : number of variable parameters of the exception
... : variable parameters of the exception (each of size_t type)
- (no return value)
+ Returns true if the execution should continue or false if the exception was unhandled
Note:
the "pointers" parameter should contain a valid exception record pointer,
but the ContextRecord pointer will be overwritten.
--*/
-static void common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext, int numParams, ...)
+static bool common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext, int numParams, ...)
{
sigset_t signal_set;
- CONTEXT context;
- EXCEPTION_RECORD record;
- EXCEPTION_POINTERS pointers;
+ CONTEXT *contextRecord;
+ EXCEPTION_RECORD *exceptionRecord;
native_context_t *ucontext;
ucontext = (native_context_t *)sigcontext;
- record.ExceptionCode = CONTEXTGetExceptionCodeForSignal(siginfo, ucontext);
- record.ExceptionFlags = EXCEPTION_IS_SIGNAL;
- record.ExceptionRecord = NULL;
- record.ExceptionAddress = GetNativeContextPC(ucontext);
- record.NumberParameters = numParams;
+ AllocateExceptionRecords(&exceptionRecord, &contextRecord);
+
+ exceptionRecord->ExceptionCode = CONTEXTGetExceptionCodeForSignal(siginfo, ucontext);
+ exceptionRecord->ExceptionFlags = EXCEPTION_IS_SIGNAL;
+ exceptionRecord->ExceptionRecord = NULL;
+ exceptionRecord->ExceptionAddress = GetNativeContextPC(ucontext);
+ exceptionRecord->NumberParameters = numParams;
va_list params;
va_start(params, numParams);
for (int i = 0; i < numParams; i++)
{
- record.ExceptionInformation[i] = va_arg(params, size_t);
+ exceptionRecord->ExceptionInformation[i] = va_arg(params, size_t);
}
- pointers.ExceptionRecord = &record;
-
// Pre-populate context with data from current frame, because ucontext doesn't have some data (e.g. SS register)
// which is required for restoring context
- RtlCaptureContext(&context);
+ RtlCaptureContext(contextRecord);
// Fill context record with required information. from pal.h:
// On non-Win32 platforms, the CONTEXT pointer in the
// PEXCEPTION_POINTERS will contain at least the CONTEXT_CONTROL registers.
- CONTEXTFromNativeContext(ucontext, &context, CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT);
-
- pointers.ContextRecord = &context;
+ CONTEXTFromNativeContext(ucontext, contextRecord, CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT);
/* Unmask signal so we can receive it again */
sigemptyset(&signal_set);
ASSERT("pthread_sigmask failed; error number is %d\n", sigmaskRet);
}
- SEHProcessException(&pointers);
+ contextRecord->ContextFlags |= CONTEXT_EXCEPTION_ACTIVE;
+ // The exception object takes ownership of the exceptionRecord and contextRecord
+ PAL_SEHException exception(exceptionRecord, contextRecord);
+
+ if (SEHProcessException(&exception))
+ {
+ // Exception handling may have modified the context, so update it.
+ CONTEXTToNativeContext(contextRecord, ucontext);
+ return true;
+ }
+
+ return false;
}
/*++
Function:
SEHProcessException
- Build the PAL exception and sent it to any handler registered.
+ Send the PAL exception to any handler registered.
Parameters:
- None
+ PAL_SEHException* exception
Return value:
- Does not return
+ Returns TRUE if the exception happened in managed code and the execution should
+ continue (with possibly modified context).
+ Returns FALSE if the exception happened in managed code and it was not handled.
+ In case the exception was handled by calling a catch handler, it doesn't return at all.
--*/
-VOID
-SEHProcessException(PEXCEPTION_POINTERS pointers);
+BOOL
+SEHProcessException(PAL_SEHException* exception);
+
+/*++
+Function:
+ AllocateExceptionRecords
+
+Parameters:
+ exceptionRecord - output pointer to the allocated Windows exception record
+ contextRecord - output pointer to the allocated Windows context record
+--*/
+VOID
+AllocateExceptionRecords(EXCEPTION_RECORD** exceptionRecord, CONTEXT** contextRecord);
#if !HAVE_MACH_EXCEPTIONS
// TODO: Implement for Mach exceptions. Not in CoreCLR surface area.
}
// Validate that the faulting address is correct; the contents of "p" (0x11).
- if (ex.ExceptionRecord.ExceptionInformation[1] != 0x11)
+ if (ex.GetExceptionRecord()->ExceptionInformation[1] != 0x11)
{
Fail("ERROR: PAL_EXCEPT ExceptionInformation[1] != 0x11\n");
}
}
// Validate that the faulting address is correct; the contents of "p" (0x22).
- if (ex.ExceptionRecord.ExceptionInformation[1] != 0x22)
+ if (ex.GetExceptionRecord()->ExceptionInformation[1] != 0x22)
{
Fail("ERROR: PAL_EXCEPT ExceptionInformation[1] != 0x22\n");
}
MethodDesc * GetUserMethodForILStub(Thread * pThread, UINT_PTR uStubSP, MethodDesc * pILStubMD, Frame ** ppFrameOut);
#ifdef FEATURE_PAL
-VOID PALAPI HandleHardwareException(PAL_SEHException* ex);
+BOOL PALAPI HandleHardwareException(PAL_SEHException* ex);
BOOL PALAPI IsSafeToHandleHardwareException(PCONTEXT contextRecord, PEXCEPTION_RECORD exceptionRecord);
#endif // FEATURE_PAL
PVOID handlerData;
// Indicate that we are performing second pass.
- ex.ExceptionRecord.ExceptionFlags = EXCEPTION_UNWINDING;
+ ex.GetExceptionRecord()->ExceptionFlags = EXCEPTION_UNWINDING;
currentFrameContext = unwindStartContext;
callerFrameContext = &contextStorage;
dispatcherContext.EstablisherFrame = establisherFrame;
dispatcherContext.ContextRecord = currentFrameContext;
+ EXCEPTION_RECORD* exceptionRecord = ex.GetExceptionRecord();
+
if (establisherFrame == ex.TargetFrameSp)
{
// We have reached the frame that will handle the exception.
- ex.ExceptionRecord.ExceptionFlags |= EXCEPTION_TARGET_UNWIND;
+ ex.GetExceptionRecord()->ExceptionFlags |= EXCEPTION_TARGET_UNWIND;
+ ExceptionTracker* pTracker = GetThread()->GetExceptionState()->GetCurrentExceptionTracker();
+ pTracker->TakeExceptionPointersOwnership(&ex);
}
// Perform unwinding of the current frame
- disposition = ProcessCLRException(&ex.ExceptionRecord,
+ disposition = ProcessCLRException(exceptionRecord,
establisherFrame,
currentFrameContext,
&dispatcherContext);
}
else
{
- // TODO: This needs to implemented. Make it fail for now.
UNREACHABLE();
}
}
controlPc = GetIP(frameContext);
unwindStartContext = *frameContext;
- if (!ExecutionManager::IsManagedCode(GetIP(&ex.ContextRecord)))
+ if (!ExecutionManager::IsManagedCode(GetIP(ex.GetContextRecord())))
{
// This is the first time we see the managed exception, set its context to the managed frame that has caused
// the exception to be thrown
- ex.ContextRecord = *frameContext;
- ex.ExceptionRecord.ExceptionAddress = (VOID*)controlPc;
+ *ex.GetContextRecord() = *frameContext;
+ ex.GetExceptionRecord()->ExceptionAddress = (VOID*)controlPc;
}
- ex.ExceptionRecord.ExceptionFlags = 0;
+ ex.GetExceptionRecord()->ExceptionFlags = 0;
memset(&dispatcherContext, 0, sizeof(DISPATCHER_CONTEXT));
disposition = ExceptionContinueSearch;
dispatcherContext.ContextRecord = frameContext;
// Find exception handler in the current frame
- disposition = ProcessCLRException(&ex.ExceptionRecord,
+ disposition = ProcessCLRException(ex.GetExceptionRecord(),
establisherFrame,
- &ex.ContextRecord,
+ ex.GetContextRecord(),
&dispatcherContext);
if (disposition == ExceptionContinueSearch)
// If the exception is hardware exceptions, we use the exception's context record directly
if (isHardwareException)
{
- frameContext = ex.ContextRecord;
+ frameContext = *ex.GetContextRecord();
}
else
{
catch (PAL_SEHException& ex2)
{
isHardwareException = false;
- ex = ex2;
+ ex = std::move(ex2);
}
}
if (pEHTracker == NULL)
{
CorruptionSeverity severity = NotCorrupting;
- if (CEHelper::IsProcessCorruptedStateException(ex.ExceptionRecord.ExceptionCode))
+ if (CEHelper::IsProcessCorruptedStateException(ex.GetExceptionRecord()->ExceptionCode))
{
severity = ProcessCorrupting;
}
IsIPInMarkedJitHelper(controlPc));
}
-VOID PALAPI HandleHardwareException(PAL_SEHException* ex)
+BOOL PALAPI HandleHardwareException(PAL_SEHException* ex)
{
- _ASSERTE(IsSafeToHandleHardwareException(&ex->ContextRecord, &ex->ExceptionRecord));
+ _ASSERTE(IsSafeToHandleHardwareException(ex->GetContextRecord(), ex->GetExceptionRecord()));
- if (ex->ExceptionRecord.ExceptionCode != STATUS_BREAKPOINT && ex->ExceptionRecord.ExceptionCode != STATUS_SINGLE_STEP)
+ if (ex->GetExceptionRecord()->ExceptionCode != STATUS_BREAKPOINT && ex->GetExceptionRecord()->ExceptionCode != STATUS_SINGLE_STEP)
{
// A hardware exception is handled only if it happened in a jitted code or
// in one of the JIT helper functions (JIT_MemSet, ...)
- PCODE controlPc = GetIP(&ex->ContextRecord);
- if (ExecutionManager::IsManagedCode(controlPc) && IsGcMarker(ex->ExceptionRecord.ExceptionCode, &ex->ContextRecord))
+ PCODE controlPc = GetIP(ex->GetContextRecord());
+ if (ExecutionManager::IsManagedCode(controlPc) && IsGcMarker(ex->GetExceptionRecord()->ExceptionCode, ex->GetContextRecord()))
{
- RtlRestoreContext(&ex->ContextRecord, &ex->ExceptionRecord);
- UNREACHABLE();
+ // Exception was handled, let the signal handler return to the exception context. Some registers in the context can
+ // have been modified by the GC.
+ return TRUE;
}
#ifdef _AMD64_
//
// Thus, we will attempt to decode the instruction @ RIP to determine if that
// is the case using the faulting context.
- if ((ex->ExceptionRecord.ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO) &&
- IsDivByZeroAnIntegerOverflow(&ex->ContextRecord))
+ if ((ex->GetExceptionRecord()->ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO) &&
+ IsDivByZeroAnIntegerOverflow(ex->GetContextRecord()))
{
// The exception was an integer overflow, so augment the exception code.
- ex->ExceptionRecord.ExceptionCode = EXCEPTION_INT_OVERFLOW;
+ ex->GetExceptionRecord()->ExceptionCode = EXCEPTION_INT_OVERFLOW;
}
#endif //_AMD64_
// managed code that called the helper, otherwise the stack
// walker would skip all the managed frames upto the next
// explicit frame.
- PAL_VirtualUnwind(&ex->ContextRecord, NULL);
- ex->ExceptionRecord.ExceptionAddress = (PVOID)GetIP(&ex->ContextRecord);
+ PAL_VirtualUnwind(ex->GetContextRecord(), NULL);
+ ex->GetExceptionRecord()->ExceptionAddress = (PVOID)GetIP(ex->GetContextRecord());
}
#ifdef _TARGET_ARM_
else if (IsIPinVirtualStub(controlPc))
{
- AdjustContextForVirtualStub(&ex->ExceptionRecord, &ex->ContextRecord);
+ AdjustContextForVirtualStub(ex->GetExceptionRecord(), ex->GetContextRecord());
}
#endif
- fef.InitAndLink(&ex->ContextRecord);
+ fef.InitAndLink(ex->GetContextRecord());
}
DispatchManagedException(*ex, true /* isHardwareException */);
Thread *pThread = GetThread();
if (pThread != NULL && g_pDebugInterface != NULL)
{
- if (ex->ExceptionRecord.ExceptionCode == STATUS_BREAKPOINT)
+ if (ex->GetExceptionRecord()->ExceptionCode == STATUS_BREAKPOINT)
{
// If this is breakpoint context, it is set up to point to an instruction after the break instruction.
// But debugger expects to see context that points to the break instruction, that's why we correct it.
- SetIP(&ex->ContextRecord, GetIP(&ex->ContextRecord) - CORDbg_BREAK_INSTRUCTION_SIZE);
- ex->ExceptionRecord.ExceptionAddress = (void *)GetIP(&ex->ContextRecord);
+ SetIP(ex->GetContextRecord(), GetIP(ex->GetContextRecord()) - CORDbg_BREAK_INSTRUCTION_SIZE);
+ ex->GetExceptionRecord()->ExceptionAddress = (void *)GetIP(ex->GetContextRecord());
}
- if (g_pDebugInterface->FirstChanceNativeException(&ex->ExceptionRecord,
- &ex->ContextRecord,
- ex->ExceptionRecord.ExceptionCode,
+ if (g_pDebugInterface->FirstChanceNativeException(ex->GetExceptionRecord(),
+ ex->GetContextRecord(),
+ ex->GetExceptionRecord()->ExceptionCode,
pThread))
{
- RtlRestoreContext(&ex->ContextRecord, &ex->ExceptionRecord);
- UNREACHABLE();
+ // Exception was handled, let the signal handler return to the exception context. Some registers in the context can
+ // have been modified by the debugger.
+ return TRUE;
}
}
}
+
+ return FALSE;
}
#endif // FEATURE_PAL
#ifndef FEATURE_PAL
// Clear any held Watson Bucketing details
- GetWatsonBucketTracker()->ClearWatsonBucketDetails();
-#endif // !FEATURE_PAL
+ GetWatsonBucketTracker()->ClearWatsonBucketDetails();
+#else // !FEATURE_PAL
+ if (m_fOwnsExceptionPointers)
+ {
+ PAL_FreeExceptionRecords(m_ptrs.ExceptionRecord, m_ptrs.ContextRecord);
+ m_fOwnsExceptionPointers = FALSE;
+ }
+#endif // !FEATURE_PAL
#endif // DACCESS_COMPILE
}
m_pInitialExplicitFrame = NULL;
m_pLimitFrame = NULL;
m_csfEHClauseOfCollapsedTracker.Clear();
+
+#ifdef FEATURE_PAL
+ m_fOwnsExceptionPointers = FALSE;
+#endif
}
ExceptionTracker(DWORD_PTR dwExceptionPc,
m_sfLastUnwoundEstablisherFrame.Clear();
m_pInitialExplicitFrame = NULL;
m_csfEHClauseOfCollapsedTracker.Clear();
+
+#ifdef FEATURE_PAL
+ m_fOwnsExceptionPointers = FALSE;
+#endif
}
~ExceptionTracker()
bool IsStackOverflowException();
+#ifdef FEATURE_PAL
+ void TakeExceptionPointersOwnership(PAL_SEHException* ex)
+ {
+ _ASSERTE(ex->GetExceptionRecord() == m_ptrs.ExceptionRecord);
+ _ASSERTE(ex->GetContextRecord() == m_ptrs.ContextRecord);
+ ex->Clear();
+ m_fOwnsExceptionPointers = TRUE;
+ }
+#endif // FEATURE_PAL
+
private:
DWORD_PTR
CallHandler(UINT_PTR dwHandlerStartPC,
StackRange m_ScannedStackRange;
DAC_EXCEPTION_POINTERS m_ptrs;
+#ifdef FEATURE_PAL
+ BOOL m_fOwnsExceptionPointers;
+#endif
OBJECTHANDLE m_hThrowable;
StackTraceInfo m_StackTraceInfo;
UINT_PTR m_uCatchToCallPC;
} \
catch (PAL_SEHException& ex) \
{ \
- exCopy = ex; \
+ exCopy = std::move(ex); \
hasCaughtException = true; \
} \
if (hasCaughtException) \