https://git.reactos.org/?p=reactos.git;a=commitdiff;h=1d58e847364d6252fe055…
commit 1d58e847364d6252fe055a8a227775d26a2427b4
Author: Timo Kreuzer <timo.kreuzer(a)reactos.org>
AuthorDate: Thu Mar 1 14:14:13 2018 +0100
Commit: Timo Kreuzer <timo.kreuzer(a)reactos.org>
CommitDate: Sat Jun 5 13:52:42 2021 +0200
[RTL] Improve RtlVirtualUnwind
* Add support for version 2 unwind info
* Implement UnwindOpSlots() and use it (based on
https://github.com/dotnet/coreclr/blob/master/src/unwinder/amd64/unwinder_a…)
* Fix handling of UWOP_PUSH_MACHFRAME
---
sdk/lib/rtl/amd64/unwind.c | 291 ++++++++++++++++++++++++++++++++++++---------
1 file changed, 234 insertions(+), 57 deletions(-)
diff --git a/sdk/lib/rtl/amd64/unwind.c b/sdk/lib/rtl/amd64/unwind.c
index 6f3ce6efd51..be0223e1dd5 100644
--- a/sdk/lib/rtl/amd64/unwind.c
+++ b/sdk/lib/rtl/amd64/unwind.c
@@ -22,8 +22,13 @@
#define UWOP_SET_FPREG 3
#define UWOP_SAVE_NONVOL 4
#define UWOP_SAVE_NONVOL_FAR 5
+#if 0 // These are deprecated / not for x64
#define UWOP_SAVE_XMM 6
#define UWOP_SAVE_XMM_FAR 7
+#else
+#define UWOP_EPILOG 6
+#define UWOP_SPARE_CODE 7
+#endif
#define UWOP_SAVE_XMM128 8
#define UWOP_SAVE_XMM128_FAR 9
#define UWOP_PUSH_MACHFRAME 10
@@ -193,27 +198,121 @@ RtlInstallFunctionTableCallback(
return FALSE;
}
+static
+__inline
+ULONG
+UnwindOpSlots(
+ _In_ UNWIND_CODE UnwindCode)
+{
+ static const UCHAR UnwindOpExtraSlotTable[] =
+ {
+ 0, // UWOP_PUSH_NONVOL
+ 1, // UWOP_ALLOC_LARGE (or 3, special cased in lookup code)
+ 0, // UWOP_ALLOC_SMALL
+ 0, // UWOP_SET_FPREG
+ 1, // UWOP_SAVE_NONVOL
+ 2, // UWOP_SAVE_NONVOL_FAR
+ 1, // UWOP_EPILOG // previously UWOP_SAVE_XMM
+ 2, // UWOP_SPARE_CODE // previously UWOP_SAVE_XMM_FAR
+ 1, // UWOP_SAVE_XMM128
+ 2, // UWOP_SAVE_XMM128_FAR
+ 0, // UWOP_PUSH_MACHFRAME
+ 2, // UWOP_SET_FPREG_LARGE
+ };
+
+ if ((UnwindCode.UnwindOp == UWOP_ALLOC_LARGE) &&
+ (UnwindCode.OpInfo != 0))
+ {
+ return 3;
+ }
+ else
+ {
+ return UnwindOpExtraSlotTable[UnwindCode.UnwindOp] + 1;
+ }
+}
+
+static
+__inline
void
-FORCEINLINE
-SetReg(PCONTEXT Context, BYTE Reg, DWORD64 Value)
+SetReg(
+ _Inout_ PCONTEXT Context,
+ _In_ BYTE Reg,
+ _In_ DWORD64 Value)
{
((DWORD64*)(&Context->Rax))[Reg] = Value;
}
+static
+__inline
+void
+SetRegFromStackValue(
+ _Inout_ PCONTEXT Context,
+ _Inout_opt_ PKNONVOLATILE_CONTEXT_POINTERS ContextPointers,
+ _In_ BYTE Reg,
+ _In_ PDWORD64 ValuePointer)
+{
+ SetReg(Context, Reg, *ValuePointer);
+ if (ContextPointers != NULL)
+ {
+ ContextPointers->IntegerContext[Reg] = ValuePointer;
+ }
+}
+
+static
+__inline
DWORD64
-FORCEINLINE
-GetReg(PCONTEXT Context, BYTE Reg)
+GetReg(
+ _In_ PCONTEXT Context,
+ _In_ BYTE Reg)
{
return ((DWORD64*)(&Context->Rax))[Reg];
}
+static
+__inline
+void
+PopReg(
+ _Inout_ PCONTEXT Context,
+ _Inout_opt_ PKNONVOLATILE_CONTEXT_POINTERS ContextPointers,
+ _In_ BYTE Reg)
+{
+ SetRegFromStackValue(Context, ContextPointers, Reg, (PDWORD64)Context->Rsp);
+ Context->Rsp += sizeof(DWORD64);
+}
+
+static
+__inline
void
-FORCEINLINE
-PopReg(PCONTEXT Context, BYTE Reg)
+SetXmmReg(
+ _Inout_ PCONTEXT Context,
+ _In_ BYTE Reg,
+ _In_ M128A Value)
+{
+ ((M128A*)(&Context->Xmm0))[Reg] = Value;
+}
+
+static
+__inline
+void
+SetXmmRegFromStackValue(
+ _Out_ PCONTEXT Context,
+ _Inout_opt_ PKNONVOLATILE_CONTEXT_POINTERS ContextPointers,
+ _In_ BYTE Reg,
+ _In_ M128A *ValuePointer)
+{
+ SetXmmReg(Context, Reg, *ValuePointer);
+ if (ContextPointers != NULL)
+ {
+ ContextPointers->FloatingContext[Reg] = ValuePointer;
+ }
+}
+
+static
+__inline
+M128A
+GetXmmReg(PCONTEXT Context, BYTE Reg)
{
- DWORD64 Value = *(DWORD64*)Context->Rsp;
- Context->Rsp += 8;
- SetReg(Context, Reg, Value);
+ return ((M128A*)(&Context->Xmm0))[Reg];
}
/*! RtlpTryToUnwindEpilog
@@ -221,18 +320,19 @@ PopReg(PCONTEXT Context, BYTE Reg)
* \return TRUE if we have been in an epilog and it could be unwound.
* FALSE if the instructions were not allowed for an epilog.
* \ref
- *
http://msdn.microsoft.com/en-us/library/8ydc79k6(VS.80).aspx
- *
http://msdn.microsoft.com/en-us/library/tawsa7cb.aspx
+ *
https://docs.microsoft.com/en-us/cpp/build/unwind-procedure
+ *
https://docs.microsoft.com/en-us/cpp/build/prolog-and-epilog
* \todo
* - Test and compare with Windows behaviour
*/
-BOOLEAN
static
__inline
+BOOLEAN
RtlpTryToUnwindEpilog(
- PCONTEXT Context,
- ULONG64 ImageBase,
- PRUNTIME_FUNCTION FunctionEntry)
+ _Inout_ PCONTEXT Context,
+ _Inout_opt_ PKNONVOLATILE_CONTEXT_POINTERS ContextPointers,
+ _In_ ULONG64 ImageBase,
+ _In_ PRUNTIME_FUNCTION FunctionEntry)
{
CONTEXT LocalContext;
BYTE *InstrPtr;
@@ -302,7 +402,7 @@ RtlpTryToUnwindEpilog(
{
/* Opcode pops a basic register from stack */
Reg = Instr & 0x7;
- PopReg(&LocalContext, Reg);
+ PopReg(&LocalContext, ContextPointers, Reg);
InstrPtr++;
continue;
}
@@ -312,7 +412,7 @@ RtlpTryToUnwindEpilog(
{
/* Opcode is pop r8 .. r15 */
Reg = ((Instr >> 8) & 0x7) + 8;
- PopReg(&LocalContext, Reg);
+ PopReg(&LocalContext, ContextPointers, Reg);
InstrPtr += 2;
continue;
}
@@ -321,6 +421,10 @@ RtlpTryToUnwindEpilog(
return FALSE;
}
+ // check for popfq
+
+ // also allow end with jmp imm, jmp [target], iretq
+
/* Check if we are at the ret instruction */
if ((DWORD64)InstrPtr != EndAddress)
{
@@ -344,6 +448,49 @@ RtlpTryToUnwindEpilog(
return TRUE;
}
+/*!
+
+ \ref
https://docs.microsoft.com/en-us/cpp/build/unwind-data-definitions-in-c
+*/
+static
+ULONG64
+GetEstablisherFrame(
+ _In_ PCONTEXT Context,
+ _In_ PUNWIND_INFO UnwindInfo,
+ _In_ ULONG_PTR CodeOffset)
+{
+ ULONG i;
+
+ /* Check if we have a frame register */
+ if (UnwindInfo->FrameRegister == 0)
+ {
+ /* No frame register means we use Rsp */
+ return Context->Rsp;
+ }
+
+ if ((CodeOffset >= UnwindInfo->SizeOfProlog) ||
+ ((UnwindInfo->Flags & UNW_FLAG_CHAININFO) != 0))
+ {
+ return GetReg(Context, UnwindInfo->FrameRegister) -
+ UnwindInfo->FrameOffset * 16;
+ }
+
+ /* Loop all unwind ops */
+ for (i = 0;
+ i < UnwindInfo->CountOfCodes;
+ i += UnwindOpSlots(UnwindInfo->UnwindCode[i]))
+ {
+ /* Check for SET_FPREG */
+ if (UnwindInfo->UnwindCode[i].UnwindOp == UWOP_SET_FPREG)
+ {
+ return GetReg(Context, UnwindInfo->FrameRegister) -
+ UnwindInfo->FrameOffset * 16;
+ }
+ }
+
+ return Context->Rsp;
+}
+
PEXCEPTION_ROUTINE
NTAPI
RtlVirtualUnwind(
@@ -354,13 +501,14 @@ RtlVirtualUnwind(
_Inout_ PCONTEXT Context,
_Outptr_ PVOID *HandlerData,
_Out_ PULONG64 EstablisherFrame,
- _Inout_ PKNONVOLATILE_CONTEXT_POINTERS ContextPointers)
+ _Inout_opt_ PKNONVOLATILE_CONTEXT_POINTERS ContextPointers)
{
PUNWIND_INFO UnwindInfo;
ULONG_PTR CodeOffset;
- ULONG i;
+ ULONG i, Offset;
UNWIND_CODE UnwindCode;
BYTE Reg;
+ PULONG LanguageHandler;
/* Use relative virtual address */
ControlPc -= ImageBase;
@@ -375,13 +523,29 @@ RtlVirtualUnwind(
/* Get a pointer to the unwind info */
UnwindInfo = RVA(ImageBase, FunctionEntry->UnwindData);
+ /* Check for chained info */
+ if (UnwindInfo->Flags & UNW_FLAG_CHAININFO)
+ {
+ UNIMPLEMENTED_DBGBREAK();
+
+ /* See
https://docs.microsoft.com/en-us/cpp/build/chained-unwind-info-structures
*/
+ FunctionEntry =
(PRUNTIME_FUNCTION)&(UnwindInfo->UnwindCode[(UnwindInfo->CountOfCodes + 1) &
~1]);
+ UnwindInfo = RVA(ImageBase, FunctionEntry->UnwindData);
+ }
+
+ /* The language specific handler data follows the unwind info */
+ LanguageHandler =
ALIGN_UP_POINTER_BY(&UnwindInfo->UnwindCode[UnwindInfo->CountOfCodes],
sizeof(ULONG));
+ *HandlerData = (LanguageHandler + 1);
+
/* Calculate relative offset to function start */
CodeOffset = ControlPc - FunctionEntry->BeginAddress;
+ *EstablisherFrame = GetEstablisherFrame(Context, UnwindInfo, CodeOffset);
+
/* Check if we are in the function epilog and try to finish it */
if (CodeOffset > UnwindInfo->SizeOfProlog)
{
- if (RtlpTryToUnwindEpilog(Context, ImageBase, FunctionEntry))
+ if (RtlpTryToUnwindEpilog(Context, ContextPointers, ImageBase, FunctionEntry))
{
/* There's no exception routine */
return NULL;
@@ -390,31 +554,10 @@ RtlVirtualUnwind(
/* Skip all Ops with an offset greater than the current Offset */
i = 0;
- while (i < UnwindInfo->CountOfCodes &&
- CodeOffset < UnwindInfo->UnwindCode[i].CodeOffset)
+ while ((i < UnwindInfo->CountOfCodes) &&
+ (UnwindInfo->UnwindCode[i].CodeOffset > CodeOffset))
{
- UnwindCode = UnwindInfo->UnwindCode[i];
- switch (UnwindCode.UnwindOp)
- {
- case UWOP_SAVE_NONVOL:
- case UWOP_SAVE_XMM:
- case UWOP_SAVE_XMM128:
- i += 2;
- break;
-
- case UWOP_SAVE_NONVOL_FAR:
- case UWOP_SAVE_XMM_FAR:
- case UWOP_SAVE_XMM128_FAR:
- i += 3;
- break;
-
- case UWOP_ALLOC_LARGE:
- i += UnwindCode.OpInfo ? 3 : 2;
- break;
-
- default:
- i++;
- }
+ i += UnwindOpSlots(UnwindInfo->UnwindCode[i]);
}
/* Process the remaining unwind ops */
@@ -425,21 +568,20 @@ RtlVirtualUnwind(
{
case UWOP_PUSH_NONVOL:
Reg = UnwindCode.OpInfo;
- SetReg(Context, Reg, *(DWORD64*)Context->Rsp);
- Context->Rsp += sizeof(DWORD64);
+ PopReg(Context, ContextPointers, Reg);
i++;
break;
case UWOP_ALLOC_LARGE:
if (UnwindCode.OpInfo)
{
- ULONG Offset = *(ULONG*)(&UnwindInfo->UnwindCode[i+1]);
+ Offset = *(ULONG*)(&UnwindInfo->UnwindCode[i+1]);
Context->Rsp += Offset;
i += 3;
}
else
{
- USHORT Offset = UnwindInfo->UnwindCode[i+1].FrameOffset;
+ Offset = UnwindInfo->UnwindCode[i+1].FrameOffset;
Context->Rsp += Offset * 8;
i += 2;
}
@@ -451,44 +593,79 @@ RtlVirtualUnwind(
break;
case UWOP_SET_FPREG:
+ Reg = UnwindInfo->FrameRegister;
+ Context->Rsp = GetReg(Context, Reg) - UnwindInfo->FrameOffset *
16;
i++;
break;
case UWOP_SAVE_NONVOL:
+ Reg = UnwindCode.OpInfo;
+ Offset = *(USHORT*)(&UnwindInfo->UnwindCode[i + 1]);
+ SetRegFromStackValue(Context, ContextPointers, Reg,
(DWORD64*)Context->Rsp + Offset);
i += 2;
break;
case UWOP_SAVE_NONVOL_FAR:
+ Reg = UnwindCode.OpInfo;
+ Offset = *(ULONG*)(&UnwindInfo->UnwindCode[i + 1]);
+ SetRegFromStackValue(Context, ContextPointers, Reg,
(DWORD64*)Context->Rsp + Offset);
i += 3;
break;
- case UWOP_SAVE_XMM:
- i += 2;
+ case UWOP_EPILOG:
+ i += 1;
break;
- case UWOP_SAVE_XMM_FAR:
- i += 3;
+ case UWOP_SPARE_CODE:
+ ASSERT(FALSE);
+ i += 2;
break;
case UWOP_SAVE_XMM128:
+ Reg = UnwindCode.OpInfo;
+ Offset = *(USHORT*)(&UnwindInfo->UnwindCode[i + 1]);
+ SetXmmRegFromStackValue(Context, ContextPointers, Reg,
(M128A*)(Context->Rsp + Offset));
i += 2;
break;
case UWOP_SAVE_XMM128_FAR:
+ Reg = UnwindCode.OpInfo;
+ Offset = *(ULONG*)(&UnwindInfo->UnwindCode[i + 1]);
+ SetXmmRegFromStackValue(Context, ContextPointers, Reg,
(M128A*)(Context->Rsp + Offset));
i += 3;
break;
case UWOP_PUSH_MACHFRAME:
- i += 1;
- break;
+ /* OpInfo is 1, when an error code was pushed, otherwise 0. */
+ Context->Rsp += UnwindCode.OpInfo * sizeof(DWORD64);
+
+ /* Now pop the MACHINE_FRAME (Yes, "magic numbers", deal with
it) */
+ Context->Rip = *(PDWORD64)(Context->Rsp + 0x00);
+ Context->SegCs = *(PDWORD64)(Context->Rsp + 0x08);
+ Context->EFlags = *(PDWORD64)(Context->Rsp + 0x10);
+ Context->SegSs = *(PDWORD64)(Context->Rsp + 0x20);
+ Context->Rsp = *(PDWORD64)(Context->Rsp + 0x18);
+ ASSERT((i + 1) == UnwindInfo->CountOfCodes);
+ goto Exit;
}
}
/* Unwind is finished, pop new Rip from Stack */
- Context->Rip = *(DWORD64*)Context->Rsp;
- Context->Rsp += sizeof(DWORD64);
+ if (Context->Rsp != 0)
+ {
+ Context->Rip = *(DWORD64*)Context->Rsp;
+ Context->Rsp += sizeof(DWORD64);
+ }
- return 0;
+Exit:
+
+ /* Check if we have a handler and return it */
+ if (UnwindInfo->Flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER))
+ {
+ return RVA(ImageBase, *LanguageHandler);
+ }
+
+ return NULL;
}
VOID