BOOL PAL_VirtualUnwind(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers)
{
int st;
unw_context_t unwContext;
unw_cursor_t cursor;
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(_ARM64_) || defined(_ARM_)
DWORD64 curPc;
#endif
if ((context->ContextFlags & CONTEXT_EXCEPTION_ACTIVE) != 0)
{
// The current frame is a source of hardware exception. Due to the fact that
// we use the low level unwinder to unwind just one frame a time, the
// unwinder doesn't have the signal_frame flag set. So it doesn't
// know that it should not decrement the PC before looking up the unwind info.
// So we compensate it by incrementing the PC before passing it to the unwinder.
// Without it, the unwinder would not find unwind info if the hardware exception
// happened in the first instruction of a function.
CONTEXTSetPC(context, CONTEXTGetPC(context) + 1);
}
#if UNWIND_CONTEXT_IS_UCONTEXT_T
WinContextToUnwindContext(context, &unwContext);
#else
st = unw_getcontext(&unwContext);
if (st < 0)
{
return FALSE;
}
#endif
st = unw_init_local(&cursor, &unwContext);
if (st < 0)
{
return FALSE;
}
#if !UNWIND_CONTEXT_IS_UCONTEXT_T
// Set the unwind context to the specified windows context
WinContextToUnwindCursor(context, &cursor);
#endif
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(_ARM64_) || defined(_ARM_)
// OSX and FreeBSD appear to do two different things when unwinding
// 1: If it reaches where it cannot unwind anymore, say a
// managed frame. It wil return 0, but also update the $pc
// 2: If it unwinds all the way to _start it will return
// 0 from the step, but $pc will stay the same.
// The behaviour of libunwind from nongnu.org is to null the PC
// So we bank the original PC here, so we can compare it after
// the step
curPc = CONTEXTGetPC(context);
#endif
st = unw_step(&cursor);
if (st < 0)
{
return FALSE;
}
// Check if the frame we have unwound to is a frame that caused
// synchronous signal, like a hardware exception and record it
// in the context flags.
if (unw_is_signal_frame(&cursor) > 0)
{
context->ContextFlags |= CONTEXT_EXCEPTION_ACTIVE;
}
else
{
context->ContextFlags &= ~CONTEXT_EXCEPTION_ACTIVE;
}
// Update the passed in windows context to reflect the unwind
//
UnwindContextToWinContext(&cursor, context);
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(_ARM64_) || defined(_ARM_)
if (st == 0 && CONTEXTGetPC(context) == curPc)
{
CONTEXTSetPC(context, 0);
}
#endif
if (contextPointers != NULL)
{
GetContextPointers(&cursor, &unwContext, contextPointers);
}
return TRUE;
}
File: src/pal/src/exception/seh-unwind.cpp
How to check if I need the extra code path enabled also for NetBSD?