[ros-diffs] [tkreuzer] 37492: Implement RtlpTryToUnwindEpilog, which tries to unwind a function, if we are in the epilog by parsing the allowed opcodes.

tkreuzer at svn.reactos.org tkreuzer at svn.reactos.org
Thu Nov 20 22:55:45 CET 2008


Author: tkreuzer
Date: Thu Nov 20 15:55:44 2008
New Revision: 37492

URL: http://svn.reactos.org/svn/reactos?rev=37492&view=rev
Log:
Implement RtlpTryToUnwindEpilog, which tries to unwind a function, if we are in the epilog by parsing the allowed opcodes.

Modified:
    branches/ros-amd64-bringup/reactos/lib/rtl/amd64/unwind.c

Modified: branches/ros-amd64-bringup/reactos/lib/rtl/amd64/unwind.c
URL: http://svn.reactos.org/svn/reactos/branches/ros-amd64-bringup/reactos/lib/rtl/amd64/unwind.c?rev=37492&r1=37491&r2=37492&view=diff
==============================================================================
--- branches/ros-amd64-bringup/reactos/lib/rtl/amd64/unwind.c [iso-8859-1] (original)
+++ branches/ros-amd64-bringup/reactos/lib/rtl/amd64/unwind.c [iso-8859-1] Thu Nov 20 15:55:44 2008
@@ -163,10 +163,146 @@
 
 void
 FORCEINLINE
-SetReg(PCONTEXT Context, UCHAR Reg, ULONG64 Value)
-{
-    ((PULONG64)(&Context->Rax))[Reg] = Value;
-}
+SetReg(PCONTEXT Context, BYTE Reg, DWORD64 Value)
+{
+    ((DWORD64*)(&Context->Rax))[Reg] = Value;
+}
+
+DWORD64
+FORCEINLINE
+GetReg(PCONTEXT Context, BYTE Reg)
+{
+    return ((DWORD64*)(&Context->Rax))[Reg];
+}
+
+void
+FORCEINLINE
+PopReg(PCONTEXT Context, BYTE Reg)
+{
+    DWORD64 Value = *(DWORD64*)Context->Rsp;
+    Context->Rsp += 8;
+    SetReg(Context, Reg, Value);
+}
+
+/* Helper function that tries to unwind epilog instructions.
+ * Returns TRUE we have been in an epilog and it could be unwound.
+ *         FALSE if the instructions were not allowed for an epilog.
+ * References:
+ *  http://msdn.microsoft.com/en-us/library/8ydc79k6(VS.80).aspx
+ *  http://msdn.microsoft.com/en-us/library/tawsa7cb.aspx
+ * TODO:
+ *  - Test and compare with Windows behaviour
+ */
+BOOLEAN
+static
+inline
+RtlpTryToUnwindEpilog(
+    PCONTEXT Context,
+    ULONG64 ImageBase,
+    PRUNTIME_FUNCTION FunctionEntry)
+{
+    CONTEXT LocalContext;
+    BYTE *InstrPtr;
+    DWORD Instr;
+    BYTE Reg, Mod;
+    ULONG64 EndAddress;
+
+    /* Make a local copy of the context */
+    LocalContext = *Context;
+
+    InstrPtr = (BYTE*)LocalContext.Rip;
+
+    /* Check if first instruction of epilog is "add rsp, x" */
+    Instr = *(DWORD*)InstrPtr;
+    if ( (Instr & 0x00fffdff) == 0x00c48148 )
+    {
+        if ( (Instr & 0x0000ff00) == 0x8300 )
+        {
+            /* This is "add rsp, 0x??" */
+            LocalContext.Rsp += Instr >> 24;
+            InstrPtr += 4;
+        }
+        else
+        {
+            /* This is "add rsp, 0x???????? */
+            LocalContext.Rsp += *(DWORD*)(InstrPtr + 3);
+            InstrPtr += 7;
+        }
+    }
+    /* Check if first instruction of epilog is "lea rsp, ..." */
+    else if ( (Instr & 0x38fffe) == 0x208d48 )
+    {
+        /* Get the register */
+        Reg = ((Instr << 8) | (Instr >> 16)) & 0x7;
+
+        LocalContext.Rsp = GetReg(&LocalContext, Reg);
+
+        /* Get adressing mode */
+        Mod = (Instr >> 22) & 0x3;
+        if (Mod == 0)
+        {
+            /* No displacement */
+            InstrPtr += 3;
+        }
+        else if (Mod == 1)
+        {
+            /* 1 byte displacement */
+            LocalContext.Rsp += Instr >> 24;
+            InstrPtr += 4;
+        }
+        else if (Mod == 2)
+        {
+            /* 4 bytes displacement */
+            LocalContext.Rsp += *(DWORD*)(InstrPtr + 3);
+            InstrPtr += 7;
+        }
+    }
+
+    /* Loop the following instructions */
+    EndAddress = FunctionEntry->EndAddress + ImageBase;
+    while((DWORD64)InstrPtr < EndAddress)
+    {
+        Instr = *(DWORD*)InstrPtr;
+
+        /* Check for a simple pop */
+        if ( (Instr & 0xf8) == 0x58 )
+        {
+            /* Opcode pops a basic register from stack */
+            Reg = Instr & 0x7;
+            PopReg(&LocalContext, Reg);
+            InstrPtr++;
+            continue;
+        }
+
+        /* Check for REX + pop */
+        if ( (Instr & 0xf8fb) == 0x5841 )
+        {
+            /* Opcode is pop r8 .. r15 */
+            Reg = (Instr & 0x7) + 8;
+            PopReg(&LocalContext, Reg);
+            InstrPtr += 2;
+            continue;
+        }
+
+        /* Check for retn / retf */
+        if ( (Instr & 0xf7) == 0xc3 )
+        {
+            /* We are finished */
+            break;
+        }
+
+        /* Opcode not allowed for Epilog */
+        return FALSE;
+    }
+
+    /* Unwind is finished, pop new Rip from Stack */
+    LocalContext.Rip = *(DWORD64*)LocalContext.Rsp;
+    LocalContext.Rsp += sizeof(DWORD64);
+
+    *Context = LocalContext;
+    return TRUE;
+}
+
 
 PEXCEPTION_ROUTINE
 NTAPI
@@ -184,7 +320,7 @@
     ULONG CodeOffset;
     ULONG i;
     UNWIND_CODE UnwindCode;
-    UCHAR Reg;
+    BYTE Reg;
 
     /* Use relative virtual address */
     ControlPc -= ImageBase;
@@ -201,6 +337,16 @@
 
     /* Calculate relative offset to function start */
     CodeOffset = ControlPc - FunctionEntry->BeginAddress;
+
+    /* Check if we are in the function epilog and try to finish it */
+    if (CodeOffset > UnwindInfo->SizeOfProlog)
+    {
+        if (RtlpTryToUnwindEpilog(Context, ImageBase, FunctionEntry))
+        {
+            /* There's no exception routine */
+            return NULL;
+        }
+    }
 
     /* Skip all Ops with an offset greater than the current Offset */
     i = 0;



More information about the Ros-diffs mailing list