Author: tkreuzer Date: Wed Nov 12 22:17:53 2008 New Revision: 37332
URL: http://svn.reactos.org/svn/reactos?rev=37332&view=rev Log: Initial Implementations of RtlVirtualUnwind and RtlWalkFrameChain. They are not yet perfect, but unwinding works!
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/rt... ============================================================================== --- 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] Wed Nov 12 22:17:53 2008 @@ -15,6 +15,48 @@ #define UNWIND_HISTORY_TABLE_GLOBAL 1 #define UNWIND_HISTORY_TABLE_LOCAL 2
+#define UWOP_PUSH_NONVOL 0 +#define UWOP_ALLOC_LARGE 1 +#define UWOP_ALLOC_SMALL 2 +#define UWOP_SET_FPREG 3 +#define UWOP_SAVE_NONVOL 4 +#define UWOP_SAVE_NONVOL_FAR 5 +#define UWOP_SAVE_XMM 6 +#define UWOP_SAVE_XMM_FAR 7 +#define UWOP_SAVE_XMM128 8 +#define UWOP_SAVE_XMM128_FAR 9 +#define UWOP_PUSH_MACHFRAME 10 + +typedef unsigned char UBYTE; + +typedef union _UNWIND_CODE +{ + struct + { + UBYTE CodeOffset; + UBYTE UnwindOp:4; + UBYTE OpInfo:4; + }; + USHORT FrameOffset; +} UNWIND_CODE, *PUNWIND_CODE; + +typedef struct _UNWIND_INFO +{ + UBYTE Version:3; + UBYTE Flags:5; + UBYTE SizeOfProlog; + UBYTE CountOfCodes; + UBYTE FrameRegister:4; + UBYTE FrameOffset:4; + UNWIND_CODE UnwindCode[1]; +/* union { + OPTIONAL ULONG ExceptionHandler; + OPTIONAL ULONG FunctionEntry; + }; + OPTIONAL ULONG ExceptionData[]; +*/ +} UNWIND_INFO, *PUNWIND_INFO; + PVOID NTAPI RtlpLookupModuleBase( @@ -119,6 +161,12 @@ return NULL; }
+void +FORCEINLINE +SetReg(PCONTEXT Context, UCHAR Reg, ULONG64 Value) +{ + ((PULONG64)(&Context->Rax))[Reg] = Value; +}
PEXCEPTION_ROUTINE NTAPI @@ -127,12 +175,126 @@ IN ULONG64 ImageBase, IN ULONG64 ControlPc, IN PRUNTIME_FUNCTION FunctionEntry, - IN OUT PCONTEXT ContextRecord, + IN OUT PCONTEXT Context, OUT PVOID *HandlerData, OUT PULONG64 EstablisherFrame, IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers) { - UNIMPLEMENTED; + PUNWIND_INFO UnwindInfo; + ULONG CodeOffset; + ULONG i; + UNWIND_CODE UnwindCode; + UCHAR Reg; + + /* Use relative virtual address */ + ControlPc -= ImageBase; + + /* Sanity checks */ + if ( (ControlPc < FunctionEntry->BeginAddress) || + (ControlPc >= FunctionEntry->EndAddress) ) + { + return NULL; + } + + /* Get a pointer to the unwind info */ + UnwindInfo = RVA(ImageBase, FunctionEntry->UnwindData); + + /* Calculate relative offset to function start */ + CodeOffset = ControlPc - FunctionEntry->BeginAddress; + + /* Skip all Ops with an offset greater than the current Offset */ + i = 0; + while (i < UnwindInfo->CountOfCodes && + CodeOffset < UnwindInfo->UnwindCode[i].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++; + } + } + + /* Process the left Ops */ + while (i < UnwindInfo->CountOfCodes) + { + UnwindCode = UnwindInfo->UnwindCode[i]; + switch (UnwindCode.UnwindOp) + { + case UWOP_PUSH_NONVOL: + Reg = UnwindCode.OpInfo; + SetReg(Context, Reg, *(DWORD64*)Context->Rsp); + Context->Rsp += sizeof(DWORD64); + i++; + break; + + case UWOP_ALLOC_LARGE: + if (UnwindCode.OpInfo) + { + ULONG Offset = *(ULONG*)(&UnwindInfo->UnwindCode[i+1]); + Context->Rsp += Offset; + i += 3; + } + else + { + USHORT Offset = UnwindInfo->UnwindCode[i+1].FrameOffset; + Context->Rsp += Offset * 8; + i += 2; + } + break; + + case UWOP_ALLOC_SMALL: + Context->Rsp += (UnwindCode.OpInfo + 1) * 8; + i++; + break; + + case UWOP_SET_FPREG: + i++; + break; + + case UWOP_SAVE_NONVOL: + i += 2; + break; + + case UWOP_SAVE_NONVOL_FAR: + i += 3; + break; + + case UWOP_SAVE_XMM: + i += 2; + break; + + case UWOP_SAVE_XMM_FAR: + i += 3; + case UWOP_SAVE_XMM128: + i += 2; + case UWOP_SAVE_XMM128_FAR: + i += 3; + case UWOP_PUSH_MACHFRAME: + i += 1; + } + } + + /* Unwind is finished, pop new Rip from Stack */ + Context->Rip = *(DWORD64*)Context->Rsp; + Context->Rsp += 8; + return 0; }
@@ -150,4 +312,63 @@ return; }
- +ULONG +NTAPI +RtlWalkFrameChain(OUT PVOID *Callers, + IN ULONG Count, + IN ULONG Flags) +{ + CONTEXT Context; + ULONG64 ControlPc, ImageBase, EstablisherFrame; + ULONG64 StackBegin, StackEnd; + PVOID HandlerData; + INT i; + PRUNTIME_FUNCTION FunctionEntry; +DPRINT1("RtlWalkFrameChain called\n"); + RtlCaptureContext(&Context); + + ControlPc = Context.Rip; + + RtlpGetStackLimits(&StackBegin, &StackEnd); + + /* Check if we want the user-mode stack frame */ + if (Flags == 1) + { + } + + /* Loop the frames */ + for (i = 0; i < Count; i++) + { + /* Lookup the FunctionEntry for the current ControlPc */ + FunctionEntry = RtlLookupFunctionEntry(ControlPc, &ImageBase, NULL); + + /* Is this a leaf function? */ + if (!FunctionEntry) + { + Context.Rip = *(ULONG64*)Context.Rsp; + Context.Rsp += sizeof(ULONG64); + DPRINT1("leaf funtion, new Rip = %p, new Rsp = %p\n", (PVOID)Context.Rip, (PVOID)Context.Rsp); + } + else + { + RtlVirtualUnwind(0, + ImageBase, + ControlPc, + FunctionEntry, + &Context, + &HandlerData, + &EstablisherFrame, + NULL); + DPRINT1("normal funtion, new Rip = %p, new Rsp = %p\n", (PVOID)Context.Rip, (PVOID)Context.Rsp); + } + + ControlPc = Context.Rip; + /* Save this frame */ + + Callers[i] = (PVOID)ControlPc; + + } + + return i; +} +