Skip to content
Merged
3 changes: 3 additions & 0 deletions src/coreclr/nativeaot/Bootstrap/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ extern "C" void IDynamicCastableGetInterfaceImplementation();
extern "C" void ObjectiveCMarshalTryGetTaggedMemory();
extern "C" void ObjectiveCMarshalGetIsTrackedReferenceCallback();
extern "C" void ObjectiveCMarshalGetOnEnteredFinalizerQueueCallback();
extern "C" void ObjectiveCMarshalGetUnhandledExceptionPropagationHandler();
#endif

typedef void(*pfn)();
Expand All @@ -134,10 +135,12 @@ static const pfn c_classlibFunctions[] = {
&ObjectiveCMarshalTryGetTaggedMemory,
&ObjectiveCMarshalGetIsTrackedReferenceCallback,
&ObjectiveCMarshalGetOnEnteredFinalizerQueueCallback,
&ObjectiveCMarshalGetUnhandledExceptionPropagationHandler,
#else
nullptr,
nullptr,
nullptr,
nullptr,
#endif
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,8 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn
byte* prevOriginalPC = null;
UIntPtr prevFramePtr = UIntPtr.Zero;
bool unwoundReversePInvoke = false;
IntPtr pReversePInvokePropagationCallback = IntPtr.Zero;
IntPtr pReversePInvokePropagationContext = IntPtr.Zero;

bool isValid = frameIter.Init(exInfo._pExContext, (exInfo._kind & ExKind.InstructionFaultFlag) != 0);
Debug.Assert(isValid, "RhThrowEx called with an unexpected context");
Expand Down Expand Up @@ -643,7 +645,29 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn
}
}

if (pCatchHandler == null)
#if FEATURE_OBJCMARSHAL
if (unwoundReversePInvoke)
{
// We did not find any managed handlers before hitting a reverse P/Invoke boundary.
// See if the classlib has a handler to propagate the exception to native code.
IntPtr pGetHandlerClasslibFunction = (IntPtr)InternalCalls.RhpGetClasslibFunctionFromCodeAddress((IntPtr)prevControlPC,
ClassLibFunctionId.ObjectiveCMarshalGetUnhandledExceptionPropagationHandler);
if (pGetHandlerClasslibFunction != IntPtr.Zero)
{
var pGetHandler = (delegate*<object, IntPtr, out IntPtr, IntPtr>)pGetHandlerClasslibFunction;
pReversePInvokePropagationCallback = pGetHandler(
exceptionObj, (IntPtr)prevControlPC, out pReversePInvokePropagationContext);
if (pReversePInvokePropagationCallback != IntPtr.Zero)
{
// Tell the second pass to unwind to this frame.
handlingFrameSP = frameIter.SP;
catchingTryRegionIdx = MaxTryRegionIdx;
}
}
}
#endif // FEATURE_OBJCMARSHAL

if (pCatchHandler == null && pReversePInvokePropagationCallback == IntPtr.Zero)
{
OnUnhandledExceptionViaClassLib(exceptionObj);

Expand All @@ -655,8 +679,8 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn
}

// We FailFast above if the exception goes unhandled. Therefore, we cannot run the second pass
// without a catch handler.
Debug.Assert(pCatchHandler != null, "We should have a handler if we're starting the second pass");
// without a catch handler or propagation callback.
Debug.Assert(pCatchHandler != null || pReversePInvokePropagationCallback != IntPtr.Zero, "We should have a handler if we're starting the second pass");

// ------------------------------------------------
//
Expand All @@ -673,12 +697,22 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn

exInfo._passNumber = 2;
startIdx = MaxTryRegionIdx;
unwoundReversePInvoke = false;
isValid = frameIter.Init(exInfo._pExContext, (exInfo._kind & ExKind.InstructionFaultFlag) != 0);
for (; isValid && ((byte*)frameIter.SP <= (byte*)handlingFrameSP); isValid = frameIter.Next(&startIdx))
for (; isValid && ((byte*)frameIter.SP <= (byte*)handlingFrameSP); isValid = frameIter.Next(&startIdx, &unwoundReversePInvoke))
{
Debug.Assert(isValid, "second-pass EH unwind failed unexpectedly");
DebugScanCallFrame(exInfo._passNumber, frameIter.ControlPC, frameIter.SP);

if (unwoundReversePInvoke)
{
Debug.Assert(pReversePInvokePropagationCallback != IntPtr.Zero, "Unwound to a reverse P/Invoke in the second pass. We should have a propagation handler.");
Debug.Assert(frameIter.SP == handlingFrameSP, "Encountered a different reverse P/Invoke frame in the second pass.");
// Found the native frame that called the reverse P/invoke.
// It is not possible to run managed second pass handlers on a native frame.
break;
}

if ((frameIter.SP == handlingFrameSP)
#if TARGET_ARM64
&& (frameIter.ControlPC == prevControlPC)
Expand All @@ -693,6 +727,18 @@ private static void DispatchEx(scoped ref StackFrameIterator frameIter, ref ExIn
InvokeSecondPass(ref exInfo, startIdx);
}

#if FEATURE_OBJCMARSHAL
if (pReversePInvokePropagationCallback != IntPtr.Zero)
{
InternalCalls.RhpCallPropagateExceptionCallback(
pReversePInvokePropagationContext, pReversePInvokePropagationCallback, frameIter.RegisterSet, ref exInfo);
// the helper should jump to propagation handler and not return
Debug.Assert(false, "unreachable");
FallbackFailFast(RhFailFastReason.InternalError, null);
}
#endif // FEATURE_OBJCMARSHAL


// ------------------------------------------------
//
// Call the handler and resume execution
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ internal enum ClassLibFunctionId
ObjectiveCMarshalTryGetTaggedMemory = 10,
ObjectiveCMarshalGetIsTrackedReferenceCallback = 11,
ObjectiveCMarshalGetOnEnteredFinalizerQueueCallback = 12,
ObjectiveCMarshalGetUnhandledExceptionPropagationHandler = 13,
}

internal static class InternalCalls
Expand Down Expand Up @@ -230,6 +231,13 @@ internal static extern unsafe IntPtr RhpCallCatchFunclet(
internal static extern unsafe bool RhpCallFilterFunclet(
object exceptionObj, byte* pFilterIP, void* pvRegDisplay);

#if FEATURE_OBJCMARSHAL
[RuntimeImport(Redhawk.BaseName, "RhpCallPropagateExceptionCallback")]
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern unsafe IntPtr RhpCallPropagateExceptionCallback(
IntPtr callbackContext, IntPtr callback, void* pvRegDisplay, ref EH.ExInfo exInfo);
#endif // FEATURE_OBJCMARSHAL

[RuntimeImport(Redhawk.BaseName, "RhpFallbackFailFast")]
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern unsafe void RhpFallbackFailFast();
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/nativeaot/Runtime/EHHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ COOP_PINVOKE_HELPER(int32_t, RhGetModuleFileName, (HANDLE moduleHandle, _Out_ co

COOP_PINVOKE_HELPER(void, RhpCopyContextFromExInfo, (void * pOSContext, int32_t cbOSContext, PAL_LIMITED_CONTEXT * pPalContext))
{
UNREFERENCED_PARAMETER(cbOSContext);
ASSERT((size_t)cbOSContext >= sizeof(CONTEXT));
memset(pOSContext, 0, cbOSContext);
CONTEXT* pContext = (CONTEXT *)pOSContext;
#if defined(UNIX_AMD64_ABI)
pContext->Rip = pPalContext->IP;
Expand Down
11 changes: 11 additions & 0 deletions src/coreclr/nativeaot/Runtime/ICodeManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ enum class ClasslibFunctionId
ObjectiveCMarshalTryGetTaggedMemory = 10,
ObjectiveCMarshalGetIsTrackedReferenceCallback = 11,
ObjectiveCMarshalGetOnEnteredFinalizerQueueCallback = 12,
ObjectiveCMarshalGetUnhandledExceptionPropagationHandler = 13,
};

enum class AssociatedDataFlags : unsigned char
Expand All @@ -165,6 +166,14 @@ enum class AssociatedDataFlags : unsigned char
HasUnboxingStubTarget = 1,
};

enum UnwindStackFrameFlags
{
USFF_None = 0,
// If this is a reverse P/Invoke frame, return the P/Invoke transition frame rather than doing a
// normal unwind.
USFF_UsePreviousTransitionFrame = 1,
};

class ICodeManager
{
public:
Expand All @@ -185,7 +194,9 @@ class ICodeManager
bool isActiveStackFrame) PURE_VIRTUAL

virtual bool UnwindStackFrame(MethodInfo * pMethodInfo,
uint32_t flags,
REGDISPLAY * pRegisterSet, // in/out
bool * pFoundReversePInvoke, // out
PInvokeTransitionFrame** ppPreviousTransitionFrame) PURE_VIRTUAL // out

virtual uintptr_t GetConservativeUpperBoundForOutgoingArgs(MethodInfo * pMethodInfo,
Expand Down
41 changes: 36 additions & 5 deletions src/coreclr/nativeaot/Runtime/StackFrameIterator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1413,6 +1413,8 @@ void StackFrameIterator::NextInternal()
{
UnwindOutOfCurrentManagedFrame:
ASSERT(m_dwFlags & MethodStateCalculated);
// Due to the lack of an ICodeManager for native code, we can't unwind from a native frame.
ASSERT((m_dwFlags & (SkipNativeFrames|UnwoundReversePInvoke)) != UnwoundReversePInvoke);
m_dwFlags &= ~(ExCollide|MethodStateCalculated|UnwoundReversePInvoke|ActiveStackFrame);
ASSERT(IsValid());

Expand All @@ -1432,14 +1434,29 @@ void StackFrameIterator::NextInternal()
uintptr_t DEBUG_preUnwindSP = m_RegDisplay.GetSP();
#endif

uint32_t unwindFlags = USFF_None;
if ((m_dwFlags & SkipNativeFrames) != 0)
{
unwindFlags |= USFF_UsePreviousTransitionFrame;
}

bool foundReversePInvoke;
PInvokeTransitionFrame* pPreviousTransitionFrame;
FAILFAST_OR_DAC_FAIL(GetCodeManager()->UnwindStackFrame(&m_methodInfo, &m_RegDisplay, &pPreviousTransitionFrame));
FAILFAST_OR_DAC_FAIL(GetCodeManager()->UnwindStackFrame(&m_methodInfo, unwindFlags, &m_RegDisplay,
&foundReversePInvoke, &pPreviousTransitionFrame));

if (foundReversePInvoke)
{
ASSERT(pPreviousTransitionFrame != nullptr || (unwindFlags & USFF_UsePreviousTransitionFrame) == 0);
m_dwFlags |= UnwoundReversePInvoke;
}

bool doingFuncletUnwind = GetCodeManager()->IsFunclet(&m_methodInfo);

if (pPreviousTransitionFrame != NULL)
{
ASSERT(!doingFuncletUnwind);
ASSERT(foundReversePInvoke);

if (pPreviousTransitionFrame == TOP_OF_STACK_MARKER)
{
Expand All @@ -1456,9 +1473,9 @@ void StackFrameIterator::NextInternal()
// see a conservative range reported by one of the thunks encountered during this "nested"
// unwind.
InternalInit(m_pThread, pPreviousTransitionFrame, GcStackWalkFlags);
m_dwFlags |= UnwoundReversePInvoke;
ASSERT(m_pInstance->IsManaged(m_ControlPC));
}
m_dwFlags |= UnwoundReversePInvoke;
}
else
{
Expand Down Expand Up @@ -1579,11 +1596,12 @@ void StackFrameIterator::NextInternal()
}

// Now that all assembly thunks and ExInfo collisions have been processed, it is guaranteed
// that the next managed frame has been located. The located frame must now be yielded
// that the next managed frame has been located. Or the next native frame
// if we are not skipping them. The located frame must now be yielded
// from the iterator with the one and only exception being cases where a managed frame must
// be skipped due to funclet collapsing.

ASSERT(m_pInstance->IsManaged(m_ControlPC));
ASSERT(m_pInstance->IsManaged(m_ControlPC) || (foundReversePInvoke && (m_dwFlags & SkipNativeFrames) == 0));

if (collapsingTargetFrame != NULL)
{
Expand Down Expand Up @@ -1692,7 +1710,8 @@ void StackFrameIterator::PrepareToYieldFrame()
if (!IsValid())
return;

ASSERT(m_pInstance->IsManaged(m_ControlPC));
ASSERT(m_pInstance->IsManaged(m_ControlPC) ||
((m_dwFlags & SkipNativeFrames) == 0 && (m_dwFlags & UnwoundReversePInvoke) != 0));

if (m_dwFlags & ApplyReturnAddressAdjustment)
{
Expand Down Expand Up @@ -1748,6 +1767,7 @@ REGDISPLAY * StackFrameIterator::GetRegisterSet()
PTR_VOID StackFrameIterator::GetEffectiveSafePointAddress()
{
ASSERT(IsValid());
ASSERT(m_effectiveSafePointAddress);
return m_effectiveSafePointAddress;
}

Expand Down Expand Up @@ -1780,6 +1800,17 @@ void StackFrameIterator::CalculateCurrentMethodState()
if (m_dwFlags & MethodStateCalculated)
return;

// Check if we are on a native frame.
if ((m_dwFlags & (SkipNativeFrames|UnwoundReversePInvoke)) == UnwoundReversePInvoke)
{
// There is no implementation of ICodeManager for native code.
m_pCodeManager = nullptr;
m_effectiveSafePointAddress = nullptr;
m_FramePointer = nullptr;
m_dwFlags |= MethodStateCalculated;
return;
}

// Assume that the caller is likely to be in the same module
if (m_pCodeManager == NULL || !m_pCodeManager->FindMethodInfo(m_ControlPC, &m_methodInfo))
{
Expand Down
7 changes: 5 additions & 2 deletions src/coreclr/nativeaot/Runtime/StackFrameIterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,10 @@ class StackFrameIterator
// The thread was interrupted in the current frame at the current IP by a signal, SuspendThread or similar.
ActiveStackFrame = 0x40,

GcStackWalkFlags = (CollapseFunclets | RemapHardwareFaultsToSafePoint),
// When encountering a reverse P/Invoke, unwind directly to the P/Invoke frame using the saved transition frame.
SkipNativeFrames = 0x80,

GcStackWalkFlags = (CollapseFunclets | RemapHardwareFaultsToSafePoint | SkipNativeFrames),
EHStackWalkFlags = ApplyReturnAddressAdjustment,
StackTraceStackWalkFlags = GcStackWalkFlags
};
Expand Down Expand Up @@ -209,7 +212,7 @@ class StackFrameIterator
GCRefKind m_HijackedReturnValueKind;
PTR_UIntNative m_pConservativeStackRangeLowerBound;
PTR_UIntNative m_pConservativeStackRangeUpperBound;
uint32_t m_dwFlags;
uint32_t m_dwFlags;
PTR_ExInfo m_pNextExInfo;
PTR_VOID m_pendingFuncletFramePointer;
PreservedRegPtrs m_funcletPtrs; // @TODO: Placing the 'scratch space' in the StackFrameIterator is not
Expand Down
Loading