Skip to content

Commit d0a342d

Browse files
committed
Resuming after catch via native exception
This commit moves the resuming after catch to using native exception handling instead of fragile context capturing, which was not correct anyways. It also adds handling of exceptions comming out of native runtime methods called from the interpreter.
1 parent be6fae0 commit d0a342d

File tree

14 files changed

+1398
-1233
lines changed

14 files changed

+1398
-1233
lines changed

src/coreclr/inc/eetwain.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ virtual void LeaveCatch(GCInfoToken gcInfoToken,
307307
PCONTEXT pCtx)=0;
308308
#else // FEATURE_EH_FUNCLETS
309309
virtual DWORD_PTR CallFunclet(OBJECTREF throwable, void* pHandler, REGDISPLAY *pRD, ExInfo *pExInfo, bool isFilter) = 0;
310-
virtual void PrepareForResumeAfterCatch(CONTEXT *pContext) = 0;
310+
virtual void ResumeAfterCatch(CONTEXT *pContext, size_t targetSSP, bool fIntercepted) = 0;
311311
#if defined(HOST_AMD64) && defined(HOST_WINDOWS)
312312
virtual void UpdateSSP(PREGDISPLAY pRD) = 0;
313313
#endif // HOST_AMD64 && HOST_WINDOWS
@@ -557,10 +557,7 @@ virtual void LeaveCatch(GCInfoToken gcInfoToken,
557557
PCONTEXT pCtx);
558558
#else // FEATURE_EH_FUNCLETS
559559
virtual DWORD_PTR CallFunclet(OBJECTREF throwable, void* pHandler, REGDISPLAY *pRD, ExInfo *pExInfo, bool isFilter);
560-
virtual void PrepareForResumeAfterCatch(CONTEXT *pContext)
561-
{
562-
// Nothing to do for non-interpreter code manager.
563-
}
560+
virtual void ResumeAfterCatch(CONTEXT *pContext, size_t targetSSP, bool fIntercepted);
564561

565562
#if defined(HOST_AMD64) && defined(HOST_WINDOWS)
566563
virtual void UpdateSSP(PREGDISPLAY pRD);
@@ -774,7 +771,7 @@ virtual void LeaveCatch(GCInfoToken gcInfoToken,
774771
}
775772
#else // FEATURE_EH_FUNCLETS
776773
virtual DWORD_PTR CallFunclet(OBJECTREF throwable, void* pHandler, REGDISPLAY *pRD, ExInfo *pExInfo, bool isFilter);
777-
virtual void PrepareForResumeAfterCatch(CONTEXT *pContext);
774+
virtual void ResumeAfterCatch(CONTEXT *pContext, size_t targetSSP, bool fIntercepted);
778775
#if defined(HOST_AMD64) && defined(HOST_WINDOWS)
779776
virtual void UpdateSSP(PREGDISPLAY pRD);
780777
#endif // HOST_AMD64 && HOST_WINDOWS

src/coreclr/vm/eetwain.cpp

Lines changed: 108 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#endif // FEATURE_INTERPRETER
2323

2424
#include "exinfo.h"
25+
#include "excep.h"
2526

2627
#ifdef TARGET_X86
2728

@@ -2075,21 +2076,68 @@ DWORD_PTR EECodeManager::CallFunclet(OBJECTREF throwable, void* pHandler, REGDIS
20752076
return dwResult;
20762077
}
20772078

2079+
void EECodeManager::ResumeAfterCatch(CONTEXT *pContext, size_t targetSSP, bool fIntercepted)
2080+
{
2081+
Thread *pThread = GetThread();
2082+
DWORD_PTR dwResumePC = GetIP(pContext);
2083+
UINT_PTR uAbortAddr = 0;
2084+
if (!fIntercepted)
2085+
{
2086+
CopyOSContext(pThread->m_OSContext, pContext);
2087+
uAbortAddr = (UINT_PTR)COMPlusCheckForAbort(dwResumePC);
2088+
}
2089+
2090+
if (uAbortAddr)
2091+
{
2092+
STRESS_LOG2(LF_EH, LL_INFO10, "Thread abort in progress, resuming under control: IP=%p, SP=%p\n", dwResumePC, GetSP(pContext));
2093+
2094+
// The dwResumePC is passed to the THROW_CONTROL_FOR_THREAD_FUNCTION ASM helper so that
2095+
// it can establish it as its return address and native stack unwinding can work properly.
2096+
#ifdef TARGET_AMD64
2097+
#ifdef TARGET_UNIX
2098+
pContext->Rdi = dwResumePC;
2099+
#else
2100+
pContext->Rcx = dwResumePC;
2101+
#endif
2102+
#elif defined(TARGET_ARM) || defined(TARGET_ARM64)
2103+
// On ARM & ARM64, we save off the original PC in Lr. This is the same as done
2104+
// in HandleManagedFault for H/W generated exceptions.
2105+
pContext->Lr = dwResumePC;
2106+
#elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
2107+
pContext->Ra = dwResumePC;
2108+
#endif
2109+
2110+
SetIP(pContext, uAbortAddr);
2111+
}
2112+
else
2113+
{
2114+
STRESS_LOG2(LF_EH, LL_INFO100, "Resuming after exception at IP=%p, SP=%p\n", GetIP(pContext), GetSP(pContext));
2115+
}
2116+
2117+
ClrRestoreNonvolatileContext(pContext, targetSSP);
2118+
}
2119+
20782120
#if defined(HOST_AMD64) && defined(HOST_WINDOWS)
2079-
void EECodeManager::UpdateSSP(PREGDISPLAY pRD)
2121+
2122+
size_t GetSSPForFrameOnCurrentStack(TADDR ip)
20802123
{
20812124
size_t *targetSSP = (size_t *)_rdsspq();
20822125
// The SSP we search is pointing to the return address of the frame represented
2083-
// by the pRD->ControlPC. So we search for the instruction pointer from
2126+
// by the passed in IP. So we search for the instruction pointer from
20842127
// the context and return one slot up from there.
20852128
if (targetSSP != NULL)
20862129
{
2087-
while (*targetSSP++ != pRD->ControlPC)
2130+
while (*targetSSP++ != ip)
20882131
{
20892132
}
20902133
}
20912134

2092-
pRD->SSP = (size_t)targetSSP;
2135+
return (size_t)targetSSP;
2136+
}
2137+
2138+
void EECodeManager::UpdateSSP(PREGDISPLAY pRD)
2139+
{
2140+
pRD->SSP = GetSSPForFrameOnCurrentStack(pRD->ControlPC);
20932141
}
20942142
#endif // HOST_AMD64 && HOST_WINDOWS
20952143

@@ -2101,13 +2149,64 @@ DWORD_PTR InterpreterCodeManager::CallFunclet(OBJECTREF throwable, void* pHandle
21012149
return 0;
21022150
}
21032151

2104-
void InterpreterCodeManager::PrepareForResumeAfterCatch(CONTEXT *pContext)
2152+
void InterpreterCodeManager::ResumeAfterCatch(CONTEXT *pContext, size_t targetSSP, bool fIntercepted)
21052153
{
21062154
Thread *pThread = GetThread();
21072155
InterpreterFrame * pInterpreterFrame = (InterpreterFrame*)pThread->GetFrame();
2108-
pInterpreterFrame->SetResumeContext(GetSP(pContext), GetIP(pContext));
2109-
pInterpreterFrame->RestoreInterpExecMethodContext(pContext);
2156+
TADDR resumeSP = GetSP(pContext);
2157+
TADDR resumeIP = GetIP(pContext);
2158+
2159+
ClrCaptureContext(pContext);
2160+
2161+
// Unwind to the caller of the Ex.RhThrowEx / Ex.RhThrowHwEx
2162+
Thread::VirtualUnwindToFirstManagedCallFrame(pContext);
2163+
2164+
#if defined(HOST_AMD64) && defined(HOST_WINDOWS)
2165+
targetSSP = GetSSPForFrameOnCurrentStack(GetIP(pContext));
2166+
#endif // HOST_AMD64 && HOST_WINDOWS
2167+
2168+
CONTEXT firstNativeContext;
2169+
size_t firstNativeSSP;
2170+
2171+
// Find the native frames chain that contains the resumeSP
2172+
do
2173+
{
2174+
// Skip all managed frames upto a native frame
2175+
while (ExecutionManager::IsManagedCode(GetIP(pContext)))
2176+
{
2177+
Thread::VirtualUnwindCallFrame(pContext);
2178+
#if defined(HOST_AMD64) && defined(HOST_WINDOWS)
2179+
if (targetSSP != 0)
2180+
{
2181+
targetSSP += sizeof(size_t);
2182+
}
2183+
#endif
2184+
}
2185+
2186+
// Save the first native context after managed frames. This will be the context where we throw the resume after catch exception from.
2187+
firstNativeContext = *pContext;
2188+
firstNativeSSP = targetSSP;
2189+
// Move over all native frames until we move over the resumeSP
2190+
while ((GetSP(pContext) < resumeSP) && !ExecutionManager::IsManagedCode(GetIP(pContext)))
2191+
{
2192+
#ifdef TARGET_UNIX
2193+
PAL_VirtualUnwind(pContext, NULL);
2194+
#else
2195+
Thread::VirtualUnwindCallFrame(pContext);
2196+
#endif
2197+
#if defined(HOST_AMD64) && defined(HOST_WINDOWS)
2198+
if (targetSSP != 0)
2199+
{
2200+
targetSSP += sizeof(size_t);
2201+
}
2202+
#endif
2203+
}
2204+
}
2205+
while (GetSP(pContext) < resumeSP);
2206+
2207+
ExecuteFunctionBelowContext((PCODE)ThrowResumeAfterCatchException, &firstNativeContext, firstNativeSSP, resumeSP, resumeIP);
21102208
}
2209+
21112210
#if defined(HOST_AMD64) && defined(HOST_WINDOWS)
21122211
void InterpreterCodeManager::UpdateSSP(PREGDISPLAY pRD)
21132212
{
@@ -2306,7 +2405,7 @@ bool InterpreterCodeManager::UnwindStackFrame(PREGDISPLAY pRD,
23062405
return true;
23072406
}
23082407

2309-
void InterpreterCodeManager::EnsureCallerContextIsValid( PREGDISPLAY pRD, EECodeInfo * pCodeInfo /*= NULL*/, unsigned flags /*= 0*/)
2408+
void InterpreterCodeManager::EnsureCallerContextIsValid(PREGDISPLAY pRD, EECodeInfo * pCodeInfo /*= NULL*/, unsigned flags /*= 0*/)
23102409
{
23112410
CONTRACTL
23122411
{
@@ -2322,9 +2421,7 @@ void InterpreterCodeManager::EnsureCallerContextIsValid( PREGDISPLAY pRD, EECod
23222421
*(pRD->pCallerContext) = *(pRD->pCurrentContext);
23232422
TADDR sp = (TADDR)GetRegdisplaySP(pRD);
23242423
VirtualUnwindInterpreterCallFrame(sp, pRD->pCallerContext);
2325-
// Preserve the context pointers, besides the SP, IP, FP and the first argument register, all the registers are
2326-
// kept untouched and keep the state it the InterpExecMethod. We use the context pointers to update the registers
2327-
// before resuming after a catch using the original context.
2424+
// Preserve the context pointers, they are used by floating point registers unwind starting at the original context.
23282425
memcpy(pRD->pCallerContextPointers, pRD->pCurrentContextPointers, sizeof(KNONVOLATILE_CONTEXT_POINTERS));
23292426

23302427
pRD->IsCallerContextValid = TRUE;

src/coreclr/vm/excep.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7307,6 +7307,48 @@ VOID DECLSPEC_NORETURN UnwindAndContinueRethrowHelperAfterCatch(Frame* pEntryFra
73077307
}
73087308
}
73097309

7310+
#if defined(HOST_AMD64) && defined(HOST_WINDOWS)
7311+
size_t GetSSPForFrameOnCurrentStack(TADDR ip);
7312+
#endif // HOST_AMD64 && HOST_WINDOWS
7313+
7314+
#ifdef FEATURE_INTERPRETER
7315+
void ThrowResumeAfterCatchException(TADDR resumeSP, TADDR resumeIP)
7316+
{
7317+
throw ResumeAfterCatchException(resumeSP, resumeIP);
7318+
}
7319+
7320+
VOID DECLSPEC_NORETURN UnwindAndContinueResumeAfterCatch(TADDR resumeSP, TADDR resumeIP)
7321+
{
7322+
STATIC_CONTRACT_NOTHROW;
7323+
STATIC_CONTRACT_GC_TRIGGERS;
7324+
STATIC_CONTRACT_MODE_ANY;
7325+
7326+
CONTEXT context;
7327+
ClrCaptureContext(&context);
7328+
7329+
// Unwind to the caller of the Ex.RhThrowEx / Ex.RhThrowHwEx
7330+
Thread::VirtualUnwindToFirstManagedCallFrame(&context);
7331+
7332+
#if defined(HOST_AMD64) && defined(HOST_WINDOWS)
7333+
size_t targetSSP = GetSSPForFrameOnCurrentStack(GetIP(&context));
7334+
#endif // HOST_AMD64 && HOST_WINDOWS
7335+
7336+
// Skip all managed frames upto a native frame
7337+
while (ExecutionManager::IsManagedCode(GetIP(&context)))
7338+
{
7339+
Thread::VirtualUnwindCallFrame(&context);
7340+
#if defined(HOST_AMD64) && defined(HOST_WINDOWS)
7341+
if (targetSSP != 0)
7342+
{
7343+
targetSSP += sizeof(size_t);
7344+
}
7345+
#endif
7346+
}
7347+
7348+
ExecuteFunctionBelowContext((PCODE)ThrowResumeAfterCatchException, &context, targetSSP, resumeSP, resumeIP);
7349+
}
7350+
#endif // FEATURE_INTERPRETER
7351+
73107352
thread_local DWORD t_dwCurrentExceptionCode;
73117353
thread_local PEXCEPTION_RECORD t_pCurrentExceptionRecord;
73127354
thread_local PCONTEXT t_pCurrentExceptionContext;

src/coreclr/vm/excep.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,10 @@ X86_ONLY(EXCEPTION_REGISTRATION_RECORD* GetNextCOMPlusSEHRecord(EXCEPTION_REGIST
807807
VOID DECLSPEC_NORETURN ContinueExceptionInterceptionUnwind();
808808
#endif // FEATURE_EH_FUNCLETS
809809

810+
#ifdef FEATURE_INTERPRETER
811+
void ThrowResumeAfterCatchException(TADDR resumeSP, TADDR resumeIP);
812+
#endif // FEATURE_INTERPRETER
813+
810814
#endif // !DACCESS_COMPILE
811815

812816
#endif // __excep_h__

0 commit comments

Comments
 (0)