summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJan Vorlicek <janvorli@microsoft.com>2015-03-13 16:06:46 +0100
committerJan Vorlicek <janvorli@microsoft.com>2015-03-13 16:25:37 +0100
commit86fab083b53c46fc7cd07fc6d4747465c4257245 (patch)
tree5c5fb23ae0c0f36be866890a91c953f7c1451c9c /src
parent6ccf4e2a5db4a8804f113b6958da43bd571c4848 (diff)
downloadcoreclr-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.S4
-rw-r--r--src/vm/amd64/unixasmhelpers.S1
-rw-r--r--src/vm/exceptionhandling.cpp16
-rw-r--r--src/vm/frames.cpp13
-rw-r--r--src/vm/frames.h4
-rw-r--r--src/vm/stackwalk.cpp7
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
}