335 lines
11 KiB
C++
335 lines
11 KiB
C++
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
|
|
// * searching exception table
|
|
// * make machine faults raise CIL exceptions
|
|
//
|
|
// C code should always invoke the filter BartokMachineFaultFilter
|
|
// before it attempts to handle any of the machine faults handled
|
|
// below. BartokMachineFaultFilter causes an appropriate exception
|
|
// to be thrown if a machine fault occurred in CIL code.
|
|
|
|
# include "hal.h"
|
|
|
|
extern BOOL KdDebuggerNotPresent;
|
|
#ifndef NOEVENTING
|
|
#include "eventing.h"
|
|
#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];
|
|
|
|
// TODO: BUGBUG_X64:
|
|
// ExceptionTableLookupReturn breaks down with 64-bit pointers.
|
|
// We will need to revisit the code
|
|
// that uses the ExceptionTableLookupReturn result (__throwDispatcher*).
|
|
|
|
#ifdef ISA_IX64
|
|
struct PtrPair {
|
|
uintptr a;
|
|
uintptr b;
|
|
};
|
|
typedef PtrPair lookupReturnType;
|
|
#else
|
|
typedef uint64 lookupReturnType;
|
|
#endif
|
|
union ExceptionTableLookupReturn {
|
|
lookupReturnType etlReturn;
|
|
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 = (int) (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;
|
|
}
|
|
|
|
Class_System_VTable * getRealVTable(Class_System_VTable * vt)
|
|
{
|
|
return (Class_System_VTable *)((uintptr)vt & (~((uintptr)3)));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ExceptionAssert is called when an unexpected condition happens during
|
|
// exception processing
|
|
|
|
void ExceptionAssert()
|
|
{
|
|
#if SPIN
|
|
// Useful for native cases when no debugger is avaiable
|
|
while (1)
|
|
;
|
|
#endif
|
|
|
|
__debugbreak();
|
|
}
|
|
|
|
// 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.
|
|
|
|
lookupReturnType __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 (exception->_throwAddress == NULL) {
|
|
exception->_throwAddress = throwAddr;
|
|
}
|
|
|
|
// search for table using throwAddr
|
|
ExceptionTableEntry *baseEntry = NULL;
|
|
ExceptionTableEntry *endEntry = NULL;
|
|
uintptr codeBase = LookupTable(throwAddr, &baseEntry, &endEntry);
|
|
|
|
#if SINGULARITY_KERNEL
|
|
#if 0
|
|
printf(" codeBase=%p baseEntry=%p endEntry=%p\n",
|
|
codeBase, baseEntry, endEntry);
|
|
#endif
|
|
#endif
|
|
|
|
if (codeBase == (uintptr) -1) {
|
|
#if SINGULARITY_KERNEL
|
|
#if 0
|
|
printf("Exception outside of any known code regions!\n");
|
|
#endif
|
|
if (!KdDebuggerNotPresent) {
|
|
ExceptionAssert();
|
|
}
|
|
#if 0
|
|
printf("Terminating by exception!\n");
|
|
#endif
|
|
Class_System_VTable::g_TerminateByException(exception);
|
|
ExceptionAssert();
|
|
#elif SINGULARITY_PROCESS
|
|
Assert("Exception outside of any known code regions!\n");
|
|
Class_System_VTable::g_TerminateByException(exception);
|
|
#endif
|
|
}
|
|
|
|
// bsearch for throwAddr
|
|
int minIndex = 0;
|
|
int maxIndex = (int) (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
|
|
#if 0
|
|
printf("Exception outside of known code region for %p\n", codeBase);
|
|
#endif
|
|
if (!KdDebuggerNotPresent) {
|
|
__debugbreak();
|
|
}
|
|
Class_System_VTable::g_TerminateByException(exception);
|
|
#if 0
|
|
printf("top-level exception handling code failed\n");
|
|
#endif
|
|
__debugbreak();
|
|
#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");
|
|
#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;
|
|
#ifndef NOEVENTING
|
|
LogExceptionInfo((UIntPtr)(codeBase + throwAddr),
|
|
0,
|
|
getRealVTable(exception->postHeader.vtableObject)->vtableType->name);
|
|
#endif
|
|
} else {
|
|
retval.exceptionClass = entry->exceptionClass;
|
|
retval.handlerAddr = entry->handlerAddr + codeBase;
|
|
#ifndef NOEVENTING
|
|
LogExceptionInfo((UIntPtr)(codeBase + throwAddr),
|
|
(UIntPtr)(retval.handlerAddr),
|
|
getRealVTable(exception->postHeader.vtableObject)->vtableType->name);
|
|
#endif
|
|
}
|
|
|
|
#if SINGULARITY_KERNEL
|
|
if (!exception->_notifiedDebugger && !KdDebuggerNotPresent) {
|
|
exception->_notifiedDebugger = true;
|
|
|
|
bool iflag =
|
|
Class_Microsoft_Singularity_Processor::g_DisableInterrupts();
|
|
Class_System_VTable * vtable = getRealVTable(exception->postHeader.vtableObject);
|
|
#if 0
|
|
if ((entry->frameSetupInfo & 0x1) != 0) {
|
|
printf("\n---- First chance %ls at %p. Handler is shared ----\n",
|
|
&(vtable->vtableType->name->m_firstChar),
|
|
codeBase + throwAddr);
|
|
}
|
|
else {
|
|
printf("\n---- First chance %ls at %p. Handler is %p ----\n",
|
|
&(vtable->vtableType->name->m_firstChar),
|
|
codeBase + throwAddr,
|
|
retval.handlerAddr);
|
|
}
|
|
if (exception->_message != NULL) {
|
|
printf("------ Message: %ls\n",
|
|
&(exception->_message->m_firstChar));
|
|
}
|
|
#endif
|
|
KdNotifyException(exception, (uint32) throwAddr);
|
|
Class_Microsoft_Singularity_Processor::g_RestoreInterrupts(iflag);
|
|
}
|
|
#endif
|
|
|
|
return(retval.etlReturn);
|
|
}
|
|
|