summaryrefslogtreecommitdiff
path: root/src/unwinder
diff options
context:
space:
mode:
authorJan Vorlicek <janvorli@microsoft.com>2015-02-03 16:02:18 -0800
committerJan Vorlicek <janvorli@microsoft.com>2015-02-03 16:02:18 -0800
commit6c2c7994f1412e8aa504800c7164de875c350fc1 (patch)
tree669203f562ea469a1ca54df80821c4722b6f6047 /src/unwinder
parent58ef03fa8aa9397b7ec9611abba0e1b3832fcea7 (diff)
downloadcoreclr-6c2c7994f1412e8aa504800c7164de875c350fc1.tar.gz
coreclr-6c2c7994f1412e8aa504800c7164de875c350fc1.tar.bz2
coreclr-6c2c7994f1412e8aa504800c7164de875c350fc1.zip
Move the windows unwinder code out of the debug folder.
It is a preparation for using the DAC unwinder code as an unwinder for the jitted code on Linux, because the jitter generates windows style unwind info. The unwinder is build as a static library and linked to mscordac. [tfs-changeset: 1409640]
Diffstat (limited to 'src/unwinder')
-rw-r--r--src/unwinder/.gitmirror1
-rw-r--r--src/unwinder/CMakeLists.txt22
-rw-r--r--src/unwinder/amd64/.gitmirror1
-rw-r--r--src/unwinder/amd64/dbs_stack_x64.cpp1500
-rw-r--r--src/unwinder/amd64/unwinder_amd64.cpp1225
-rw-r--r--src/unwinder/amd64/unwinder_amd64.h57
-rw-r--r--src/unwinder/arm/.gitmirror1
-rw-r--r--src/unwinder/arm/unwinder_arm.cpp1469
-rw-r--r--src/unwinder/arm/unwinder_arm.h55
-rw-r--r--src/unwinder/arm64/.gitmirror1
-rw-r--r--src/unwinder/arm64/unwinder_arm64.cpp1580
-rw-r--r--src/unwinder/arm64/unwinder_arm64.h55
-rw-r--r--src/unwinder/dac/.gitmirror1
-rw-r--r--src/unwinder/dac/CMakeLists.txt10
-rw-r--r--src/unwinder/dac/dirs.proj18
-rw-r--r--src/unwinder/dac/hostlocal/.gitmirror1
-rw-r--r--src/unwinder/dac/hostlocal/unwinder_dac.nativeproj13
-rw-r--r--src/unwinder/dac/hostwinamd64/.gitmirror1
-rw-r--r--src/unwinder/dac/hostwinamd64/CMakeLists.txt5
-rw-r--r--src/unwinder/dac/hostwinamd64/unwinder_dac.nativeproj15
-rw-r--r--src/unwinder/dac/hostwinx86/.gitmirror1
-rw-r--r--src/unwinder/dac/hostwinx86/unwinder_dac.nativeproj15
-rw-r--r--src/unwinder/dirs.proj19
-rw-r--r--src/unwinder/stdafx.cpp13
-rw-r--r--src/unwinder/stdafx.h19
-rw-r--r--src/unwinder/unwinder.cpp163
-rw-r--r--src/unwinder/unwinder.h64
-rw-r--r--src/unwinder/unwinder.targets60
28 files changed, 6385 insertions, 0 deletions
diff --git a/src/unwinder/.gitmirror b/src/unwinder/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/unwinder/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/unwinder/CMakeLists.txt b/src/unwinder/CMakeLists.txt
new file mode 100644
index 0000000000..0ed0160b8a
--- /dev/null
+++ b/src/unwinder/CMakeLists.txt
@@ -0,0 +1,22 @@
+include_directories(BEFORE ${VM_DIR})
+include_directories(BEFORE ${VM_DIR}/${ARCH_SOURCES_DIR})
+include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR})
+include_directories(BEFORE ${CLR_DIR}/src/unwinder)
+include_directories(${CLR_DIR}/src/debug/ee)
+include_directories(${CLR_DIR}/src/gc)
+include_directories(${CLR_DIR}/src/gcdump)
+include_directories(${CLR_DIR}/src/debug/daccess)
+
+if(IS_64BIT_BUILD EQUAL 1)
+ include_directories(amd64)
+
+ set(UNWINDER_SOURCES
+ unwinder.cpp
+ amd64/unwinder_amd64.cpp
+ )
+
+ convert_to_absolute_path(UNWINDER_SOURCES ${UNWINDER_SOURCES})
+
+ add_subdirectory(dac)
+endif(IS_64BIT_BUILD EQUAL 1)
+
diff --git a/src/unwinder/amd64/.gitmirror b/src/unwinder/amd64/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/unwinder/amd64/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/unwinder/amd64/dbs_stack_x64.cpp b/src/unwinder/amd64/dbs_stack_x64.cpp
new file mode 100644
index 0000000000..83f2a00abf
--- /dev/null
+++ b/src/unwinder/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;
+}
diff --git a/src/unwinder/amd64/unwinder_amd64.cpp b/src/unwinder/amd64/unwinder_amd64.cpp
new file mode 100644
index 0000000000..de7f94f977
--- /dev/null
+++ b/src/unwinder/amd64/unwinder_amd64.cpp
@@ -0,0 +1,1225 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//
+
+#include "stdafx.h"
+#include "unwinder_amd64.h"
+
+//---------------------------------------------------------------------------------------
+//
+// Given the target address of an UNWIND_INFO structure, this function retrieves all the memory used for
+// the UNWIND_INFO, including the variable size array of UNWIND_CODE. The function returns a host copy
+// of the UNWIND_INFO.
+//
+// Arguments:
+// taUnwindInfo - the target address of an UNWIND_INFO
+//
+// Return Value:
+// Return a host copy of the UNWIND_INFO, including the array of UNWIND_CODE.
+//
+// Notes:
+// The host copy of UNWIND_INFO is created from DAC memory, which will be flushed when the DAC cache
+// is flushed (i.e. when the debugee is continued). Thus, the caller doesn't need to worry about freeing
+// this memory.
+//
+
+UNWIND_INFO * DacGetUnwindInfo(TADDR taUnwindInfo)
+{
+ PTR_UNWIND_INFO pUnwindInfo = PTR_UNWIND_INFO(taUnwindInfo);
+ DWORD cbUnwindInfo = offsetof(UNWIND_INFO, UnwindCode) +
+ pUnwindInfo->CountOfUnwindCodes * sizeof(UNWIND_CODE);
+
+ // Check if there is a chained unwind info. If so, it has an extra RUNTIME_FUNCTION tagged to the end.
+ if ((pUnwindInfo->Flags & UNW_FLAG_CHAININFO) != 0)
+ {
+ // If there is an odd number of UNWIND_CODE, we need to adjust for alignment.
+ if ((pUnwindInfo->CountOfUnwindCodes & 1) != 0)
+ {
+ cbUnwindInfo += sizeof(UNWIND_CODE);
+ }
+ cbUnwindInfo += sizeof(RUNTIME_FUNCTION);
+ }
+ return reinterpret_cast<UNWIND_INFO *>(DacInstantiateTypeByAddress(taUnwindInfo, cbUnwindInfo, true));
+}
+
+//---------------------------------------------------------------------------------------
+//
+// This function is just a wrapper over OOPStackUnwinder. The runtime can call this function to
+// virtually unwind a CONTEXT out-of-process.
+//
+// Arguments:
+// pContext - This is an in-out parameter. On entry, this is the CONTEXT to be unwound.
+// On exit, this is the caller CONTEXT.
+//
+// Return Value:
+// TRUE if the unwinding is successful
+//
+// Notes:
+// This function overwrites the specified CONTEXT to store the caller CONTEXT.
+//
+
+BOOL DacUnwindStackFrame(CONTEXT * pContext, KNONVOLATILE_CONTEXT_POINTERS* pContextPointers)
+{
+ OOPStackUnwinderAMD64 unwinder;
+ BOOL res = unwinder.Unwind(pContext);
+
+ if (res && pContextPointers)
+ {
+ for (int i = 0; i < 16; i++)
+ {
+ *(&pContextPointers->Rax + i) = &pContext->Rax + i;
+ }
+ }
+
+ return res;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Unwind the given CONTEXT to the caller CONTEXT. The given CONTEXT will be overwritten.
+//
+// Arguments:
+// pContext - in-out parameter storing the specified CONTEXT on entry and the unwound CONTEXT on exit
+//
+// Return Value:
+// TRUE if the unwinding is successful
+//
+
+BOOL OOPStackUnwinderAMD64::Unwind(CONTEXT * pContext)
+{
+ HRESULT hr = E_FAIL;
+
+ ULONG64 uControlPC = (DWORD64)dac_cast<PCODE>(::GetIP(pContext));
+
+ // get the module base
+ ULONG64 uImageBase;
+ hr = GetModuleBase(uControlPC, &uImageBase);
+ if (FAILED(hr))
+ {
+ return FALSE;
+ }
+
+ // get the function entry
+ IMAGE_RUNTIME_FUNCTION_ENTRY functionEntry;
+ hr = GetFunctionEntry(uControlPC, &functionEntry, sizeof(functionEntry));
+ if (FAILED(hr))
+ {
+ return FALSE;
+ }
+
+ // call VirtualUnwind() to do the real work
+ ULONG64 EstablisherFrame;
+ hr = VirtualUnwind(uImageBase, uControlPC, &functionEntry, pContext, &EstablisherFrame);
+
+ return (hr == S_OK);
+}
+
+
+//
+//
+// <NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE>
+//
+// Everything below is borrowed from dbghelp.dll
+//
+// <NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE>
+//
+//
+
+
+//----------------------------------------------------------------------------
+//
+// 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
+OOPStackUnwinderAMD64::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
+OOPStackUnwinderAMD64::UnwindPrologue(
+ __in ULONG64 ImageBase,
+ __in ULONG64 ControlPc,
+ __in ULONG64 FrameBase,
+ __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry,
+ __inout PCONTEXT 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;
+ M128A* FloatingRegister;
+ ULONG FrameOffset;
+ ULONG Index;
+ ULONG64 IntegerAddress;
+ PULONG64 IntegerRegister;
+ BOOLEAN MachineFrame;
+ ULONG OpInfo;
+ ULONG PrologOffset;
+ ULONG64 ReturnAddress;
+ ULONG64 StackAddress;
+ PUNWIND_INFO UnwindInfo;
+ ULONG UnwindOp;
+
+ //
+ // Process the unwind codes.
+ //
+
+ FloatingRegister = &ContextRecord->Xmm0;
+ IntegerRegister = &ContextRecord->Rax;
+ Index = 0;
+ MachineFrame = FALSE;
+ PrologOffset = (ULONG)(ControlPc - (FunctionEntry->BeginAddress + ImageBase));
+
+ UnwindInfo = DacGetUnwindInfo(ImageBase + FunctionEntry->UnwindInfoAddress);
+ if (UnwindInfo == NULL)
+ {
+ return HRESULT_FROM_WIN32(ERROR_READ_FAULT);
+ }
+
+ while (Index < UnwindInfo->CountOfUnwindCodes) {
+
+ //
+ // 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 > UWOP_PUSH_MACHFRAME) {
+ 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 UWOP_PUSH_NONVOL:
+ IntegerAddress = ContextRecord->Rsp;
+ if ((Status =
+ ReadAllMemory(IntegerAddress,
+ &IntegerRegister[OpInfo],
+ sizeof(ULONG64))) != S_OK) {
+ 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 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 UWOP_ALLOC_SMALL:
+ ContextRecord->Rsp += (OpInfo * 8) + 8;
+ break;
+
+ //
+ // Establish the the frame pointer register.
+ //
+ // The operation information is not used.
+ //
+
+ case 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 UWOP_SAVE_NONVOL:
+ Index += 1;
+ FrameOffset = UnwindInfo->UnwindCode[Index].FrameOffset * 8;
+ IntegerAddress = FrameBase + FrameOffset;
+ if ((Status =
+ ReadAllMemory(IntegerAddress,
+ &IntegerRegister[OpInfo],
+ sizeof(ULONG64))) != S_OK) {
+ goto Fail;
+ }
+ break;
+
+ //
+ // Save nonvolatile integer register on the stack using a
+ // 32-bit displacment.
+ //
+ // The operation information is the register number.
+ //
+
+ case UWOP_SAVE_NONVOL_FAR:
+ Index += 2;
+ FrameOffset = UnwindInfo->UnwindCode[Index - 1].FrameOffset;
+ FrameOffset += UnwindInfo->UnwindCode[Index].FrameOffset << 16;
+ IntegerAddress = FrameBase + FrameOffset;
+ if ((Status =
+ ReadAllMemory(IntegerAddress,
+ &IntegerRegister[OpInfo],
+ sizeof(ULONG64))) != S_OK) {
+ 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 UWOP_SAVE_XMM:
+ Index += 1;
+ FrameOffset = UnwindInfo->UnwindCode[Index].FrameOffset * 8;
+ FloatingAddress = FrameBase + FrameOffset;
+ FloatingRegister[OpInfo].High = 0;
+ if ((Status =
+ ReadAllMemory(FloatingAddress,
+ &FloatingRegister[OpInfo].Low,
+ sizeof(ULONG64))) != S_OK) {
+ 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 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 =
+ ReadAllMemory(FloatingAddress,
+ &FloatingRegister[OpInfo].Low,
+ sizeof(ULONG64))) != S_OK) {
+ 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 UWOP_SAVE_XMM128:
+ Index += 1;
+ FrameOffset = UnwindInfo->UnwindCode[Index].FrameOffset * 16;
+ FloatingAddress = FrameBase + FrameOffset;
+ if ((Status =
+ ReadAllMemory(FloatingAddress,
+ &FloatingRegister[OpInfo],
+ sizeof(M128A))) != S_OK) {
+ 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 UWOP_SAVE_XMM128_FAR:
+ Index += 2;
+ FrameOffset = UnwindInfo->UnwindCode[Index - 1].FrameOffset;
+ FrameOffset += UnwindInfo->UnwindCode[Index].FrameOffset << 16;
+ FloatingAddress = FrameBase + FrameOffset;
+ if ((Status =
+ ReadAllMemory(FloatingAddress,
+ &FloatingRegister[OpInfo],
+ sizeof(M128A))) != S_OK) {
+ 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 UWOP_PUSH_MACHFRAME:
+ MachineFrame = TRUE;
+ ReturnAddress = ContextRecord->Rsp;
+ StackAddress = ContextRecord->Rsp + (3 * 8);
+ if (OpInfo != 0) {
+ ReturnAddress += 8;
+ StackAddress += 8;
+ }
+
+ if ((Status =
+ ReadAllMemory(ReturnAddress,
+ &ContextRecord->Rip,
+ sizeof(ULONG64))) != S_OK) {
+ goto Fail;
+ }
+ if ((Status =
+ ReadAllMemory(StackAddress,
+ &ContextRecord->Rsp,
+ sizeof(ULONG64))) != S_OK) {
+ 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 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 & UNW_FLAG_CHAININFO) != 0) {
+
+ _PIMAGE_RUNTIME_FUNCTION_ENTRY ChainEntry;
+
+ Index = UnwindInfo->CountOfUnwindCodes;
+ 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];
+
+ Status = UnwindPrologue(ImageBase,
+ ControlPc,
+ FrameBase,
+ ChainEntry,
+ ContextRecord);
+
+ return Status;
+
+ } else {
+
+ if (MachineFrame == FALSE) {
+ if ((Status =
+ ReadAllMemory(ContextRecord->Rsp,
+ &ContextRecord->Rip,
+ sizeof(ULONG64))) != S_OK) {
+ return Status;
+ }
+ ContextRecord->Rsp += 8;
+ }
+
+ return S_OK;
+ }
+
+ Fail:
+ return Status;
+}
+
+HRESULT
+OOPStackUnwinderAMD64::VirtualUnwind(
+ __in ULONG64 ImageBase,
+ __in ULONG64 ControlPc,
+ __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry,
+ __inout PCONTEXT 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 = 0;
+ ULONG Index;
+ BOOL InEpilogue;
+ PULONG64 IntegerRegister;
+ PUCHAR NextByte;
+ _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryFunctionEntry;
+ ULONG PrologOffset;
+ ULONG RegisterNumber;
+ PUNWIND_INFO UnwindInfo;
+ 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.
+ //
+
+ UnwindInfo = DacGetUnwindInfo(ImageBase + FunctionEntry->UnwindInfoAddress);
+ if (UnwindInfo == NULL)
+ {
+ return HRESULT_FROM_WIN32(ERROR_READ_FAULT);
+ }
+
+ PrologOffset = (ULONG)(ControlPc - (FunctionEntry->BeginAddress + ImageBase));
+ UnwindFrameReg = UnwindInfo->FrameRegister;
+ if (UnwindFrameReg == 0) {
+ *EstablisherFrame = ContextRecord->Rsp;
+
+ } else if ((PrologOffset >= UnwindInfo->SizeOfProlog) ||
+ ((UnwindInfo->Flags & UNW_FLAG_CHAININFO) != 0)) {
+ *EstablisherFrame = (&ContextRecord->Rax)[UnwindFrameReg];
+ *EstablisherFrame -= UnwindInfo->FrameOffset * 16;
+
+ } else {
+
+ // Read all the data.
+ UnwindInfo = DacGetUnwindInfo(ImageBase + FunctionEntry->UnwindInfoAddress);
+ if (UnwindInfo == NULL)
+ {
+ return HRESULT_FROM_WIN32(ERROR_READ_FAULT);
+ }
+
+ Index = 0;
+ while (Index < UnwindInfo->CountOfUnwindCodes) {
+ if (UnwindInfo->UnwindCode[Index].UnwindOp == UWOP_SET_FPREG) {
+ break;
+ }
+
+ Index += 1;
+ }
+
+ if (PrologOffset >= UnwindInfo->UnwindCode[Index].CodeOffset) {
+ *EstablisherFrame = (&ContextRecord->Rax)[UnwindFrameReg];
+ *EstablisherFrame -= UnwindInfo->FrameOffset * 16;
+
+ } else {
+ *EstablisherFrame = ContextRecord->Rsp;
+ }
+ }
+
+ if ((Status =
+ ReadMemory(ControlPc, InstrBuffer, sizeof(InstrBuffer),
+ &InstrBytes)) != S_OK) {
+
+ // 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 & 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 =
+ ReadAllMemory(ContextRecord->Rsp,
+ &IntegerRegister[RegisterNumber],
+ sizeof(ULONG64))) != S_OK) {
+ 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 =
+ ReadAllMemory(ContextRecord->Rsp,
+ &IntegerRegister[RegisterNumber],
+ sizeof(ULONG64))) != S_OK) {
+ 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 =
+ ReadAllMemory(ContextRecord->Rsp,
+ &ContextRecord->Rip,
+ sizeof(ULONG64))) != S_OK) {
+ 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
+OOPStackUnwinderAMD64::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;
+ PUNWIND_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;
+ UnwindInfo = DacGetUnwindInfo(ImageBase + UnwindRel);
+ if ((UnwindInfo == NULL) || ((UnwindInfo->Flags & UNW_FLAG_CHAININFO) == 0))
+ {
+ break;
+ }
+
+ Index = UnwindInfo->CountOfUnwindCodes;
+ 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;
+
+ } while (TRUE);
+
+ return UnwindAbs;
+}
+
+_PIMAGE_RUNTIME_FUNCTION_ENTRY
+OOPStackUnwinderAMD64::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 (GetModuleBase(ControlPc, &TargetImageBase) != S_OK ||
+ 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;
+ }
+}
diff --git a/src/unwinder/amd64/unwinder_amd64.h b/src/unwinder/amd64/unwinder_amd64.h
new file mode 100644
index 0000000000..4c0bd46529
--- /dev/null
+++ b/src/unwinder/amd64/unwinder_amd64.h
@@ -0,0 +1,57 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//
+
+#ifndef __unwinder_amd64_h__
+#define __unwinder_amd64_h__
+
+#include "unwinder.h"
+
+
+//---------------------------------------------------------------------------------------
+//
+// See the comment for the base class code:OOPStackUnwinder.
+//
+
+class OOPStackUnwinderAMD64 : public OOPStackUnwinder
+{
+public:
+ // Unwind the given CONTEXT to the caller CONTEXT. The CONTEXT will be overwritten.
+ BOOL Unwind(CONTEXT * pContext);
+
+ //
+ // Everything below comes from dbghelp.dll.
+ //
+
+protected:
+ static BYTE s_UnwindOpSlotTable[];
+
+ HRESULT UnwindPrologue(__in DWORD64 ImageBase,
+ __in DWORD64 ControlPc,
+ __in DWORD64 FrameBase,
+ __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry,
+ __inout PCONTEXT ContextRecord);
+
+ HRESULT VirtualUnwind(__in DWORD64 ImageBase,
+ __in DWORD64 ControlPc,
+ __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry,
+ __inout PCONTEXT ContextRecord,
+ __out PDWORD64 EstablisherFrame);
+
+ DWORD64 LookupPrimaryUnwindInfo
+ (__in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry,
+ __in DWORD64 ImageBase,
+ __out _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryEntry);
+
+ _PIMAGE_RUNTIME_FUNCTION_ENTRY SameFunction
+ (__in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry,
+ __in DWORD64 ImageBase,
+ __in DWORD64 ControlPc,
+ __out _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionReturnBuffer);
+};
+
+#endif // __unwinder_amd64_h__
+
diff --git a/src/unwinder/arm/.gitmirror b/src/unwinder/arm/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/unwinder/arm/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/unwinder/arm/unwinder_arm.cpp b/src/unwinder/arm/unwinder_arm.cpp
new file mode 100644
index 0000000000..c2463f58f7
--- /dev/null
+++ b/src/unwinder/arm/unwinder_arm.cpp
@@ -0,0 +1,1469 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//
+
+#include "stdafx.h"
+#include "utilcode.h"
+
+#include "unwinder_arm.h"
+
+#define DBS_EXTEND64(x) ((DWORD64)x)
+#define MEMORY_READ_BYTE(params, addr) (*dac_cast<PTR_BYTE>(addr))
+#define MEMORY_READ_DWORD(params, addr) (*dac_cast<PTR_DWORD>(addr))
+#define MEMORY_READ_QWORD(params, addr) (*dac_cast<PTR_UINT64>(addr))
+#define MAX_PROLOG_SIZE 16
+#define MAX_EPILOG_SIZE 16
+
+#define STATUS_UNWIND_UNSUPPORTED_VERSION STATUS_UNSUCCESSFUL
+
+
+#define UPDATE_CONTEXT_POINTERS(Params, RegisterNumber, Address)
+#define UPDATE_FP_CONTEXT_POINTERS(Params, RegisterNumber, Address)
+#define VALIDATE_STACK_ADDRESS(Params, Context, DataSize, Alignment, OutStatus)
+#define UNWIND_PARAMS_SET_TRAP_FRAME(Params, Address)
+
+
+//
+// Macro for accessing an integer register by index.
+//
+
+#define CONTEXT_REGISTER(ctx, idx) ((&(ctx)->R0)[idx])
+
+
+//
+// The ConditionTable is used to look up the state of a condition
+// based on the CPSR flags N,Z,C,V, which reside in the upper 4
+// bits. To use this table, take the condition you are interested
+// in and use it as the index to look up the UINT16 from the table.
+// Then right-shift that value by the upper 4 bits of the CPSR,
+// and the low bit will be the result.
+//
+// The bits in the CPSR are ordered (MSB to LSB): N,Z,C,V. Taken
+// together, this is called the CpsrFlags.
+//
+// The macros below are defined such that:
+//
+// N = (NSET_MASK >> CpsrFlags) & 1
+// Z = (ZSET_MASK >> CpsrFlags) & 1
+// C = (CSET_MASK >> CpsrFlags) & 1
+// V = (VSET_MASK >> CpsrFlags) & 1
+//
+// Also:
+//
+// (N == V) = (NEQUALV_MASK >> CpsrFlags) & 1
+//
+
+#define NSET_MASK (0xff00)
+#define ZSET_MASK (0xf0f0)
+#define CSET_MASK (0xcccc)
+#define VSET_MASK (0xaaaa)
+
+#define NEQUALV_MASK ((NSET_MASK & VSET_MASK) | (~NSET_MASK & ~VSET_MASK))
+
+static const UINT16 ConditionTable[16] =
+{
+ ZSET_MASK, // EQ: Z
+ ~ZSET_MASK, // NE: !Z
+ CSET_MASK, // CS: C
+ ~CSET_MASK, // CC: !C
+ NSET_MASK, // MI: N
+ ~NSET_MASK, // PL: !N
+ VSET_MASK, // VS: V
+ ~VSET_MASK, // VC: !V
+ CSET_MASK & ~ZSET_MASK, // HI: C & !Z
+ ~CSET_MASK | ZSET_MASK, // LO: !C | Z
+ NEQUALV_MASK, // GE: N == V
+ ~NEQUALV_MASK, // LT: N != V
+ NEQUALV_MASK & ~ZSET_MASK, // GT: (N == V) & !Z
+ ~NEQUALV_MASK | ZSET_MASK, // LE: (N != V) | Z
+ 0xffff, // AL: always
+ 0x0000 // NV: never
+};
+
+
+//
+// This table describes the size of each unwind code, in bytes (lower nibble),
+// along with the size of the corresponding machine code, in halfwords
+// (upper nibble).
+//
+
+static const BYTE UnwindOpTable[256] =
+{
+ 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
+ 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
+ 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
+ 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
+ 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
+ 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
+ 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
+ 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
+
+ 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,
+ 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,
+ 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,
+ 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,
+ 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
+ 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21,
+ 0x21,0x21,0x21,0x21,0x21,0x21,0x21,0x21, 0x22,0x22,0x22,0x22,0x12,0x12,0x02,0x22,
+ 0x01,0x01,0x01,0x01,0x01,0x22,0x22,0x13, 0x14,0x23,0x24,0x11,0x21,0x10,0x20,0x00
+};
+
+
+typedef struct _ARM_CONTEXT_OFFSETS
+{
+ UINT16 Alignment;
+ UINT16 TotalSize;
+ UINT16 RegOffset[13];
+ UINT16 FpRegOffset[32];
+ UINT16 SpOffset;
+ UINT16 LrOffset;
+ UINT16 PcOffset;
+ UINT16 CpsrOffset;
+ UINT16 FpscrOffset;
+} ARM_CONTEXT_OFFSETS, *PARM_CONTEXT_OFFSETS;
+
+static const ARM_CONTEXT_OFFSETS TrapFrameOffsets =
+{ 8, 272, { 248,252,256,260, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, 72 },
+ { 184, 192, 200, 208, 216, 224, 232, 240, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
+ ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0}, 64, 68, 264,
+ 268, 176};
+
+static const ARM_CONTEXT_OFFSETS MachineFrameOffsets =
+{ 8, 8, { ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0 },
+ {~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0,
+ ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0}, 0, ~0, 4, ~0 , ~0};
+
+static const ARM_CONTEXT_OFFSETS ContextOffsets =
+{ 16, 416, { 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52 },
+ { 80, 88, 96, 104, 112, 120, 128, 136, 144, 152, 160, 168, 176, 184, 192, 200,
+ 208, 216, 224, 232, 240, 248, 256, 264, 272, 280, 288, 296, 304, 312, 320,
+ 328}, 56, 60, 64, 68, 72};
+
+
+//
+// This table provides the register mask described by the given C/L/R/Reg bit
+// combinations in the compact pdata format, along with the number of VFP
+// registers to save in bits 16-19.
+//
+
+static const ULONG RegisterMaskLookup[1 << 6] =
+{ // C L R Reg
+ 0x00010, // 0 0 0 000
+ 0x00030, // 0 0 0 001
+ 0x00070, // 0 0 0 010
+ 0x000f0, // 0 0 0 011
+ 0x001f0, // 0 0 0 100
+ 0x003f0, // 0 0 0 101
+ 0x007f0, // 0 0 0 110
+ 0x00ff0, // 0 0 0 111
+
+ 0x10000, // 0 0 1 000
+ 0x20000, // 0 0 1 001
+ 0x30000, // 0 0 1 010
+ 0x40000, // 0 0 1 011
+ 0x50000, // 0 0 1 100
+ 0x60000, // 0 0 1 101
+ 0x70000, // 0 0 1 110
+ 0x00000, // 0 0 1 111
+
+ 0x04010, // 0 1 0 000
+ 0x04030, // 0 1 0 001
+ 0x04070, // 0 1 0 010
+ 0x040f0, // 0 1 0 011
+ 0x041f0, // 0 1 0 100
+ 0x043f0, // 0 1 0 101
+ 0x047f0, // 0 1 0 110
+ 0x04ff0, // 0 1 0 111
+
+ 0x14000, // 0 1 1 000
+ 0x24000, // 0 1 1 001
+ 0x34000, // 0 1 1 010
+ 0x44000, // 0 1 1 011
+ 0x54000, // 0 1 1 100
+ 0x64000, // 0 1 1 101
+ 0x74000, // 0 1 1 110
+ 0x04000, // 0 1 1 111
+
+ 0x00810, // 1 0 0 000
+ 0x00830, // 1 0 0 001
+ 0x00870, // 1 0 0 010
+ 0x008f0, // 1 0 0 011
+ 0x009f0, // 1 0 0 100
+ 0x00bf0, // 1 0 0 101
+ 0x00ff0, // 1 0 0 110
+ 0x0ffff, // 1 0 0 111
+
+ 0x1ffff, // 1 0 1 000
+ 0x2ffff, // 1 0 1 001
+ 0x3ffff, // 1 0 1 010
+ 0x4ffff, // 1 0 1 011
+ 0x5ffff, // 1 0 1 100
+ 0x6ffff, // 1 0 1 101
+ 0x7ffff, // 1 0 1 110
+ 0x0ffff, // 1 0 1 111
+
+ 0x04810, // 1 1 0 000
+ 0x04830, // 1 1 0 001
+ 0x04870, // 1 1 0 010
+ 0x048f0, // 1 1 0 011
+ 0x049f0, // 1 1 0 100
+ 0x04bf0, // 1 1 0 101
+ 0x04ff0, // 1 1 0 110
+ 0x0ffff, // 1 1 0 111
+
+ 0x14800, // 1 1 1 000
+ 0x24800, // 1 1 1 001
+ 0x34800, // 1 1 1 010
+ 0x44800, // 1 1 1 011
+ 0x54800, // 1 1 1 100
+ 0x64800, // 1 1 1 101
+ 0x74800, // 1 1 1 110
+ 0x04800 // 1 1 1 111
+};
+
+
+
+NTSTATUS
+RtlpUnwindCustom(
+ __inout PT_CONTEXT ContextRecord,
+ __in BYTE Opcode,
+ __in PVOID UnwindParams
+ )
+
+/*++
+
+Routine Description:
+
+ Handles custom unwinding operations involving machine-specific
+ frames.
+
+Arguments:
+
+ ContextRecord - Supplies the address of a context record.
+
+ Opcode - The opcode to decode.
+
+ UnwindParams - Additional parameters shared with caller.
+
+Return Value:
+
+ An NTSTATUS indicating either STATUS_SUCCESS if everything went ok, or
+ another status code if there were problems.
+
+--*/
+
+{
+ const ARM_CONTEXT_OFFSETS *Offsets;
+ ULONG RegIndex;
+ ULONG SourceAddress;
+ NTSTATUS Status;
+
+ //
+ // Determine which set of offsets to use
+ //
+
+ switch (Opcode)
+ {
+ case 0:
+ Offsets = &TrapFrameOffsets;
+ break;
+
+ case 1:
+ Offsets = &MachineFrameOffsets;
+ break;
+
+ case 2:
+ Offsets = &ContextOffsets;
+ break;
+
+ default:
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Handle general registers first
+ //
+
+ Status = STATUS_SUCCESS;
+ VALIDATE_STACK_ADDRESS(UnwindParams, ContextRecord, Offsets->TotalSize, Offsets->Alignment, &Status);
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ for (RegIndex = 0; RegIndex < 13; RegIndex++) {
+ if (Offsets->RegOffset[RegIndex] != (UINT16)~0) {
+ SourceAddress = ContextRecord->Sp + Offsets->RegOffset[RegIndex];
+ UPDATE_CONTEXT_POINTERS(UnwindParams, RegIndex, SourceAddress);
+ CONTEXT_REGISTER(ContextRecord, RegIndex) =
+ MEMORY_READ_DWORD(UnwindParams, SourceAddress);
+ }
+ }
+
+ for (RegIndex = 0; RegIndex < 32; RegIndex++) {
+ if (Offsets->FpRegOffset[RegIndex] != (UINT16)~0) {
+ SourceAddress = ContextRecord->Sp + Offsets->FpRegOffset[RegIndex];
+ UPDATE_FP_CONTEXT_POINTERS(UnwindParams, RegIndex, SourceAddress);
+ ContextRecord->D[RegIndex] = MEMORY_READ_QWORD(UnwindParams, SourceAddress);
+ }
+ }
+
+ //
+ // For the trap frame case, remember the trap frame at the current SP.
+ //
+
+ if (Opcode == 0) {
+ UNWIND_PARAMS_SET_TRAP_FRAME(UnwindParams, ContextRecord->Sp);
+ }
+
+ //
+ // Link register and PC next
+ //
+
+ if (Offsets->LrOffset != (UINT16)~0) {
+ SourceAddress = ContextRecord->Sp + Offsets->LrOffset;
+ ContextRecord->Lr = MEMORY_READ_DWORD(UnwindParams, SourceAddress);
+ }
+ if (Offsets->PcOffset != (UINT16)~0) {
+ SourceAddress = ContextRecord->Sp + Offsets->PcOffset;
+ ContextRecord->Pc = MEMORY_READ_DWORD(UnwindParams, SourceAddress);
+
+ //
+ // If we pull the PC out of one of these, this means we are not
+ // unwinding from a call, but rather from another frame.
+ //
+
+ ContextRecord->ContextFlags &= ~CONTEXT_UNWOUND_TO_CALL;
+ }
+
+ //
+ // Finally the stack pointer
+ //
+
+ if (Offsets->SpOffset != (UINT16)~0) {
+ SourceAddress = ContextRecord->Sp + Offsets->SpOffset;
+ ContextRecord->Sp = MEMORY_READ_DWORD(UnwindParams, SourceAddress);
+ } else {
+ ContextRecord->Sp += Offsets->TotalSize;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+RtlpPopVfpRegisterRange(
+ __inout PT_CONTEXT ContextRecord,
+ __in ULONG RegStart,
+ __in ULONG RegStop,
+ __in PVOID UnwindParams
+ )
+
+/*++
+
+Routine Description:
+
+ Pops a series of floating-point registers in the provided inclusive range.
+
+Arguments:
+
+ ContextRecord - Supplies the address of a context record.
+
+ RegStart - Specifies the index of the first register to pop.
+
+ RegStop - Specifies the index of the final register to pop.
+
+ UnwindParams - Additional parameters shared with caller.
+
+Return Value:
+
+ An NTSTATUS indicating either STATUS_SUCCESS if everything went ok, or
+ another status code if there were problems.
+
+--*/
+
+{
+ ULONG RegCount;
+ ULONG RegIndex;
+ NTSTATUS Status;
+
+ //
+ // Count and validate the number of registers.
+ //
+
+ RegCount = RegStop + 1 - RegStart;
+ Status = STATUS_SUCCESS;
+ VALIDATE_STACK_ADDRESS(UnwindParams, ContextRecord, 8 * RegCount, 8, &Status);
+ if (Status != STATUS_SUCCESS) {
+ return Status;
+ }
+
+ //
+ // Then pop each register in sequence.
+ //
+
+ for (RegIndex = RegStart; RegIndex <= RegStop; RegIndex++) {
+ UPDATE_FP_CONTEXT_POINTERS(UnwindParams, RegIndex, ContextRecord->Sp);
+ ContextRecord->D[RegIndex] = MEMORY_READ_QWORD(UnwindParams, ContextRecord->Sp);
+ ContextRecord->Sp += 8;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+FORCEINLINE
+WORD
+RtlpRangeToMask(
+ __in ULONG Start,
+ __in ULONG Stop,
+ __in ULONG Lr
+ )
+
+/*++
+
+Routine Description:
+
+ Generate a register mask from a start/stop range, plus a flag
+ indicating whether or not to include LR in the list.
+
+Arguments:
+
+ Start - Supplies the index of the first register in the range.
+
+ Stop - Supplies the index of the last register in the range.
+
+ Lr - Supplies a value which, if non-zero, indicates that the LR
+ register is to be included in the mask.
+
+Return Value:
+
+ A WORD value containing a bitmask of the registers.
+
+--*/
+
+{
+ WORD Result;
+
+ Result = 0;
+ if (Start <= Stop) {
+ Result |= ((1 << (Stop + 1)) - 1) - ((1 << Start) - 1);
+ }
+ return Result | ((Lr != 0) ? (1 << 14) : 0);
+}
+
+NTSTATUS
+RtlpPopRegisterMask(
+ __inout PT_CONTEXT ContextRecord,
+ __in WORD RegMask,
+ __in PVOID UnwindParams
+ )
+/*++
+
+Routine Description:
+
+ Pops a series of integer registers based on a provided register mask.
+
+Arguments:
+
+ ContextRecord - Supplies the address of a context record.
+
+ RegMask - Specifies a 16-bit mask of registers to pop.
+
+ UnwindParams - Additional parameters shared with caller.
+
+Return Value:
+
+ An NTSTATUS indicating either STATUS_SUCCESS if everything went ok, or
+ another status code if there were problems.
+
+--*/
+
+{
+ ULONG RegCount;
+ ULONG RegIndex;
+ NTSTATUS Status;
+
+ //
+ // Count and validate the number of registers.
+ //
+
+ RegCount = 0;
+ for (RegIndex = 0; RegIndex < 15; RegIndex++) {
+ RegCount += (RegMask >> RegIndex) & 1;
+ }
+
+ Status = STATUS_SUCCESS;
+ VALIDATE_STACK_ADDRESS(UnwindParams, ContextRecord, 4 * RegCount, 4, &Status);
+ if (Status != STATUS_SUCCESS) {
+ return Status;
+ }
+
+ //
+ // Then pop each register in sequence.
+ //
+
+ for (RegIndex = 0; RegIndex < 15; RegIndex++) {
+ if ((RegMask & (1 << RegIndex)) != 0) {
+ UPDATE_CONTEXT_POINTERS(UnwindParams, RegIndex, ContextRecord->Sp);
+ CONTEXT_REGISTER(ContextRecord, RegIndex) =
+ MEMORY_READ_DWORD(UnwindParams, ContextRecord->Sp);
+ ContextRecord->Sp += 4;
+ }
+ }
+
+ //
+ // If we popped LR, move it to the PC.
+ //
+
+ if ((RegMask & 0x4000) != 0) {
+ ContextRecord->Pc = ContextRecord->Lr;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+FORCEINLINE
+BOOLEAN
+RtlpCheckCondition(
+ __in PT_CONTEXT ContextRecord,
+ __in ULONG Condition
+ )
+
+/*++
+
+Routine Description:
+
+ Checks the condition codes against the provided condition, and determines
+ whether or not the instruction will be executed.
+
+Arguments:
+
+ ContextRecord - Supplies the address of a context record.
+
+ Condition - The condition to test (only low 4 bits matter).
+
+Return Value:
+
+ TRUE if the condition is met; FALSE otherwise.
+
+--*/
+
+{
+ return (ConditionTable[Condition & 0xf] >> (ContextRecord->Cpsr >> 28)) & 1;
+}
+
+ULONG
+RtlpComputeScopeSize(
+ __in ULONG UnwindCodePtr,
+ __in ULONG UnwindCodesEndPtr,
+ __in BOOLEAN IsEpilog,
+ __in PVOID UnwindParams
+ )
+
+/*++
+
+Routine Description:
+
+ Computes the size of an prolog or epilog
+
+Arguments:
+
+ UnwindCodePtr - Supplies a pointer to the start of the unwind
+ code sequence.
+
+ UnwindCodesEndPtr - Supplies a pointer to the byte immediately
+ following the unwind code table, as described by the header.
+
+ IsEpilog - Specifies TRUE if the scope describes an epilog,
+ or FALSE if it describes a prolog.
+
+ UnwindParams - Additional parameters shared with caller.
+
+Return Value:
+
+ The size of the scope described by the unwind codes, in halfword units.
+
+--*/
+
+{
+ ULONG ScopeSize;
+ BYTE TableValue;
+ BYTE Opcode;
+
+ //
+ // Iterate through the unwind codes until we hit an end marker.
+ // While iterating, accumulate the total scope size.
+ //
+
+ ScopeSize = 0;
+ Opcode = 0;
+ while (UnwindCodePtr < UnwindCodesEndPtr && (Opcode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr)) < 0xfd) {
+ TableValue = UnwindOpTable[Opcode];
+ ScopeSize += TableValue >> 4;
+ UnwindCodePtr += TableValue & 0xf;
+ }
+
+ //
+ // Handle the special epilog-only end codes.
+ //
+
+ if (Opcode >= 0xfd && Opcode <= 0xfe && IsEpilog) {
+ ScopeSize += Opcode - 0xfc;
+ }
+ return ScopeSize;
+}
+
+
+HRESULT
+RtlpUnwindFunctionCompact(
+ __in ULONG ControlPcRva,
+ __in PIMAGE_ARM_RUNTIME_FUNCTION_ENTRY FunctionEntry,
+ __inout PT_CONTEXT ContextRecord,
+ __out PULONG EstablisherFrame,
+ __deref_opt_out_opt PEXCEPTION_ROUTINE *HandlerRoutine,
+ __out PVOID *HandlerData,
+ __in PVOID UnwindParams
+ )
+{
+ ULONG CBit;
+ ULONG ComputeFramePointerLength;
+ ULONG CurrentOffset;
+ ULONG EpilogLength;
+ ULONG FunctionLength;
+ ULONG HBit;
+ ULONG OffsetInFunction;
+ ULONG OffsetInScope;
+ ULONG PopMask;
+ ULONG PrologLength;
+ ULONG PushMask;
+ ULONG PushPopParamsLength;
+ ULONG PushPopFloatingPointLength;
+ ULONG PushPopIntegerLength;
+ ULONG RetBits;
+ ULONG ReturnLength;
+ ULONG ScopeStart;
+ ULONG StackAdjustLength;
+ ULONG StackAdjust;
+ NTSTATUS Status;
+ ULONG UnwindData;
+ ULONG VfpSaveCount;
+
+ UnwindData = FunctionEntry->UnwindData;
+ Status = STATUS_SUCCESS;
+
+ //
+ // Compact records always describe an unwind to a call.
+ //
+
+ ContextRecord->ContextFlags |= CONTEXT_UNWOUND_TO_CALL;
+
+ //
+ // Extract the basic information about how to do a full unwind.
+ //
+
+ FunctionLength = (UnwindData >> 2) & 0x7ff;
+ RetBits = (UnwindData >> 13) & 3;
+ HBit = (UnwindData >> 15) & 1;
+ CBit = (UnwindData >> 21) & 1;
+ StackAdjust = (UnwindData >> 22) & 0x3ff;
+
+ //
+ // Determine push/pop masks based on this information. This comes
+ // from a mix of the C, L, R, and Reg fields.
+ //
+
+ VfpSaveCount = RegisterMaskLookup[(UnwindData >> 16) & 0x3f];
+ PushMask = PopMask = VfpSaveCount & 0xffff;
+ VfpSaveCount >>= 16;
+
+ //
+ // Move LR->PC for the pop case if the Ret field is 0. This must be
+ // accurate so that the opcode size computation below is correct.
+ //
+
+ if (RetBits == 0) {
+ _ASSERTE((PopMask & 0x4000) != 0);
+ PopMask = (PopMask & ~0x4000) | 0x8000;
+ }
+
+ //
+ // If the stack adjustment is folded into the push/pop, encode this
+ // by setting one of the low 4 bits of the push/pop mask and recovering
+ // the actual stack adjustment.
+ //
+
+ if (StackAdjust >= 0x3f4) {
+ PushMask |= StackAdjust & 4;
+ PopMask |= StackAdjust & 8;
+ StackAdjust = (StackAdjust & 3) + 1;
+ }
+
+ //
+ // If we're near the start of the function (within 9 halfwords),
+ // see if we are within the prolog.
+ //
+ // N.B. If the low 2 bits of the UnwindData are 2, then we have
+ // no prolog.
+ //
+
+ OffsetInFunction = (ControlPcRva - (FunctionEntry->BeginAddress & ~1)) / 2;
+ OffsetInScope = 0;
+ if (OffsetInFunction < 9 && (UnwindData & 3) != 2) {
+
+ //
+ // Compute sizes for each opcode in the prolog.
+ //
+
+ PushPopParamsLength = (HBit != 0) ? 1 : 0;
+ PushPopIntegerLength = (PushMask == 0) ? 0 :
+ ((PushMask & 0xbf00) == 0) ? 1 : 2;
+ ComputeFramePointerLength = (CBit == 0) ? 0 :
+ ((PushMask & ~0x4800) == 0) ? 1 : 2;
+ PushPopFloatingPointLength = (VfpSaveCount != 0) ? 2 : 0;
+ StackAdjustLength = (StackAdjust == 0 || (PushMask & 4) != 0) ? 0 :
+ (StackAdjust < 0x80) ? 1 : 2;
+
+ //
+ // Compute the total prolog length and determine if we are within
+ // its scope.
+ //
+ // N.B. We must execute prolog operations backwards to unwind, so
+ // our final scope offset in this case is the distance from the end.
+ //
+
+ PrologLength = PushPopParamsLength +
+ PushPopIntegerLength +
+ ComputeFramePointerLength +
+ PushPopFloatingPointLength +
+ StackAdjustLength;
+
+ if (OffsetInFunction < PrologLength) {
+ OffsetInScope = PrologLength - OffsetInFunction;
+ }
+ }
+
+ //
+ // If we're near the end of the function (within 8 halfwords), see if
+ // we are within the epilog.
+ //
+ // N.B. If Ret == 3, then we have no epilog.
+ //
+
+ if (OffsetInScope == 0 && OffsetInFunction + 8 >= FunctionLength && RetBits != 3) {
+
+ //
+ // Compute sizes for each opcode in the epilog.
+ //
+
+ StackAdjustLength = (StackAdjust == 0 || (PopMask & 8) != 0) ? 0 :
+ (StackAdjust < 0x80) ? 1 : 2;
+ PushPopFloatingPointLength = (VfpSaveCount != 0) ? 2 : 0;
+ ComputeFramePointerLength = 0;
+ PushPopIntegerLength = (PopMask == 0 || (HBit != 0 && RetBits == 0 && PopMask == 0x8000)) ? 0 :
+ ((PopMask & 0x7f00) == 0) ? 1 : 2;
+ PushPopParamsLength = (HBit == 0) ? 0 : (RetBits == 0) ? 2 : 1;
+ ReturnLength = RetBits;
+
+ //
+ // Compute the total epilog length and determine if we are within
+ // its scope.
+ //
+
+ EpilogLength = StackAdjustLength +
+ PushPopFloatingPointLength +
+ PushPopIntegerLength +
+ PushPopParamsLength +
+ ReturnLength;
+
+ ScopeStart = FunctionLength - EpilogLength;
+ if (OffsetInFunction > ScopeStart) {
+ OffsetInScope = OffsetInFunction - ScopeStart;
+ PushMask = PopMask & 0x1fff;
+ if (HBit == 0) {
+ PushMask |= ((PopMask >> 1) & 0x4000);
+ }
+ }
+ }
+
+ //
+ // Process operations backwards, in the order: stack deallocation,
+ // VFP register popping, integer register popping, parameter home
+ // area recovery.
+ //
+ // First case is simple: we process everything with no regard for
+ // the current offset within the scope.
+ //
+
+ if (OffsetInScope == 0) {
+
+ ContextRecord->Sp += 4 * StackAdjust;
+ if (VfpSaveCount != 0) {
+ Status = RtlpPopVfpRegisterRange(ContextRecord, 8, 8 + VfpSaveCount - 1, UnwindParams);
+ }
+ PushMask &= 0xfff0;
+ if (PushMask != 0) {
+ Status = RtlpPopRegisterMask(ContextRecord, (WORD)PushMask, UnwindParams);
+ }
+ if (HBit != 0) {
+ ContextRecord->Sp += 4 * 4;
+ }
+ }
+
+ //
+ // Second case is more complex: we must step along each operation
+ // to ensure it should be executed.
+ //
+
+ else {
+
+ CurrentOffset = 0;
+ if (CurrentOffset >= OffsetInScope && StackAdjustLength != 0) {
+ ContextRecord->Sp += 4 * StackAdjust;
+ }
+ CurrentOffset += StackAdjustLength;
+
+ if (CurrentOffset >= OffsetInScope && PushPopFloatingPointLength != 0) {
+ Status = RtlpPopVfpRegisterRange(ContextRecord, 8, 8 + VfpSaveCount - 1, UnwindParams);
+ }
+ CurrentOffset += PushPopFloatingPointLength;
+
+ //
+ // N.B. We don't need to undo any side effects of frame pointer linkage
+ //
+
+ CurrentOffset += ComputeFramePointerLength;
+
+ //
+ // N.B. In the epilog case above, we copied PopMask to PushMask
+ //
+
+ if (CurrentOffset >= OffsetInScope && PushPopIntegerLength != 0) {
+ PushMask &= 0xfff0;
+ Status = RtlpPopRegisterMask(ContextRecord, (WORD)PushMask, UnwindParams);
+ if (StackAdjustLength == 0) {
+ ContextRecord->Sp += 4 * StackAdjust;
+ }
+ }
+ CurrentOffset += PushPopIntegerLength;
+
+ //
+ // N.B. In the epilog case, we also need to pop the return address
+ //
+
+ if (CurrentOffset >= OffsetInScope && PushPopParamsLength != 0) {
+ if (PushPopParamsLength == 2) {
+ Status = RtlpPopRegisterMask(ContextRecord, 1 << 14, UnwindParams);
+ }
+ ContextRecord->Sp += 4 * 4;
+ }
+ }
+
+ //
+ // If we succeeded, post-process the results a bit
+ //
+
+ if (Status == STATUS_SUCCESS) {
+
+ //
+ // Since we always POP to the LR, recover the final PC from there.
+ // Also set the establisher frame equal to the final stack pointer.
+ //
+
+ ContextRecord->Pc = ContextRecord->Lr;
+ *EstablisherFrame = ContextRecord->Sp;
+
+ if (ARGUMENT_PRESENT(HandlerRoutine)) {
+ *HandlerRoutine = NULL;
+ }
+ *HandlerData = NULL;
+ }
+
+ return Status;
+}
+
+
+
+HRESULT
+RtlpUnwindFunctionFull(
+ __in ULONG ControlPcRva,
+ __in ULONG ImageBase,
+ __in PIMAGE_ARM_RUNTIME_FUNCTION_ENTRY FunctionEntry,
+ __inout PT_CONTEXT ContextRecord,
+ __out PULONG EstablisherFrame,
+ __deref_opt_out_opt PEXCEPTION_ROUTINE *HandlerRoutine,
+ __out PVOID *HandlerData,
+ __in PVOID UnwindParams
+ )
+
+/*++
+
+Routine Description:
+
+ This function virtually unwinds the specified function by parsing the
+ .xdata record to determine where in the function the provided ControlPc
+ is, and then executing unwind codes that map to the function's prolog
+ or epilog behavior.
+
+ If a context pointers record is specified (in the UnwindParams), then
+ the address where each nonvolatile register is restored from is recorded
+ in the appropriate element of the context pointers record.
+
+Arguments:
+
+ ControlPcRva - Supplies the address where control left the specified
+ function, as an offset relative to the IamgeBase.
+
+ ImageBase - Supplies the base address of the image that contains the
+ function being unwound.
+
+ FunctionEntry - Supplies the address of the function table entry for the
+ specified function. If appropriate, this should have already been
+ probed.
+
+ ContextRecord - Supplies the address of a context record.
+
+ EstablisherFrame - Supplies a pointer to a variable that receives the
+ the establisher frame pointer value.
+
+ HandlerRoutine - Supplies an optional pointer to a variable that receives
+ the handler routine address. If control did not leave the specified
+ function in either the prolog or an epilog and a handler of the
+ proper type is associated with the function, then the address of the
+ language specific exception handler is returned. Otherwise, NULL is
+ returned.
+
+ HandlerData - Supplies a pointer to a variable that receives a pointer
+ the the language handler data.
+
+ UnwindParams - Additional parameters shared with caller.
+
+Return Value:
+
+ STATUS_SUCCESS if the unwind could be completed, a failure status otherwise.
+ Unwind can only fail when validation bounds are specified.
+
+--*/
+
+{
+ ULONG CurCode;
+ ULONG EpilogScopeCount;
+ PEXCEPTION_ROUTINE ExceptionHandler;
+ PVOID ExceptionHandlerData;
+ ULONG FunctionLength;
+ ULONG HeaderWord;
+ ULONG OffsetInFunction;
+ ULONG Param;
+ ULONG ScopeNum;
+ ULONG ScopeSize;
+ ULONG ScopeStart;
+ ULONG SkipHalfwords;
+ HRESULT Status;
+ BYTE TableValue;
+ ULONG UnwindCodePtr;
+ ULONG UnwindCodesEndPtr;
+ ULONG UnwindDataPtr;
+ ULONG UnwindIndex;
+ ULONG UnwindWords;
+
+ //
+ // Unless we encounter a special frame, assume that any unwinding
+ // will return us to the return address of a call and set the flag
+ // appropriately (it will be cleared again if the special cases apply).
+ //
+
+ ContextRecord->ContextFlags |= CONTEXT_UNWOUND_TO_CALL;
+
+ //
+ // Fetch the header word from the .xdata blob
+ //
+
+ UnwindDataPtr = ImageBase + FunctionEntry->UnwindData;
+ HeaderWord = MEMORY_READ_DWORD(UnwindParams, UnwindDataPtr);
+ UnwindDataPtr += 4;
+
+ //
+ // Verify the version before we do anything else
+ //
+
+ if (((HeaderWord >> 18) & 3) != 0) {
+ return E_UNEXPECTED;
+ }
+
+ FunctionLength = HeaderWord & 0x3ffff;
+ OffsetInFunction = (ControlPcRva - (FunctionEntry->BeginAddress & ~1)) / 2;
+
+ if (OffsetInFunction >= FunctionLength) {
+ return E_UNEXPECTED;
+ }
+
+ //
+ // Determine the number of epilog scope records and the maximum number
+ // of unwind codes.
+ //
+
+ UnwindWords = (HeaderWord >> 28) & 15;
+ EpilogScopeCount = (HeaderWord >> 23) & 31;
+ if (EpilogScopeCount == 0 && UnwindWords == 0) {
+ EpilogScopeCount = MEMORY_READ_DWORD(UnwindParams, UnwindDataPtr);
+ UnwindDataPtr += 4;
+ UnwindWords = (EpilogScopeCount >> 16) & 0xff;
+ EpilogScopeCount &= 0xffff;
+ }
+ if ((HeaderWord & (1 << 21)) != 0) {
+ UnwindIndex = EpilogScopeCount;
+ EpilogScopeCount = 0;
+ }
+
+ //
+ // If exception data is present, extract it now.
+ //
+
+ ExceptionHandler = NULL;
+ ExceptionHandlerData = NULL;
+ if ((HeaderWord & (1 << 20)) != 0) {
+ ExceptionHandler = (PEXCEPTION_ROUTINE)(ImageBase +
+ MEMORY_READ_DWORD(UnwindParams, UnwindDataPtr + 4 * (EpilogScopeCount + UnwindWords)));
+ ExceptionHandlerData = (PVOID)(UnwindDataPtr + 4 * (EpilogScopeCount + UnwindWords + 1));
+ }
+
+ //
+ // Unless we are in a prolog/epilog, we execute the unwind codes
+ // that immediately follow the epilog scope list.
+ //
+
+ UnwindCodePtr = UnwindDataPtr + 4 * EpilogScopeCount;
+ UnwindCodesEndPtr = UnwindCodePtr + 4 * UnwindWords;
+ SkipHalfwords = 0;
+
+ //
+ // If we're near the start of the function, and this function has a prolog,
+ // compute the size of the prolog from the unwind codes. If we're in the
+ // midst of it, we still execute starting at unwind code index 0, but we may
+ // need to skip some to account for partial execution of the prolog.
+ //
+
+ if (OffsetInFunction < MAX_PROLOG_SIZE && ((HeaderWord & (1 << 22)) == 0)) {
+ ScopeSize = RtlpComputeScopeSize(UnwindCodePtr, UnwindCodesEndPtr, FALSE, UnwindParams);
+
+ if (OffsetInFunction < ScopeSize) {
+ SkipHalfwords = ScopeSize - OffsetInFunction;
+ ExceptionHandler = NULL;
+ ExceptionHandlerData = NULL;
+ goto ExecuteCodes;
+ }
+ }
+
+ //
+ // We're not in the prolog, now check to see if we are in the epilog.
+ // In the simple case, the 'E' bit is set indicating there is a single
+ // epilog that lives at the end of the function. If we're near the end
+ // of the function, compute the actual size of the epilog from the
+ // unwind codes. If we're in the midst of it, adjust the unwind code
+ // pointer to the start of the codes and determine how many we need to skip.
+ //
+
+ if ((HeaderWord & (1 << 21)) != 0) {
+ if (OffsetInFunction + MAX_EPILOG_SIZE >= FunctionLength) {
+ ScopeSize = RtlpComputeScopeSize(UnwindCodePtr + UnwindIndex, UnwindCodesEndPtr, TRUE, UnwindParams);
+ ScopeStart = FunctionLength - ScopeSize;
+
+ if (OffsetInFunction >= ScopeStart) {
+ UnwindCodePtr += UnwindIndex;
+ SkipHalfwords = OffsetInFunction - ScopeStart;
+ ExceptionHandler = NULL;
+ ExceptionHandlerData = NULL;
+ }
+ }
+ }
+
+ //
+ // In the multiple-epilog case, we scan forward to see if we are within
+ // shooting distance of any of the epilogs. If we are, we compute the
+ // actual size of the epilog from the unwind codes and proceed like the
+ // simple case above.
+ //
+
+ else {
+ for (ScopeNum = 0; ScopeNum < EpilogScopeCount; ScopeNum++) {
+ HeaderWord = MEMORY_READ_DWORD(UnwindParams, UnwindDataPtr);
+ UnwindDataPtr += 4;
+
+ //
+ // The scope records are stored in order. If we hit a record that
+ // starts after our current position, we must not be in an epilog.
+ //
+
+ ScopeStart = HeaderWord & 0x3ffff;
+ if (OffsetInFunction < ScopeStart) {
+ break;
+ }
+
+ if (OffsetInFunction < ScopeStart + MAX_EPILOG_SIZE) {
+ UnwindIndex = HeaderWord >> 24;
+ ScopeSize = RtlpComputeScopeSize(UnwindCodePtr + UnwindIndex, UnwindCodesEndPtr, TRUE, UnwindParams);
+
+ if (RtlpCheckCondition(ContextRecord, HeaderWord >> 20) &&
+ OffsetInFunction < ScopeStart + ScopeSize) {
+
+ UnwindCodePtr += UnwindIndex;
+ SkipHalfwords = OffsetInFunction - ScopeStart;
+ ExceptionHandler = NULL;
+ ExceptionHandlerData = NULL;
+ break;
+ }
+ }
+ }
+ }
+
+ExecuteCodes:
+
+ //
+ // Skip over unwind codes until we account for the number of halfwords
+ // to skip.
+ //
+
+ while (UnwindCodePtr < UnwindCodesEndPtr && SkipHalfwords > 0) {
+ CurCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ if (CurCode >= 0xfd) {
+ break;
+ }
+ TableValue = UnwindOpTable[CurCode];
+ SkipHalfwords -= TableValue >> 4;
+ UnwindCodePtr += TableValue & 0xf;
+ }
+
+ //
+ // Now execute codes until we hit the end.
+ //
+
+ Status = STATUS_SUCCESS;
+ while (UnwindCodePtr < UnwindCodesEndPtr && Status == STATUS_SUCCESS) {
+
+ CurCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ UnwindCodePtr++;
+
+ //
+ // 0x00-0x7f: 2-byte stack adjust ... add sp, sp, #0xval
+ //
+
+ if (CurCode < 0x80) {
+ ContextRecord->Sp += (CurCode & 0x7f) * 4;
+ }
+
+ //
+ // 0x80-0xbf: 4-byte bitmasked pop ... pop {r0-r12, lr}
+ //
+
+ else if (CurCode < 0xc0) {
+ if (UnwindCodePtr >= UnwindCodesEndPtr) {
+ Status = E_FAIL;
+ } else {
+ Param = ((CurCode & 0x20) << 9) |
+ ((CurCode & 0x1f) << 8) |
+ MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ UnwindCodePtr++;
+ Status = RtlpPopRegisterMask(ContextRecord, (WORD)Param, UnwindParams);
+ }
+ }
+
+ //
+ // 0xc0-0xcf: 2-byte stack restore ... mov sp, rX
+ //
+
+ else if (CurCode < 0xd0) {
+ ContextRecord->Sp = CONTEXT_REGISTER(ContextRecord, CurCode & 0x0f);
+ }
+
+ else {
+ switch (CurCode) {
+
+ //
+ // 0xd0-0xd7: 2-byte range pop ... pop {r4-r7, lr}
+ //
+
+ case 0xd0: case 0xd1: case 0xd2: case 0xd3:
+ case 0xd4: case 0xd5: case 0xd6: case 0xd7:
+ Status = RtlpPopRegisterMask(ContextRecord,
+ RtlpRangeToMask(4, 4 + (CurCode & 3), CurCode & 4),
+ UnwindParams);
+ break;
+
+ //
+ // 0xd8-0xdf: 4-byte range pop ... pop {r4-r11, lr}
+ //
+
+ case 0xd8: case 0xd9: case 0xda: case 0xdb:
+ case 0xdc: case 0xdd: case 0xde: case 0xdf:
+ Status = RtlpPopRegisterMask(ContextRecord,
+ RtlpRangeToMask(4, 8 + (CurCode & 3), CurCode & 4),
+ UnwindParams);
+ break;
+
+ //
+ // 0xe0-0xe7: 4-byte range vpop ... vpop {d8-d15}
+ //
+
+ case 0xe0: case 0xe1: case 0xe2: case 0xe3:
+ case 0xe4: case 0xe5: case 0xe6: case 0xe7:
+ Status = RtlpPopVfpRegisterRange(ContextRecord,
+ 8, 8 + (CurCode & 0x07),
+ UnwindParams);
+ break;
+
+ //
+ // 0xe8-0xeb: 4-byte stack adjust ... addw sp, sp, #0xval
+ //
+
+ case 0xe8: case 0xe9: case 0xea: case 0xeb:
+ if (UnwindCodePtr >= UnwindCodesEndPtr) {
+ Status = E_FAIL;
+ break;
+ }
+ ContextRecord->Sp += 4 * 256 * (CurCode & 3);
+ ContextRecord->Sp += 4 * MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ UnwindCodePtr++;
+ break;
+
+ //
+ // 0xec-0xed: 2-byte bitmasked pop ... pop {r0-r7,lr}
+ //
+
+ case 0xec: case 0xed:
+ if (UnwindCodePtr >= UnwindCodesEndPtr) {
+ Status = E_FAIL;
+ break;
+ }
+ Status = RtlpPopRegisterMask(ContextRecord,
+ MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr)
+ | ((CurCode << 14) & 0x4000),
+ UnwindParams);
+ UnwindCodePtr++;
+ break;
+
+ //
+ // 0xee: 0-byte custom opcode
+ //
+
+ case 0xee:
+ if (UnwindCodePtr >= UnwindCodesEndPtr) {
+ Status = E_FAIL;
+ break;
+ }
+ Param = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ UnwindCodePtr++;
+ if ((Param & 0xf0) == 0x00) {
+ Status = RtlpUnwindCustom(ContextRecord,
+ Param & 0x0f,
+ UnwindParams);
+ } else {
+ Status = E_FAIL;
+ }
+ break;
+
+ //
+ // 0xef: 4-byte stack restore with post-increment ... ldr pc, [sp], #X
+ //
+
+ case 0xef:
+ if (UnwindCodePtr >= UnwindCodesEndPtr) {
+ Status = E_FAIL;
+ break;
+ }
+ Param = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ UnwindCodePtr++;
+ if ((Param & 0xf0) == 0x00) {
+ Status = RtlpPopRegisterMask(ContextRecord,
+ 0x4000,
+ UnwindParams);
+ ContextRecord->Sp += ((Param & 15) - 1) * 4;
+ } else {
+ Status = E_FAIL;
+ }
+ break;
+
+ //
+ // 0xf5: 4-byte range vpop ... vpop {d0-d15}
+ //
+
+ case 0xf5:
+ if (UnwindCodePtr >= UnwindCodesEndPtr) {
+ Status = E_FAIL;
+ break;
+ }
+ Param = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ UnwindCodePtr++;
+ Status = RtlpPopVfpRegisterRange(ContextRecord,
+ Param >> 4, Param & 0x0f,
+ UnwindParams);
+ break;
+
+ //
+ // 0xf6: 4-byte range vpop ... vpop {d16-d31}
+ //
+
+ case 0xf6:
+ if (UnwindCodePtr >= UnwindCodesEndPtr) {
+ Status = E_FAIL;
+ break;
+ }
+ Param = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ UnwindCodePtr++;
+ Status = RtlpPopVfpRegisterRange(ContextRecord,
+ 16 + (Param >> 4), 16 + (Param & 0x0f),
+ UnwindParams);
+ break;
+
+ //
+ // 0xf7: 2-byte stack adjust ... add sp, sp, <reg>
+ // 0xf9: 4-byte stack adjust ... add sp, sp, <reg>
+ //
+
+ case 0xf7:
+ case 0xf9:
+ if (UnwindCodePtr + 2 > UnwindCodesEndPtr) {
+ Status = E_FAIL;
+ break;
+ }
+ ContextRecord->Sp += 4 * 256 * MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ ContextRecord->Sp += 4 * MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr + 1);
+ UnwindCodePtr += 2;
+ break;
+
+ //
+ // 0xf8: 2-byte stack adjust ... add sp, sp, <reg>
+ // 0xfa: 4-byte stack adjust ... add sp, sp, <reg>
+ //
+
+ case 0xf8:
+ case 0xfa:
+ if (UnwindCodePtr + 3 > UnwindCodesEndPtr) {
+ Status = E_FAIL;
+ break;
+ }
+ ContextRecord->Sp += 4 * 256 * 256 * MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ ContextRecord->Sp += 4 * 256 * MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr + 1);
+ ContextRecord->Sp += 4 * MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr + 2);
+ UnwindCodePtr += 3;
+ break;
+
+ //
+ // 0xfb: 2-byte no-op/misc instruction
+ // 0xfc: 4-byte no-op/misc instruction
+ //
+
+ case 0xfb:
+ case 0xfc:
+ break;
+
+ //
+ // 0xfd: 2-byte end (epilog)
+ // 0xfe: 4-byte end (epilog)
+ // 0xff: generic end
+ //
+
+ case 0xfd:
+ case 0xfe:
+ case 0xff:
+ goto finished;
+
+ default:
+ Status = E_FAIL;
+ break;
+ }
+ }
+ }
+
+ //
+ // If we succeeded, post-process the results a bit
+ //
+finished:
+ if (Status == STATUS_SUCCESS) {
+
+ //
+ // Since we always POP to the LR, recover the final PC from there, unless
+ // it was overwritten due to a special case custom unwinding operation.
+ // Also set the establisher frame equal to the final stack pointer.
+ //
+
+ if ((ContextRecord->ContextFlags & CONTEXT_UNWOUND_TO_CALL) != 0) {
+ ContextRecord->Pc = ContextRecord->Lr;
+ }
+ *EstablisherFrame = ContextRecord->Sp;
+
+ if (ARGUMENT_PRESENT(HandlerRoutine)) {
+ *HandlerRoutine = ExceptionHandler;
+ }
+ *HandlerData = ExceptionHandlerData;
+ }
+
+ return Status;
+}
+
+
+BOOL OOPStackUnwinderArm::Unwind(T_CONTEXT * pContext)
+{
+ DWORD64 ImageBase = 0;
+ HRESULT hr = GetModuleBase(DBS_EXTEND64(pContext->Pc), &ImageBase);
+ if (hr != S_OK)
+ return FALSE;
+
+ PEXCEPTION_ROUTINE DummyHandlerRoutine;
+ PVOID DummyHandlerData;
+ ULONG DummyEstablisherFrame;
+
+ DWORD startingPc = pContext->Pc;
+ DWORD startingSp = pContext->Sp;
+
+ IMAGE_ARM_RUNTIME_FUNCTION_ENTRY Rfe;
+ if (FAILED(GetFunctionEntry(DBS_EXTEND64(pContext->Pc), &Rfe, sizeof(Rfe))))
+ return FALSE;
+
+ if ((Rfe.UnwindData & 3) != 0)
+ {
+ hr = RtlpUnwindFunctionCompact(pContext->Pc - (ULONG)ImageBase,
+ &Rfe,
+ pContext,
+ &DummyEstablisherFrame,
+ &DummyHandlerRoutine,
+ &DummyHandlerData,
+ NULL);
+
+ }
+ else
+ {
+ hr = RtlpUnwindFunctionFull(pContext->Pc - (ULONG)ImageBase,
+ (ULONG)ImageBase,
+ &Rfe,
+ pContext,
+ &DummyEstablisherFrame,
+ &DummyHandlerRoutine,
+ &DummyHandlerData,
+ NULL);
+ }
+
+ // PC == 0 means unwinding is finished.
+ // Same if no forward progress is made
+ if (pContext->Pc == 0 || (startingPc == pContext->Pc && startingSp == pContext->Sp))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+BOOL DacUnwindStackFrame(T_CONTEXT *pContext, T_KNONVOLATILE_CONTEXT_POINTERS* pContextPointers)
+{
+ OOPStackUnwinderArm unwinder;
+ BOOL res = unwinder.Unwind(pContext);
+
+ if (res && pContextPointers)
+ {
+ for (int i = 0; i < 8; i++)
+ {
+ *(&pContextPointers->R4 + i) = &pContext->R4 + i;
+ }
+ }
+
+ return res;
+}
diff --git a/src/unwinder/arm/unwinder_arm.h b/src/unwinder/arm/unwinder_arm.h
new file mode 100644
index 0000000000..7eb5d7a065
--- /dev/null
+++ b/src/unwinder/arm/unwinder_arm.h
@@ -0,0 +1,55 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//
+
+#ifndef __unwinder_arm__
+#define __unwinder_arm__
+
+#include "unwinder.h"
+
+
+//---------------------------------------------------------------------------------------
+//
+// See the comment for the base class code:OOPStackUnwinder.
+//
+
+class OOPStackUnwinderArm : public OOPStackUnwinder
+{
+public:
+ // Unwind the given CONTEXT to the caller CONTEXT. The CONTEXT will be overwritten.
+ BOOL Unwind(T_CONTEXT * pContext);
+
+ //
+ // Everything below comes from dbghelp.dll.
+ //
+
+protected:
+ HRESULT UnwindPrologue(__in DWORD64 ImageBase,
+ __in DWORD64 ControlPc,
+ __in DWORD64 FrameBase,
+ __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry,
+ __inout PT_CONTEXT ContextRecord);
+
+ HRESULT VirtualUnwind(__in DWORD64 ImageBase,
+ __in DWORD64 ControlPc,
+ __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry,
+ __inout PT_CONTEXT ContextRecord,
+ __out PDWORD64 EstablisherFrame);
+
+ DWORD64 LookupPrimaryUnwindInfo
+ (__in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry,
+ __in DWORD64 ImageBase,
+ __out _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryEntry);
+
+ _PIMAGE_RUNTIME_FUNCTION_ENTRY SameFunction
+ (__in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry,
+ __in DWORD64 ImageBase,
+ __in DWORD64 ControlPc,
+ __out _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionReturnBuffer);
+};
+
+#endif // __unwinder_arm__
+
diff --git a/src/unwinder/arm64/.gitmirror b/src/unwinder/arm64/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/unwinder/arm64/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/unwinder/arm64/unwinder_arm64.cpp b/src/unwinder/arm64/unwinder_arm64.cpp
new file mode 100644
index 0000000000..a56e42469e
--- /dev/null
+++ b/src/unwinder/arm64/unwinder_arm64.cpp
@@ -0,0 +1,1580 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//
+
+#include "stdafx.h"
+#include "utilcode.h"
+#include "crosscomp.h"
+
+#include "unwinder_arm64.h"
+
+typedef struct _ARM64_KTRAP_FRAME {
+
+//
+// Exception active indicator.
+//
+// 0 - interrupt frame.
+// 1 - exception frame.
+// 2 - service frame.
+//
+
+ /* +0x000 */ UCHAR ExceptionActive; // always valid
+ /* +0x001 */ UCHAR ContextFromKFramesUnwound; // set if KeContextFromKFrames created this frame
+ /* +0x002 */ UCHAR DebugRegistersValid; // always valid
+ /* +0x003 */ union {
+ UCHAR PreviousMode; // system services only
+ UCHAR PreviousIrql; // interrupts only
+ };
+
+//
+// Page fault information (page faults only)
+// Previous trap frame address (system services only)
+//
+// Organized this way to allow first couple words to be used
+// for scratch space in the general case
+//
+
+ /* +0x004 */ ULONG FaultStatus; // page faults only
+ /* +0x008 */ union {
+ ULONG64 FaultAddress; // page faults only
+ ULONG64 TrapFrame; // system services only
+ };
+
+//
+// The ARM architecture does not have an architectural trap frame. On
+// an exception or interrupt, the processor switches to an
+// exception-specific processor mode in which at least the LR and SP
+// registers are banked. Software is responsible for preserving
+// registers which reflect the processor state in which the
+// exception occurred rather than any intermediate processor modes.
+//
+
+//
+// Volatile floating point state is dynamically allocated; this
+// pointer may be NULL if the FPU was not enabled at the time the
+// trap was taken.
+//
+
+ /* +0x010 */ PVOID VfpState;
+
+//
+// Debug registers
+//
+
+ /* +0x018 */ ULONG Bcr[ARM64_MAX_BREAKPOINTS];
+ /* +0x038 */ ULONG64 Bvr[ARM64_MAX_BREAKPOINTS];
+ /* +0x078 */ ULONG Wcr[ARM64_MAX_WATCHPOINTS];
+ /* +0x080 */ ULONG64 Wvr[ARM64_MAX_WATCHPOINTS];
+
+//
+// Volatile registers X0-X17, and the FP, SP, LR
+//
+
+ /* +0x090 */ ULONG Spsr;
+ /* +0x094 */ ULONG Esr;
+ /* +0x098 */ ULONG64 Sp;
+ /* +0x0A0 */ ULONG64 X[19];
+ /* +0x138 */ ULONG64 Lr;
+ /* +0x140 */ ULONG64 Fp;
+ /* +0x148 */ ULONG64 Pc;
+ /* +0x150 */
+
+} ARM64_KTRAP_FRAME, *PARM64_KTRAP_FRAME;
+
+typedef struct _ARM64_VFP_STATE
+{
+ struct _ARM64_VFP_STATE *Link; // link to next state entry
+ ULONG Fpcr; // FPCR register
+ ULONG Fpsr; // FPSR register
+ NEON128 V[32]; // All V registers (0-31)
+} ARM64_VFP_STATE, *PARM64_VFP_STATE, KARM64_VFP_STATE, *PKARM64_VFP_STATE;
+
+//
+// Parameters describing the unwind codes.
+//
+
+#define STATUS_UNWIND_UNSUPPORTED_VERSION STATUS_UNSUCCESSFUL
+#define STATUS_UNWIND_NOT_IN_FUNCTION STATUS_UNSUCCESSFUL
+#define STATUS_UNWIND_INVALID_SEQUENCE STATUS_UNSUCCESSFUL
+
+//
+// Macros for accessing memory. These can be overridden if other code
+// (in particular the debugger) needs to use them.
+
+#define MEMORY_READ_BYTE(params, addr) (*(const BYTE *)(addr))
+#define MEMORY_READ_DWORD(params, addr) (*(const DWORD *)(addr))
+#define MEMORY_READ_QWORD(params, addr) (*(const UINT64 *)(addr))
+
+typedef struct _ARM64_UNWIND_PARAMS
+{
+ ULONG_PTR ControlPc;
+ PULONG_PTR LowLimit;
+ PULONG_PTR HighLimit;
+ PKNONVOLATILE_CONTEXT_POINTERS ContextPointers;
+} ARM64_UNWIND_PARAMS, *PARM64_UNWIND_PARAMS;
+
+#define UNWIND_PARAMS_SET_TRAP_FRAME(Params, Address, Size)
+
+#define UPDATE_CONTEXT_POINTERS(Params, RegisterNumber, Address)
+#define UPDATE_FP_CONTEXT_POINTERS(Params, RegisterNumber, Address)
+#define VALIDATE_STACK_ADDRESS_EX(Params, Context, Address, DataSize, Alignment, OutStatus)
+#define VALIDATE_STACK_ADDRESS(Params, Context, DataSize, Alignment, OutStatus)
+
+//
+// Macros to clarify opcode parsing
+//
+
+#define OPCODE_IS_END(Op) (((Op) & 0xfe) == 0xe4)
+
+//
+// This table describes the size of each unwind code, in bytes
+//
+
+static const BYTE UnwindCodeSizeTable[256] =
+{
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,
+ 4,1,2,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1
+};
+
+NTSTATUS
+RtlpUnwindCustom(
+ __inout PT_CONTEXT ContextRecord,
+ __in BYTE Opcode,
+ __in PARM64_UNWIND_PARAMS UnwindParams
+ )
+
+/*++
+
+Routine Description:
+
+ Handles custom unwinding operations involving machine-specific
+ frames.
+
+Arguments:
+
+ ContextRecord - Supplies the address of a context record.
+
+ Opcode - The opcode to decode.
+
+ UnwindParams - Additional parameters shared with caller.
+
+Return Value:
+
+ An NTSTATUS indicating either STATUS_SUCCESS if everything went ok, or
+ another status code if there were problems.
+
+--*/
+
+{
+ ULONG Fpcr;
+ ULONG Fpsr;
+ ULONG RegIndex;
+ ULONG_PTR SourceAddress;
+ ULONG_PTR StartingSp;
+ NTSTATUS Status;
+ ULONG_PTR VfpStateAddress;
+
+ StartingSp = ContextRecord->Sp;
+ Status = STATUS_SUCCESS;
+
+ //
+ // The opcode describes the special-case stack
+ //
+
+ switch (Opcode)
+ {
+
+ //
+ // Trap frame case
+ //
+
+ case 0xe8: // MSFT_OP_TRAP_FRAME:
+
+ //
+ // Ensure there is enough valid space for the trap frame
+ //
+
+ VALIDATE_STACK_ADDRESS(UnwindParams, ContextRecord, sizeof(ARM64_KTRAP_FRAME), 16, &Status);
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ //
+ // Restore X0-X17, and D0-D7
+ //
+
+ SourceAddress = StartingSp + FIELD_OFFSET(ARM64_KTRAP_FRAME, X);
+ for (RegIndex = 0; RegIndex < 18; RegIndex++) {
+ UPDATE_CONTEXT_POINTERS(UnwindParams, RegIndex, SourceAddress);
+ ContextRecord->X[RegIndex] = MEMORY_READ_QWORD(UnwindParams, SourceAddress);
+ SourceAddress += sizeof(ULONG_PTR);
+ }
+
+ SourceAddress = StartingSp + FIELD_OFFSET(ARM64_KTRAP_FRAME, VfpState);
+ VfpStateAddress = MEMORY_READ_QWORD(UnwindParams, SourceAddress);
+ if (VfpStateAddress != 0) {
+
+ SourceAddress = VfpStateAddress + FIELD_OFFSET(KARM64_VFP_STATE, Fpcr);
+ Fpcr = MEMORY_READ_DWORD(UnwindParams, SourceAddress);
+ SourceAddress = VfpStateAddress + FIELD_OFFSET(KARM64_VFP_STATE, Fpsr);
+ Fpsr = MEMORY_READ_DWORD(UnwindParams, SourceAddress);
+ if (Fpcr != -1 && Fpsr != -1) {
+
+ ContextRecord->Fpcr = Fpcr;
+ ContextRecord->Fpsr = Fpsr;
+
+ SourceAddress = VfpStateAddress + FIELD_OFFSET(KARM64_VFP_STATE, V);
+ for (RegIndex = 0; RegIndex < 32; RegIndex++) {
+ UPDATE_FP_CONTEXT_POINTERS(UnwindParams, RegIndex, SourceAddress);
+ ContextRecord->V[RegIndex].Low = MEMORY_READ_QWORD(UnwindParams, SourceAddress);
+ ContextRecord->V[RegIndex].High = MEMORY_READ_QWORD(UnwindParams, SourceAddress + 8);
+ SourceAddress += 2 * sizeof(ULONGLONG);
+ }
+ }
+ }
+
+ //
+ // Restore R11, R12, SP, LR, PC, and the status registers
+ //
+
+ SourceAddress = StartingSp + FIELD_OFFSET(ARM64_KTRAP_FRAME, Spsr);
+ ContextRecord->Cpsr = MEMORY_READ_DWORD(UnwindParams, SourceAddress);
+
+ SourceAddress = StartingSp + FIELD_OFFSET(ARM64_KTRAP_FRAME, Sp);
+ ContextRecord->Sp = MEMORY_READ_QWORD(UnwindParams, SourceAddress);
+
+ SourceAddress = StartingSp + FIELD_OFFSET(ARM64_KTRAP_FRAME, Lr);
+ ContextRecord->Lr = MEMORY_READ_QWORD(UnwindParams, SourceAddress);
+
+ SourceAddress = StartingSp + FIELD_OFFSET(ARM64_KTRAP_FRAME, Fp);
+ ContextRecord->Fp = MEMORY_READ_QWORD(UnwindParams, SourceAddress);
+
+ SourceAddress = StartingSp + FIELD_OFFSET(ARM64_KTRAP_FRAME, Pc);
+ ContextRecord->Pc = MEMORY_READ_QWORD(UnwindParams, SourceAddress);
+
+ //
+ // Set the trap frame and clear the unwound-to-call flag
+ //
+
+ UNWIND_PARAMS_SET_TRAP_FRAME(UnwindParams, StartingSp, sizeof(ARM64_KTRAP_FRAME));
+ ContextRecord->ContextFlags &= ~CONTEXT_UNWOUND_TO_CALL;
+ break;
+
+ //
+ // Context case
+ //
+
+ case 0xea: // MSFT_OP_CONTEXT:
+
+ //
+ // Ensure there is enough valid space for the full CONTEXT structure
+ //
+
+ VALIDATE_STACK_ADDRESS(UnwindParams, ContextRecord, sizeof(CONTEXT), 16, &Status);
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ //
+ // Restore X0-X28, and D0-D31
+ //
+
+ SourceAddress = StartingSp + FIELD_OFFSET(T_CONTEXT, X);
+ for (RegIndex = 0; RegIndex < 29; RegIndex++) {
+ UPDATE_CONTEXT_POINTERS(UnwindParams, RegIndex, SourceAddress);
+ ContextRecord->X[RegIndex] = MEMORY_READ_QWORD(UnwindParams, SourceAddress);
+ SourceAddress += sizeof(ULONG_PTR);
+ }
+
+ SourceAddress = StartingSp + FIELD_OFFSET(T_CONTEXT, V);
+ for (RegIndex = 0; RegIndex < 32; RegIndex++) {
+ UPDATE_FP_CONTEXT_POINTERS(UnwindParams, RegIndex, SourceAddress);
+ ContextRecord->V[RegIndex].Low = MEMORY_READ_QWORD(UnwindParams, SourceAddress);
+ ContextRecord->V[RegIndex].High = MEMORY_READ_QWORD(UnwindParams, SourceAddress + 8);
+ SourceAddress += 2 * sizeof(ULONGLONG);
+ }
+
+ //
+ // Restore SP, LR, PC, and the status registers
+ //
+
+ SourceAddress = StartingSp + FIELD_OFFSET(T_CONTEXT, Cpsr);
+ ContextRecord->Cpsr = MEMORY_READ_DWORD(UnwindParams, SourceAddress);
+
+ SourceAddress = StartingSp + FIELD_OFFSET(T_CONTEXT, Fp);
+ ContextRecord->Fp = MEMORY_READ_QWORD(UnwindParams, SourceAddress);
+
+ SourceAddress = StartingSp + FIELD_OFFSET(T_CONTEXT, Lr);
+ ContextRecord->Lr = MEMORY_READ_QWORD(UnwindParams, SourceAddress);
+
+ SourceAddress = StartingSp + FIELD_OFFSET(T_CONTEXT, Sp);
+ ContextRecord->Sp = MEMORY_READ_QWORD(UnwindParams, SourceAddress);
+
+ SourceAddress = StartingSp + FIELD_OFFSET(T_CONTEXT, Pc);
+ ContextRecord->Pc = MEMORY_READ_QWORD(UnwindParams, SourceAddress);
+
+ SourceAddress = StartingSp + FIELD_OFFSET(T_CONTEXT, Fpcr);
+ ContextRecord->Fpcr = MEMORY_READ_DWORD(UnwindParams, SourceAddress);
+
+ SourceAddress = StartingSp + FIELD_OFFSET(T_CONTEXT, Fpsr);
+ ContextRecord->Fpsr = MEMORY_READ_DWORD(UnwindParams, SourceAddress);
+
+ //
+ // Inherit the unwound-to-call flag from this context
+ //
+
+ SourceAddress = StartingSp + FIELD_OFFSET(T_CONTEXT, ContextFlags);
+ ContextRecord->ContextFlags &= ~CONTEXT_UNWOUND_TO_CALL;
+ ContextRecord->ContextFlags |=
+ MEMORY_READ_DWORD(UnwindParams, SourceAddress) & CONTEXT_UNWOUND_TO_CALL;
+ break;
+
+ default:
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+ULONG
+RtlpComputeScopeSize(
+ __in ULONG_PTR UnwindCodePtr,
+ __in ULONG_PTR UnwindCodesEndPtr,
+ __in BOOLEAN IsEpilog,
+ __in PARM64_UNWIND_PARAMS UnwindParams
+ )
+
+/*++
+
+Routine Description:
+
+ Computes the size of an prolog or epilog, in words.
+
+Arguments:
+
+ UnwindCodePtr - Supplies a pointer to the start of the unwind
+ code sequence.
+
+ UnwindCodesEndPtr - Supplies a pointer to the byte immediately
+ following the unwind code table, as described by the header.
+
+ IsEpilog - Specifies TRUE if the scope describes an epilog,
+ or FALSE if it describes a prolog.
+
+ UnwindParams - Additional parameters shared with caller.
+
+Return Value:
+
+ The size of the scope described by the unwind codes, in halfword units.
+
+--*/
+
+{
+ ULONG ScopeSize;
+ BYTE Opcode;
+
+ //
+ // Iterate through the unwind codes until we hit an end marker.
+ // While iterating, accumulate the total scope size.
+ //
+
+ ScopeSize = 0;
+ Opcode = 0;
+ while (UnwindCodePtr < UnwindCodesEndPtr) {
+ Opcode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ if (OPCODE_IS_END(Opcode)) {
+ break;
+ }
+
+ UnwindCodePtr += UnwindCodeSizeTable[Opcode];
+ ScopeSize++;
+ }
+
+ //
+ // Epilogs have one extra instruction at the end that needs to be
+ // accounted for.
+ //
+
+ if (IsEpilog) {
+ ScopeSize++;
+ }
+
+ return ScopeSize;
+}
+
+NTSTATUS
+RtlpUnwindRestoreRegisterRange(
+ __inout PT_CONTEXT ContextRecord,
+ __in LONG SpOffset,
+ __in ULONG FirstRegister,
+ __in ULONG RegisterCount,
+ __in PARM64_UNWIND_PARAMS UnwindParams
+ )
+
+/*++
+
+Routine Description:
+
+ Restores a series of integer registers from the stack.
+
+Arguments:
+
+ ContextRecord - Supplies the address of a context record.
+
+ SpOffset - Specifies a stack offset. Positive values are simply used
+ as a base offset. Negative values assume a predecrement behavior:
+ a 0 offset is used for restoration, but the absoute value of the
+ offset is added to the final Sp.
+
+ FirstRegister - Specifies the index of the first register to restore.
+
+ RegisterCount - Specifies the number of registers to restore.
+
+ UnwindParams - Additional parameters shared with caller.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG_PTR CurAddress;
+ ULONG RegIndex;
+ NTSTATUS Status;
+
+ //
+ // Compute the source address and validate it.
+ //
+
+ CurAddress = ContextRecord->Sp;
+ if (SpOffset >= 0) {
+ CurAddress += SpOffset;
+ }
+
+ Status = STATUS_SUCCESS;
+ VALIDATE_STACK_ADDRESS(UnwindParams, ContextRecord, 8 * RegisterCount, 8, &Status);
+ if (Status != STATUS_SUCCESS) {
+ return Status;
+ }
+
+ //
+ // Restore the registers
+ //
+
+ for (RegIndex = 0; RegIndex < RegisterCount; RegIndex++) {
+ UPDATE_CONTEXT_POINTERS(UnwindParams, RegIndex, CurAddress);
+ ContextRecord->X[FirstRegister + RegIndex] = MEMORY_READ_QWORD(UnwindParams, CurAddress);
+ CurAddress += 8;
+ }
+ if (SpOffset < 0) {
+ ContextRecord->Sp -= SpOffset;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+RtlpUnwindRestoreFpRegisterRange(
+ __inout PT_CONTEXT ContextRecord,
+ __in LONG SpOffset,
+ __in ULONG FirstRegister,
+ __in ULONG RegisterCount,
+ __in PARM64_UNWIND_PARAMS UnwindParams
+ )
+
+/*++
+
+Routine Description:
+
+ Restores a series of floating-point registers from the stack.
+
+Arguments:
+
+ ContextRecord - Supplies the address of a context record.
+
+ SpOffset - Specifies a stack offset. Positive values are simply used
+ as a base offset. Negative values assume a predecrement behavior:
+ a 0 offset is used for restoration, but the absoute value of the
+ offset is added to the final Sp.
+
+ FirstRegister - Specifies the index of the first register to restore.
+
+ RegisterCount - Specifies the number of registers to restore.
+
+ UnwindParams - Additional parameters shared with caller.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG_PTR CurAddress;
+ ULONG RegIndex;
+ NTSTATUS Status;
+
+ //
+ // Compute the source address and validate it.
+ //
+
+ CurAddress = ContextRecord->Sp;
+ if (SpOffset >= 0) {
+ CurAddress += SpOffset;
+ }
+
+ Status = STATUS_SUCCESS;
+ VALIDATE_STACK_ADDRESS(UnwindParams, ContextRecord, 8 * RegisterCount, 8, &Status);
+ if (Status != STATUS_SUCCESS) {
+ return Status;
+ }
+
+ //
+ // Restore the registers
+ //
+
+ for (RegIndex = 0; RegIndex < RegisterCount; RegIndex++) {
+ UPDATE_FP_CONTEXT_POINTERS(UnwindParams, RegIndex, CurAddress);
+ ContextRecord->V[FirstRegister + RegIndex].Low = MEMORY_READ_QWORD(UnwindParams, CurAddress);
+ CurAddress += 8;
+ }
+ if (SpOffset < 0) {
+ ContextRecord->Sp -= SpOffset;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+RtlpUnwindFunctionFull(
+ __in DWORD64 ControlPcRva,
+ __in ULONG_PTR ImageBase,
+ __in PT_RUNTIME_FUNCTION FunctionEntry,
+ __inout T_CONTEXT *ContextRecord,
+ __out PDWORD64 EstablisherFrame,
+ __deref_opt_out_opt PEXCEPTION_ROUTINE *HandlerRoutine,
+ __out PVOID *HandlerData,
+ __in PARM64_UNWIND_PARAMS UnwindParams
+ )
+
+/*++
+
+Routine Description:
+
+ This function virtually unwinds the specified function by parsing the
+ .xdata record to determine where in the function the provided ControlPc
+ is, and then executing unwind codes that map to the function's prolog
+ or epilog behavior.
+
+ If a context pointers record is specified (in the UnwindParams), then
+ the address where each nonvolatile register is restored from is recorded
+ in the appropriate element of the context pointers record.
+
+Arguments:
+
+ ControlPcRva - Supplies the address where control left the specified
+ function, as an offset relative to the IamgeBase.
+
+ ImageBase - Supplies the base address of the image that contains the
+ function being unwound.
+
+ FunctionEntry - Supplies the address of the function table entry for the
+ specified function. If appropriate, this should have already been
+ probed.
+
+ ContextRecord - Supplies the address of a context record.
+
+ EstablisherFrame - Supplies a pointer to a variable that receives the
+ the establisher frame pointer value.
+
+ HandlerRoutine - Supplies an optional pointer to a variable that receives
+ the handler routine address. If control did not leave the specified
+ function in either the prolog or an epilog and a handler of the
+ proper type is associated with the function, then the address of the
+ language specific exception handler is returned. Otherwise, NULL is
+ returned.
+
+ HandlerData - Supplies a pointer to a variable that receives a pointer
+ the the language handler data.
+
+ UnwindParams - Additional parameters shared with caller.
+
+Return Value:
+
+ STATUS_SUCCESS if the unwind could be completed, a failure status otherwise.
+ Unwind can only fail when validation bounds are specified.
+
+--*/
+
+{
+ ULONG AccumulatedSaveNexts;
+ ULONG CurCode;
+ ULONG EpilogScopeCount;
+ PEXCEPTION_ROUTINE ExceptionHandler;
+ PVOID ExceptionHandlerData;
+ BOOLEAN FinalPcFromLr;
+ ULONG FunctionLength;
+ ULONG HeaderWord;
+ ULONG NextCode;
+ ULONG Offset;
+ DWORD64 OffsetInFunction;
+ ULONG ScopeNum;
+ ULONG ScopeSize;
+ ULONG ScopeStart;
+ DWORD64 SkipWords;
+ NTSTATUS Status;
+ ULONG_PTR UnwindCodePtr;
+ ULONG_PTR UnwindCodesEndPtr;
+ ULONG_PTR UnwindDataPtr;
+ ULONG UnwindIndex;
+ ULONG UnwindWords;
+
+ //
+ // Unless a special frame is enountered, assume that any unwinding
+ // will return us to the return address of a call and set the flag
+ // appropriately (it will be cleared again if the special cases apply).
+ //
+
+ ContextRecord->ContextFlags |= CONTEXT_UNWOUND_TO_CALL;
+
+ //
+ // By default, unwinding is done by popping to the LR, then copying
+ // that LR to the PC. However, some special opcodes require different
+ // behavior.
+ //
+
+ FinalPcFromLr = TRUE;
+
+ //
+ // Fetch the header word from the .xdata blob
+ //
+
+ UnwindDataPtr = ImageBase + FunctionEntry->UnwindData;
+ HeaderWord = MEMORY_READ_DWORD(UnwindParams, UnwindDataPtr);
+ UnwindDataPtr += 4;
+
+ //
+ // Verify the version before we do anything else
+ //
+
+ if (((HeaderWord >> 18) & 3) != 0) {
+ return STATUS_UNWIND_UNSUPPORTED_VERSION;
+ }
+
+ FunctionLength = HeaderWord & 0x3ffff;
+ OffsetInFunction = (ControlPcRva - FunctionEntry->BeginAddress) / 4;
+
+ //
+ // Determine the number of epilog scope records and the maximum number
+ // of unwind codes.
+ //
+
+ UnwindWords = (HeaderWord >> 27) & 31;
+ EpilogScopeCount = (HeaderWord >> 22) & 31;
+ if (EpilogScopeCount == 0 && UnwindWords == 0) {
+ EpilogScopeCount = MEMORY_READ_DWORD(UnwindParams, UnwindDataPtr);
+ UnwindDataPtr += 4;
+ UnwindWords = (EpilogScopeCount >> 16) & 0xff;
+ EpilogScopeCount &= 0xffff;
+ }
+ if ((HeaderWord & (1 << 21)) != 0) {
+ UnwindIndex = EpilogScopeCount;
+ EpilogScopeCount = 0;
+ }
+
+ //
+ // If exception data is present, extract it now.
+ //
+
+ ExceptionHandler = NULL;
+ ExceptionHandlerData = NULL;
+ if ((HeaderWord & (1 << 20)) != 0) {
+ ExceptionHandler = (PEXCEPTION_ROUTINE)(ImageBase +
+ MEMORY_READ_DWORD(UnwindParams, UnwindDataPtr + 4 * (EpilogScopeCount + UnwindWords)));
+ ExceptionHandlerData = (PVOID)(UnwindDataPtr + 4 * (EpilogScopeCount + UnwindWords + 1));
+ }
+
+ //
+ // Unless we are in a prolog/epilog, we execute the unwind codes
+ // that immediately follow the epilog scope list.
+ //
+
+ UnwindCodePtr = UnwindDataPtr + 4 * EpilogScopeCount;
+ UnwindCodesEndPtr = UnwindCodePtr + 4 * UnwindWords;
+ SkipWords = 0;
+
+ //
+ // If we're near the start of the function, and this function has a prolog,
+ // compute the size of the prolog from the unwind codes. If we're in the
+ // midst of it, we still execute starting at unwind code index 0, but we may
+ // need to skip some to account for partial execution of the prolog.
+ //
+ // N.B. As an optimization here, note that each byte of unwind codes can
+ // describe at most one 32-bit instruction. Thus, the largest prologue
+ // that could possibly be described by UnwindWords (which is 4 * the
+ // number of unwind code bytes) is 4 * UnwindWords words. If
+ // OffsetInFunction is larger than this value, it is guaranteed to be
+ // in the body of the function.
+ //
+
+ if (OffsetInFunction < 4 * UnwindWords) {
+ ScopeSize = RtlpComputeScopeSize(UnwindCodePtr, UnwindCodesEndPtr, FALSE, UnwindParams);
+
+ if (OffsetInFunction < ScopeSize) {
+ SkipWords = ScopeSize - OffsetInFunction;
+ ExceptionHandler = NULL;
+ ExceptionHandlerData = NULL;
+ goto ExecuteCodes;
+ }
+ }
+
+ //
+ // We're not in the prolog, now check to see if we are in the epilog.
+ // In the simple case, the 'E' bit is set indicating there is a single
+ // epilog that lives at the end of the function. If we're near the end
+ // of the function, compute the actual size of the epilog from the
+ // unwind codes. If we're in the midst of it, adjust the unwind code
+ // pointer to the start of the codes and determine how many we need to skip.
+ //
+ // N.B. Similar to the prolog case above, the maximum number of halfwords
+ // that an epilog can cover is limited by UnwindWords. In the epilog
+ // case, however, the starting index within the unwind code table is
+ // non-zero, and so the maximum number of unwind codes that can pertain
+ // to an epilog is (UnwindWords * 4 - UnwindIndex), thus further
+ // constraining the bounds of the epilog.
+ //
+
+ if ((HeaderWord & (1 << 21)) != 0) {
+ if (OffsetInFunction + (4 * UnwindWords - UnwindIndex) >= FunctionLength) {
+ ScopeSize = RtlpComputeScopeSize(UnwindCodePtr + UnwindIndex, UnwindCodesEndPtr, TRUE, UnwindParams);
+ ScopeStart = FunctionLength - ScopeSize;
+
+ if (OffsetInFunction >= ScopeStart) {
+ UnwindCodePtr += UnwindIndex;
+ SkipWords = OffsetInFunction - ScopeStart;
+ ExceptionHandler = NULL;
+ ExceptionHandlerData = NULL;
+ }
+ }
+ }
+
+ //
+ // In the multiple-epilog case, we scan forward to see if we are within
+ // shooting distance of any of the epilogs. If we are, we compute the
+ // actual size of the epilog from the unwind codes and proceed like the
+ // simple case above.
+ //
+
+ else {
+ for (ScopeNum = 0; ScopeNum < EpilogScopeCount; ScopeNum++) {
+ HeaderWord = MEMORY_READ_DWORD(UnwindParams, UnwindDataPtr);
+ UnwindDataPtr += 4;
+
+ //
+ // The scope records are stored in order. If we hit a record that
+ // starts after our current position, we must not be in an epilog.
+ //
+
+ ScopeStart = HeaderWord & 0x3ffff;
+ if (OffsetInFunction < ScopeStart) {
+ break;
+ }
+
+ UnwindIndex = HeaderWord >> 22;
+ if (OffsetInFunction < ScopeStart + (4 * UnwindWords - UnwindIndex)) {
+ ScopeSize = RtlpComputeScopeSize(UnwindCodePtr + UnwindIndex, UnwindCodesEndPtr, TRUE, UnwindParams);
+
+ if (OffsetInFunction < ScopeStart + ScopeSize) {
+
+ UnwindCodePtr += UnwindIndex;
+ SkipWords = OffsetInFunction - ScopeStart;
+ ExceptionHandler = NULL;
+ ExceptionHandlerData = NULL;
+ break;
+ }
+ }
+ }
+ }
+
+ExecuteCodes:
+
+ //
+ // Skip over unwind codes until we account for the number of halfwords
+ // to skip.
+ //
+
+ while (UnwindCodePtr < UnwindCodesEndPtr && SkipWords > 0) {
+ CurCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ if (OPCODE_IS_END(CurCode)) {
+ break;
+ }
+ UnwindCodePtr += UnwindCodeSizeTable[CurCode];
+ SkipWords--;
+ }
+
+ //
+ // Now execute codes until we hit the end.
+ //
+
+ Status = STATUS_SUCCESS;
+ AccumulatedSaveNexts = 0;
+ while (UnwindCodePtr < UnwindCodesEndPtr && Status == STATUS_SUCCESS) {
+
+ CurCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ UnwindCodePtr += 1;
+
+ //
+ // alloc_s (000xxxxx): allocate small stack with size < 1024 (2^5 * 16)
+ //
+
+ if (CurCode <= 0x1f) {
+ if (AccumulatedSaveNexts != 0) {
+ return STATUS_UNWIND_INVALID_SEQUENCE;
+ }
+ ContextRecord->Sp += 16 * (CurCode & 0x1f);
+ }
+
+ //
+ // save_r19r20_x (001zzzzz): save <r19,r20> pair at [sp-#Z*8]!, pre-indexed offset >= -248
+ //
+
+ else if (CurCode <= 0x3f) {
+ Status = RtlpUnwindRestoreRegisterRange(
+ ContextRecord,
+ -8 * (CurCode & 0x1f),
+ 19,
+ 2 + 2 * AccumulatedSaveNexts,
+ UnwindParams);
+ AccumulatedSaveNexts = 0;
+ }
+
+ //
+ // save_fplr (01zzzzzz): save <r29,lr> pair at [sp+#Z*8], offset <= 504
+ //
+
+ else if (CurCode <= 0x7f) {
+ if (AccumulatedSaveNexts != 0) {
+ return STATUS_UNWIND_INVALID_SEQUENCE;
+ }
+ Status = RtlpUnwindRestoreRegisterRange(
+ ContextRecord,
+ 8 * (CurCode & 0x3f),
+ 29,
+ 2,
+ UnwindParams);
+ }
+
+ //
+ // save_fplr_x (10zzzzzz): save <r29,lr> pair at [sp-(#Z+1)*8]!, pre-indexed offset >= -512
+ //
+
+ else if (CurCode <= 0xbf) {
+ if (AccumulatedSaveNexts != 0) {
+ return STATUS_UNWIND_INVALID_SEQUENCE;
+ }
+ Status = RtlpUnwindRestoreRegisterRange(
+ ContextRecord,
+ -8 * ((CurCode & 0x3f) + 1),
+ 29,
+ 2,
+ UnwindParams);
+ }
+
+ //
+ // alloc_m (11000xxx|xxxxxxxx): allocate large stack with size < 32k (2^11 * 16).
+ //
+
+ else if (CurCode <= 0xc7) {
+ if (AccumulatedSaveNexts != 0) {
+ return STATUS_UNWIND_INVALID_SEQUENCE;
+ }
+ ContextRecord->Sp += 16 * ((CurCode & 7) << 8);
+ ContextRecord->Sp += 16 * MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ UnwindCodePtr++;
+ }
+
+ //
+ // save_regp (110010xx|xxzzzzzz): save r(19+#X) pair at [sp+#Z*8], offset <= 504
+ //
+
+ else if (CurCode <= 0xcb) {
+ NextCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ UnwindCodePtr++;
+ Status = RtlpUnwindRestoreRegisterRange(
+ ContextRecord,
+ 8 * (NextCode & 0x3f),
+ 19 + ((CurCode & 3) << 2) + (NextCode >> 6),
+ 2 + 2 * AccumulatedSaveNexts,
+ UnwindParams);
+ AccumulatedSaveNexts = 0;
+ }
+
+ //
+ // save_regp_x (110011xx|xxzzzzzz): save pair r(19+#X) at [sp-(#Z+1)*8]!, pre-indexed offset >= -512
+ //
+
+ else if (CurCode <= 0xcf) {
+ NextCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ UnwindCodePtr++;
+ Status = RtlpUnwindRestoreRegisterRange(
+ ContextRecord,
+ -8 * ((NextCode & 0x3f) + 1),
+ 19 + ((CurCode & 3) << 2) + (NextCode >> 6),
+ 2 + 2 * AccumulatedSaveNexts,
+ UnwindParams);
+ AccumulatedSaveNexts = 0;
+ }
+
+ //
+ // save_reg (110100xx|xxzzzzzz): save reg r(19+#X) at [sp+#Z*8], offset <= 504
+ //
+
+ else if (CurCode <= 0xd3) {
+ if (AccumulatedSaveNexts != 0) {
+ return STATUS_UNWIND_INVALID_SEQUENCE;
+ }
+ NextCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ UnwindCodePtr++;
+ Status = RtlpUnwindRestoreRegisterRange(
+ ContextRecord,
+ 8 * (NextCode & 0x3f),
+ 19 + ((CurCode & 3) << 2) + (NextCode >> 6),
+ 1,
+ UnwindParams);
+ }
+
+ //
+ // save_reg_x (1101010x|xxxzzzzz): save reg r(19+#X) at [sp-(#Z+1)*8]!, pre-indexed offset >= -256
+ //
+
+ else if (CurCode <= 0xd5) {
+ if (AccumulatedSaveNexts != 0) {
+ return STATUS_UNWIND_INVALID_SEQUENCE;
+ }
+ NextCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ UnwindCodePtr++;
+ Status = RtlpUnwindRestoreRegisterRange(
+ ContextRecord,
+ -8 * ((NextCode & 0x1f) + 1),
+ 19 + ((CurCode & 1) << 3) + (NextCode >> 5),
+ 1,
+ UnwindParams);
+ }
+
+ //
+ // save_lrpair (1101011x|xxzzzzzz): save pair <r19+2*#X,lr> at [sp+#Z*8], offset <= 504
+ //
+
+ else if (CurCode <= 0xd7) {
+ if (AccumulatedSaveNexts != 0) {
+ return STATUS_UNWIND_INVALID_SEQUENCE;
+ }
+ NextCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ UnwindCodePtr++;
+ Status = RtlpUnwindRestoreRegisterRange(
+ ContextRecord,
+ 8 * (NextCode & 0x3f),
+ 19 + 2 * (((CurCode & 1) << 2) + (NextCode >> 6)),
+ 1,
+ UnwindParams);
+ if (Status == STATUS_SUCCESS) {
+ RtlpUnwindRestoreRegisterRange(
+ ContextRecord,
+ 8 * (NextCode & 0x3f) + 8,
+ 30,
+ 1,
+ UnwindParams);
+ }
+ }
+
+ //
+ // save_fregp (1101100x|xxzzzzzz): save pair d(8+#X) at [sp+#Z*8], offset <= 504
+ //
+
+ else if (CurCode <= 0xd9) {
+ NextCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ UnwindCodePtr++;
+ Status = RtlpUnwindRestoreFpRegisterRange(
+ ContextRecord,
+ 8 * (NextCode & 0x3f),
+ 8 + ((CurCode & 1) << 2) + (NextCode >> 6),
+ 2 + AccumulatedSaveNexts,
+ UnwindParams);
+ AccumulatedSaveNexts = 0;
+ }
+
+ //
+ // save_fregp_x (1101101x|xxzzzzzz): save pair d(8+#X), at [sp-(#Z+1)*8]!, pre-indexed offset >= -512
+ //
+
+ else if (CurCode <= 0xdb) {
+ NextCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ UnwindCodePtr++;
+ Status = RtlpUnwindRestoreFpRegisterRange(
+ ContextRecord,
+ -8 * ((NextCode & 0x3f) + 1),
+ 8 + ((CurCode & 1) << 2) + (NextCode >> 6),
+ 2 + AccumulatedSaveNexts,
+ UnwindParams);
+ AccumulatedSaveNexts = 0;
+ }
+
+ //
+ // save_freg (1101110x|xxzzzzzz): save reg d(9+#X) at [sp+#Z*8], offset <= 504
+ //
+
+ else if (CurCode <= 0xdd) {
+ if (AccumulatedSaveNexts != 0) {
+ return STATUS_UNWIND_INVALID_SEQUENCE;
+ }
+ NextCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ UnwindCodePtr++;
+ Status = RtlpUnwindRestoreFpRegisterRange(
+ ContextRecord,
+ 8 * (NextCode & 0x3f),
+ 8 + ((CurCode & 1) << 2) + (NextCode >> 6),
+ 1,
+ UnwindParams);
+ }
+
+ //
+ // save_freg_x (11011110|xxxzzzzz): save reg d(8+#X) at [sp-(#Z+1)*8]!, pre-indexed offset >= -256
+ //
+
+ else if (CurCode == 0xde) {
+ if (AccumulatedSaveNexts != 0) {
+ return STATUS_UNWIND_INVALID_SEQUENCE;
+ }
+ NextCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ UnwindCodePtr++;
+ Status = RtlpUnwindRestoreFpRegisterRange(
+ ContextRecord,
+ -8 * ((NextCode & 0x1f) + 1),
+ 8 + (NextCode >> 5),
+ 1,
+ UnwindParams);
+ }
+
+ //
+ // alloc_l (11100000|xxxxxxxx|xxxxxxxx|xxxxxxxx): allocate large stack with size < 256M
+ //
+
+ else if (CurCode == 0xe0) {
+ if (AccumulatedSaveNexts != 0) {
+ return STATUS_UNWIND_INVALID_SEQUENCE;
+ }
+ ContextRecord->Sp += 16 * (MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr) << 16);
+ UnwindCodePtr++;
+ ContextRecord->Sp += 16 * (MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr) << 8);
+ UnwindCodePtr++;
+ ContextRecord->Sp += 16 * MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ UnwindCodePtr++;
+ }
+
+ //
+ // set_fp (11100001): set up r29: with: mov r29,sp
+ //
+
+ else if (CurCode == 0xe1) {
+ if (AccumulatedSaveNexts != 0) {
+ return STATUS_UNWIND_INVALID_SEQUENCE;
+ }
+ ContextRecord->Sp = ContextRecord->Fp;
+ }
+
+ //
+ // add_fp (11100010|xxxxxxxx): set up r29 with: add r29,sp,#x*8
+ //
+
+ else if (CurCode == 0xe2) {
+ if (AccumulatedSaveNexts != 0) {
+ return STATUS_UNWIND_INVALID_SEQUENCE;
+ }
+ ContextRecord->Sp = ContextRecord->Fp - 8 * MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
+ UnwindCodePtr++;
+ }
+
+ //
+ // nop (11100011): no unwind operation is required
+ //
+
+ else if (CurCode == 0xe3) {
+ if (AccumulatedSaveNexts != 0) {
+ return STATUS_UNWIND_INVALID_SEQUENCE;
+ }
+ }
+
+ //
+ // end (11100100): end of unwind code
+ //
+
+ else if (CurCode == 0xe4) {
+ if (AccumulatedSaveNexts != 0) {
+ return STATUS_UNWIND_INVALID_SEQUENCE;
+ }
+ goto finished;
+ }
+
+ //
+ // end_c (11100101): end of unwind code in current chained scope
+ //
+
+ else if (CurCode == 0xe5) {
+ if (AccumulatedSaveNexts != 0) {
+ return STATUS_UNWIND_INVALID_SEQUENCE;
+ }
+ goto finished;
+ }
+
+ //
+ // save_next (11100110): save next non-volatile Int or FP register pair.
+ //
+
+ else if (CurCode == 0xe6) {
+ AccumulatedSaveNexts++;
+ }
+
+ //
+ // custom_0 (111010xx): restore custom structure
+ //
+
+ else if (CurCode >= 0xe8 && CurCode <= 0xeb) {
+ if (AccumulatedSaveNexts != 0) {
+ return STATUS_UNWIND_INVALID_SEQUENCE;
+ }
+ Status = RtlpUnwindCustom(ContextRecord, (BYTE) CurCode, UnwindParams);
+ FinalPcFromLr = FALSE;
+ }
+
+ //
+ // Anything else is invalid
+ //
+
+ else {
+ return STATUS_UNWIND_INVALID_SEQUENCE;
+ }
+ }
+
+ //
+ // If we succeeded, post-process the results a bit
+ //
+finished:
+ if (Status == STATUS_SUCCESS) {
+
+ //
+ // Since we always POP to the LR, recover the final PC from there, unless
+ // it was overwritten due to a special case custom unwinding operation.
+ // Also set the establisher frame equal to the final stack pointer.
+ //
+
+ if (FinalPcFromLr) {
+ ContextRecord->Pc = ContextRecord->Lr;
+ }
+ *EstablisherFrame = ContextRecord->Sp;
+
+ if (ARGUMENT_PRESENT(HandlerRoutine)) {
+ *HandlerRoutine = ExceptionHandler;
+ }
+ *HandlerData = ExceptionHandlerData;
+ }
+
+ return Status;
+}
+
+NTSTATUS
+RtlpUnwindFunctionCompact(
+ __in DWORD64 ControlPcRva,
+ __in PT_RUNTIME_FUNCTION FunctionEntry,
+ __inout T_CONTEXT *ContextRecord,
+ __out PDWORD64 EstablisherFrame,
+ __deref_opt_out_opt PEXCEPTION_ROUTINE *HandlerRoutine,
+ __out PVOID *HandlerData,
+ __in PARM64_UNWIND_PARAMS UnwindParams
+ )
+
+/*++
+
+Routine Description:
+
+ This function virtually unwinds the specified function by parsing the
+ compact .pdata record to determine where in the function the provided
+ ControlPc is, and then executing a standard, well-defined set of
+ operations.
+
+ If a context pointers record is specified (in the UnwindParams), then
+ the address where each nonvolatile register is restored from is recorded
+ in the appropriate element of the context pointers record.
+
+Arguments:
+
+ ControlPcRva - Supplies the address where control left the specified
+ function, as an offset relative to the IamgeBase.
+
+ FunctionEntry - Supplies the address of the function table entry for the
+ specified function. If appropriate, this should have already been
+ probed.
+
+ ContextRecord - Supplies the address of a context record.
+
+ EstablisherFrame - Supplies a pointer to a variable that receives the
+ the establisher frame pointer value.
+
+ HandlerRoutine - Supplies an optional pointer to a variable that receives
+ the handler routine address. If control did not leave the specified
+ function in either the prolog or an epilog and a handler of the
+ proper type is associated with the function, then the address of the
+ language specific exception handler is returned. Otherwise, NULL is
+ returned.
+
+ HandlerData - Supplies a pointer to a variable that receives a pointer
+ the the language handler data.
+
+ UnwindParams - Additional parameters shared with caller.
+
+Return Value:
+
+ STATUS_SUCCESS if the unwind could be completed, a failure status otherwise.
+ Unwind can only fail when validation bounds are specified.
+
+--*/
+
+{
+ ULONG Count;
+ ULONG Cr;
+ ULONG CurrentOffset;
+ ULONG EpilogLength;
+ ULONG Flag;
+ ULONG FloatSize;
+ ULONG FrameSize;
+ ULONG FRegOpcodes;
+ ULONG FunctionLength;
+ ULONG HBit;
+ ULONG HOpcodes;
+ ULONG IRegOpcodes;
+ ULONG InstCount;
+ ULONG IntSize;
+ ULONG LocalSize;
+ DWORD64 OffsetInFunction;
+ DWORD64 OffsetInScope;
+ ULONG PrologLength;
+ ULONG RegF;
+ ULONG RegI;
+ ULONG RegSize;
+ ULONG ScopeStart;
+ ULONG StackAdjustOpcodes;
+ NTSTATUS Status;
+ ULONG UnwindData;
+
+ UnwindData = FunctionEntry->UnwindData;
+ Status = STATUS_SUCCESS;
+
+ //
+ // Compact records always describe an unwind to a call.
+ //
+
+ ContextRecord->ContextFlags |= CONTEXT_UNWOUND_TO_CALL;
+
+ //
+ // Extract the basic information about how to do a full unwind.
+ //
+
+ Flag = UnwindData & 3;
+ FunctionLength = (UnwindData >> 2) & 0x7ff;
+ RegF = (UnwindData >> 13) & 7;
+ RegI = (UnwindData >> 16) & 0xf;
+ HBit = (UnwindData >> 20) & 1;
+ Cr = (UnwindData >> 21) & 3;
+ FrameSize = (UnwindData >> 23) & 0x1ff;
+
+ if (Flag == 3) {
+ return STATUS_UNWIND_INVALID_SEQUENCE;
+ }
+ if (Cr == 2) {
+ return STATUS_UNWIND_INVALID_SEQUENCE;
+ }
+
+ //
+ // Determine the size of the locals
+ //
+
+ IntSize = RegI * 8;
+ if (Cr == 1) {
+ IntSize += 8;
+ }
+ FloatSize = (RegF == 0) ? 0 : (RegF + 1) * 8;
+ RegSize = (IntSize + FloatSize + 8*8 * HBit + 0xf) & ~0xf;
+ if (RegSize > 16 * FrameSize) {
+ return STATUS_UNWIND_INVALID_SEQUENCE;
+ }
+ LocalSize = 16 * FrameSize - RegSize;
+
+ //
+ // If we're near the start of the function (within 17 words),
+ // see if we are within the prolog.
+ //
+ // N.B. If the low 2 bits of the UnwindData are 2, then we have
+ // no prolog.
+ //
+
+ OffsetInFunction = (ControlPcRva - FunctionEntry->BeginAddress) / 4;
+ OffsetInScope = 0;
+ if (OffsetInFunction < 17 && Flag != 2) {
+
+ //
+ // Compute sizes for each opcode in the prolog.
+ //
+
+ IRegOpcodes = (IntSize + 8) / 16;
+ FRegOpcodes = (FloatSize + 8) / 16;
+ HOpcodes = 4 * HBit;
+ StackAdjustOpcodes = (Cr == 3) ? 1 : 0;
+ if (Cr != 3 || LocalSize > 512) {
+ StackAdjustOpcodes += (LocalSize > 4088) ? 2 : (LocalSize > 0) ? 1 : 0;
+ }
+
+ //
+ // Compute the total prolog length and determine if we are within
+ // its scope.
+ //
+ // N.B. We must execute prolog operations backwards to unwind, so
+ // our final scope offset in this case is the distance from the end.
+ //
+
+ PrologLength = IRegOpcodes + FRegOpcodes + HOpcodes + StackAdjustOpcodes;
+
+ if (OffsetInFunction < PrologLength) {
+ OffsetInScope = PrologLength - OffsetInFunction;
+ }
+ }
+
+ //
+ // If we're near the end of the function (within 15 words), see if
+ // we are within the epilog.
+ //
+ // N.B. If the low 2 bits of the UnwindData are 2, then we have
+ // no epilog.
+ //
+
+ if (OffsetInScope == 0 && OffsetInFunction + 15 >= FunctionLength && Flag != 2) {
+
+ //
+ // Compute sizes for each opcode in the epilog.
+ //
+
+ IRegOpcodes = (IntSize + 8) / 16;
+ FRegOpcodes = (FloatSize + 8) / 16;
+ HOpcodes = HBit;
+ StackAdjustOpcodes = (Cr == 3) ? 1 : 0;
+ if (Cr != 3 || LocalSize > 512) {
+ StackAdjustOpcodes += (LocalSize > 4088) ? 2 : (LocalSize > 0) ? 1 : 0;
+ }
+
+ //
+ // Compute the total epilog length and determine if we are within
+ // its scope.
+ //
+
+ EpilogLength = IRegOpcodes + FRegOpcodes + HOpcodes + StackAdjustOpcodes + 1;
+
+ ScopeStart = FunctionLength - EpilogLength;
+ if (OffsetInFunction > ScopeStart) {
+ OffsetInScope = OffsetInFunction - ScopeStart;
+ }
+ }
+
+ //
+ // Process operations backwards, in the order: stack/frame deallocation,
+ // VFP register popping, integer register popping, parameter home
+ // area recovery.
+ //
+ // First case is simple: we process everything with no regard for
+ // the current offset within the scope.
+ //
+
+ Status = STATUS_SUCCESS;
+ if (OffsetInScope == 0) {
+
+ if (Cr == 3) {
+ Status = RtlpUnwindRestoreRegisterRange(ContextRecord, 0, 29, 2, UnwindParams);
+ }
+ ContextRecord->Sp += LocalSize;
+
+ if (RegF != 0 && Status == STATUS_SUCCESS) {
+ Status = RtlpUnwindRestoreFpRegisterRange(ContextRecord, IntSize, 8, RegF + 1, UnwindParams);
+ }
+
+ if (Cr == 1 && Status == STATUS_SUCCESS) {
+ Status = RtlpUnwindRestoreRegisterRange(ContextRecord, IntSize - 8, 30, 1, UnwindParams);
+ }
+ if (RegI > 0 && Status == STATUS_SUCCESS) {
+ Status = RtlpUnwindRestoreRegisterRange(ContextRecord, 0, 19, RegI, UnwindParams);
+ }
+ ContextRecord->Sp += RegSize;
+ }
+
+ //
+ // Second case is more complex: we must step along each operation
+ // to ensure it should be executed.
+ //
+
+ else {
+
+ CurrentOffset = 0;
+ if (Cr == 3) {
+ if (LocalSize <= 512) {
+ if (CurrentOffset++ >= OffsetInScope) {
+ Status = RtlpUnwindRestoreRegisterRange(ContextRecord, -(LONG)LocalSize, 29, 2, UnwindParams);
+ }
+ LocalSize = 0;
+ }
+ }
+ while (LocalSize != 0) {
+ Count = (LocalSize + 4087) % 4088 + 1;
+ if (CurrentOffset++ >= OffsetInScope) {
+ ContextRecord->Sp += Count;
+ }
+ LocalSize -= Count;
+ }
+
+ if (HBit != 0) {
+ CurrentOffset += 4;
+ }
+
+ if (RegF != 0 && Status == STATUS_SUCCESS) {
+ RegF++;
+ while (RegF != 0) {
+ Count = 2 - (RegF & 1);
+ RegF -= Count;
+ if (CurrentOffset++ >= OffsetInScope) {
+ Status = RtlpUnwindRestoreFpRegisterRange(
+ ContextRecord,
+ (RegF == 0 && RegI == 0) ? (-(LONG)RegSize) : (IntSize + 8 * RegF),
+ 8 + RegF,
+ Count,
+ UnwindParams);
+ }
+ }
+ }
+
+ if (Cr == 1 && Status == STATUS_SUCCESS) {
+ if (RegI % 2 == 0) {
+ if (CurrentOffset++ >= OffsetInScope) {
+ Status = RtlpUnwindRestoreRegisterRange(ContextRecord, IntSize - 8, 30, 1, UnwindParams);
+ }
+ } else {
+ if (CurrentOffset++ >= OffsetInScope) {
+ RegI--;
+ Status = RtlpUnwindRestoreRegisterRange(ContextRecord, IntSize - 8, 30, 1, UnwindParams);
+ if (Status == STATUS_SUCCESS) {
+ Status = RtlpUnwindRestoreRegisterRange(ContextRecord, IntSize - 16, 19 + RegI, 1, UnwindParams);
+ }
+ }
+ }
+ }
+
+ while (RegI != 0 && Status == STATUS_SUCCESS) {
+ Count = 2 - (RegI & 1);
+ RegI -= Count;
+ if (CurrentOffset++ >= OffsetInScope) {
+ Status = RtlpUnwindRestoreRegisterRange(
+ ContextRecord,
+ (RegI == 0) ? (-(LONG)RegSize) : (8 * RegI),
+ 19 + RegI,
+ Count,
+ UnwindParams);
+ }
+ }
+ }
+
+ //
+ // If we succeeded, post-process the results a bit
+ //
+
+ if (Status == STATUS_SUCCESS) {
+
+ ContextRecord->Pc = ContextRecord->Lr;
+ *EstablisherFrame = ContextRecord->Sp;
+
+ if (ARGUMENT_PRESENT(HandlerRoutine)) {
+ *HandlerRoutine = NULL;
+ }
+ *HandlerData = NULL;
+ }
+
+ return Status;
+}
+
+BOOL OOPStackUnwinderArm64::Unwind(T_CONTEXT * pContext)
+{
+ DWORD64 ImageBase = 0;
+ HRESULT hr = GetModuleBase(pContext->Pc, &ImageBase);
+ if (hr != S_OK)
+ return FALSE;
+
+ PEXCEPTION_ROUTINE DummyHandlerRoutine;
+ PVOID DummyHandlerData;
+ DWORD64 DummyEstablisherFrame;
+
+ DWORD64 startingPc = pContext->Pc;
+ DWORD64 startingSp = pContext->Sp;
+
+ T_RUNTIME_FUNCTION Rfe;
+ if (FAILED(GetFunctionEntry(pContext->Pc, &Rfe, sizeof(Rfe))))
+ return FALSE;
+
+ if ((Rfe.UnwindData & 3) != 0)
+ {
+ hr = RtlpUnwindFunctionCompact(pContext->Pc - (ULONG)ImageBase,
+ &Rfe,
+ pContext,
+ &DummyEstablisherFrame,
+ &DummyHandlerRoutine,
+ &DummyHandlerData,
+ NULL);
+
+ }
+ else
+ {
+ hr = RtlpUnwindFunctionFull(pContext->Pc - (ULONG)ImageBase,
+ (ULONG)ImageBase,
+ &Rfe,
+ pContext,
+ &DummyEstablisherFrame,
+ &DummyHandlerRoutine,
+ &DummyHandlerData,
+ NULL);
+ }
+
+ // PC == 0 means unwinding is finished.
+ // Same if no forward progress is made
+ if (pContext->Pc == 0 || (startingPc == pContext->Pc && startingSp == pContext->Sp))
+ return FALSE;
+
+ return TRUE;
+}
+
+BOOL DacUnwindStackFrame(T_CONTEXT *pContext, T_KNONVOLATILE_CONTEXT_POINTERS* pContextPointers)
+{
+ OOPStackUnwinderArm64 unwinder;
+ BOOL res = unwinder.Unwind(pContext);
+
+ if (res && pContextPointers)
+ {
+ for (int i = 0; i < 12; i++)
+ {
+ *(&pContextPointers->X19 + i) = &pContext->X19 + i;
+ }
+ }
+
+ return res;
+}
diff --git a/src/unwinder/arm64/unwinder_arm64.h b/src/unwinder/arm64/unwinder_arm64.h
new file mode 100644
index 0000000000..7396e85e26
--- /dev/null
+++ b/src/unwinder/arm64/unwinder_arm64.h
@@ -0,0 +1,55 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//
+
+#ifndef __unwinder_arm64__
+#define __unwinder_arm64__
+
+#include "unwinder.h"
+
+
+//---------------------------------------------------------------------------------------
+//
+// See the comment for the base class code:OOPStackUnwinder.
+//
+
+class OOPStackUnwinderArm64 : public OOPStackUnwinder
+{
+public:
+ // Unwind the given CONTEXT to the caller CONTEXT. The CONTEXT will be overwritten.
+ BOOL Unwind(T_CONTEXT * pContext);
+
+ //
+ // Everything below comes from dbghelp.dll.
+ //
+
+protected:
+ HRESULT UnwindPrologue(__in DWORD64 ImageBase,
+ __in DWORD64 ControlPc,
+ __in DWORD64 FrameBase,
+ __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry,
+ __inout PT_CONTEXT ContextRecord);
+
+ HRESULT VirtualUnwind(__in DWORD64 ImageBase,
+ __in DWORD64 ControlPc,
+ __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry,
+ __inout PT_CONTEXT ContextRecord,
+ __out PDWORD64 EstablisherFrame);
+
+ DWORD64 LookupPrimaryUnwindInfo
+ (__in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry,
+ __in DWORD64 ImageBase,
+ __out _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryEntry);
+
+ _PIMAGE_RUNTIME_FUNCTION_ENTRY SameFunction
+ (__in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry,
+ __in DWORD64 ImageBase,
+ __in DWORD64 ControlPc,
+ __out _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionReturnBuffer);
+};
+
+#endif // __unwinder_arm64__
+
diff --git a/src/unwinder/dac/.gitmirror b/src/unwinder/dac/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/unwinder/dac/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/unwinder/dac/CMakeLists.txt b/src/unwinder/dac/CMakeLists.txt
new file mode 100644
index 0000000000..e82046197c
--- /dev/null
+++ b/src/unwinder/dac/CMakeLists.txt
@@ -0,0 +1,10 @@
+include(${CLR_DIR}/dac.cmake)
+
+add_definitions(-DFEATURE_NO_HOST)
+add_definitions(-D_TARGET_AMD64_=1)
+add_definitions(-DDBG_TARGET_64BIT=1)
+add_definitions(-DDBG_TARGET_AMD64=1)
+add_definitions(-DDBG_TARGET_WIN64=1)
+add_definitions(-D_WIN64=1)
+
+add_library(unwinder_dac ${UNWINDER_SOURCES})
diff --git a/src/unwinder/dac/dirs.proj b/src/unwinder/dac/dirs.proj
new file mode 100644
index 0000000000..77738732ed
--- /dev/null
+++ b/src/unwinder/dac/dirs.proj
@@ -0,0 +1,18 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+
+ <PropertyGroup>
+ <BuildInPhase1>true</BuildInPhase1>
+ <BuildInPhaseDefault>false</BuildInPhaseDefault>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ </PropertyGroup>
+
+ <!--The following projects will build during PHASE 1-->
+ <ItemGroup Condition="'$(BuildExePhase)' == '1'">
+ <ProjectFile Include="hostlocal\unwinder_dac.nativeproj" />
+ </ItemGroup>
+
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/unwinder/dac/hostlocal/.gitmirror b/src/unwinder/dac/hostlocal/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/unwinder/dac/hostlocal/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/unwinder/dac/hostlocal/unwinder_dac.nativeproj b/src/unwinder/dac/hostlocal/unwinder_dac.nativeproj
new file mode 100644
index 0000000000..3b562e07f9
--- /dev/null
+++ b/src/unwinder/dac/hostlocal/unwinder_dac.nativeproj
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+
+ <PropertyGroup>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ <OutputName>unwinder_dac</OutputName>
+ </PropertyGroup>
+
+ <!-- compile items -->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\unwinder\unwinder.targets" />
+
+</Project>
diff --git a/src/unwinder/dac/hostwinamd64/.gitmirror b/src/unwinder/dac/hostwinamd64/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/unwinder/dac/hostwinamd64/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/unwinder/dac/hostwinamd64/CMakeLists.txt b/src/unwinder/dac/hostwinamd64/CMakeLists.txt
new file mode 100644
index 0000000000..66a595f033
--- /dev/null
+++ b/src/unwinder/dac/hostwinamd64/CMakeLists.txt
@@ -0,0 +1,5 @@
+remove_definitions(-DPROFILING_SUPPORTED)
+add_definitions(-DPROFILING_SUPPORTED_DATA)
+add_definitions(-DDACCESS_COMPILE)
+
+add_library(unwinder_dac_amd64 ${UNWINDER_SOURCES})
diff --git a/src/unwinder/dac/hostwinamd64/unwinder_dac.nativeproj b/src/unwinder/dac/hostwinamd64/unwinder_dac.nativeproj
new file mode 100644
index 0000000000..e0dff469a4
--- /dev/null
+++ b/src/unwinder/dac/hostwinamd64/unwinder_dac.nativeproj
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+
+ <!-- xplat Windows host, local target DAC build -->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\xplat\SetTargetLocal.props" />
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\xplat\SetHostWinAMD64.props" />
+ <PropertyGroup>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ <OutputName>unwinder_dac_amd64</OutputName>
+ </PropertyGroup>
+
+ <!-- compile items -->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\unwinder\unwinder.targets" />
+
+</Project>
diff --git a/src/unwinder/dac/hostwinx86/.gitmirror b/src/unwinder/dac/hostwinx86/.gitmirror
new file mode 100644
index 0000000000..f507630f94
--- /dev/null
+++ b/src/unwinder/dac/hostwinx86/.gitmirror
@@ -0,0 +1 @@
+Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file
diff --git a/src/unwinder/dac/hostwinx86/unwinder_dac.nativeproj b/src/unwinder/dac/hostwinx86/unwinder_dac.nativeproj
new file mode 100644
index 0000000000..a3538c3383
--- /dev/null
+++ b/src/unwinder/dac/hostwinx86/unwinder_dac.nativeproj
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+
+ <!-- xplat win32 host, local target DAC build -->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\xplat\SetTargetLocal.props" />
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\xplat\SetHostWinx86.props" />
+ <PropertyGroup>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ <OutputName>unwinder_dac_x86</OutputName>
+ </PropertyGroup>
+
+ <!-- compile items -->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\unwinder\unwinder.targets" />
+
+</Project>
diff --git a/src/unwinder/dirs.proj b/src/unwinder/dirs.proj
new file mode 100644
index 0000000000..8b511971e3
--- /dev/null
+++ b/src/unwinder/dirs.proj
@@ -0,0 +1,19 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+
+ <PropertyGroup>
+ <BuildInPhase1>true</BuildInPhase1>
+ <BuildInPhaseDefault>false</BuildInPhaseDefault>
+ <BuildCoreBinaries>true</BuildCoreBinaries>
+ <BuildSysBinaries>true</BuildSysBinaries>
+ </PropertyGroup>
+
+ <!--The following projects will build during PHASE 1-->
+ <ItemGroup Condition="'$(BuildExePhase)' == '1'">
+ <ProjectFile Include="dac\dirs.proj" />
+ </ItemGroup>
+
+ <!--Import the targets-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\tools\Microsoft.DevDiv.Traversal.targets" />
+</Project>
diff --git a/src/unwinder/stdafx.cpp b/src/unwinder/stdafx.cpp
new file mode 100644
index 0000000000..184cc8de11
--- /dev/null
+++ b/src/unwinder/stdafx.cpp
@@ -0,0 +1,13 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// File: stdafx.cpp
+//
+
+//
+// Host for precompiled headers.
+//
+//*****************************************************************************
+#include "stdafx.h" // Precompiled header key.
diff --git a/src/unwinder/stdafx.h b/src/unwinder/stdafx.h
new file mode 100644
index 0000000000..840877ea5b
--- /dev/null
+++ b/src/unwinder/stdafx.h
@@ -0,0 +1,19 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+//*****************************************************************************
+// File: stdafx.h
+//
+
+// Prevent the inclusion of Random.h from disabling rand(). rand() is used by some other headers we include
+// and there's no reason why DAC should be forbidden from using it.
+#define DO_NOT_DISABLE_RAND
+
+#define USE_COM_CONTEXT_DEF
+
+#include <common.h>
+#include <debugger.h>
+#include <methoditer.h>
+#include <dacprivate.h>
+#include <dacimpl.h>
diff --git a/src/unwinder/unwinder.cpp b/src/unwinder/unwinder.cpp
new file mode 100644
index 0000000000..6431a83ea8
--- /dev/null
+++ b/src/unwinder/unwinder.cpp
@@ -0,0 +1,163 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//
+
+#include "stdafx.h"
+#include "unwinder.h"
+
+EXTERN_C void GetRuntimeStackWalkInfo(IN ULONG64 ControlPc,
+ OUT UINT_PTR* pModuleBase,
+ OUT UINT_PTR* pFuncEntry);
+
+#if !defined(_TARGET_ARM_) && !defined(_TARGET_ARM64_)
+//---------------------------------------------------------------------------------------
+//
+// Read an UNWIND_INFO structure given its address. The UNWIND_INFO structure is variable sized.
+// This is just a simple wrapper over the platform-specific DAC function which does the real work.
+//
+// Arguments:
+// taUnwindInfo - target address of the beginning of the UNWIND_INFO structure
+//
+// Return Value:
+// Return the specified UNWIND_INFO. It lives in the DAC cache, so the caller doesn't need to explicitly
+// free the memory. It'll get flushed when the DAC cache is flushed (i.e. when we continue).
+//
+
+UNWIND_INFO * OOPStackUnwinder::GetUnwindInfo(TADDR taUnwindInfo)
+{
+ return DacGetUnwindInfo(taUnwindInfo);
+}
+#endif // !_TARGET_ARM_ && !_TARGET_ARM64_
+
+//---------------------------------------------------------------------------------------
+//
+// This is a simple wrapper over code:OOPStackUnwinder::ReadMemory(). Unlike ReadMemory(),
+// it fails if we don't successfully read all the specified bytes.
+//
+// Arguments:
+// address - the address to be read
+// pbBuffer - the buffer to store the read memory
+// cbRequest - the number of bytes requested
+//
+// Return Value:
+// S_OK if all the memory is read successfully.
+// HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY) if only part of the memory is read.
+// Failure HRs otherwise.
+//
+
+HRESULT OOPStackUnwinder::ReadAllMemory( DWORD64 address,
+ __in_ecount(cbRequest) PVOID pbBuffer,
+ DWORD cbRequest)
+{
+ DWORD cbDone = 0;
+ HRESULT hr = ReadMemory(address, pbBuffer, cbRequest, &cbDone);
+ if (SUCCEEDED(hr) && (cbDone != cbRequest))
+ {
+ return HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY);
+ }
+ else
+ {
+ return hr;
+ }
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Read the specified memory from out-of-process. This function uses DacReadAll() to do the trick.
+//
+// Arguments:
+// address - the address to be read
+// pbBuffer - the buffer to store the read memory
+// cbRequest - the number of bytes requested
+// pcbDone - out parameter; returns the number of bytes read
+//
+// Return Value:
+// S_OK if all the memory is read successfully
+//
+
+HRESULT OOPStackUnwinder::ReadMemory( DWORD64 address,
+ __in_ecount(cbRequest) PVOID pbBuffer,
+ DWORD cbRequest,
+ __out_opt PDWORD pcbDone)
+{
+ _ASSERTE(pcbDone != NULL);
+
+ HRESULT hr = DacReadAll(TO_TADDR(address), pbBuffer, cbRequest, false);
+ if (SUCCEEDED(hr))
+ {
+ *pcbDone = cbRequest;
+
+ // On X64, we need to replace any patches which are within the requested memory range.
+ // This is because the X64 unwinder needs to disassemble the native instructions in order to determine
+ // whether the IP is in an epilog.
+#if defined(_TARGET_AMD64_)
+ MemoryRange range(dac_cast<PTR_VOID>((TADDR)address), cbRequest);
+ hr = DacReplacePatchesInHostMemory(range, pbBuffer);
+#endif // _TARGET_AMD64_
+ }
+ else
+ {
+ *pcbDone = 0;
+ }
+
+ return hr;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Given a control PC, return the base of the module it is in. For jitted managed code, this is the
+// start of the code heap.
+//
+// Arguments:
+// address - the specified address
+// pdwBase - out parameter; returns the module base
+//
+// Return Value:
+// S_OK if we retrieve the module base successfully;
+// E_FAIL otherwise
+//
+
+HRESULT OOPStackUnwinder::GetModuleBase( DWORD64 address,
+ __out PDWORD64 pdwBase)
+{
+ GetRuntimeStackWalkInfo(address, reinterpret_cast<UINT_PTR *>(pdwBase), NULL);
+ return ((*pdwBase == NULL) ? E_FAIL : S_OK);
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Given a control PC, return the function entry of the functoin it is in.
+//
+// Arguments:
+// address - the specified IP
+// pBuffer - the buffer to store the retrieved function entry
+// cbBuffer - the size of the buffer
+//
+// Return Value:
+// S_OK if we retrieve the function entry successfully;
+// E_INVALIDARG if the buffer is too small;
+// E_FAIL otherwise
+//
+
+HRESULT OOPStackUnwinder::GetFunctionEntry( DWORD64 address,
+ __out_ecount(cbBuffer) PVOID pBuffer,
+ DWORD cbBuffer)
+{
+ if (cbBuffer < sizeof(RUNTIME_FUNCTION))
+ {
+ return E_INVALIDARG;
+ }
+
+ PVOID pFuncEntry = NULL;
+ GetRuntimeStackWalkInfo(address, NULL, reinterpret_cast<UINT_PTR *>(&pFuncEntry));
+ if (pFuncEntry == NULL)
+ {
+ return E_FAIL;
+ }
+
+ memcpy(pBuffer, pFuncEntry, cbBuffer);
+ return S_OK;
+}
diff --git a/src/unwinder/unwinder.h b/src/unwinder/unwinder.h
new file mode 100644
index 0000000000..21d64eae3f
--- /dev/null
+++ b/src/unwinder/unwinder.h
@@ -0,0 +1,64 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+//
+
+#ifndef __unwinder_h__
+#define __unwinder_h__
+
+
+//---------------------------------------------------------------------------------------
+//
+// OOPStackUnwinder is the abstract base class for unwinding stack frames. Each of the two 64-bit platforms
+// has its own derived class. Although the name of this class and its derived classes have changed, they
+// are actually borrowed from dbghelp.dll. (StackWalk64() is built on top of these classes.) We have ripped
+// out everything we don't need such as symbol lookup and various state, and keep just enough code to support
+// VirtualUnwind(). The managed debugging infrastructure can't call RtlVirtualUnwind() because it doesn't
+// work from out-of-processr
+//
+// Notes:
+// To see what we have changed in the borrowed source, you can diff the original version and our version.
+// For example, on X64, you can diff clr\src\Debug\daccess\amd64\dbs_stack_x64.cpp (the original) and
+// clr\src\Debug\daccess\amd64\unwinder_amd64.cpp.
+//
+
+class OOPStackUnwinder
+{
+public:
+ // Unwind the given CONTEXT to the caller CONTEXT. The CONTEXT will be overwritten.
+ // Derived classes are expected to override this function for each processor architecture.
+ virtual BOOL Unwind(T_CONTEXT * pContext) = 0;
+
+protected:
+#if !defined(_TARGET_ARM_) && !defined(_TARGET_ARM64_)
+ // the unwind info on ARM is significantly different than AMD64, as such we don't need this function.
+ // Read an UNWIND_INFO structure given its address. The UNWIND_INFO structure is variable sized.
+ virtual UNWIND_INFO * GetUnwindInfo(TADDR taUnwindInfo);
+#endif // !_TARGET_ARM_ && !_TARGET_ARM64_
+
+ // This is a simple wrapper over ReadMemory(). Unlike ReadMemory(), it fails if we don't successfully
+ // read all the specified bytes.
+ virtual HRESULT ReadAllMemory( DWORD64 address,
+ __in_ecount(cbRequest) PVOID pbBuffer,
+ DWORD cbRequest);
+
+ // Read the specified memory.
+ virtual HRESULT ReadMemory( DWORD64 address,
+ __in_ecount(cbRequest) PVOID pbBuffer,
+ DWORD cbRequest,
+ __out_opt PDWORD pcbDone);
+
+ // Given a control PC, return the base of the module it is in. For jitted managed code, this is the
+ // start of the code heap.
+ virtual HRESULT GetModuleBase( DWORD64 address,
+ __out PDWORD64 pdwBase);
+
+ // Given a control PC, return the function entry of the functoin it is in.
+ virtual HRESULT GetFunctionEntry( DWORD64 address,
+ __out_ecount(cbBuffer) PVOID pBuffer,
+ DWORD cbBuffer);
+};
+
+#endif // __unwinder_h__
diff --git a/src/unwinder/unwinder.targets b/src/unwinder/unwinder.targets
new file mode 100644
index 0000000000..18cd2bfa7c
--- /dev/null
+++ b/src/unwinder/unwinder.targets
@@ -0,0 +1,60 @@
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+
+ <!--Import the settings-->
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\clr.props" />
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\dac.props" />
+ <Import Project="$(_NTDRIVE)$(_NTROOT)\ndp\clr\src\debug\SetDebugTargetLocal.props" />
+ <!--Leaf project Properties-->
+ <PropertyGroup>
+ <UseStl Condition="'$(BuildForCoreSystem)' != 'true'">true</UseStl>
+
+ <UserIncludes>
+ $(UserIncludes);
+ $(ClrSrcDirectory)\unwinder;
+ $(ClrSrcDirectory)\debug\daccess;
+ $(ClrSrcDirectory)\vm;
+ $(ClrSrcDirectory)\vm\$(TargetCpu);
+ $(ClrSrcDirectory)\debug\inc;
+ $(ClrSrcDirectory)\debug\inc\$(TargetCpu);
+ $(ClrSrcDirectory)\debug\inc\dump;
+ $(ClrSrcDirectory)\debug\ee;
+ $(ClrSrcDirectory)\inc;
+ $(VCToolsIncPath);
+ </UserIncludes>
+
+ <CDefines>$(CDefines);UNICODE;_UNICODE;$(USER_SPECIFIC_C_DEFINES);FEATURE_NO_HOST</CDefines>
+
+ <OutputName Condition="'$(OutputName)' == ''">unwinder_dac</OutputName>
+ <OutputPath>$(ClrLibDest)</OutputPath>
+ <TargetType>LIBRARY</TargetType>
+ <PCHHeader>stdafx.h</PCHHeader>
+ <EnableCxxPCHHeaders>true</EnableCxxPCHHeaders>
+ <PCHCompile>$(ClrSrcDirectory)\unwinder\stdafx.cpp</PCHCompile>
+
+ <UnwinderSourcesDir>$(ClrSrcDirectory)\unwinder</UnwinderSourcesDir>
+ <Amd64SourcesDir>$(ClrSrcDirectory)\unwinder\amd64</Amd64SourcesDir>
+ <ArmSourcesDir>$(ClrSrcDirectory)\unwinder\arm</ArmSourcesDir>
+ <Arm64SourcesDir>$(ClrSrcDirectory)\unwinder\arm64</Arm64SourcesDir>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <CppCompile Include="$(UnwinderSourcesDir)\unwinder.cpp" />
+ </ItemGroup>
+
+ <!-- AMD64_SOURCES -->
+ <ItemGroup Condition="'$(TargetArch)' == 'amd64'">
+ <CppCompile Include="$(Amd64SourcesDir)\unwinder_amd64.cpp" />
+ </ItemGroup>
+ <!-- ARM_SOURCES -->
+ <ItemGroup Condition="'$(TargetArch)' == 'arm'">
+ <CppCompile Include="$(ArmSourcesDir)\unwinder_arm.cpp" />
+ </ItemGroup>
+ <!-- ARM64_SOURCES -->
+ <ItemGroup Condition="'$(TargetArch)' == 'arm64'">
+ <CppCompile Include="$(Arm64SourcesDir)\unwinder_arm64.cpp" />
+ </ItemGroup>
+
+ <Import Project="$(Clrbase)\clr.targets" />
+
+</Project>
+