summaryrefslogtreecommitdiff
path: root/src/debug/daccess/amd64/dbs_stack_x64.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/debug/daccess/amd64/dbs_stack_x64.cpp')
-rw-r--r--src/debug/daccess/amd64/dbs_stack_x64.cpp1500
1 files changed, 1500 insertions, 0 deletions
diff --git a/src/debug/daccess/amd64/dbs_stack_x64.cpp b/src/debug/daccess/amd64/dbs_stack_x64.cpp
new file mode 100644
index 0000000000..83f2a00abf
--- /dev/null
+++ b/src/debug/daccess/amd64/dbs_stack_x64.cpp
@@ -0,0 +1,1500 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//----------------------------------------------------------------------------
+//
+
+//
+// Stack unwinding implementation for x64.
+//
+
+//
+//----------------------------------------------------------------------------
+
+#include "pch.cpp"
+#pragma hdrstop
+
+//----------------------------------------------------------------------------
+//
+// Copied OS code.
+//
+// This must be kept in sync with the system unwinder.
+// base\ntos\rtl\amd64\exdsptch.c
+//
+//----------------------------------------------------------------------------
+
+//
+// Lookup table providing the number of slots used by each unwind code.
+//
+
+UCHAR
+DbsX64StackUnwinder::s_UnwindOpSlotTable[] =
+{
+ 1, // UWOP_PUSH_NONVOL
+ 2, // UWOP_ALLOC_LARGE (or 3, special cased in lookup code)
+ 1, // UWOP_ALLOC_SMALL
+ 1, // UWOP_SET_FPREG
+ 2, // UWOP_SAVE_NONVOL
+ 3, // UWOP_SAVE_NONVOL_FAR
+ 2, // UWOP_SAVE_XMM
+ 3, // UWOP_SAVE_XMM_FAR
+ 2, // UWOP_SAVE_XMM128
+ 3, // UWOP_SAVE_XMM128_FAR
+ 1 // UWOP_PUSH_MACHFRAME
+};
+
+//
+// ****** temp - defin elsewhere ******
+//
+
+#define SIZE64_PREFIX 0x48
+#define ADD_IMM8_OP 0x83
+#define ADD_IMM32_OP 0x81
+#define JMP_IMM8_OP 0xeb
+#define JMP_IMM32_OP 0xe9
+#define JMP_IND_OP 0xff
+#define LEA_OP 0x8d
+#define REP_PREFIX 0xf3
+#define POP_OP 0x58
+#define RET_OP 0xc3
+#define RET_OP_2 0xc2
+
+#define IS_REX_PREFIX(x) (((x) & 0xf0) == 0x40)
+
+HRESULT
+DbsX64StackUnwinder::UnwindPrologue(
+ __in ULONG64 ImageBase,
+ __in ULONG64 ControlPc,
+ __in ULONG64 FrameBase,
+ __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry,
+ __inout PAMD64_CONTEXT ContextRecord
+ )
+
+/*++
+
+Routine Description:
+
+ This function processes unwind codes and reverses the state change
+ effects of a prologue. If the specified unwind information contains
+ chained unwind information, then that prologue is unwound recursively.
+ As the prologue is unwound state changes are recorded in the specified
+ context structure and optionally in the specified context pointers
+ structures.
+
+Arguments:
+
+ ImageBase - Supplies the base address of the image that contains the
+ function being unwound.
+
+ ControlPc - Supplies the address where control left the specified
+ function.
+
+ FrameBase - Supplies the base of the stack frame subject function stack
+ frame.
+
+ FunctionEntry - Supplies the address of the function table entry for the
+ specified function.
+
+ ContextRecord - Supplies the address of a context record.
+
+--*/
+
+{
+
+ HRESULT Status = E_UNEXPECTED;
+ ULONG64 FloatingAddress;
+ PAMD64_M128 FloatingRegister;
+ ULONG FrameOffset;
+ ULONG Index;
+ ULONG64 IntegerAddress;
+ PULONG64 IntegerRegister;
+ BOOLEAN MachineFrame;
+ ULONG OpInfo;
+ ULONG PrologOffset;
+ ULONG64 ReturnAddress;
+ ULONG64 StackAddress;
+ ULONG64 UnwindInfoBuffer[32];
+ PAMD64_UNWIND_INFO UnwindInfo;
+ ULONG UnwindOp;
+
+ //
+ // Process the unwind codes.
+ //
+
+ FloatingRegister = &ContextRecord->Xmm0;
+ IntegerRegister = &ContextRecord->Rax;
+ Index = 0;
+ MachineFrame = FALSE;
+ PrologOffset = (ULONG)(ControlPc - (FunctionEntry->BeginAddress + ImageBase));
+
+ m_Services->Status(1, "Prol: RIP %I64X, 0x%X bytes in function at %I64X\n",
+ ControlPc, PrologOffset,
+ FunctionEntry->BeginAddress + ImageBase);
+ m_Services->Status(1, "Prol: Read unwind info at %I64X\n",
+ FunctionEntry->UnwindInfoAddress + ImageBase);
+
+ if ((Status =
+ GetUnwindInfo(ImageBase, FunctionEntry->UnwindInfoAddress,
+ false,
+ UnwindInfoBuffer, sizeof(UnwindInfoBuffer),
+ (PVOID*)&UnwindInfo)) != S_OK) {
+ m_Services->Status(1, "Prol: Unable to read unwind info\n");
+ return Status;
+ }
+
+ m_Services->Status(1, " Unwind info has 0x%X codes\n",
+ UnwindInfo->CountOfCodes);
+
+ while (Index < UnwindInfo->CountOfCodes) {
+
+ m_Services->Status(1, " %02X: Code %X offs %03X, RSP %I64X\n",
+ Index, UnwindInfo->UnwindCode[Index].UnwindOp,
+ UnwindInfo->UnwindCode[Index].CodeOffset,
+ ContextRecord->Rsp);
+
+ //
+ // If the prologue offset is greater than the next unwind code offset,
+ // then simulate the effect of the unwind code.
+ //
+
+ UnwindOp = UnwindInfo->UnwindCode[Index].UnwindOp;
+ if (UnwindOp > AMD64_UWOP_PUSH_MACHFRAME) {
+ m_Services->Status(1, "Prol: Invalid unwind op %X at index %X\n",
+ UnwindOp, Index);
+ goto Fail;
+ }
+
+ OpInfo = UnwindInfo->UnwindCode[Index].OpInfo;
+ if (PrologOffset >= UnwindInfo->UnwindCode[Index].CodeOffset) {
+ switch (UnwindOp) {
+
+ //
+ // Push nonvolatile integer register.
+ //
+ // The operation information is the register number of the
+ // register than was pushed.
+ //
+
+ case AMD64_UWOP_PUSH_NONVOL:
+ IntegerAddress = ContextRecord->Rsp;
+ if ((Status = m_Services->
+ ReadAllMemory(IntegerAddress,
+ &IntegerRegister[OpInfo],
+ sizeof(ULONG64))) != S_OK) {
+ m_Services->Status(1, "Prol: Op %X memory "
+ "read failed at %I64X\n",
+ UnwindOp, IntegerAddress);
+ goto Fail;
+ }
+
+ ContextRecord->Rsp += 8;
+ break;
+
+ //
+ // Allocate a large sized area on the stack.
+ //
+ // The operation information determines if the size is
+ // 16- or 32-bits.
+ //
+
+ case AMD64_UWOP_ALLOC_LARGE:
+ Index += 1;
+ FrameOffset = UnwindInfo->UnwindCode[Index].FrameOffset;
+ if (OpInfo != 0) {
+ Index += 1;
+ FrameOffset += (UnwindInfo->UnwindCode[Index].FrameOffset << 16);
+ } else {
+ // The 16-bit form is scaled.
+ FrameOffset *= 8;
+ }
+
+ ContextRecord->Rsp += FrameOffset;
+ break;
+
+ //
+ // Allocate a small sized area on the stack.
+ //
+ // The operation information is the size of the unscaled
+ // allocation size (8 is the scale factor) minus 8.
+ //
+
+ case AMD64_UWOP_ALLOC_SMALL:
+ ContextRecord->Rsp += (OpInfo * 8) + 8;
+ break;
+
+ //
+ // Establish the the frame pointer register.
+ //
+ // The operation information is not used.
+ //
+
+ case AMD64_UWOP_SET_FPREG:
+ ContextRecord->Rsp = IntegerRegister[UnwindInfo->FrameRegister];
+ ContextRecord->Rsp -= UnwindInfo->FrameOffset * 16;
+ break;
+
+ //
+ // Save nonvolatile integer register on the stack using a
+ // 16-bit displacment.
+ //
+ // The operation information is the register number.
+ //
+
+ case AMD64_UWOP_SAVE_NONVOL:
+ Index += 1;
+ FrameOffset = UnwindInfo->UnwindCode[Index].FrameOffset * 8;
+ IntegerAddress = FrameBase + FrameOffset;
+ if ((Status = m_Services->
+ ReadAllMemory(IntegerAddress,
+ &IntegerRegister[OpInfo],
+ sizeof(ULONG64))) != S_OK) {
+ m_Services->Status(1, "Prol: Op %X memory read "
+ "failed at %I64X\n",
+ UnwindOp, IntegerAddress);
+ goto Fail;
+ }
+ break;
+
+ //
+ // Save nonvolatile integer register on the stack using a
+ // 32-bit displacment.
+ //
+ // The operation information is the register number.
+ //
+
+ case AMD64_UWOP_SAVE_NONVOL_FAR:
+ Index += 2;
+ FrameOffset = UnwindInfo->UnwindCode[Index - 1].FrameOffset;
+ FrameOffset += UnwindInfo->UnwindCode[Index].FrameOffset << 16;
+ IntegerAddress = FrameBase + FrameOffset;
+ if ((Status = m_Services->
+ ReadAllMemory(IntegerAddress,
+ &IntegerRegister[OpInfo],
+ sizeof(ULONG64))) != S_OK) {
+ m_Services->Status(1, "Prol: Op %X memory read "
+ "failed at %I64X\n",
+ UnwindOp, IntegerAddress);
+ goto Fail;
+ }
+ break;
+
+ //
+ // Save a nonvolatile XMM(64) register on the stack using a
+ // 16-bit displacement.
+ //
+ // The operation information is the register number.
+ //
+
+ case AMD64_UWOP_SAVE_XMM:
+ Index += 1;
+ FrameOffset = UnwindInfo->UnwindCode[Index].FrameOffset * 8;
+ FloatingAddress = FrameBase + FrameOffset;
+ FloatingRegister[OpInfo].High = 0;
+ if ((Status = m_Services->
+ ReadAllMemory(FloatingAddress,
+ &FloatingRegister[OpInfo].Low,
+ sizeof(ULONG64))) != S_OK) {
+ m_Services->Status(1, "Prol: Op %X memory read "
+ "failed at %I64X\n",
+ UnwindOp, FloatingAddress);
+ goto Fail;
+ }
+ break;
+
+ //
+ // Save a nonvolatile XMM(64) register on the stack using a
+ // 32-bit displacement.
+ //
+ // The operation information is the register number.
+ //
+
+ case AMD64_UWOP_SAVE_XMM_FAR:
+ Index += 2;
+ FrameOffset = UnwindInfo->UnwindCode[Index - 1].FrameOffset;
+ FrameOffset += UnwindInfo->UnwindCode[Index].FrameOffset << 16;
+ FloatingAddress = FrameBase + FrameOffset;
+ FloatingRegister[OpInfo].High = 0;
+ if ((Status = m_Services->
+ ReadAllMemory(FloatingAddress,
+ &FloatingRegister[OpInfo].Low,
+ sizeof(ULONG64))) != S_OK) {
+ m_Services->Status(1, "Prol: Op %X memory read "
+ "failed at %I64X\n",
+ UnwindOp, FloatingAddress);
+ goto Fail;
+ }
+ break;
+
+ //
+ // Save a nonvolatile XMM(128) register on the stack using a
+ // 16-bit displacement.
+ //
+ // The operation information is the register number.
+ //
+
+ case AMD64_UWOP_SAVE_XMM128:
+ Index += 1;
+ FrameOffset = UnwindInfo->UnwindCode[Index].FrameOffset * 16;
+ FloatingAddress = FrameBase + FrameOffset;
+ if ((Status = m_Services->
+ ReadAllMemory(FloatingAddress,
+ &FloatingRegister[OpInfo],
+ sizeof(AMD64_M128))) != S_OK) {
+ m_Services->Status(1, "Prol: Op %X memory read "
+ "failed at %I64X\n",
+ UnwindOp, FloatingAddress);
+ goto Fail;
+ }
+ break;
+
+ //
+ // Save a nonvolatile XMM(128) register on the stack using a
+ // 32-bit displacement.
+ //
+ // The operation information is the register number.
+ //
+
+ case AMD64_UWOP_SAVE_XMM128_FAR:
+ Index += 2;
+ FrameOffset = UnwindInfo->UnwindCode[Index - 1].FrameOffset;
+ FrameOffset += UnwindInfo->UnwindCode[Index].FrameOffset << 16;
+ FloatingAddress = FrameBase + FrameOffset;
+ if ((Status = m_Services->
+ ReadAllMemory(FloatingAddress,
+ &FloatingRegister[OpInfo],
+ sizeof(AMD64_M128))) != S_OK) {
+ m_Services->Status(1, "Prol: Op %X memory read "
+ "failed at %I64X\n",
+ UnwindOp, FloatingAddress);
+ goto Fail;
+ }
+ break;
+
+ //
+ // Push a machine frame on the stack.
+ //
+ // The operation information determines whether the machine
+ // frame contains an error code or not.
+ //
+
+ case AMD64_UWOP_PUSH_MACHFRAME:
+ MachineFrame = TRUE;
+ ReturnAddress = ContextRecord->Rsp;
+ StackAddress = ContextRecord->Rsp + (3 * 8);
+ if (OpInfo != 0) {
+ ReturnAddress += 8;
+ StackAddress += 8;
+ }
+
+ m_RestartFrame = true;
+ m_TrapAddr = ReturnAddress -
+ FIELD_OFFSET(AMD64_KTRAP_FRAME, Rip);
+
+ if ((Status = m_Services->
+ ReadAllMemory(ReturnAddress,
+ &ContextRecord->Rip,
+ sizeof(ULONG64))) != S_OK) {
+ m_Services->Status(1, "Prol: Op %X memory "
+ "read 1 failed at %I64X\n",
+ UnwindOp, ReturnAddress);
+ goto Fail;
+ }
+ if ((Status = m_Services->
+ ReadAllMemory(StackAddress,
+ &ContextRecord->Rsp,
+ sizeof(ULONG64))) != S_OK) {
+ m_Services->Status(1, "Prol: Op %X memory "
+ "read 2 failed at %I64X\n",
+ UnwindOp, StackAddress);
+ goto Fail;
+ }
+ break;
+
+ //
+ // Unused codes.
+ //
+
+ default:
+ break;
+ }
+
+ Index += 1;
+
+ } else {
+
+ //
+ // Skip this unwind operation by advancing the slot index by the
+ // number of slots consumed by this operation.
+ //
+
+ Index += s_UnwindOpSlotTable[UnwindOp];
+
+ //
+ // Special case any unwind operations that can consume a variable
+ // number of slots.
+ //
+
+ switch (UnwindOp) {
+
+ //
+ // A non-zero operation information indicates that an
+ // additional slot is consumed.
+ //
+
+ case AMD64_UWOP_ALLOC_LARGE:
+ if (OpInfo != 0) {
+ Index += 1;
+ }
+
+ break;
+
+ //
+ // No other special cases.
+ //
+
+ default:
+ break;
+ }
+ }
+ }
+
+ //
+ // If chained unwind information is specified, then recursively unwind
+ // the chained information. Otherwise, determine the return address if
+ // a machine frame was not encountered during the scan of the unwind
+ // codes.
+ //
+
+ if ((UnwindInfo->Flags & AMD64_UNW_FLAG_CHAININFO) != 0) {
+
+ _PIMAGE_RUNTIME_FUNCTION_ENTRY ChainEntry;
+
+ Index = UnwindInfo->CountOfCodes;
+ if ((Index & 1) != 0) {
+ Index += 1;
+ }
+
+ // GetUnwindInfo looks for CHAININFO and reads
+ // the trailing RUNTIME_FUNCTION so we can just
+ // directly use the data sitting in UnwindInfo.
+ ChainEntry = (_PIMAGE_RUNTIME_FUNCTION_ENTRY)
+ &UnwindInfo->UnwindCode[Index];
+
+ m_Services->Status(1, " Chain with entry at %I64X\n",
+ FunctionEntry->UnwindInfoAddress + ImageBase +
+ (ULONG64)((PUCHAR)&UnwindInfo->UnwindCode[Index] -
+ (PUCHAR)UnwindInfo));
+
+ Status = UnwindPrologue(ImageBase,
+ ControlPc,
+ FrameBase,
+ ChainEntry,
+ ContextRecord);
+
+ FreeUnwindInfo(UnwindInfo, UnwindInfoBuffer);
+ return Status;
+
+ } else {
+ FreeUnwindInfo(UnwindInfo, UnwindInfoBuffer);
+
+ if (MachineFrame == FALSE) {
+ if ((Status = m_Services->
+ ReadAllMemory(ContextRecord->Rsp,
+ &ContextRecord->Rip,
+ sizeof(ULONG64))) != S_OK) {
+ return Status;
+ }
+ ContextRecord->Rsp += 8;
+ }
+
+ m_Services->Status(1, "Prol: Returning with RIP %I64X, RSP %I64X\n",
+ ContextRecord->Rip, ContextRecord->Rsp);
+ return S_OK;
+ }
+
+ Fail:
+ FreeUnwindInfo(UnwindInfo, UnwindInfoBuffer);
+ m_Services->Status(1, "Prol: Unwind failed, 0x%08X\n", Status);
+ return Status;
+}
+
+HRESULT
+DbsX64StackUnwinder::VirtualUnwind(
+ __in ULONG64 ImageBase,
+ __in ULONG64 ControlPc,
+ __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry,
+ __inout PAMD64_CONTEXT ContextRecord,
+ __out PULONG64 EstablisherFrame
+ )
+
+/*++
+
+Routine Description:
+
+ This function virtually unwinds the specified function by executing its
+ prologue code backward or its epilogue code forward.
+
+ If a context pointers record is specified, then the address where each
+ nonvolatile registers is restored from is recorded in the appropriate
+ element of the context pointers record.
+
+Arguments:
+
+ ImageBase - Supplies the base address of the image that contains the
+ function being unwound.
+
+ ControlPc - Supplies the address where control left the specified
+ function.
+
+ FunctionEntry - Supplies the address of the function table entry for the
+ specified function.
+
+ ContextRecord - Supplies the address of a context record.
+
+ EstablisherFrame - Supplies a pointer to a variable that receives the
+ the establisher frame pointer value.
+
+--*/
+
+{
+
+ HRESULT Status;
+ ULONG64 BranchTarget;
+ LONG Displacement;
+ ULONG FrameRegister;
+ ULONG Index;
+ LOGICAL InEpilogue;
+ PULONG64 IntegerRegister;
+ PUCHAR NextByte;
+ _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryFunctionEntry;
+ ULONG PrologOffset;
+ ULONG RegisterNumber;
+ PAMD64_UNWIND_INFO UnwindInfo;
+ ULONG64 UnwindInfoBuffer[8];
+ ULONG Done;
+ UCHAR InstrBuffer[32];
+ ULONG InstrBytes;
+ ULONG Bytes;
+ ULONG UnwindFrameReg;
+
+ //
+ // If the specified function does not use a frame pointer, then the
+ // establisher frame is the contents of the stack pointer. This may
+ // not actually be the real establisher frame if control left the
+ // function from within the prologue. In this case the establisher
+ // frame may be not required since control has not actually entered
+ // the function and prologue entries cannot refer to the establisher
+ // frame before it has been established, i.e., if it has not been
+ // established, then no save unwind codes should be encountered during
+ // the unwind operation.
+ //
+ // If the specified function uses a frame pointer and control left the
+ // function outside of the prologue or the unwind information contains
+ // a chained information structure, then the establisher frame is the
+ // contents of the frame pointer.
+ //
+ // If the specified function uses a frame pointer and control left the
+ // function from within the prologue, then the set frame pointer unwind
+ // code must be looked up in the unwind codes to detetermine if the
+ // contents of the stack pointer or the contents of the frame pointer
+ // should be used for the establisher frame. This may not actually be
+ // the real establisher frame. In this case the establisher frame may
+ // not be required since control has not actually entered the function
+ // and prologue entries cannot refer to the establisher frame before it
+ // has been established, i.e., if it has not been established, then no
+ // save unwind codes should be encountered during the unwind operation.
+ //
+ // N.B. The correctness of these assumptions is based on the ordering of
+ // unwind codes.
+ //
+
+ if ((Status = GetUnwindInfo(ImageBase, FunctionEntry->UnwindInfoAddress,
+ true,
+ UnwindInfoBuffer, sizeof(UnwindInfoBuffer),
+ (PVOID*)&UnwindInfo)) != S_OK) {
+ return Status;
+ }
+
+ PrologOffset = (ULONG)(ControlPc - (FunctionEntry->BeginAddress + ImageBase));
+ UnwindFrameReg = UnwindInfo->FrameRegister;
+ if (UnwindFrameReg == 0) {
+ *EstablisherFrame = ContextRecord->Rsp;
+
+ } else if ((PrologOffset >= UnwindInfo->SizeOfProlog) ||
+ ((UnwindInfo->Flags & AMD64_UNW_FLAG_CHAININFO) != 0)) {
+ *EstablisherFrame = (&ContextRecord->Rax)[UnwindFrameReg];
+ *EstablisherFrame -= UnwindInfo->FrameOffset * 16;
+
+ } else {
+
+ // Read all the data.
+ if ((Status = GetUnwindInfo(ImageBase,
+ FunctionEntry->UnwindInfoAddress,
+ false,
+ UnwindInfoBuffer,
+ sizeof(UnwindInfoBuffer),
+ (PVOID*)&UnwindInfo)) != S_OK) {
+ return Status;
+ }
+
+ Index = 0;
+ while (Index < UnwindInfo->CountOfCodes) {
+ if (UnwindInfo->UnwindCode[Index].UnwindOp == AMD64_UWOP_SET_FPREG) {
+ break;
+ }
+
+ Index += 1;
+ }
+
+ if (PrologOffset >= UnwindInfo->UnwindCode[Index].CodeOffset) {
+ *EstablisherFrame = (&ContextRecord->Rax)[UnwindFrameReg];
+ *EstablisherFrame -= UnwindInfo->FrameOffset * 16;
+
+ } else {
+ *EstablisherFrame = ContextRecord->Rsp;
+ }
+
+ FreeUnwindInfo(UnwindInfo, UnwindInfoBuffer);
+ }
+
+ if ((Status = m_Services->
+ ReadMemory(ControlPc, InstrBuffer, sizeof(InstrBuffer),
+ &InstrBytes)) != S_OK) {
+ m_Services->Status(1, "Unable to read instruction stream at %I64X\n",
+ ControlPc);
+
+ // We need the code to look for epilogue ops.
+ // It's very rare to be stopped in an epilogue when
+ // getting a stack trace, so if we can't read the
+ // code just assume we aren't in an epilogue.
+ InstrBytes = 0;
+ }
+
+ //
+ // If the point at which control left the specified function is in an
+ // epilogue, then emulate the execution of the epilogue forward and
+ // return no exception handler.
+ //
+
+ IntegerRegister = &ContextRecord->Rax;
+ NextByte = InstrBuffer;
+ Bytes = InstrBytes;
+
+ //
+ // Check for one of:
+ //
+ // add rsp, imm8
+ // or
+ // add rsp, imm32
+ // or
+ // lea rsp, -disp8[fp]
+ // or
+ // lea rsp, -disp32[fp]
+ //
+
+ if (Bytes >= 4 &&
+ (NextByte[0] == SIZE64_PREFIX) &&
+ (NextByte[1] == ADD_IMM8_OP) &&
+ (NextByte[2] == 0xc4)) {
+
+ //
+ // add rsp, imm8.
+ //
+
+ NextByte += 4;
+ Bytes -= 4;
+
+ } else if (Bytes >= 7 &&
+ (NextByte[0] == SIZE64_PREFIX) &&
+ (NextByte[1] == ADD_IMM32_OP) &&
+ (NextByte[2] == 0xc4)) {
+
+ //
+ // add rsp, imm32.
+ //
+
+ NextByte += 7;
+ Bytes -= 7;
+
+ } else if (Bytes >= 4 &&
+ ((NextByte[0] & 0xf8) == SIZE64_PREFIX) &&
+ (NextByte[1] == LEA_OP)) {
+
+ FrameRegister = ((NextByte[0] & 0x7) << 3) | (NextByte[2] & 0x7);
+ if ((FrameRegister != 0) &&
+ (FrameRegister == UnwindFrameReg)) {
+ if ((NextByte[2] & 0xf8) == 0x60) {
+
+ //
+ // lea rsp, disp8[fp].
+ //
+
+ NextByte += 4;
+ Bytes -= 4;
+
+ } else if (Bytes >= 7 &&
+ (NextByte[2] &0xf8) == 0xa0) {
+
+ //
+ // lea rsp, disp32[fp].
+ //
+
+ NextByte += 7;
+ Bytes -= 7;
+ }
+ }
+ }
+
+ //
+ // Check for any number of:
+ //
+ // pop nonvolatile-integer-register[0..15].
+ //
+
+ while (TRUE) {
+ if (Bytes >= 1 &&
+ (NextByte[0] & 0xf8) == POP_OP) {
+ NextByte += 1;
+ Bytes -= 1;
+
+ } else if (Bytes >= 2 &&
+ IS_REX_PREFIX(NextByte[0]) &&
+ ((NextByte[1] & 0xf8) == POP_OP)) {
+
+ NextByte += 2;
+ Bytes -= 2;
+
+ } else {
+ break;
+ }
+ }
+
+ //
+ // If the next instruction is a return or an appropriate jump, then
+ // control is currently in an epilogue and execution of the epilogue
+ // should be emulated. Otherwise, execution is not in an epilogue and
+ // the prologue should be unwound.
+ //
+
+ InEpilogue = FALSE;
+ if ((Bytes >= 1 &&
+ ((NextByte[0] == RET_OP) ||
+ (NextByte[0] == RET_OP_2))) ||
+ (Bytes >= 2 &&
+ ((NextByte[0] == REP_PREFIX) && (NextByte[1] == RET_OP)))) {
+
+ //
+ // A return is an unambiguous indication of an epilogue.
+ //
+
+ InEpilogue = TRUE;
+
+ } else if ((Bytes >= 2 && NextByte[0] == JMP_IMM8_OP) ||
+ (Bytes >= 5 && NextByte[0] == JMP_IMM32_OP)) {
+
+ //
+ // An unconditional branch to a target that is equal to the start of
+ // or outside of this routine is logically a call to another function.
+ //
+
+ BranchTarget = (ULONG64)(NextByte - InstrBuffer) + ControlPc - ImageBase;
+ if (NextByte[0] == JMP_IMM8_OP) {
+ BranchTarget += 2 + (CHAR)NextByte[1];
+ } else {
+ BranchTarget += 5 + *((LONG UNALIGNED *)&NextByte[1]);
+ }
+
+ //
+ // Determine whether the branch target refers to code within this
+ // function. If not, then it is an epilogue indicator.
+ //
+ // A branch to the start of self implies a recursive call, so
+ // is treated as an epilogue.
+ //
+
+ if (BranchTarget < FunctionEntry->BeginAddress ||
+ BranchTarget >= FunctionEntry->EndAddress) {
+
+ _IMAGE_RUNTIME_FUNCTION_ENTRY PrimaryEntryBuffer;
+
+ //
+ // The branch target is outside of the region described by
+ // this function entry. See whether it is contained within
+ // an indirect function entry associated with this same
+ // function.
+ //
+ // If not, then the branch target really is outside of
+ // this function.
+ //
+
+ PrimaryFunctionEntry =
+ SameFunction(FunctionEntry,
+ ImageBase,
+ BranchTarget + ImageBase,
+ &PrimaryEntryBuffer);
+
+ if ((PrimaryFunctionEntry == NULL) ||
+ (BranchTarget == PrimaryFunctionEntry->BeginAddress)) {
+
+ InEpilogue = TRUE;
+ }
+
+ } else if ((BranchTarget == FunctionEntry->BeginAddress) &&
+ ((UnwindInfo->Flags & AMD64_UNW_FLAG_CHAININFO) == 0)) {
+
+ InEpilogue = TRUE;
+ }
+
+ } else if (Bytes >= 2 &&
+ (NextByte[0] == JMP_IND_OP) && (NextByte[1] == 0x25)) {
+
+ //
+ // An unconditional jump indirect.
+ //
+ // This is a jmp outside of the function, probably a tail call
+ // to an import function.
+ //
+
+ InEpilogue = TRUE;
+
+ } else if (Bytes >= 3 &&
+ ((NextByte[0] & 0xf8) == SIZE64_PREFIX) &&
+ (NextByte[1] == 0xff) &&
+ (NextByte[2] & 0x38) == 0x20) {
+
+ //
+ // This is an indirect jump opcode: 0x48 0xff /4. The 64-bit
+ // flag (REX.W) is always redundant here, so its presence is
+ // overloaded to indicate a branch out of the function - a tail
+ // call.
+ //
+ // Such an opcode is an unambiguous epilogue indication.
+ //
+
+ InEpilogue = TRUE;
+ }
+
+ if (InEpilogue != FALSE) {
+ NextByte = InstrBuffer;
+ Bytes = InstrBytes;
+
+ //
+ // Emulate one of (if any):
+ //
+ // add rsp, imm8
+ // or
+ // add rsp, imm32
+ // or
+ // lea rsp, disp8[frame-register]
+ // or
+ // lea rsp, disp32[frame-register]
+ //
+
+ if (Bytes >= 1 &&
+ (NextByte[0] & 0xf8) == SIZE64_PREFIX) {
+
+ if (Bytes >= 4 &&
+ NextByte[1] == ADD_IMM8_OP) {
+
+ //
+ // add rsp, imm8.
+ //
+
+ ContextRecord->Rsp += (CHAR)NextByte[3];
+ NextByte += 4;
+ Bytes -= 4;
+
+ } else if (Bytes >= 7 &&
+ NextByte[1] == ADD_IMM32_OP) {
+
+ //
+ // add rsp, imm32.
+ //
+
+ Displacement = NextByte[3] | (NextByte[4] << 8);
+ Displacement |= (NextByte[5] << 16) | (NextByte[6] << 24);
+ ContextRecord->Rsp += Displacement;
+ NextByte += 7;
+ Bytes -= 7;
+
+ } else if (Bytes >= 4 &&
+ NextByte[1] == LEA_OP) {
+ if ((NextByte[2] & 0xf8) == 0x60) {
+
+ //
+ // lea rsp, disp8[frame-register].
+ //
+
+ ContextRecord->Rsp = IntegerRegister[FrameRegister];
+ ContextRecord->Rsp += (CHAR)NextByte[3];
+ NextByte += 4;
+ Bytes -= 4;
+
+ } else if (Bytes >= 7 &&
+ (NextByte[2] & 0xf8) == 0xa0) {
+
+ //
+ // lea rsp, disp32[frame-register].
+ //
+
+ Displacement = NextByte[3] | (NextByte[4] << 8);
+ Displacement |= (NextByte[5] << 16) | (NextByte[6] << 24);
+ ContextRecord->Rsp = IntegerRegister[FrameRegister];
+ ContextRecord->Rsp += Displacement;
+ NextByte += 7;
+ Bytes -= 7;
+ }
+ }
+ }
+
+ //
+ // Emulate any number of (if any):
+ //
+ // pop nonvolatile-integer-register.
+ //
+
+ while (TRUE) {
+ if (Bytes >= 1 &&
+ (NextByte[0] & 0xf8) == POP_OP) {
+
+ //
+ // pop nonvolatile-integer-register[0..7]
+ //
+
+ RegisterNumber = NextByte[0] & 0x7;
+ if ((Status = m_Services->
+ ReadAllMemory(ContextRecord->Rsp,
+ &IntegerRegister[RegisterNumber],
+ sizeof(ULONG64))) != S_OK) {
+ m_Services->Status(1, "Unable to read stack at %I64X\n",
+ ContextRecord->Rsp);
+ return Status;
+ }
+ ContextRecord->Rsp += 8;
+ NextByte += 1;
+ Bytes -= 1;
+
+ } else if (Bytes >= 2 &&
+ IS_REX_PREFIX(NextByte[0]) &&
+ (NextByte[1] & 0xf8) == POP_OP) {
+
+ //
+ // pop nonvolatile-integer-register[8..15]
+ //
+
+ RegisterNumber = ((NextByte[0] & 1) << 3) | (NextByte[1] & 0x7);
+ if ((Status = m_Services->
+ ReadAllMemory(ContextRecord->Rsp,
+ &IntegerRegister[RegisterNumber],
+ sizeof(ULONG64))) != S_OK) {
+ m_Services->Status(1, "Unable to read stack at %I64X\n",
+ ContextRecord->Rsp);
+ return Status;
+ }
+ ContextRecord->Rsp += 8;
+ NextByte += 2;
+ Bytes -= 2;
+
+ } else {
+ break;
+ }
+ }
+
+ //
+ // Emulate return and return null exception handler.
+ //
+ // Note: this instruction might in fact be a jmp, however
+ // we want to emulate a return regardless.
+ //
+
+ if ((Status = m_Services->
+ ReadAllMemory(ContextRecord->Rsp,
+ &ContextRecord->Rip,
+ sizeof(ULONG64))) != S_OK) {
+ m_Services->Status(1, "Unable to read stack at %I64X\n",
+ ContextRecord->Rsp);
+ return Status;
+ }
+ ContextRecord->Rsp += 8;
+ return S_OK;
+ }
+
+ //
+ // Control left the specified function outside an epilogue. Unwind the
+ // subject function and any chained unwind information.
+ //
+
+ return UnwindPrologue(ImageBase,
+ ControlPc,
+ *EstablisherFrame,
+ FunctionEntry,
+ ContextRecord);
+}
+
+ULONG64
+DbsX64StackUnwinder::LookupPrimaryUnwindInfo(
+ __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry,
+ __in ULONG64 ImageBase,
+ __out _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryEntry
+ )
+
+/*++
+
+Routine Description:
+
+ This function determines whether the supplied function entry is a primary
+ function entry or a chained function entry. If it is a chained function
+ entry, the unwind information associated with the primary function entry
+ is returned.
+
+Arguments:
+
+ FunctionEntry - Supplies a pointer to the function entry for which the
+ associated primary function entry will be located.
+
+ ImageBase - Supplies the base address of the image containing the
+ supplied function entry.
+
+ PrimaryEntry - Supplies the address of a variable that receives a pointer
+ to the primary function entry.
+
+Return Value:
+
+ A pointer to the unwind information for the primary function entry is
+ returned as the function value.
+
+--*/
+
+{
+
+ ULONG Index;
+ ULONG64 UnwindInfoBuffer[32];
+ PAMD64_UNWIND_INFO UnwindInfo;
+ ULONG UnwindRel;
+ ULONG64 UnwindAbs;
+
+ //
+ // Locate the unwind information and determine whether it is chained.
+ // If the unwind information is chained, then locate the parent function
+ // entry and loop again.
+ //
+
+ UnwindRel = FunctionEntry->UnwindInfoAddress;
+ // Copy the function entry before it becomes invalid.
+ *PrimaryEntry = *FunctionEntry;
+
+ do {
+ UnwindAbs = ImageBase + UnwindRel;
+ if (GetUnwindInfo(ImageBase, UnwindRel,
+ false,
+ UnwindInfoBuffer, sizeof(UnwindInfoBuffer),
+ (PVOID*)&UnwindInfo) != S_OK ||
+ (UnwindInfo->Flags & AMD64_UNW_FLAG_CHAININFO) == 0) {
+ break;
+ }
+
+ Index = UnwindInfo->CountOfCodes;
+ if ((Index & 1) != 0) {
+ Index += 1;
+ }
+
+ FunctionEntry = (_PIMAGE_RUNTIME_FUNCTION_ENTRY)
+ &UnwindInfo->UnwindCode[Index];
+ UnwindRel = FunctionEntry->UnwindInfoAddress;
+
+ // Copy the function entry before it becomes invalid.
+ *PrimaryEntry = *FunctionEntry;
+
+ FreeUnwindInfo(UnwindInfo, UnwindInfoBuffer);
+
+ } while (TRUE);
+
+ return UnwindAbs;
+}
+
+_PIMAGE_RUNTIME_FUNCTION_ENTRY
+DbsX64StackUnwinder::SameFunction(
+ __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry,
+ __in ULONG64 ImageBase,
+ __in ULONG64 ControlPc,
+ __out _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionReturnBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This function determines whether the address supplied by ControlPc lies
+ anywhere within the function associated with FunctionEntry.
+
+Arguments:
+
+ FunctionEntry - Supplies a pointer to a function entry (primary or chained)
+ associated with the function.
+
+ ImageBase - Supplies the base address of the image containing the supplied
+ function entry.
+
+ ControlPc - Supplies the address that will be tested for inclusion within
+ the function associated with FunctionEntry.
+
+Return Value:
+
+ If the address of the unwind information for the specified function is
+ equal to the address of the unwind information for the control PC, then
+ a pointer to a function table entry that describes the primary function
+ table entry is returned as the function value. Otherwise, NULL is returned.
+
+--*/
+
+{
+
+ _IMAGE_RUNTIME_FUNCTION_ENTRY TargetFunctionEntry;
+ ULONG64 TargetImageBase;
+ ULONG64 UnwindInfo1;
+ ULONG64 UnwindInfo2;
+
+ //
+ // Find the unwind information referenced by the primary function entry
+ // associated with the specified function entry.
+ //
+
+ UnwindInfo1 = LookupPrimaryUnwindInfo(FunctionEntry, ImageBase,
+ FunctionReturnBuffer);
+
+ //
+ // Determine the function entry containing the control Pc and similarly
+ // resolve it's primary function entry.
+ //
+
+ if (m_Services->GetModuleBase(ControlPc, &TargetImageBase) != S_OK ||
+ m_Services->GetFunctionEntry(ControlPc,
+ &TargetFunctionEntry,
+ sizeof(TargetFunctionEntry)) != S_OK) {
+ return NULL;
+ }
+
+ UnwindInfo2 = LookupPrimaryUnwindInfo(&TargetFunctionEntry,
+ TargetImageBase,
+ FunctionReturnBuffer);
+
+ //
+ // If the address of the two sets of unwind information are equal, then
+ // return the address of the primary function entry. Otherwise, return
+ // NULL.
+ //
+
+ if (UnwindInfo1 == UnwindInfo2) {
+ return FunctionReturnBuffer;
+
+ } else {
+ return NULL;
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// DbsX64StackUnwinder.
+//
+//----------------------------------------------------------------------------
+
+#define DBHX64_SAVE_TRAP(_DbhFrame) ((_DbhFrame)->Reserved[0])
+
+//
+// Flags word.
+//
+
+#define DBHX64_IS_RESTART_FLAG (0x1UI64)
+
+#define DBHX64_GET_IS_RESTART(_DbhFrame) \
+ ((((_DbhFrame)->Reserved[2]) & DBHX64_IS_RESTART_FLAG) != 0)
+#define DBHX64_SET_IS_RESTART(_DbhFrame, _IsRestart) \
+ ((_DbhFrame)->Reserved[2] = \
+ (((_DbhFrame)->Reserved[2]) & ~DBHX64_IS_RESTART_FLAG) | \
+ ((_IsRestart) ? DBHX64_IS_RESTART_FLAG : 0))
+
+DbsX64StackUnwinder::DbsX64StackUnwinder(__in_opt DbsStackServices* Services)
+ : DbsStackUnwinder(Services, "x64", IMAGE_FILE_MACHINE_AMD64,
+ sizeof(m_Context),
+ sizeof(_IMAGE_RUNTIME_FUNCTION_ENTRY),
+ sizeof(AMD64_UNWIND_INFO), 16, 8, 1)
+{
+ m_ContextBuffer = &m_Context;
+}
+
+HRESULT
+DbsX64StackUnwinder::Unwind(void)
+{
+ HRESULT Status;
+
+ ClearUnwindDerived();
+
+ if (SUCCEEDED(Status = BaseUnwind()))
+ {
+ return Status;
+ }
+
+ //
+ // Unable to do a normal unwind, so check for
+ // alternate transitions like kernel/user boundaries.
+ // If this fails just return the original error
+ // as that's more likely to be interesting.
+ //
+
+ DWORD64 ImageBase;
+ _IMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry;
+ DWORD64 StackPointer;
+
+ if (UnwindNtKernelCallback(&ImageBase,
+ &FunctionEntry,
+ sizeof(FunctionEntry),
+ &StackPointer) != S_OK)
+ {
+ return Status;
+ }
+
+ m_InstructionPointer = ImageBase + FunctionEntry.BeginAddress;
+ m_CallPointer = m_InstructionPointer;
+ m_Context.Rip = m_InstructionPointer;
+ m_StackPointer = StackPointer;
+ m_Context.Rsp = m_StackPointer;
+ m_RestartFrame = true;
+
+ return S_OK;
+}
+
+DWORD
+DbsX64StackUnwinder::
+GetFullUnwindInfoSize(__in PVOID InfoHeader)
+{
+ PAMD64_UNWIND_INFO UnwindInfo = (PAMD64_UNWIND_INFO)InfoHeader;
+
+ DWORD UnwindInfoSize = FIELD_OFFSET(AMD64_UNWIND_INFO, UnwindCode) +
+ UnwindInfo->CountOfCodes * sizeof(AMD64_UNWIND_CODE);
+
+ // An extra alignment code and function entry may be added on to handle
+ // the chained info case where the chain function entry is just
+ // beyond the end of the normal code array.
+ if ((UnwindInfo->Flags & AMD64_UNW_FLAG_CHAININFO) != 0)
+ {
+ if ((UnwindInfo->CountOfCodes & 1) != 0)
+ {
+ UnwindInfoSize += sizeof(AMD64_UNWIND_CODE);
+ }
+ UnwindInfoSize += sizeof(_IMAGE_RUNTIME_FUNCTION_ENTRY);
+ }
+
+ return UnwindInfoSize;
+}
+
+HRESULT
+DbsX64StackUnwinder::DbhStart(__inout LPSTACKFRAME64 StackFrame,
+ __in DWORD DbhVersion,
+ __in_bcount(DbhStorageBytes) PVOID DbhStorage,
+ __in DWORD DbhStorageBytes,
+ __inout PVOID Context)
+{
+ HRESULT Status;
+
+ if ((StackFrame->AddrPC.Offset &&
+ StackFrame->AddrPC.Mode != AddrModeFlat) ||
+ (StackFrame->AddrStack.Offset &&
+ StackFrame->AddrStack.Mode != AddrModeFlat) ||
+ (StackFrame->AddrFrame.Offset &&
+ StackFrame->AddrFrame.Mode != AddrModeFlat))
+ {
+ return E_INVALIDARG;
+ }
+
+ if ((Status = DbsStackUnwinder::
+ DbhStart(StackFrame, DbhVersion, DbhStorage, DbhStorageBytes,
+ Context)) != S_OK)
+ {
+ return Status;
+ }
+
+ // dbghelp doesn't give a context size so we
+ // have to assume the buffer is large enough.
+ memcpy(&m_Context, Context, sizeof(m_Context));
+
+ //
+ // Override context values from the stack frame if necessary.
+ //
+
+ if (StackFrame->AddrPC.Offset)
+ {
+ m_Context.Rip = StackFrame->AddrPC.Offset;
+ }
+ if (StackFrame->AddrStack.Offset)
+ {
+ m_Context.Rsp = StackFrame->AddrStack.Offset;
+ }
+ if (StackFrame->AddrFrame.Offset)
+ {
+ m_Context.Rbp = StackFrame->AddrFrame.Offset;
+ }
+ UpdateAbstractPointers();
+ m_CallPointer = m_InstructionPointer;
+
+ SetRestart();
+ return S_OK;
+}
+
+HRESULT
+DbsX64StackUnwinder::
+DbhContinue(__inout LPSTACKFRAME64 StackFrame,
+ __in DWORD DbhVersion,
+ __in_bcount(DbhStorageBytes) PVOID DbhStorage,
+ __in DWORD DbhStorageBytes,
+ __inout PVOID Context)
+{
+ HRESULT Status;
+
+ if ((Status = DbsStackUnwinder::
+ DbhContinue(StackFrame, DbhVersion,
+ DbhStorage, DbhStorageBytes,
+ Context)) != S_OK)
+ {
+ return Status;
+ }
+
+ if (DBHX64_GET_IS_RESTART(StackFrame))
+ {
+ m_RestartFrame = true;
+ // The base DbhContinue always assumes it
+ // isn't a restart frame, so override it.
+ m_CallPointer = m_InstructionPointer;
+ }
+
+ return Status;
+}
+
+HRESULT
+DbsX64StackUnwinder::DbhUpdatePreUnwind(__inout LPSTACKFRAME64 StackFrame)
+{
+ HRESULT Status;
+
+ if ((Status = DbsStackUnwinder::DbhUpdatePreUnwind(StackFrame)) != S_OK)
+ {
+ return Status;
+ }
+
+ DBHX64_SET_IS_RESTART(StackFrame, m_RestartFrame);
+ return S_OK;
+}
+
+HRESULT
+DbsX64StackUnwinder::DbhUpdatePostUnwind(__inout LPSTACKFRAME64 StackFrame,
+ __in HRESULT UnwindStatus)
+{
+ HRESULT Status;
+
+ if ((Status = DbsStackUnwinder::DbhUpdatePostUnwind(StackFrame,
+ UnwindStatus)) != S_OK)
+ {
+ return Status;
+ }
+
+ // The frame pointer is an artificial value set
+ // to a pointer below the return address. This
+ // matches an RBP-chain style of frame while
+ // also allowing easy access to the return
+ // address and homed arguments above it.
+ StackFrame->AddrFrame.Offset = m_FramePointer;
+
+ DBHX64_SAVE_TRAP(StackFrame) = m_TrapAddr;
+ return S_OK;
+}
+
+void
+DbsX64StackUnwinder::UpdateAbstractPointers(void)
+{
+ m_InstructionPointer = m_Context.Rip;
+ m_StackPointer = m_Context.Rsp;
+ m_FramePointer = m_Context.Rbp;
+}
+
+HRESULT
+DbsX64StackUnwinder::BaseUnwind(void)
+{
+ HRESULT Status;
+ _IMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry;
+
+ Status = m_Services->GetFunctionEntry(m_CallPointer,
+ &FunctionEntry,
+ sizeof(FunctionEntry));
+ if (Status == S_OK)
+ {
+ DWORD64 ImageBase;
+ DWORD64 EstablisherFrame;
+
+ //
+ // The return value coming out of mainCRTStartup is set by some
+ // run-time routine to be 0; this serves to cause an error if someone
+ // actually does a return from the mainCRTStartup frame.
+ //
+
+ if ((Status = m_Services->
+ GetModuleBase(m_Context.Rip, &ImageBase)) != S_OK ||
+ (Status = VirtualUnwind(ImageBase,
+ m_Context.Rip,
+ &FunctionEntry,
+ &m_Context,
+ &EstablisherFrame)) != S_OK)
+ {
+ return Status;
+ }
+
+ DWORD64 OldIp = m_InstructionPointer;
+
+ UpdateAbstractPointers();
+ UpdateCallPointer();
+ m_FramePointer = m_StackPointer - 2 * sizeof(DWORD64);
+
+ // Check for end frame.
+ if (m_Context.Rip == 0 ||
+ (m_Context.Rip == OldIp &&
+ EstablisherFrame == m_Context.Rsp))
+ {
+ return S_FALSE;
+ }
+ }
+ else if (Status == E_NOINTERFACE)
+ {
+ //
+ // If there's no function entry for a function
+ // we assume that it's a leaf and that RSP points
+ // directly to the return address.
+ //
+
+ m_Services->Status(1, "Leaf %I64X RSP %I64X\n",
+ m_Context.Rip, m_Context.Rsp);
+
+ if ((Status = m_Services->
+ ReadAllMemory(m_Context.Rsp, &m_Context.Rip,
+ sizeof(m_Context.Rip))) != S_OK)
+ {
+ return Status;
+ }
+
+ // Update the context values to what they should be in
+ // the caller.
+ m_Context.Rsp += sizeof(m_Context.Rip);
+ UpdateAbstractPointers();
+ m_CallPointer = m_InstructionPointer - 1;
+ m_FramePointer = m_StackPointer - 2 * sizeof(DWORD64);
+ }
+ else
+ {
+ return Status;
+ }
+
+ m_FrameIndex++;
+ if (!m_RestartFrame)
+ {
+ AdjustForNoReturn(&m_InstructionPointer);
+ }
+ return S_OK;
+}