Initial Implementations of RtlVirtualUnwind and RtlWalkFrameChain. They are not yet perfect, but unwinding works!

svn path=/branches/ros-amd64-bringup/; revision=37332
This commit is contained in:
Timo Kreuzer
2008-11-13 04:17:53 +00:00
parent 714e1a9b56
commit f391b2f222
+223 -2
View File
@@ -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 @@ RtlLookupFunctionEntry(
return NULL;
}
void
FORCEINLINE
SetReg(PCONTEXT Context, UCHAR Reg, ULONG64 Value)
{
((PULONG64)(&Context->Rax))[Reg] = Value;
}
PEXCEPTION_ROUTINE
NTAPI
@@ -127,12 +175,126 @@ RtlVirtualUnwind(
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 @@ RtlUnwindEx(
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;
}