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