Skip to content

Commit 9750f6d

Browse files
committed
[RTL/x64] Improve RtlUnwindInternal
- Implement RtlpExecuteHandlerForUnwind in asm - Implement RtlpExecuteHandlerForUnwindHandler - Use the correct context in DISPATCHER_CONTEXT when unwinding - Handle collided unwinds
1 parent 4d21384 commit 9750f6d

File tree

4 files changed

+149
-11
lines changed

4 files changed

+149
-11
lines changed

sdk/lib/rtl/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ elseif(ARCH STREQUAL "amd64")
108108
list(APPEND ASM_SOURCE
109109
amd64/debug_asm.S
110110
amd64/except_asm.S
111-
amd64/slist.S)
111+
amd64/slist.S
112+
amd64/unwind-asm.s)
112113
list(APPEND SOURCE
113114
bitmap64.c
114115
byteswap.c

sdk/lib/rtl/amd64/unwind-asm.s

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* PROJECT: ReactOS runtime library
3+
* LICENSE: MIT (https://spdx.org/licenses/MIT)
4+
* PURPOSE: Unwinding related x64 asm functions
5+
* COPYRIGHT: Copyright 2025 Timo Kreuzer <[email protected]>
6+
*/
7+
8+
#include <asm.inc>
9+
#include <ksamd64.inc>
10+
11+
.code64
12+
13+
EXTERN RtlpExecuteHandlerForUnwindHandler:PROC
14+
15+
//
16+
// EXCEPTION_DISPOSITION
17+
// RtlpExecuteHandlerForUnwind(
18+
// _Inout_ PEXCEPTION_RECORD* ExceptionRecord,
19+
// _In_ PVOID EstablisherFrame,
20+
// _Inout_ PCONTEXT ContextRecord,
21+
// _In_ PDISPATCHER_CONTEXT DispatcherContext);
22+
//
23+
PUBLIC RtlpExecuteHandlerForUnwind
24+
#ifdef _USE_ML
25+
RtlpExecuteHandlerForUnwind PROC FRAME:RtlpExecuteHandlerForUnwindHandler
26+
#else
27+
.PROC RtlpExecuteHandlerForUnwind
28+
.seh_handler RtlpExecuteHandlerForUnwindHandler, @unwind, @except
29+
.seh_handlerdata
30+
.long 0 // Count
31+
.seh_code
32+
#endif
33+
34+
/* Save ExceptionRecord->ExceptionFlags in the home space */
35+
mov eax, [rcx + ErExceptionFlags]
36+
mov [rsp + P1Home], eax
37+
38+
/* Save DispatcherContext in the home space */
39+
mov [rsp + P4Home], r9
40+
41+
sub rsp, 40
42+
.ALLOCSTACK 40
43+
.ENDPROLOG
44+
45+
/* Call the language handler */
46+
call qword ptr [r9 + DcLanguageHandler]
47+
48+
/* This nop prevents RtlVirtualUnwind from unwinding the epilog,
49+
which would lead to ignoring the handler */
50+
nop
51+
52+
add rsp, 40
53+
ret
54+
55+
#ifdef _USE_ML
56+
RtlpExecuteHandlerForUnwind ENDP
57+
#else
58+
.ENDP
59+
#endif
60+
61+
END

sdk/lib/rtl/amd64/unwind.c

Lines changed: 76 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -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);

sdk/lib/rtl/rtlp.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,6 @@ RtlpExecuteHandlerForException(PEXCEPTION_RECORD ExceptionRecord,
184184
PCONTEXT Context,
185185
PVOID DispatcherContext,
186186
PEXCEPTION_ROUTINE ExceptionHandler);
187-
#endif
188187

189188
EXCEPTION_DISPOSITION
190189
NTAPI
@@ -193,6 +192,16 @@ RtlpExecuteHandlerForUnwind(PEXCEPTION_RECORD ExceptionRecord,
193192
PCONTEXT Context,
194193
PVOID DispatcherContext,
195194
PEXCEPTION_ROUTINE ExceptionHandler);
195+
#else
196+
EXCEPTION_DISPOSITION
197+
NTAPI
198+
RtlpExecuteHandlerForUnwind(
199+
_Inout_ struct _EXCEPTION_RECORD *ExceptionRecord,
200+
_In_ PVOID EstablisherFrame,
201+
_Inout_ struct _CONTEXT *ContextRecord,
202+
_In_ PVOID DispatcherContext);
203+
204+
#endif
196205

197206
VOID
198207
NTAPI

0 commit comments

Comments
 (0)