Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions src/coreclr/vm/excep.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3053,14 +3053,17 @@ void StackTraceInfo::AppendElement(OBJECTHANDLE hThrowable, UINT_PTR currentIP,
// This is a workaround to fix the generation of stack traces from exception objects so that
// they point to the line that actually generated the exception instead of the line
// following.
if (pCf->IsIPadjusted())
if (pCf != NULL)
{
stackTraceElem.flags |= STEF_IP_ADJUSTED;
}
else if (!pCf->HasFaulted() && stackTraceElem.ip != 0)
{
stackTraceElem.ip -= STACKWALK_CONTROLPC_ADJUST_OFFSET;
stackTraceElem.flags |= STEF_IP_ADJUSTED;
if (pCf->IsIPadjusted())
{
stackTraceElem.flags |= STEF_IP_ADJUSTED;
}
else if (!pCf->HasFaulted() && stackTraceElem.ip != 0)
{
stackTraceElem.ip -= STACKWALK_CONTROLPC_ADJUST_OFFSET;
stackTraceElem.flags |= STEF_IP_ADJUSTED;
}
}

#ifndef TARGET_UNIX // Watson is supported on Windows only
Expand Down
12 changes: 8 additions & 4 deletions src/coreclr/vm/exceptionhandling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -932,7 +932,12 @@ ProcessCLRExceptionNew(IN PEXCEPTION_RECORD pExceptionRecord,

Thread* pThread = GetThread();

if (pThread->HasThreadStateNC(Thread::TSNC_ProcessedUnhandledException))
// Skip native frames of asm helpers that have the ProcessCLRException set as their personality routine.
// There is nothing to do for those with the new exception handling.
// Also skip all frames when processing unhandled exceptions. That allows them to reach the host app
// level and let 3rd party the chance to handle them.
if (!ExecutionManager::IsManagedCode((PCODE)pDispatcherContext->ControlPc) ||
pThread->HasThreadStateNC(Thread::TSNC_ProcessedUnhandledException))
{
if ((pExceptionRecord->ExceptionFlags & EXCEPTION_UNWINDING))
{
Expand Down Expand Up @@ -8520,7 +8525,7 @@ static StackWalkAction MoveToNextNonSkippedFrame(StackFrameIterator* pStackFrame
return retVal;
}

extern "C" size_t CallDescrWorkerInternalReturnAddressOffset;
bool IsCallDescrWorkerInternalReturnAddress(PCODE pCode);

extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollideClauseIdx, bool* fUnwoundReversePInvoke, bool* pfIsExceptionIntercepted)
{
Expand Down Expand Up @@ -8575,8 +8580,7 @@ extern "C" bool QCALLTYPE SfiNext(StackFrameIterator* pThis, uint* uExCollideCla
}
else
{
size_t CallDescrWorkerInternalReturnAddress = (size_t)CallDescrWorkerInternal + CallDescrWorkerInternalReturnAddressOffset;
if (GetIP(pThis->m_crawl.GetRegisterSet()->pCallerContext) == CallDescrWorkerInternalReturnAddress)
if (IsCallDescrWorkerInternalReturnAddress(GetIP(pThis->m_crawl.GetRegisterSet()->pCallerContext)))
{
invalidRevPInvoke = true;
}
Expand Down
16 changes: 14 additions & 2 deletions src/coreclr/vm/exceptmacros.h
Original file line number Diff line number Diff line change
Expand Up @@ -277,15 +277,22 @@ VOID DECLSPEC_NORETURN UnwindAndContinueRethrowHelperAfterCatch(Frame* pEntryFra
#ifdef TARGET_UNIX
VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex, bool isHardwareException);

#define INSTALL_MANAGED_EXCEPTION_DISPATCHER \
#define INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX \
PAL_SEHException exCopy; \
bool hasCaughtException = false; \
try {

#define UNINSTALL_MANAGED_EXCEPTION_DISPATCHER \
#define INSTALL_MANAGED_EXCEPTION_DISPATCHER \
INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX

#define UNINSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(nativeRethrow) \
} \
catch (PAL_SEHException& ex) \
{ \
if (nativeRethrow) \
{ \
throw; \
} \
exCopy = std::move(ex); \
hasCaughtException = true; \
} \
Expand All @@ -294,6 +301,9 @@ VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex, bool isHar
DispatchManagedException(exCopy, false);\
}

#define UNINSTALL_MANAGED_EXCEPTION_DISPATCHER \
UNINSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(false)

// Install trap that catches unhandled managed exception and dumps its stack
#define INSTALL_UNHANDLED_MANAGED_EXCEPTION_TRAP \
try {
Expand All @@ -315,7 +325,9 @@ VOID DECLSPEC_NORETURN DispatchManagedException(PAL_SEHException& ex, bool isHar
#else // TARGET_UNIX

#define INSTALL_MANAGED_EXCEPTION_DISPATCHER
#define INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX
#define UNINSTALL_MANAGED_EXCEPTION_DISPATCHER
#define UNINSTALL_MANAGED_EXCEPTION_DISPATCHER_EX

#define INSTALL_UNHANDLED_MANAGED_EXCEPTION_TRAP
#define UNINSTALL_UNHANDLED_MANAGED_EXCEPTION_TRAP
Expand Down
132 changes: 87 additions & 45 deletions src/coreclr/vm/prestub.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2523,6 +2523,16 @@ Stub * MakeInstantiatingStubWorker(MethodDesc *pMD)
}
#endif // defined(FEATURE_SHARE_GENERIC_CODE)

extern "C" size_t CallDescrWorkerInternalReturnAddressOffset;

bool IsCallDescrWorkerInternalReturnAddress(PCODE pCode)
{
LIMITED_METHOD_CONTRACT;
size_t CallDescrWorkerInternalReturnAddress = (size_t)CallDescrWorkerInternal + CallDescrWorkerInternalReturnAddressOffset;

return pCode == CallDescrWorkerInternalReturnAddress;
}

//=============================================================================
// This function generates the real code when from Preemptive mode.
// It is specifically designed to work with the UnmanagedCallersOnlyAttribute.
Expand Down Expand Up @@ -2556,17 +2566,33 @@ static PCODE PreStubWorker_Preemptive(
// No GC frame is needed here since there should be no OBJECTREFs involved
// in this call due to UnmanagedCallersOnlyAttribute semantics.

INSTALL_MANAGED_EXCEPTION_DISPATCHER;
INSTALL_UNWIND_AND_CONTINUE_HANDLER;
EX_TRY
{
bool propagateExceptionToNativeCode = IsCallDescrWorkerInternalReturnAddress(pTransitionBlock->m_ReturnAddress);
INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX;
INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX;

// Make sure the method table is restored, and method instantiation if present
pMD->CheckRestore();
CONSISTENCY_CHECK(GetAppDomain()->CheckCanExecuteManagedCode(pMD));
// Make sure the method table is restored, and method instantiation if present
pMD->CheckRestore();
CONSISTENCY_CHECK(GetAppDomain()->CheckCanExecuteManagedCode(pMD));

pbRetVal = pMD->DoPrestub(NULL, CallerGCMode::Preemptive);
pbRetVal = pMD->DoPrestub(NULL, CallerGCMode::Preemptive);

UNINSTALL_UNWIND_AND_CONTINUE_HANDLER;
UNINSTALL_MANAGED_EXCEPTION_DISPATCHER;
UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_EX(propagateExceptionToNativeCode);
UNINSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(propagateExceptionToNativeCode);
}
EX_CATCH
{
GCX_COOP();
if (g_isNewExceptionHandlingEnabled)
{
OBJECTHANDLE ohThrowable = currentThread->LastThrownObjectHandle();
_ASSERTE(ohThrowable);
StackTraceInfo::AppendElement(ohThrowable, 0, (UINT_PTR)pTransitionBlock, pMD, NULL);
}
EX_RETHROW;
}
EX_END_CATCH(SwallowAllExceptions)

{
HardwareExceptionHolder;
Expand Down Expand Up @@ -2618,60 +2644,76 @@ extern "C" PCODE STDCALL PreStubWorker(TransitionBlock* pTransitionBlock, Method

pPFrame->Push(CURRENT_THREAD);

INSTALL_MANAGED_EXCEPTION_DISPATCHER;
INSTALL_UNWIND_AND_CONTINUE_HANDLER;
EX_TRY
{
bool propagateExceptionToNativeCode = IsCallDescrWorkerInternalReturnAddress(pTransitionBlock->m_ReturnAddress);

// Make sure the method table is restored, and method instantiation if present
pMD->CheckRestore();
CONSISTENCY_CHECK(GetAppDomain()->CheckCanExecuteManagedCode(pMD));
INSTALL_MANAGED_EXCEPTION_DISPATCHER_EX;
INSTALL_UNWIND_AND_CONTINUE_HANDLER_EX;

MethodTable* pDispatchingMT = NULL;
if (pMD->IsVtableMethod())
{
OBJECTREF curobj = pPFrame->GetThis();
// Make sure the method table is restored, and method instantiation if present
pMD->CheckRestore();
CONSISTENCY_CHECK(GetAppDomain()->CheckCanExecuteManagedCode(pMD));

if (curobj != NULL) // Check for virtual function called non-virtually on a NULL object
MethodTable* pDispatchingMT = NULL;
if (pMD->IsVtableMethod())
{
pDispatchingMT = curobj->GetMethodTable();
OBJECTREF curobj = pPFrame->GetThis();

if (pDispatchingMT->IsIDynamicInterfaceCastable())
if (curobj != NULL) // Check for virtual function called non-virtually on a NULL object
{
MethodTable* pMDMT = pMD->GetMethodTable();
TypeHandle objectType(pDispatchingMT);
TypeHandle methodType(pMDMT);
pDispatchingMT = curobj->GetMethodTable();

GCStress<cfg_any>::MaybeTrigger();
INDEBUG(curobj = NULL); // curobj is unprotected and CanCastTo() can trigger GC
if (!objectType.CanCastTo(methodType))
if (pDispatchingMT->IsIDynamicInterfaceCastable())
{
// Apparently IDynamicInterfaceCastable magic was involved when we chose this method to be called
// that's why we better stick to the MethodTable it belongs to, otherwise
// DoPrestub() will fail not being able to find implementation for pMD in pDispatchingMT.
MethodTable* pMDMT = pMD->GetMethodTable();
TypeHandle objectType(pDispatchingMT);
TypeHandle methodType(pMDMT);

pDispatchingMT = pMDMT;
GCStress<cfg_any>::MaybeTrigger();
INDEBUG(curobj = NULL); // curobj is unprotected and CanCastTo() can trigger GC
if (!objectType.CanCastTo(methodType))
{
// Apparently IDynamicInterfaceCastable magic was involved when we chose this method to be called
// that's why we better stick to the MethodTable it belongs to, otherwise
// DoPrestub() will fail not being able to find implementation for pMD in pDispatchingMT.

pDispatchingMT = pMDMT;
}
}
}

// For value types, the only virtual methods are interface implementations.
// Thus pDispatching == pMT because there
// is no inheritance in value types. Note the BoxedEntryPointStubs are shared
// between all sharable generic instantiations, so the == test is on
// canonical method tables.
// For value types, the only virtual methods are interface implementations.
// Thus pDispatching == pMT because there
// is no inheritance in value types. Note the BoxedEntryPointStubs are shared
// between all sharable generic instantiations, so the == test is on
// canonical method tables.
#ifdef _DEBUG
MethodTable* pMDMT = pMD->GetMethodTable(); // put this here to see what the MT is in debug mode
_ASSERTE(!pMD->GetMethodTable()->IsValueType() ||
(pMD->IsUnboxingStub() && (pDispatchingMT->GetCanonicalMethodTable() == pMDMT->GetCanonicalMethodTable())));
MethodTable* pMDMT = pMD->GetMethodTable(); // put this here to see what the MT is in debug mode
_ASSERTE(!pMD->GetMethodTable()->IsValueType() ||
(pMD->IsUnboxingStub() && (pDispatchingMT->GetCanonicalMethodTable() == pMDMT->GetCanonicalMethodTable())));
#endif // _DEBUG
}
}
}

GCX_PREEMP_THREAD_EXISTS(CURRENT_THREAD);
GCX_PREEMP_THREAD_EXISTS(CURRENT_THREAD);
{
pbRetVal = pMD->DoPrestub(pDispatchingMT, CallerGCMode::Coop);
}

UNINSTALL_UNWIND_AND_CONTINUE_HANDLER_EX(propagateExceptionToNativeCode);
UNINSTALL_MANAGED_EXCEPTION_DISPATCHER_EX(propagateExceptionToNativeCode);
}
EX_CATCH
{
pbRetVal = pMD->DoPrestub(pDispatchingMT, CallerGCMode::Coop);
if (g_isNewExceptionHandlingEnabled)
{
OBJECTHANDLE ohThrowable = CURRENT_THREAD->LastThrownObjectHandle();
_ASSERTE(ohThrowable);
StackTraceInfo::AppendElement(ohThrowable, 0, (UINT_PTR)pTransitionBlock, pMD, NULL);
}
EX_RETHROW;
}

UNINSTALL_UNWIND_AND_CONTINUE_HANDLER;
UNINSTALL_MANAGED_EXCEPTION_DISPATCHER;
EX_END_CATCH(SwallowAllExceptions)

{
HardwareExceptionHolder;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using System;

namespace Dependency
{
public class DependencyClass
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<CLRTestKind>BuildOnly</CLRTestKind>
<OutputType>Library</OutputType>
</PropertyGroup>
<ItemGroup>
<Compile Include="dependencytodelete.cs" />
</ItemGroup>
</Project>
43 changes: 43 additions & 0 deletions src/tests/Regressions/coreclr/GitHub_76531/test76531.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Reflection;
using System.IO;
using System.Runtime.CompilerServices;
using Xunit;

namespace Test76531
{
internal class Test
{
private static Dependency.DependencyClass? value;

static Test()
{
value = new Dependency.DependencyClass();
}
}

public class Program
{
[MethodImpl(MethodImplOptions.NoInlining)]
static void TestMethod()
{
try
{
Test test = new ();
}
catch (TypeInitializationException)
{
// This catch fails with issue #76531
}
}

[Fact]
public static void TestEntryPoint()
{
File.Delete(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "dependencytodelete.dll"));
TestMethod();
}
}
}
12 changes: 12 additions & 0 deletions src/tests/Regressions/coreclr/GitHub_76531/test76531.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Needed for mechanical merging of all remaining tests, this particular project may not actually need process isolation -->
<RequiresProcessIsolation>true</RequiresProcessIsolation>
</PropertyGroup>
<ItemGroup>
<Compile Include="test76531.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="dependencytodelete.csproj" />
</ItemGroup>
</Project>
Loading