@@ -661,6 +661,46 @@ RtlpIsStackPointerValid(
661661 ((StackPointer & 7 ) == 0 );
662662}
663663
664+ EXCEPTION_DISPOSITION
665+ RtlpExecuteHandlerForUnwindHandler (
666+ _Inout_ PEXCEPTION_RECORD ExceptionRecord ,
667+ _In_ PVOID EstablisherFrame ,
668+ _Inout_ PCONTEXT ContextRecord ,
669+ _In_ PDISPATCHER_CONTEXT DispatcherContext )
670+ {
671+ /* Get a pointer to the register home space for RtlpExecuteHandlerForUnwind */
672+ PULONG64 HomeSpace = (PULONG64 )EstablisherFrame + 6 ;
673+
674+ /* Get the ExceptionFlags value, which was saved in the home space */
675+ ULONG ExceptionFlags = (ULONG )HomeSpace [0 ];
676+
677+ /* Get the DispatcherContext, which was saved in the home space */
678+ PDISPATCHER_CONTEXT PreviousDispatcherContext = (PDISPATCHER_CONTEXT )HomeSpace [3 ];
679+
680+ /* Check if the original call to RtlpExecuteHandlerForUnwind was an unwind */
681+ if (IS_UNWINDING (ExceptionFlags ))
682+ {
683+ /* Check if the current call to this function is due to unwinding */
684+ if (IS_UNWINDING (ExceptionRecord -> ExceptionFlags ))
685+ {
686+ /* We are unwinding over the call to a termination handler. This
687+ could be due to an exception or longjmp. We need to make sure
688+ to not run this termination handler again. To achieve that,
689+ we copy the contents of the original dispatcher context back
690+ over the current dispatcher context and return
691+ ExceptionCollidedUnwind. RtlUnwindInternal will take the
692+ original context, and continue unwing there. */
693+ * DispatcherContext = * PreviousDispatcherContext ;
694+ return ExceptionCollidedUnwind ;
695+ }
696+ }
697+
698+ // TODO: properly handle nested exceptions
699+
700+ return ExceptionContinueSearch ;
701+ }
702+
703+
664704/*!
665705 \remark The implementation is based on the description in this blog: http://www.nynaeve.net/?p=106
666706
@@ -704,7 +744,8 @@ RtlpUnwindInternal(
704744 UnwindContext = * ContextRecord ;
705745
706746 /* Set up the constant fields of the dispatcher context */
707- DispatcherContext .ContextRecord = & UnwindContext ;
747+ DispatcherContext .ContextRecord =
748+ (HandlerType == UNW_FLAG_UHANDLER ) ? ContextRecord : & UnwindContext ;
708749 DispatcherContext .HistoryTable = HistoryTable ;
709750 DispatcherContext .TargetIp = (ULONG64 )TargetIp ;
710751
@@ -793,12 +834,11 @@ RtlpUnwindInternal(
793834 /* Loop all nested handlers */
794835 do
795836 {
796- /// TODO: call RtlpExecuteHandlerForUnwind instead
797837 /* Call the language specific handler */
798- Disposition = ExceptionRoutine (ExceptionRecord ,
799- (PVOID )EstablisherFrame ,
800- ContextRecord ,
801- & DispatcherContext );
838+ Disposition = RtlpExecuteHandlerForUnwind (ExceptionRecord ,
839+ (PVOID )EstablisherFrame ,
840+ ContextRecord ,
841+ & DispatcherContext );
802842
803843 /* Clear exception flags for the next iteration */
804844 ExceptionRecord -> ExceptionFlags &= ~(EXCEPTION_TARGET_UNWIND |
@@ -828,12 +868,39 @@ RtlpUnwindInternal(
828868
829869 if (Disposition == ExceptionCollidedUnwind )
830870 {
831- /// TODO
832- __debugbreak ();
871+ /* We collided with another unwind, so we need to continue
872+ "after" the handler, skipping the termination handler
873+ that resulted in this unwind. The installed handler has
874+ already copied the original dispatcher context, we now
875+ need to copy back the original context. */
876+ UnwindContext = * ContextRecord = * DispatcherContext .ContextRecord ;
877+
878+ /* The original context was from "before" the unwind, so we
879+ need to do an additional virtual unwind to restore the
880+ unwind contxt. */
881+ RtlVirtualUnwind (HandlerType ,
882+ DispatcherContext .ImageBase ,
883+ UnwindContext .Rip ,
884+ DispatcherContext .FunctionEntry ,
885+ & UnwindContext ,
886+ & DispatcherContext .HandlerData ,
887+ & EstablisherFrame ,
888+ NULL );
889+
890+ /* Restore the context pointer and establisher frame. */
891+ DispatcherContext .ContextRecord = & UnwindContext ;
892+ EstablisherFrame = DispatcherContext .EstablisherFrame ;
893+
894+ /* Set the exception flags to indicate that we collided
895+ with an unwind and continue the handler loop, which
896+ will run any additional handlers from the previous
897+ unwind. */
898+ ExceptionRecord -> ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND ;
899+ continue ;
833900 }
834901
835902 /* This must be ExceptionContinueSearch now */
836- if (Disposition != ExceptionContinueSearch )
903+ else if (Disposition != ExceptionContinueSearch )
837904 {
838905 __debugbreak ();
839906 RtlRaiseStatus (STATUS_INVALID_DISPOSITION );
0 commit comments