432 lines
15 KiB
C++
432 lines
15 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// halexn.cpp - Singularity/Bartok Exception Handling
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// Runtime support for CIL exceptions
|
|
// * searching exception table
|
|
// * make machine faults raise CIL exceptions
|
|
//
|
|
|
|
#if SINGULARITY
|
|
# include "hal.h"
|
|
#else
|
|
# include "brt.h"
|
|
# include <malloc.h>
|
|
#endif
|
|
|
|
extern "C" void __cdecl _throwDispatcher();
|
|
extern "C" void __cdecl _throwArithmeticException();
|
|
extern "C" void __cdecl _throwDivideByZeroException();
|
|
extern "C" void __cdecl _throwNullReferenceException();
|
|
extern "C" void __cdecl _throwOverflowException();
|
|
extern "C" void __cdecl _throwStackOverflowException();
|
|
|
|
#ifdef SINGULARITY
|
|
extern void DumpStack(UIntPtr ebp);
|
|
extern BOOL KdDebuggerNotPresent;
|
|
#endif
|
|
|
|
// See Bartok\tables\ExceptionTable.cs for a description of ExceptionTableEntry
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
struct ExceptionTableEntry {
|
|
uintptr scopeBaseAddr;
|
|
union {
|
|
struct {
|
|
Class_System_Type *exceptionClass; // low bit is zero
|
|
uintptr handlerAddr;
|
|
};
|
|
struct {
|
|
uintptr frameSetupInfo; // low bit is one
|
|
uintptr spillSize;
|
|
};
|
|
};
|
|
};
|
|
|
|
struct TableEntry {
|
|
ExceptionTableEntry * tableBaseAddr;
|
|
ExceptionTableEntry * tableEndAddr;
|
|
};
|
|
|
|
extern TableEntry TableBase[1];
|
|
extern TableEntry TableBound[1];
|
|
|
|
// This will break down with 64 bit pointers. We will need to revisit the code
|
|
// that uses the ExceptionTableLookupReturn result (__throwDispatcher*).
|
|
STATIC_ASSERT(sizeof(uint64) == 2 * sizeof(uintptr));
|
|
|
|
union ExceptionTableLookupReturn {
|
|
uint64 qword;
|
|
struct {
|
|
Class_System_Type *exceptionClass;
|
|
uintptr handlerAddr;
|
|
};
|
|
struct {
|
|
uintptr frameSetupInfo;
|
|
uintptr spillSize;
|
|
};
|
|
};
|
|
|
|
static uintptr LookupTable(uintptr throwAddr,
|
|
ExceptionTableEntry **tableBaseEntry,
|
|
ExceptionTableEntry **tableEndEntry) {
|
|
#if 0
|
|
printf("LookupTable(throwAddr=%p, tableBase=%p, tableEnd=%p)\n",
|
|
throwAddr, tableBaseEntry, tableEndEntry);
|
|
printf(" TableBase=%p, TableBound=%p, maxIndex = %d\n",
|
|
TableBase, TableBound, TableBound - TableBase);
|
|
printf(" callSiteTableCount=%d\n",
|
|
Class_System_GCs_CallStack::c_callSiteTableCount);
|
|
printf(" codeBaseStartTable=%p\n",
|
|
Class_System_GCs_CallStack::c_codeBaseStartTable);
|
|
printf(" returnAddressToCallSiteSetNumbers=%p\n",
|
|
Class_System_GCs_CallStack::c_returnAddressToCallSiteSetNumbers);
|
|
printf(" callSiteSetCount=%p\n",
|
|
Class_System_GCs_CallStack::c_callSiteSetCount);
|
|
#endif
|
|
|
|
// search to find which table to use
|
|
int maxIndex = TableBound - TableBase;
|
|
uintptr codeBase = (uintptr) -1;
|
|
uintptr relCodeAddr = 0;
|
|
for (int i = 0; i < maxIndex; i++) {
|
|
TableEntry *entry = &TableBase[i];
|
|
#if 0
|
|
printf(" TableBase[%d] base=%p end=%p codeBaseStartTable[]=%p\n",
|
|
i, entry->tableBaseAddr, entry->tableEndAddr,
|
|
Class_System_GCs_CallStack::c_codeBaseStartTable[i]);
|
|
#endif
|
|
codeBase =
|
|
((uintptr*)Class_System_GCs_CallStack::c_codeBaseStartTable)[i];
|
|
|
|
if (throwAddr < codeBase) {
|
|
continue;
|
|
}
|
|
relCodeAddr = throwAddr - codeBase;
|
|
*tableBaseEntry = entry->tableBaseAddr;
|
|
*tableEndEntry = entry->tableEndAddr;
|
|
|
|
#if 0
|
|
printf(" relCodeAddr = %p\n", relCodeAddr);
|
|
printf(" tableBase scopeBaseAddr=%p class=%p handler=%p\n",
|
|
(*tableBaseEntry)->scopeBaseAddr,
|
|
(*tableBaseEntry)->exceptionClass,
|
|
(*tableBaseEntry)->handlerAddr);
|
|
printf(" tableEnd scopeBaseAddr=%p class=%p handler=%p\n",
|
|
(*tableEndEntry)->scopeBaseAddr,
|
|
(*tableEndEntry)->exceptionClass,
|
|
(*tableEndEntry)->handlerAddr);
|
|
#endif
|
|
|
|
if ((relCodeAddr >= (*tableBaseEntry)->scopeBaseAddr)
|
|
&& (relCodeAddr <= (*tableEndEntry)->scopeBaseAddr)) {
|
|
return codeBase;
|
|
}
|
|
}
|
|
|
|
return (uintptr) -1;
|
|
//exit(-2);
|
|
//__asm int 3;
|
|
}
|
|
|
|
#if SINGULARITY
|
|
Class_System_VTable * getRealVTable(Class_System_VTable * vt)
|
|
{
|
|
return (Class_System_VTable *)((uintptr)vt & (~((uintptr)3)));
|
|
}
|
|
#endif
|
|
|
|
// search an exception table
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
// search an exception table
|
|
// - Returns the exception in eax.
|
|
// - Returns the handler address in edx.
|
|
// OR if the shared unwind handler should be used
|
|
// - Returns the frameSetupInfo in eax.
|
|
// - Returns the spill area size in edx.
|
|
|
|
uint64 __fastcall ExceptionTableLookup(Class_System_Exception *exception,
|
|
uintptr throwAddr) {
|
|
#if 0
|
|
printf("\n");
|
|
printf("ExceptionTableLookup(exception=%p, vtable=%p, throwAddr=%p)\n",
|
|
exception, ((uintptr *)exception)[0], throwAddr);
|
|
#endif
|
|
|
|
#if SINGULARITY
|
|
if (exception->_throwAddress == NULL) {
|
|
exception->_throwAddress = throwAddr;
|
|
}
|
|
#endif
|
|
|
|
// search for table using throwAddr
|
|
ExceptionTableEntry *baseEntry = NULL;
|
|
ExceptionTableEntry *endEntry = NULL;
|
|
uintptr codeBase = LookupTable(throwAddr, &baseEntry, &endEntry);
|
|
|
|
#if 0
|
|
printf(" codeBase=%p baseEntry=%p endEntry=%p\n",
|
|
codeBase, baseEntry, endEntry);
|
|
#endif
|
|
|
|
if (codeBase == (uintptr) -1) {
|
|
#if SINGULARITY_KERNEL
|
|
printf("Exception outside of any known code regions!\n");
|
|
if (!KdDebuggerNotPresent) {
|
|
__asm int 3;
|
|
}
|
|
Class_System_VTable::g_TerminateByException(exception);
|
|
__asm int 3;
|
|
#elif SINGULARITY_PROCESS
|
|
Assert("Exception outside of any known code regions!\n");
|
|
Class_System_VTable::g_TerminateByException(exception);
|
|
__asm int 3;
|
|
#else
|
|
Class_System_VTable::g_TerminateByException(exception);
|
|
exit(-2);
|
|
#endif
|
|
}
|
|
|
|
// bsearch for throwAddr
|
|
int minIndex = 0;
|
|
int maxIndex = endEntry-baseEntry;
|
|
throwAddr -= codeBase;
|
|
|
|
if (throwAddr < baseEntry[minIndex].scopeBaseAddr ||
|
|
throwAddr > baseEntry[maxIndex].scopeBaseAddr) {
|
|
// BUGBUG: callback to C# code that may trigger GC
|
|
#if SINGULARITY_KERNEL
|
|
printf("Exception outside of known code region for %p\n", codeBase);
|
|
if (!KdDebuggerNotPresent) {
|
|
__asm int 3;
|
|
}
|
|
Class_System_VTable::g_TerminateByException(exception);
|
|
printf("top-level exception handling code failed\n");
|
|
__asm int 3;
|
|
#elif SINGULARITY_PROCESS
|
|
Assert("Exception outside of known code region for.\n");
|
|
Class_System_VTable::g_TerminateByException(exception);
|
|
Assert("top-level exception handling code failed\n");
|
|
__asm int 3;
|
|
#else
|
|
Class_System_VTable::g_TerminateByException(exception);
|
|
fprintf(stderr, "top-level exception handling code failed");
|
|
fflush(stderr);
|
|
__asm int 3;
|
|
exit(-2);
|
|
#endif
|
|
}
|
|
while (minIndex+1 < maxIndex) {
|
|
int midIndex = (minIndex+maxIndex)/2;
|
|
uintptr midAddr = baseEntry[midIndex].scopeBaseAddr;
|
|
if (throwAddr < midAddr) {
|
|
maxIndex = midIndex;
|
|
}
|
|
else {
|
|
minIndex = midIndex;
|
|
}
|
|
}
|
|
ExceptionTableEntry *entry = &baseEntry[minIndex];
|
|
|
|
// back up to first entry containing throwAddr (there may be several)
|
|
uintptr baseAddr;
|
|
for (baseAddr = entry->scopeBaseAddr;
|
|
entry->scopeBaseAddr == baseAddr && entry >= baseEntry;
|
|
entry--) {
|
|
continue;
|
|
}
|
|
|
|
// check each of the handlers in turn
|
|
|
|
for (entry++; entry->scopeBaseAddr <= throwAddr; entry++) {
|
|
#if 0
|
|
printf(" entry=%p[%d] "
|
|
"scopeBaseAddr=%p exceptionClass=%p handler=%p\n",
|
|
entry, entry - baseEntry,
|
|
entry->scopeBaseAddr, entry->exceptionClass, entry->handlerAddr);
|
|
#endif
|
|
|
|
// 0 now means "no frame pointer omission and no callee save registers
|
|
// have been saved to the stack":
|
|
// Assert(entry->exceptionClass);
|
|
|
|
Assert(((entry->frameSetupInfo & 0x1) != 0)
|
|
|| (entry->handlerAddr != NULL));
|
|
if (((entry->frameSetupInfo & 0x1) != 0)
|
|
|| Class_System_VTable::g_IsExceptionHandler(entry->exceptionClass,
|
|
exception)) {
|
|
#if 0
|
|
printf("Found matching exception entry: %p\n", entry);
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
Assert(entry->scopeBaseAddr == baseAddr);
|
|
|
|
ExceptionTableLookupReturn retval;
|
|
|
|
if((entry->frameSetupInfo & 0x1) != 0) {
|
|
retval.frameSetupInfo = entry->frameSetupInfo;
|
|
retval.spillSize = entry->spillSize;
|
|
#if SINGULARITY
|
|
Class_Microsoft_Singularity_Tracing::g_Log
|
|
(9, "Throw {0} from {1:x8} to shared unwind handler",
|
|
getRealVTable(exception->postHeader.vtableObject)->vtableType->name,
|
|
(UIntPtr)(codeBase + throwAddr));
|
|
#endif
|
|
}
|
|
else {
|
|
retval.exceptionClass = entry->exceptionClass;
|
|
retval.handlerAddr = entry->handlerAddr + codeBase;
|
|
#if SINGULARITY
|
|
Class_Microsoft_Singularity_Tracing::g_Log
|
|
(9, "Throw {0} from {1:x8} to {2:x8}",
|
|
getRealVTable(exception->postHeader.vtableObject)->vtableType->name,
|
|
(UIntPtr)(codeBase + throwAddr),
|
|
(UIntPtr)(retval.handlerAddr));
|
|
#endif
|
|
}
|
|
|
|
#if SINGULARITY_KERNEL || SINGULARITY_PROCESS
|
|
if (!exception->_notifiedDebugger) {
|
|
exception->_notifiedDebugger = true;
|
|
|
|
bool iflag =
|
|
Class_Microsoft_Singularity_Processor::g_DisableInterrupts();
|
|
Class_System_VTable * vtable = getRealVTable(exception->postHeader.vtableObject);
|
|
if((entry->frameSetupInfo & 0x1) != 0) {
|
|
Class_Microsoft_Singularity_Tracing::g_Log
|
|
(2, "First chance {0} at {1:x8}. Handler is shared",
|
|
vtable->vtableType->name,
|
|
(UIntPtr)(codeBase + throwAddr));
|
|
}
|
|
else {
|
|
Class_Microsoft_Singularity_Tracing::g_Log
|
|
(2, "First chance {0} at {1:x8}. Handler is {2:x8}",
|
|
vtable->vtableType->name,
|
|
(UIntPtr)(codeBase + throwAddr),
|
|
(UIntPtr)retval.handlerAddr);
|
|
}
|
|
if (exception->_message != NULL) {
|
|
Class_Microsoft_Singularity_Tracing::g_Log
|
|
(0, " Message: {0}", exception->_message, 0);
|
|
}
|
|
|
|
if (!KdDebuggerNotPresent) {
|
|
KdDebugTrapData trapData, *trapDataPtr = &trapData;
|
|
trapData.tag = KdDebugTrapData::FIRST_CHANCE_EXCEPTION;
|
|
trapData.u.firstChanceException.throwAddr = throwAddr;
|
|
__asm {
|
|
// int 3; // uncomment this line to cause every exception to break here.
|
|
mov eax, exception;
|
|
mov fs:[0]Struct_Microsoft_Singularity_X86_ProcessorContext.exception, eax;
|
|
mov eax, trapDataPtr;
|
|
int 29; // Notify debugging stub of first chance exception.
|
|
}
|
|
}
|
|
Class_Microsoft_Singularity_Processor::g_RestoreInterrupts(iflag);
|
|
}
|
|
#endif
|
|
|
|
return(retval.qword);
|
|
}
|
|
|
|
#if !SINGULARITY
|
|
|
|
void alterContinuation(PCONTEXT contextRecord, DWORD newPC) {
|
|
// assign edx location of instruction that faulted
|
|
contextRecord->Edx = contextRecord->Eip;
|
|
contextRecord->Eip = (DWORD) newPC;
|
|
}
|
|
|
|
// BartokMachineFaultFilter:
|
|
// This filter is invoked when when a machine fault occurs. If the
|
|
// machine fault occurred in MSIL code, it modifies the context so
|
|
// that the appropriate exception is thrown when the filter returns.
|
|
//
|
|
|
|
LONG WINAPI BartokMachineFaultFilter(struct _EXCEPTION_POINTERS *exnInfo) {
|
|
PEXCEPTION_RECORD exceptionRecord = exnInfo->ExceptionRecord;
|
|
PCONTEXT contextRecord = exnInfo->ContextRecord;
|
|
|
|
#if 0
|
|
printf("BartokMachineFaultFilter(exnInfo=%p)\n", exnInfo);
|
|
#endif
|
|
|
|
// check if it is an exception in MSIL code.
|
|
|
|
// points at instruction that faulted
|
|
uintptr throwAddr = contextRecord->Eip;
|
|
|
|
// search for table using throwAddr
|
|
ExceptionTableEntry *baseEntry = NULL;
|
|
ExceptionTableEntry *endEntry = NULL;
|
|
uintptr codeBase = LookupTable(throwAddr, &baseEntry, &endEntry);
|
|
|
|
int minIndex = 0;
|
|
int maxIndex = endEntry-baseEntry;
|
|
throwAddr -= codeBase;
|
|
|
|
if (throwAddr < baseEntry[minIndex].scopeBaseAddr ||
|
|
throwAddr > baseEntry[maxIndex].scopeBaseAddr) {
|
|
// nope, it isn't.
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
switch (exceptionRecord->ExceptionCode) {
|
|
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
|
case EXCEPTION_INT_DIVIDE_BY_ZERO: {
|
|
alterContinuation(contextRecord,
|
|
(DWORD) _throwDivideByZeroException);
|
|
return EXCEPTION_CONTINUE_EXECUTION;
|
|
}
|
|
case EXCEPTION_INT_OVERFLOW: {
|
|
alterContinuation(contextRecord,(DWORD) _throwOverflowException);
|
|
return EXCEPTION_CONTINUE_EXECUTION;
|
|
}
|
|
case EXCEPTION_STACK_OVERFLOW: {
|
|
alterContinuation(contextRecord,
|
|
(DWORD) _throwStackOverflowException);
|
|
return EXCEPTION_CONTINUE_EXECUTION;
|
|
}
|
|
case EXCEPTION_ACCESS_VIOLATION: {
|
|
DWORD *exceptionInformation = exceptionRecord->ExceptionInformation;
|
|
// BUGBUG: how much memory is protected? NB we must trap negative offsets
|
|
// from null because the first access through a null reference may be
|
|
// to a header word (e.g. opening the object for read in a memory transaction)
|
|
if (exceptionInformation[1] < 4096) {
|
|
exceptionInformation[1] >= 0xfffff000) {
|
|
alterContinuation(contextRecord,
|
|
(DWORD) _throwNullReferenceException);
|
|
return EXCEPTION_CONTINUE_EXECUTION;
|
|
}
|
|
else {
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
}
|
|
default: {
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the stack pointer is too close to the bottom of the stack, abort the
|
|
// process otherwise reset the guard page.
|
|
// This function is called when we handle stack overflow exception.
|
|
|
|
void ResetGuardPage(){
|
|
|
|
if (_resetstkoflw() == 0) {
|
|
fprintf(stderr, "Reset from stack overflow failed. Abort.");
|
|
fflush(stderr);
|
|
_exit(-2);
|
|
}
|
|
}
|
|
|
|
#endif // !SINGULARITY
|