@@ -970,7 +970,7 @@ ProcessCLRExceptionNew(IN PEXCEPTION_RECORD pExceptionRecord,
970
970
else
971
971
{
972
972
OBJECTREF oref = ExceptionTracker::CreateThrowable (pExceptionRecord, FALSE );
973
- DispatchManagedException (oref, pContextRecord);
973
+ DispatchManagedException (oref, pContextRecord, pExceptionRecord );
974
974
}
975
975
}
976
976
#endif // !HOST_UNIX
@@ -5646,7 +5646,7 @@ void FirstChanceExceptionNotification()
5646
5646
#endif // TARGET_UNIX
5647
5647
}
5648
5648
5649
- VOID DECLSPEC_NORETURN DispatchManagedException (OBJECTREF throwable, CONTEXT* pExceptionContext)
5649
+ VOID DECLSPEC_NORETURN DispatchManagedException (OBJECTREF throwable, CONTEXT* pExceptionContext, EXCEPTION_RECORD* pExceptionRecord )
5650
5650
{
5651
5651
STATIC_CONTRACT_THROWS;
5652
5652
STATIC_CONTRACT_GC_TRIGGERS;
@@ -5660,14 +5660,32 @@ VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, CONTEXT* pE
5660
5660
5661
5661
ULONG_PTR hr = GetHRFromThrowable (throwable);
5662
5662
5663
- EXCEPTION_RECORD exceptionRecord ;
5664
- exceptionRecord .ExceptionCode = EXCEPTION_COMPLUS;
5665
- exceptionRecord .ExceptionFlags = EXCEPTION_NONCONTINUABLE | EXCEPTION_SOFTWARE_ORIGINATE;
5666
- exceptionRecord .ExceptionAddress = (void *)(void (*)(OBJECTREF))&DispatchManagedException;
5667
- exceptionRecord .NumberParameters = MarkAsThrownByUs (exceptionRecord .ExceptionInformation , hr);
5668
- exceptionRecord .ExceptionRecord = NULL ;
5663
+ EXCEPTION_RECORD newExceptionRecord ;
5664
+ newExceptionRecord .ExceptionCode = EXCEPTION_COMPLUS;
5665
+ newExceptionRecord .ExceptionFlags = EXCEPTION_NONCONTINUABLE | EXCEPTION_SOFTWARE_ORIGINATE;
5666
+ newExceptionRecord .ExceptionAddress = (void *)(void (*)(OBJECTREF))&DispatchManagedException;
5667
+ newExceptionRecord .NumberParameters = MarkAsThrownByUs (newExceptionRecord .ExceptionInformation , hr);
5668
+ newExceptionRecord .ExceptionRecord = NULL ;
5669
5669
5670
- ExInfo exInfo (pThread, &exceptionRecord, pExceptionContext, ExKind::Throw);
5670
+ ExInfo exInfo (pThread, &newExceptionRecord, pExceptionContext, ExKind::Throw);
5671
+
5672
+ #ifdef HOST_WINDOWS
5673
+ // On Windows, this enables the possibility to propagate a longjmp across managed frames. Longjmp
5674
+ // behaves like a SEH exception, but only runs the second (unwinding) pass.
5675
+ // NOTE: This is a best effort purely for backward compatibility with the legacy exception handling.
5676
+ // Skipping over managed frames using setjmp/longjmp is
5677
+ // is unsupported and it is not guaranteed to work reliably in all cases.
5678
+ // https://learn.microsoft.com/dotnet/standard/native-interop/exceptions-interoperability#setjmplongjmp-behaviors
5679
+ if ((pExceptionRecord != NULL ) && (pExceptionRecord->ExceptionCode == STATUS_LONGJUMP))
5680
+ {
5681
+ // longjmp over managed frames. The EXCEPTION_RECORD::ExceptionInformation store the
5682
+ // jmp_buf and the return value for STATUS_LONGJUMP, so we extract it here. When the
5683
+ // exception handling code moves out of the managed frames, we call the longjmp with
5684
+ // these arguments again to continue its propagation.
5685
+ exInfo.m_pLongJmpBuf = (jmp_buf *)pExceptionRecord->ExceptionInformation [0 ];
5686
+ exInfo.m_longJmpReturnValue = (int )pExceptionRecord->ExceptionInformation [1 ];
5687
+ }
5688
+ #endif // HOST_WINDOWS
5671
5689
5672
5690
if (pThread->IsAbortInitiated () && IsExceptionOfType (kThreadAbortException ,&throwable))
5673
5691
{
@@ -7683,6 +7701,14 @@ size_t GetSSPForFrameOnCurrentStack(TADDR ip)
7683
7701
}
7684
7702
#endif // HOST_AMD64 && HOST_WINDOWS
7685
7703
7704
+ #ifdef HOST_WINDOWS
7705
+ VOID DECLSPEC_NORETURN PropagateLongJmpThroughNativeFrames (jmp_buf *pJmpBuf, int retVal)
7706
+ {
7707
+ WRAPPER_NO_CONTRACT;
7708
+ longjmp (*pJmpBuf, retVal);
7709
+ }
7710
+ #endif // HOST_WINDOWS
7711
+
7686
7712
extern " C" void * QCALLTYPE CallCatchFunclet (QCall::ObjectHandleOnStack exceptionObj, BYTE* pHandlerIP, REGDISPLAY* pvRegDisplay, ExInfo* exInfo)
7687
7713
{
7688
7714
QCALL_CONTRACT;
@@ -7746,6 +7772,11 @@ extern "C" void * QCALLTYPE CallCatchFunclet(QCall::ObjectHandleOnStack exceptio
7746
7772
7747
7773
ExInfo* pExInfo = (PTR_ExInfo)pThread->GetExceptionState ()->GetCurrentExceptionTracker ();
7748
7774
7775
+ #ifdef HOST_WINDOWS
7776
+ jmp_buf * pLongJmpBuf = pExInfo->m_pLongJmpBuf ;
7777
+ int longJmpReturnValue = pExInfo->m_longJmpReturnValue ;
7778
+ #endif // HOST_WINDOWS
7779
+
7749
7780
#ifdef HOST_UNIX
7750
7781
Interop::ManagedToNativeExceptionCallback propagateExceptionCallback = pExInfo->m_propagateExceptionCallback ;
7751
7782
void * propagateExceptionContext = pExInfo->m_propagateExceptionContext ;
@@ -7864,29 +7895,43 @@ extern "C" void * QCALLTYPE CallCatchFunclet(QCall::ObjectHandleOnStack exceptio
7864
7895
#elif defined(HOST_RISCV64) || defined(HOST_LOONGARCH64)
7865
7896
pvRegDisplay->pCurrentContext ->Ra = GetIP (pvRegDisplay->pCurrentContext );
7866
7897
#endif
7867
- SetIP (pvRegDisplay->pCurrentContext , (PCODE)(void (*)(Object*))PropagateExceptionThroughNativeFrames);
7868
7898
#if defined(HOST_AMD64)
7869
7899
SetSP (pvRegDisplay->pCurrentContext , targetSp - 8 );
7870
7900
#elif defined(HOST_X86)
7871
7901
SetSP (pvRegDisplay->pCurrentContext , targetSp - 4 );
7872
7902
#endif
7903
+
7904
+ // The SECOND_ARG_REG is defined only for Windows, it is used to handle longjmp propagation over managed frames
7873
7905
#ifdef HOST_AMD64
7874
7906
#ifdef UNIX_AMD64_ABI
7875
7907
#define FIRST_ARG_REG Rdi
7876
7908
#else
7877
7909
#define FIRST_ARG_REG Rcx
7910
+ #define SECOND_ARG_REG Rdx
7878
7911
#endif
7879
7912
#elif defined(HOST_X86)
7880
7913
#define FIRST_ARG_REG Ecx
7881
7914
#elif defined(HOST_ARM64)
7882
7915
#define FIRST_ARG_REG X0
7916
+ #define SECOND_ARG_REG X1
7883
7917
#elif defined(HOST_ARM)
7884
7918
#define FIRST_ARG_REG R0
7885
7919
#elif defined(HOST_RISCV64) || defined(HOST_LOONGARCH64)
7886
7920
#define FIRST_ARG_REG A0
7887
7921
#endif
7888
-
7889
- pvRegDisplay->pCurrentContext ->FIRST_ARG_REG = (size_t )OBJECTREFToObject (exceptionObj.Get ());
7922
+ #ifdef HOST_WINDOWS
7923
+ if (pLongJmpBuf != NULL )
7924
+ {
7925
+ SetIP (pvRegDisplay->pCurrentContext , (PCODE)PropagateLongJmpThroughNativeFrames);
7926
+ pvRegDisplay->pCurrentContext ->FIRST_ARG_REG = (size_t )pLongJmpBuf;
7927
+ pvRegDisplay->pCurrentContext ->SECOND_ARG_REG = (size_t )longJmpReturnValue;
7928
+ }
7929
+ else
7930
+ #endif
7931
+ {
7932
+ SetIP (pvRegDisplay->pCurrentContext , (PCODE)(void (*)(Object*))PropagateExceptionThroughNativeFrames);
7933
+ pvRegDisplay->pCurrentContext ->FIRST_ARG_REG = (size_t )OBJECTREFToObject (exceptionObj.Get ());
7934
+ }
7890
7935
#undef FIRST_ARG_REG
7891
7936
ClrRestoreNonvolatileContext (pvRegDisplay->pCurrentContext , targetSSP);
7892
7937
}
@@ -8087,7 +8132,7 @@ extern "C" BOOL QCALLTYPE EHEnumNext(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClause*
8087
8132
ExtendedEHClauseEnumerator *pExtendedEHEnum = (ExtendedEHClauseEnumerator*)pEHEnum;
8088
8133
StackFrameIterator *pFrameIter = pExtendedEHEnum->pFrameIter ;
8089
8134
8090
- if (pEHEnum->iCurrentPos < pExtendedEHEnum->EHCount )
8135
+ while (pEHEnum->iCurrentPos < pExtendedEHEnum->EHCount )
8091
8136
{
8092
8137
IJitManager* pJitMan = pFrameIter->m_crawl .GetJitManager ();
8093
8138
const METHODTOKEN& MethToken = pFrameIter->m_crawl .GetMethodToken ();
@@ -8140,6 +8185,13 @@ extern "C" BOOL QCALLTYPE EHEnumNext(EH_CLAUSE_ENUMERATOR* pEHEnum, RhEHClause*
8140
8185
{
8141
8186
result = FALSE ;
8142
8187
}
8188
+ #ifdef HOST_WINDOWS
8189
+ // When processing longjmp, only finally clauses are considered.
8190
+ if ((pExInfo->m_pLongJmpBuf == NULL ) || (flags & COR_ILEXCEPTION_CLAUSE_FINALLY) || (flags & COR_ILEXCEPTION_CLAUSE_FAULT))
8191
+ #endif // HOST_WINDOWS
8192
+ {
8193
+ break ;
8194
+ }
8143
8195
}
8144
8196
END_QCALL;
8145
8197
0 commit comments