summaryrefslogtreecommitdiff
path: root/src/debug/daccess/arm/unwinder_arm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/debug/daccess/arm/unwinder_arm.cpp')
-rw-r--r--src/debug/daccess/arm/unwinder_arm.cpp1469
1 files changed, 1469 insertions, 0 deletions
diff --git a/src/debug/daccess/arm/unwinder_arm.cpp b/src/debug/daccess/arm/unwinder_arm.cpp
new file mode 100644
index 0000000000..c2463f58f7
--- /dev/null
+++ b/src/debug/daccess/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;
+}