diff options
author | Jan Vorlicek <janvorli@microsoft.com> | 2015-03-13 16:06:46 +0100 |
---|---|---|
committer | Jan Vorlicek <janvorli@microsoft.com> | 2015-03-13 16:25:37 +0100 |
commit | 86fab083b53c46fc7cd07fc6d4747465c4257245 (patch) | |
tree | 5c5fb23ae0c0f36be866890a91c953f7c1451c9c /src | |
parent | 6ccf4e2a5db4a8804f113b6958da43bd571c4848 (diff) | |
download | coreclr-86fab083b53c46fc7cd07fc6d4747465c4257245.tar.gz coreclr-86fab083b53c46fc7cd07fc6d4747465c4257245.tar.bz2 coreclr-86fab083b53c46fc7cd07fc6d4747465c4257245.zip |
Fix managed exception unwinding through CallDescrWorkerInternal
This change fixes issue with exception unwinding in the case when the unwinding
passed through a frame of the CallDescrWorkerInternal function. This function
had personality routine on it, but a windows style personality routine was specified.
The windows one has a completely different signature, so the code was crashing.
When looking into that, I've found that even if I have implemented a proper
Unix style personality routine, it cannot work the same way on Linux as it
used to work on Windows.
This personality routine's goal is to pop Frames from the Frame list in the current
thread so that all frames upto the frame handling the exception are popped.
There are two problems on Linux. First, unlike on Windows, the personality
routine is not passed the RSP of the frame handling the exception in an official
way. Although it can be extracted from the private_2 member of the exception
object during the 2nd pass, it is an implementation detail that we cannot rely on.
Moreover, even if we used that, it would still not be the right frame in all cases
due to the fact that we implement exception filters by catching and rethrowing and
so the frame we would get would be the frame of a filtering catch in case
there was one.
My solution to this problem is to add destructor to the Frame type and let it
pop the frame being destroyed if it is still in the list in the current thread.
That way the native code unwinding automatically takes care of popping the frames.
As an additional changes, I've added handling of the case when the
Thread::VirtualUnwindToFirstManagedCallFrame walks out of stack, fixed a stack
alignment issue in the recently added StartUnwindingNativeFrames function and
a cosmetic change in the UnwindManagedExceptionPass1.
Diffstat (limited to 'src')
-rw-r--r-- | src/vm/amd64/calldescrworkeramd64.S | 4 | ||||
-rw-r--r-- | src/vm/amd64/unixasmhelpers.S | 1 | ||||
-rw-r--r-- | src/vm/exceptionhandling.cpp | 16 | ||||
-rw-r--r-- | src/vm/frames.cpp | 13 | ||||
-rw-r--r-- | src/vm/frames.h | 4 | ||||
-rw-r--r-- | src/vm/stackwalk.cpp | 7 |
6 files changed, 35 insertions, 10 deletions
diff --git a/src/vm/amd64/calldescrworkeramd64.S b/src/vm/amd64/calldescrworkeramd64.S index eced6b9569..efee6f325a 100644 --- a/src/vm/amd64/calldescrworkeramd64.S +++ b/src/vm/amd64/calldescrworkeramd64.S @@ -15,7 +15,7 @@ // // EXTERN_C void FastCallFinalizeWorker(Object *obj, PCODE funcPtr); // -NESTED_ENTRY FastCallFinalizeWorker, _TEXT, CallDescrWorkerUnwindFrameChainHandler +NESTED_ENTRY FastCallFinalizeWorker, _TEXT, NoHandler push_nonvol_reg rbp mov rbp, rsp END_PROLOGUE @@ -41,7 +41,7 @@ NESTED_END FastCallFinalizeWorker, _TEXT //extern "C" void CallDescrWorkerInternal(CallDescrData * pCallDescrData); -NESTED_ENTRY CallDescrWorkerInternal, _TEXT, CallDescrWorkerUnwindFrameChainHandler +NESTED_ENTRY CallDescrWorkerInternal, _TEXT, NoHandler push_nonvol_reg rbp mov rbp, rsp push_nonvol_reg rbx diff --git a/src/vm/amd64/unixasmhelpers.S b/src/vm/amd64/unixasmhelpers.S index c6ed71d332..fb968459d3 100644 --- a/src/vm/amd64/unixasmhelpers.S +++ b/src/vm/amd64/unixasmhelpers.S @@ -225,6 +225,7 @@ LEAF_ENTRY StartUnwindingNativeFrames, _TEXT // Store return address to the stack push_register rax + push_nonvol_reg rbp call EXTERNAL_C_FUNC(__cxa_rethrow) LEAF_END StartUnwindingNativeFrames, _TEXT diff --git a/src/vm/exceptionhandling.cpp b/src/vm/exceptionhandling.cpp index 9e01aed991..909397e18a 100644 --- a/src/vm/exceptionhandling.cpp +++ b/src/vm/exceptionhandling.cpp @@ -4546,20 +4546,20 @@ VOID DECLSPEC_NORETURN UnwindManagedExceptionPass1(PAL_SEHException& ex) // detects that there was a second pass and that it needs to recreate the tracker. firstPassFlags.SetUnwindHasStarted(); - *currentFlags = firstPassFlags; - - // Pop the last managed frame so that when the native frames are unwound and - // the UnwindManagedExceptionPass1 is resumed at the next managed frame, that - // managed frame is the current one set in the thread object. - GetThread()->GetFrame()->Pop(); - // Tell the tracker that we are starting interleaved handling of the exception. // The interleaved handling is used when an exception unwinding passes through // interleaved managed and native stack frames. In that case, instead of // performing first pass of the unwinding over all the stack range and then // second pass over the same range, we unwind each managed / native subrange // separately, performing both passes on a subrange before moving to the next one. - GetThread()->GetExceptionState()->GetFlags()->SetIsInterleavedHandling(); + firstPassFlags.SetIsInterleavedHandling(); + + *currentFlags = firstPassFlags; + + // Pop the last managed frame so that when the native frames are unwound and + // the UnwindManagedExceptionPass1 is resumed at the next managed frame, that + // managed frame is the current one set in the thread object. + GetThread()->GetFrame()->Pop(); // Now we need to unwind the native frames until we reach managed frames again or the exception is // handled in the native code. diff --git a/src/vm/frames.cpp b/src/vm/frames.cpp index 365964512e..bc1d48e252 100644 --- a/src/vm/frames.cpp +++ b/src/vm/frames.cpp @@ -479,6 +479,19 @@ VOID Frame::Pop(Thread *pThread) pThread->SetFrame(m_Next); } +#ifdef FEATURE_PAL +Frame::~Frame() +{ + // When the frame is destroyed, make sure it is no longer in the + // frame chain managed by the Thread. + Thread* pThread = GetThread(); + if (pThread != NULL && pThread->GetFrame() == this) + { + Pop(pThread); + } +} +#endif FEATURE_PAL + //----------------------------------------------------------------------- #endif // #ifndef DACCESS_COMPILE //--------------------------------------------------------------- diff --git a/src/vm/frames.h b/src/vm/frames.h index 05a7a175f6..3b08f62a6f 100644 --- a/src/vm/frames.h +++ b/src/vm/frames.h @@ -418,6 +418,10 @@ class Frame : public FrameBase public: +#ifdef FEATURE_PAL + virtual ~Frame(); +#endif // FEATURE_PAL + //------------------------------------------------------------------------ // Special characteristics of a frame //------------------------------------------------------------------------ diff --git a/src/vm/stackwalk.cpp b/src/vm/stackwalk.cpp index 01cfcf4790..0b8ab95cb6 100644 --- a/src/vm/stackwalk.cpp +++ b/src/vm/stackwalk.cpp @@ -787,6 +787,13 @@ UINT_PTR Thread::VirtualUnwindToFirstManagedCallFrame(T_CONTEXT* pContext) } uControlPc = GetIP(pContext); + + if (uControlPc == 0) + { + _ASSERTE(!"Thread::VirtualUnwindToFirstManagedCallFrame: PAL_VirtualUnwind reached end of the stack"); + EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); + } + #endif // !FEATURE_PAL } |