From f2b57ca853f409c0284e9f1a5525a27769a7e4ff Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Thu, 25 Jul 2024 16:06:10 +0200 Subject: [PATCH] Fix missing frame on inner exception stack trace in release build There is a problem in release builds of the runtime that was discovered by SOS tests. When an exception is thrown from reflection code, the inner exception was missing the frame(s) from the reflection called code on the stack trace and had them in the `_remoteStackTraceString` instead. So getting the exception stack trace as string still worked correctly. In checked or debug builds, it was working as expected. It turned out that the issue was caused by the fact that when an exception is rethrown as native one when exception unwinding reaches native frames, it used `RealCOMPlusThrow` method. The catch was that while in debug / checked builds it was using this method with Object* argument, in release builds, the `OBJECTREF` and Object* are the same thing, so the `RealCOMPlusThrow` with the explicit Object* was not compiled and we were using the one with the OBJECTREF argument. Unfortunately, that one has a different semantics - to save the current stack trace into the `_remoteStackTraceString` and then building it from scratch. The fix was to rename the RealCOMPlusThrow version with the Object* argument to a different name so that it can be built for release, checked and debug builds and used for the specific purpose of propagating managed exception through the native frames. --- src/coreclr/vm/excep.cpp | 4 +--- src/coreclr/vm/excep.h | 2 +- src/coreclr/vm/exceptionhandling.cpp | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/coreclr/vm/excep.cpp b/src/coreclr/vm/excep.cpp index 7acb54ee42e2c..05ed4b3421a6a 100644 --- a/src/coreclr/vm/excep.cpp +++ b/src/coreclr/vm/excep.cpp @@ -2509,8 +2509,7 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrow(OBJECTREF throwable) RealCOMPlusThrow(throwable, FALSE); } -#ifdef USE_CHECKED_OBJECTREFS -VOID DECLSPEC_NORETURN RealCOMPlusThrow(Object *exceptionObj) +VOID DECLSPEC_NORETURN PropagateExceptionThroughNativeFrames(Object *exceptionObj) { CONTRACTL { @@ -2523,7 +2522,6 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrow(Object *exceptionObj) OBJECTREF throwable = ObjectToOBJECTREF(exceptionObj); RealCOMPlusThrowWorker(throwable, FALSE); } -#endif // USE_CHECKED_OBJECTREFS // this function finds the managed callback to get a resource // string from the then current local domain and calls it diff --git a/src/coreclr/vm/excep.h b/src/coreclr/vm/excep.h index e5b35b85a5ed8..391ea14633bb0 100644 --- a/src/coreclr/vm/excep.h +++ b/src/coreclr/vm/excep.h @@ -253,7 +253,7 @@ VOID DECLSPEC_NORETURN RealCOMPlusThrowNonLocalized(RuntimeExceptionKind reKind, //========================================================================== VOID DECLSPEC_NORETURN RealCOMPlusThrow(OBJECTREF throwable); -VOID DECLSPEC_NORETURN RealCOMPlusThrow(Object *exceptionObj); +VOID DECLSPEC_NORETURN PropagateExceptionThroughNativeFrames(Object *exceptionObj); //========================================================================== // Throw an undecorated runtime exception. diff --git a/src/coreclr/vm/exceptionhandling.cpp b/src/coreclr/vm/exceptionhandling.cpp index f5b34a3d6cd28..e23852a53775b 100644 --- a/src/coreclr/vm/exceptionhandling.cpp +++ b/src/coreclr/vm/exceptionhandling.cpp @@ -7872,7 +7872,7 @@ extern "C" void * QCALLTYPE CallCatchFunclet(QCall::ObjectHandleOnStack exceptio #elif defined(HOST_RISCV) || defined(HOST_LOONGARCH64) pvRegDisplay->pCurrentContext->Ra = GetIP(pvRegDisplay->pCurrentContext); #endif - SetIP(pvRegDisplay->pCurrentContext, (PCODE)(void (*)(Object*))RealCOMPlusThrow); + SetIP(pvRegDisplay->pCurrentContext, (PCODE)(void (*)(Object*))PropagateExceptionThroughNativeFrames); #if defined(HOST_AMD64) SetSP(pvRegDisplay->pCurrentContext, targetSp - 8); #elif defined(HOST_X86)