diff options
Diffstat (limited to 'src/debug/daccess/arm64/unwinder_arm64.cpp')
-rw-r--r-- | src/debug/daccess/arm64/unwinder_arm64.cpp | 1580 |
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; +} |