mirror of
https://github.com/reactos/reactos.git
synced 2025-07-29 15:02:03 +00:00
[RTL/x64] Improve RtlUnwindInternal
- Implement RtlpExecuteHandlerForUnwind in asm - Implement RtlpExecuteHandlerForUnwindHandler - Use the correct context in DISPATCHER_CONTEXT when unwinding - Handle collided unwinds
This commit is contained in:
parent
4d21384d8f
commit
9750f6decd
4 changed files with 149 additions and 11 deletions
|
@ -108,7 +108,8 @@ elseif(ARCH STREQUAL "amd64")
|
||||||
list(APPEND ASM_SOURCE
|
list(APPEND ASM_SOURCE
|
||||||
amd64/debug_asm.S
|
amd64/debug_asm.S
|
||||||
amd64/except_asm.S
|
amd64/except_asm.S
|
||||||
amd64/slist.S)
|
amd64/slist.S
|
||||||
|
amd64/unwind-asm.s)
|
||||||
list(APPEND SOURCE
|
list(APPEND SOURCE
|
||||||
bitmap64.c
|
bitmap64.c
|
||||||
byteswap.c
|
byteswap.c
|
||||||
|
|
61
sdk/lib/rtl/amd64/unwind-asm.s
Normal file
61
sdk/lib/rtl/amd64/unwind-asm.s
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* PROJECT: ReactOS runtime library
|
||||||
|
* LICENSE: MIT (https://spdx.org/licenses/MIT)
|
||||||
|
* PURPOSE: Unwinding related x64 asm functions
|
||||||
|
* COPYRIGHT: Copyright 2025 Timo Kreuzer <timo.kreuzer@reactos.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <asm.inc>
|
||||||
|
#include <ksamd64.inc>
|
||||||
|
|
||||||
|
.code64
|
||||||
|
|
||||||
|
EXTERN RtlpExecuteHandlerForUnwindHandler:PROC
|
||||||
|
|
||||||
|
//
|
||||||
|
// EXCEPTION_DISPOSITION
|
||||||
|
// RtlpExecuteHandlerForUnwind(
|
||||||
|
// _Inout_ PEXCEPTION_RECORD* ExceptionRecord,
|
||||||
|
// _In_ PVOID EstablisherFrame,
|
||||||
|
// _Inout_ PCONTEXT ContextRecord,
|
||||||
|
// _In_ PDISPATCHER_CONTEXT DispatcherContext);
|
||||||
|
//
|
||||||
|
PUBLIC RtlpExecuteHandlerForUnwind
|
||||||
|
#ifdef _USE_ML
|
||||||
|
RtlpExecuteHandlerForUnwind PROC FRAME:RtlpExecuteHandlerForUnwindHandler
|
||||||
|
#else
|
||||||
|
.PROC RtlpExecuteHandlerForUnwind
|
||||||
|
.seh_handler RtlpExecuteHandlerForUnwindHandler, @unwind, @except
|
||||||
|
.seh_handlerdata
|
||||||
|
.long 0 // Count
|
||||||
|
.seh_code
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Save ExceptionRecord->ExceptionFlags in the home space */
|
||||||
|
mov eax, [rcx + ErExceptionFlags]
|
||||||
|
mov [rsp + P1Home], eax
|
||||||
|
|
||||||
|
/* Save DispatcherContext in the home space */
|
||||||
|
mov [rsp + P4Home], r9
|
||||||
|
|
||||||
|
sub rsp, 40
|
||||||
|
.ALLOCSTACK 40
|
||||||
|
.ENDPROLOG
|
||||||
|
|
||||||
|
/* Call the language handler */
|
||||||
|
call qword ptr [r9 + DcLanguageHandler]
|
||||||
|
|
||||||
|
/* This nop prevents RtlVirtualUnwind from unwinding the epilog,
|
||||||
|
which would lead to ignoring the handler */
|
||||||
|
nop
|
||||||
|
|
||||||
|
add rsp, 40
|
||||||
|
ret
|
||||||
|
|
||||||
|
#ifdef _USE_ML
|
||||||
|
RtlpExecuteHandlerForUnwind ENDP
|
||||||
|
#else
|
||||||
|
.ENDP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
END
|
|
@ -661,6 +661,46 @@ RtlpIsStackPointerValid(
|
||||||
((StackPointer & 7) == 0);
|
((StackPointer & 7) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EXCEPTION_DISPOSITION
|
||||||
|
RtlpExecuteHandlerForUnwindHandler(
|
||||||
|
_Inout_ PEXCEPTION_RECORD ExceptionRecord,
|
||||||
|
_In_ PVOID EstablisherFrame,
|
||||||
|
_Inout_ PCONTEXT ContextRecord,
|
||||||
|
_In_ PDISPATCHER_CONTEXT DispatcherContext)
|
||||||
|
{
|
||||||
|
/* Get a pointer to the register home space for RtlpExecuteHandlerForUnwind */
|
||||||
|
PULONG64 HomeSpace = (PULONG64)EstablisherFrame + 6;
|
||||||
|
|
||||||
|
/* Get the ExceptionFlags value, which was saved in the home space */
|
||||||
|
ULONG ExceptionFlags = (ULONG)HomeSpace[0];
|
||||||
|
|
||||||
|
/* Get the DispatcherContext, which was saved in the home space */
|
||||||
|
PDISPATCHER_CONTEXT PreviousDispatcherContext = (PDISPATCHER_CONTEXT)HomeSpace[3];
|
||||||
|
|
||||||
|
/* Check if the original call to RtlpExecuteHandlerForUnwind was an unwind */
|
||||||
|
if (IS_UNWINDING(ExceptionFlags))
|
||||||
|
{
|
||||||
|
/* Check if the current call to this function is due to unwinding */
|
||||||
|
if (IS_UNWINDING(ExceptionRecord->ExceptionFlags))
|
||||||
|
{
|
||||||
|
/* We are unwinding over the call to a termination handler. This
|
||||||
|
could be due to an exception or longjmp. We need to make sure
|
||||||
|
to not run this termination handler again. To achieve that,
|
||||||
|
we copy the contents of the original dispatcher context back
|
||||||
|
over the current dispatcher context and return
|
||||||
|
ExceptionCollidedUnwind. RtlUnwindInternal will take the
|
||||||
|
original context, and continue unwing there. */
|
||||||
|
*DispatcherContext = *PreviousDispatcherContext;
|
||||||
|
return ExceptionCollidedUnwind;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: properly handle nested exceptions
|
||||||
|
|
||||||
|
return ExceptionContinueSearch;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\remark The implementation is based on the description in this blog: http://www.nynaeve.net/?p=106
|
\remark The implementation is based on the description in this blog: http://www.nynaeve.net/?p=106
|
||||||
|
|
||||||
|
@ -704,7 +744,8 @@ RtlpUnwindInternal(
|
||||||
UnwindContext = *ContextRecord;
|
UnwindContext = *ContextRecord;
|
||||||
|
|
||||||
/* Set up the constant fields of the dispatcher context */
|
/* Set up the constant fields of the dispatcher context */
|
||||||
DispatcherContext.ContextRecord = &UnwindContext;
|
DispatcherContext.ContextRecord =
|
||||||
|
(HandlerType == UNW_FLAG_UHANDLER) ? ContextRecord : &UnwindContext;
|
||||||
DispatcherContext.HistoryTable = HistoryTable;
|
DispatcherContext.HistoryTable = HistoryTable;
|
||||||
DispatcherContext.TargetIp = (ULONG64)TargetIp;
|
DispatcherContext.TargetIp = (ULONG64)TargetIp;
|
||||||
|
|
||||||
|
@ -793,9 +834,8 @@ RtlpUnwindInternal(
|
||||||
/* Loop all nested handlers */
|
/* Loop all nested handlers */
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
/// TODO: call RtlpExecuteHandlerForUnwind instead
|
|
||||||
/* Call the language specific handler */
|
/* Call the language specific handler */
|
||||||
Disposition = ExceptionRoutine(ExceptionRecord,
|
Disposition = RtlpExecuteHandlerForUnwind(ExceptionRecord,
|
||||||
(PVOID)EstablisherFrame,
|
(PVOID)EstablisherFrame,
|
||||||
ContextRecord,
|
ContextRecord,
|
||||||
&DispatcherContext);
|
&DispatcherContext);
|
||||||
|
@ -828,12 +868,39 @@ RtlpUnwindInternal(
|
||||||
|
|
||||||
if (Disposition == ExceptionCollidedUnwind)
|
if (Disposition == ExceptionCollidedUnwind)
|
||||||
{
|
{
|
||||||
/// TODO
|
/* We collided with another unwind, so we need to continue
|
||||||
__debugbreak();
|
"after" the handler, skipping the termination handler
|
||||||
|
that resulted in this unwind. The installed handler has
|
||||||
|
already copied the original dispatcher context, we now
|
||||||
|
need to copy back the original context. */
|
||||||
|
UnwindContext = *ContextRecord = *DispatcherContext.ContextRecord;
|
||||||
|
|
||||||
|
/* The original context was from "before" the unwind, so we
|
||||||
|
need to do an additional virtual unwind to restore the
|
||||||
|
unwind contxt. */
|
||||||
|
RtlVirtualUnwind(HandlerType,
|
||||||
|
DispatcherContext.ImageBase,
|
||||||
|
UnwindContext.Rip,
|
||||||
|
DispatcherContext.FunctionEntry,
|
||||||
|
&UnwindContext,
|
||||||
|
&DispatcherContext.HandlerData,
|
||||||
|
&EstablisherFrame,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* Restore the context pointer and establisher frame. */
|
||||||
|
DispatcherContext.ContextRecord = &UnwindContext;
|
||||||
|
EstablisherFrame = DispatcherContext.EstablisherFrame;
|
||||||
|
|
||||||
|
/* Set the exception flags to indicate that we collided
|
||||||
|
with an unwind and continue the handler loop, which
|
||||||
|
will run any additional handlers from the previous
|
||||||
|
unwind. */
|
||||||
|
ExceptionRecord->ExceptionFlags |= EXCEPTION_COLLIDED_UNWIND;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This must be ExceptionContinueSearch now */
|
/* This must be ExceptionContinueSearch now */
|
||||||
if (Disposition != ExceptionContinueSearch)
|
else if (Disposition != ExceptionContinueSearch)
|
||||||
{
|
{
|
||||||
__debugbreak();
|
__debugbreak();
|
||||||
RtlRaiseStatus(STATUS_INVALID_DISPOSITION);
|
RtlRaiseStatus(STATUS_INVALID_DISPOSITION);
|
||||||
|
|
|
@ -184,7 +184,6 @@ RtlpExecuteHandlerForException(PEXCEPTION_RECORD ExceptionRecord,
|
||||||
PCONTEXT Context,
|
PCONTEXT Context,
|
||||||
PVOID DispatcherContext,
|
PVOID DispatcherContext,
|
||||||
PEXCEPTION_ROUTINE ExceptionHandler);
|
PEXCEPTION_ROUTINE ExceptionHandler);
|
||||||
#endif
|
|
||||||
|
|
||||||
EXCEPTION_DISPOSITION
|
EXCEPTION_DISPOSITION
|
||||||
NTAPI
|
NTAPI
|
||||||
|
@ -193,6 +192,16 @@ RtlpExecuteHandlerForUnwind(PEXCEPTION_RECORD ExceptionRecord,
|
||||||
PCONTEXT Context,
|
PCONTEXT Context,
|
||||||
PVOID DispatcherContext,
|
PVOID DispatcherContext,
|
||||||
PEXCEPTION_ROUTINE ExceptionHandler);
|
PEXCEPTION_ROUTINE ExceptionHandler);
|
||||||
|
#else
|
||||||
|
EXCEPTION_DISPOSITION
|
||||||
|
NTAPI
|
||||||
|
RtlpExecuteHandlerForUnwind(
|
||||||
|
_Inout_ struct _EXCEPTION_RECORD *ExceptionRecord,
|
||||||
|
_In_ PVOID EstablisherFrame,
|
||||||
|
_Inout_ struct _CONTEXT *ContextRecord,
|
||||||
|
_In_ PVOID DispatcherContext);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
NTAPI
|
NTAPI
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue