From b857bd19dce80b88b49e7bb21560bafa5edfdaff Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sun, 13 Apr 2025 18:29:46 +0200 Subject: [PATCH 1/2] Use [UN]INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX to backpatch CallDescrWorkerInternal SEH record on x86/funclets --- src/coreclr/vm/exceptionhandling.h | 6 +++++ src/coreclr/vm/exceptmacros.h | 38 ++++++++++++++++++++++++++++-- src/coreclr/vm/i386/asmhelpers.asm | 22 ----------------- src/coreclr/vm/prestub.cpp | 4 ++-- src/coreclr/vm/virtualcallstub.cpp | 4 ++-- 5 files changed, 46 insertions(+), 28 deletions(-) diff --git a/src/coreclr/vm/exceptionhandling.h b/src/coreclr/vm/exceptionhandling.h index 8668b787280008..8ce4478354de6e 100644 --- a/src/coreclr/vm/exceptionhandling.h +++ b/src/coreclr/vm/exceptionhandling.h @@ -22,6 +22,12 @@ ProcessCLRException(IN PEXCEPTION_RECORD pExceptionRecord, IN OUT PT_CONTEXT pContextRecord, IN OUT PT_DISPATCHER_CONTEXT pDispatcherContext); +EXTERN_C EXCEPTION_DISPOSITION __cdecl +CallDescrWorkerUnwindFrameChainHandler(IN PEXCEPTION_RECORD pExceptionRecord, + IN PVOID pEstablisherFrame, + IN OUT PCONTEXT pContextRecord, + IN OUT PDISPATCHER_CONTEXT pDispatcherContext); + VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, CONTEXT *pExceptionContext, EXCEPTION_RECORD *pExceptionRecord = NULL); VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable); VOID DECLSPEC_NORETURN DispatchManagedException(RuntimeExceptionKind reKind); diff --git a/src/coreclr/vm/exceptmacros.h b/src/coreclr/vm/exceptmacros.h index ff3eafd842da4c..06f01a6243c1d6 100644 --- a/src/coreclr/vm/exceptmacros.h +++ b/src/coreclr/vm/exceptmacros.h @@ -277,13 +277,13 @@ VOID DECLSPEC_NORETURN UnwindAndContinueRethrowHelperAfterCatch(Frame* pEntryFra #ifdef TARGET_UNIX VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex, bool isHardwareException); -#define INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX \ +#define INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(nativeRethrow) \ PAL_SEHException exCopy; \ bool hasCaughtException = false; \ try { #define INSTALL_MANAGED_EXCEPTION_DISPATCHER \ - INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX + INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(false) #define UNINSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(nativeRethrow) \ } \ @@ -322,6 +322,40 @@ VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex, bool isHar UNREACHABLE(); \ } +#elif defined(TARGET_X86) && defined(TARGET_WINDOWS) && defined(FEATURE_EH_FUNCLETS) + +#define INSTALL_MANAGED_EXCEPTION_DISPATCHER +#define UNINSTALL_MANAGED_EXCEPTION_DISPATCHER + +#define INSTALL_UNHANDLED_MANAGED_EXCEPTION_TRAP +#define UNINSTALL_UNHANDLED_MANAGED_EXCEPTION_TRAP + +// We use [UN]INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX to backpatch the SEH record installed +// in CallDescrWorkerInternal from ProcessCLRException to CallDescrWorkerUnwindFrameChainHandler +// and back. This ensures that class loading exceptions are propagated through unmanaged code +// before being forwarded to the managed one. + +#define INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(nativeRethrow) \ + { \ + PEXCEPTION_REGISTRATION_RECORD pExceptionRecord = NULL; \ + if (nativeRethrow) \ + { \ + pExceptionRecord = GetCurrentSEHRecord(); \ + while (pExceptionRecord->Handler != (PEXCEPTION_ROUTINE)ProcessCLRException) \ + { \ + pExceptionRecord = pExceptionRecord->Next; \ + _ASSERTE(pExceptionRecord != EXCEPTION_CHAIN_END); \ + } \ + pExceptionRecord->Handler = (PEXCEPTION_ROUTINE)CallDescrWorkerUnwindFrameChainHandler; \ + } + +#define UNINSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(nativeRethrow) \ + if (nativeRethrow) \ + { \ + pExceptionRecord->Handler = (PEXCEPTION_ROUTINE)ProcessCLRException; \ + } \ + } + #else // TARGET_UNIX #define INSTALL_MANAGED_EXCEPTION_DISPATCHER diff --git a/src/coreclr/vm/i386/asmhelpers.asm b/src/coreclr/vm/i386/asmhelpers.asm index 5656aee6c1e230..e64931d6405882 100644 --- a/src/coreclr/vm/i386/asmhelpers.asm +++ b/src/coreclr/vm/i386/asmhelpers.asm @@ -1215,30 +1215,8 @@ _ThePreStub@0 proc public push esi -ifdef FEATURE_EH_FUNCLETS - cmp [esi + 24], CallDescrWorkerInternalReturnAddress - jne NoSEHReplace - - ; If we were called from CallDescrWorkerInternal then swap the last - ; SEH registration for _CallDescrWorkerUnwindFrameChainHandler to ensure - ; that class loading exceptions are propagated through unmanaged code - ; before being forwarded to the managed one. - mov edi, fs:[0] - ; mov esi, [edi] - ; mov fs:[0], esi - mov [edi + 4], _CallDescrWorkerUnwindFrameChainHandler - call _PreStubWorker@8 - mov [edi + 4], _ProcessCLRException - ; mov fs:[0], edi - jmp AfterPreStubWorker - -NoSEHReplace: -endif ; FEATURE_EH_FUNCLETS - call _PreStubWorker@8 -AfterPreStubWorker: - ; eax now contains replacement stub. PreStubWorker will never return ; NULL (it throws an exception if stub creation fails.) diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 50a8ce012b5f60..4aef0bf52c68fd 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -1859,7 +1859,7 @@ extern "C" PCODE STDCALL PreStubWorker(TransitionBlock* pTransitionBlock, Method { bool propagateExceptionToNativeCode = IsCallDescrWorkerInternalReturnAddress(pTransitionBlock->m_ReturnAddress); - INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX; + INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(propagateExceptionToNativeCode); INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX; // Make sure the method table is restored, and method instantiation if present @@ -2409,7 +2409,7 @@ EXTERN_C PCODE STDCALL ExternalMethodFixupWorker(TransitionBlock * pTransitionBl bool propagateExceptionToNativeCode = IsCallDescrWorkerInternalReturnAddress(pTransitionBlock->m_ReturnAddress); - INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX; + INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(propagateExceptionToNativeCode); INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX; bool fVirtual = false; diff --git a/src/coreclr/vm/virtualcallstub.cpp b/src/coreclr/vm/virtualcallstub.cpp index 70fb83c007eb99..1e7c66e23c642c 100644 --- a/src/coreclr/vm/virtualcallstub.cpp +++ b/src/coreclr/vm/virtualcallstub.cpp @@ -1710,7 +1710,7 @@ PCODE VSD_ResolveWorker(TransitionBlock * pTransitionBlock, if (pObj == NULL) { pSDFrame->SetForNullReferenceException(); pSDFrame->Push(CURRENT_THREAD); - INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX; + INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(propagateExceptionToNativeCode); INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX; COMPlusThrow(kNullReferenceException); UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_EX(propagateExceptionToNativeCode); @@ -1750,7 +1750,7 @@ PCODE VSD_ResolveWorker(TransitionBlock * pTransitionBlock, pSDFrame->SetRepresentativeSlot(pRepresentativeMT, representativeToken.GetSlotNumber()); pSDFrame->Push(CURRENT_THREAD); - INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX; + INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(propagateExceptionToNativeCode); INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX; // For Virtual Delegates the m_siteAddr is a field of a managed object From 6bc6001552b5dba54110165a6b33c5ccb90b046d Mon Sep 17 00:00:00 2001 From: Filip Navara Date: Sun, 13 Apr 2025 20:46:22 +0200 Subject: [PATCH 2/2] Take 2 --- src/coreclr/vm/exceptionhandling.h | 8 ++++---- src/coreclr/vm/exceptmacros.h | 27 ++++++++++++++------------- src/coreclr/vm/prestub.cpp | 4 ++-- src/coreclr/vm/virtualcallstub.cpp | 4 ++-- 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/coreclr/vm/exceptionhandling.h b/src/coreclr/vm/exceptionhandling.h index 8ce4478354de6e..cd6e1bb8de8b08 100644 --- a/src/coreclr/vm/exceptionhandling.h +++ b/src/coreclr/vm/exceptionhandling.h @@ -23,10 +23,10 @@ ProcessCLRException(IN PEXCEPTION_RECORD pExceptionRecord, IN OUT PT_DISPATCHER_CONTEXT pDispatcherContext); EXTERN_C EXCEPTION_DISPOSITION __cdecl -CallDescrWorkerUnwindFrameChainHandler(IN PEXCEPTION_RECORD pExceptionRecord, - IN PVOID pEstablisherFrame, - IN OUT PCONTEXT pContextRecord, - IN OUT PDISPATCHER_CONTEXT pDispatcherContext); +CallDescrWorkerUnwindFrameChainHandler(IN PEXCEPTION_RECORD pExceptionRecord, + IN PVOID pEstablisherFrame, + IN OUT PT_CONTEXT pContextRecord, + IN OUT PT_DISPATCHER_CONTEXT pDispatcherContext); VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable, CONTEXT *pExceptionContext, EXCEPTION_RECORD *pExceptionRecord = NULL); VOID DECLSPEC_NORETURN DispatchManagedException(OBJECTREF throwable); diff --git a/src/coreclr/vm/exceptmacros.h b/src/coreclr/vm/exceptmacros.h index 06f01a6243c1d6..2f0ff66e2384b2 100644 --- a/src/coreclr/vm/exceptmacros.h +++ b/src/coreclr/vm/exceptmacros.h @@ -277,13 +277,13 @@ VOID DECLSPEC_NORETURN UnwindAndContinueRethrowHelperAfterCatch(Frame* pEntryFra #ifdef TARGET_UNIX VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex, bool isHardwareException); -#define INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(nativeRethrow) \ +#define INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX \ PAL_SEHException exCopy; \ bool hasCaughtException = false; \ try { #define INSTALL_MANAGED_EXCEPTION_DISPATCHER \ - INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(false) + INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX #define UNINSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(nativeRethrow) \ } \ @@ -332,28 +332,29 @@ VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex, bool isHar // We use [UN]INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX to backpatch the SEH record installed // in CallDescrWorkerInternal from ProcessCLRException to CallDescrWorkerUnwindFrameChainHandler -// and back. This ensures that class loading exceptions are propagated through unmanaged code -// before being forwarded to the managed one. +// when throwing an exception. This ensures that class loading exceptions are propagated through +// unmanaged code before being forwarded to the managed one. -#define INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(nativeRethrow) \ +#define INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX \ + try \ + { + +#define UNINSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(nativeRethrow) \ + } \ + catch (...) \ { \ - PEXCEPTION_REGISTRATION_RECORD pExceptionRecord = NULL; \ if (nativeRethrow) \ { \ - pExceptionRecord = GetCurrentSEHRecord(); \ + PEXCEPTION_REGISTRATION_RECORD pExceptionRecord = GetCurrentSEHRecord(); \ + _ASSERTE(pExceptionRecord != EXCEPTION_CHAIN_END); \ while (pExceptionRecord->Handler != (PEXCEPTION_ROUTINE)ProcessCLRException) \ { \ pExceptionRecord = pExceptionRecord->Next; \ _ASSERTE(pExceptionRecord != EXCEPTION_CHAIN_END); \ } \ pExceptionRecord->Handler = (PEXCEPTION_ROUTINE)CallDescrWorkerUnwindFrameChainHandler; \ - } - -#define UNINSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(nativeRethrow) \ - if (nativeRethrow) \ - { \ - pExceptionRecord->Handler = (PEXCEPTION_ROUTINE)ProcessCLRException; \ } \ + throw; \ } #else // TARGET_UNIX diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index 4aef0bf52c68fd..50a8ce012b5f60 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -1859,7 +1859,7 @@ extern "C" PCODE STDCALL PreStubWorker(TransitionBlock* pTransitionBlock, Method { bool propagateExceptionToNativeCode = IsCallDescrWorkerInternalReturnAddress(pTransitionBlock->m_ReturnAddress); - INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(propagateExceptionToNativeCode); + INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX; INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX; // Make sure the method table is restored, and method instantiation if present @@ -2409,7 +2409,7 @@ EXTERN_C PCODE STDCALL ExternalMethodFixupWorker(TransitionBlock * pTransitionBl bool propagateExceptionToNativeCode = IsCallDescrWorkerInternalReturnAddress(pTransitionBlock->m_ReturnAddress); - INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(propagateExceptionToNativeCode); + INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX; INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX; bool fVirtual = false; diff --git a/src/coreclr/vm/virtualcallstub.cpp b/src/coreclr/vm/virtualcallstub.cpp index 1e7c66e23c642c..70fb83c007eb99 100644 --- a/src/coreclr/vm/virtualcallstub.cpp +++ b/src/coreclr/vm/virtualcallstub.cpp @@ -1710,7 +1710,7 @@ PCODE VSD_ResolveWorker(TransitionBlock * pTransitionBlock, if (pObj == NULL) { pSDFrame->SetForNullReferenceException(); pSDFrame->Push(CURRENT_THREAD); - INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(propagateExceptionToNativeCode); + INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX; INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX; COMPlusThrow(kNullReferenceException); UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_EX(propagateExceptionToNativeCode); @@ -1750,7 +1750,7 @@ PCODE VSD_ResolveWorker(TransitionBlock * pTransitionBlock, pSDFrame->SetRepresentativeSlot(pRepresentativeMT, representativeToken.GetSlotNumber()); pSDFrame->Push(CURRENT_THREAD); - INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(propagateExceptionToNativeCode); + INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX; INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX; // For Virtual Delegates the m_siteAddr is a field of a managed object