From 6c2c7994f1412e8aa504800c7164de875c350fc1 Mon Sep 17 00:00:00 2001 From: Jan Vorlicek Date: Tue, 3 Feb 2015 16:02:18 -0800 Subject: Move the windows unwinder code out of the debug folder. It is a preparation for using the DAC unwinder code as an unwinder for the jitted code on Linux, because the jitter generates windows style unwind info. The unwinder is build as a static library and linked to mscordac. [tfs-changeset: 1409640] --- src/CMakeLists.txt | 1 + src/debug/daccess/CMakeLists.txt | 2 - src/debug/daccess/amd64/dbs_stack_x64.cpp | 1500 ------------------- src/debug/daccess/amd64/unwinder_amd64.cpp | 1225 --------------- src/debug/daccess/amd64/unwinder_amd64.h | 57 - src/debug/daccess/arm/unwinder_arm.cpp | 1469 ------------------ src/debug/daccess/arm/unwinder_arm.h | 55 - src/debug/daccess/arm64/unwinder_arm64.cpp | 1580 -------------------- src/debug/daccess/arm64/unwinder_arm64.h | 55 - src/debug/daccess/daccess.targets | 6 - src/debug/daccess/unwinder.cpp | 163 -- src/debug/daccess/unwinder.h | 64 - src/dirs.proj | 1 + src/dlls/mscordac/CMakeLists.txt | 1 + src/dlls/mscordac/mscordac.targets | 3 + src/unwinder/.gitmirror | 1 + src/unwinder/CMakeLists.txt | 22 + src/unwinder/amd64/.gitmirror | 1 + src/unwinder/amd64/dbs_stack_x64.cpp | 1500 +++++++++++++++++++ src/unwinder/amd64/unwinder_amd64.cpp | 1225 +++++++++++++++ src/unwinder/amd64/unwinder_amd64.h | 57 + src/unwinder/arm/.gitmirror | 1 + src/unwinder/arm/unwinder_arm.cpp | 1469 ++++++++++++++++++ src/unwinder/arm/unwinder_arm.h | 55 + src/unwinder/arm64/.gitmirror | 1 + src/unwinder/arm64/unwinder_arm64.cpp | 1580 ++++++++++++++++++++ src/unwinder/arm64/unwinder_arm64.h | 55 + src/unwinder/dac/.gitmirror | 1 + src/unwinder/dac/CMakeLists.txt | 10 + src/unwinder/dac/dirs.proj | 18 + src/unwinder/dac/hostlocal/.gitmirror | 1 + src/unwinder/dac/hostlocal/unwinder_dac.nativeproj | 13 + src/unwinder/dac/hostwinamd64/.gitmirror | 1 + src/unwinder/dac/hostwinamd64/CMakeLists.txt | 5 + .../dac/hostwinamd64/unwinder_dac.nativeproj | 15 + src/unwinder/dac/hostwinx86/.gitmirror | 1 + .../dac/hostwinx86/unwinder_dac.nativeproj | 15 + src/unwinder/dirs.proj | 19 + src/unwinder/stdafx.cpp | 13 + src/unwinder/stdafx.h | 19 + src/unwinder/unwinder.cpp | 163 ++ src/unwinder/unwinder.h | 64 + src/unwinder/unwinder.targets | 60 + 43 files changed, 6391 insertions(+), 6176 deletions(-) delete mode 100644 src/debug/daccess/amd64/dbs_stack_x64.cpp delete mode 100644 src/debug/daccess/amd64/unwinder_amd64.cpp delete mode 100644 src/debug/daccess/amd64/unwinder_amd64.h delete mode 100644 src/debug/daccess/arm/unwinder_arm.cpp delete mode 100644 src/debug/daccess/arm/unwinder_arm.h delete mode 100644 src/debug/daccess/arm64/unwinder_arm64.cpp delete mode 100644 src/debug/daccess/arm64/unwinder_arm64.h delete mode 100644 src/debug/daccess/unwinder.cpp delete mode 100644 src/debug/daccess/unwinder.h create mode 100644 src/unwinder/.gitmirror create mode 100644 src/unwinder/CMakeLists.txt create mode 100644 src/unwinder/amd64/.gitmirror create mode 100644 src/unwinder/amd64/dbs_stack_x64.cpp create mode 100644 src/unwinder/amd64/unwinder_amd64.cpp create mode 100644 src/unwinder/amd64/unwinder_amd64.h create mode 100644 src/unwinder/arm/.gitmirror create mode 100644 src/unwinder/arm/unwinder_arm.cpp create mode 100644 src/unwinder/arm/unwinder_arm.h create mode 100644 src/unwinder/arm64/.gitmirror create mode 100644 src/unwinder/arm64/unwinder_arm64.cpp create mode 100644 src/unwinder/arm64/unwinder_arm64.h create mode 100644 src/unwinder/dac/.gitmirror create mode 100644 src/unwinder/dac/CMakeLists.txt create mode 100644 src/unwinder/dac/dirs.proj create mode 100644 src/unwinder/dac/hostlocal/.gitmirror create mode 100644 src/unwinder/dac/hostlocal/unwinder_dac.nativeproj create mode 100644 src/unwinder/dac/hostwinamd64/.gitmirror create mode 100644 src/unwinder/dac/hostwinamd64/CMakeLists.txt create mode 100644 src/unwinder/dac/hostwinamd64/unwinder_dac.nativeproj create mode 100644 src/unwinder/dac/hostwinx86/.gitmirror create mode 100644 src/unwinder/dac/hostwinx86/unwinder_dac.nativeproj create mode 100644 src/unwinder/dirs.proj create mode 100644 src/unwinder/stdafx.cpp create mode 100644 src/unwinder/stdafx.h create mode 100644 src/unwinder/unwinder.cpp create mode 100644 src/unwinder/unwinder.h create mode 100644 src/unwinder/unwinder.targets (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 30b8a199a9..35914f2f10 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -30,6 +30,7 @@ add_subdirectory(strongname) add_subdirectory(binder) add_subdirectory(classlibnative) add_subdirectory(dlls) +add_subdirectory(unwinder) if(WIN32) add_subdirectory(ipcman) diff --git a/src/debug/daccess/CMakeLists.txt b/src/debug/daccess/CMakeLists.txt index 92dce0ef0e..5c012ce13f 100644 --- a/src/debug/daccess/CMakeLists.txt +++ b/src/debug/daccess/CMakeLists.txt @@ -38,9 +38,7 @@ if(IS_64BIT_BUILD EQUAL 1) set(DACCESS_SOURCES ${DACCESS_SOURCES} - unwinder.cpp amd64/primitives.cpp - amd64/unwinder_amd64.cpp ) else(IS_64BIT_BUILD EQUAL 1) set(DACCESS_SOURCES diff --git a/src/debug/daccess/amd64/dbs_stack_x64.cpp b/src/debug/daccess/amd64/dbs_stack_x64.cpp deleted file mode 100644 index 83f2a00abf..0000000000 --- a/src/debug/daccess/amd64/dbs_stack_x64.cpp +++ /dev/null @@ -1,1500 +0,0 @@ -// -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -//---------------------------------------------------------------------------- -// - -// -// Stack unwinding implementation for x64. -// - -// -//---------------------------------------------------------------------------- - -#include "pch.cpp" -#pragma hdrstop - -//---------------------------------------------------------------------------- -// -// Copied OS code. -// -// This must be kept in sync with the system unwinder. -// base\ntos\rtl\amd64\exdsptch.c -// -//---------------------------------------------------------------------------- - -// -// Lookup table providing the number of slots used by each unwind code. -// - -UCHAR -DbsX64StackUnwinder::s_UnwindOpSlotTable[] = -{ - 1, // UWOP_PUSH_NONVOL - 2, // UWOP_ALLOC_LARGE (or 3, special cased in lookup code) - 1, // UWOP_ALLOC_SMALL - 1, // UWOP_SET_FPREG - 2, // UWOP_SAVE_NONVOL - 3, // UWOP_SAVE_NONVOL_FAR - 2, // UWOP_SAVE_XMM - 3, // UWOP_SAVE_XMM_FAR - 2, // UWOP_SAVE_XMM128 - 3, // UWOP_SAVE_XMM128_FAR - 1 // UWOP_PUSH_MACHFRAME -}; - -// -// ****** temp - defin elsewhere ****** -// - -#define SIZE64_PREFIX 0x48 -#define ADD_IMM8_OP 0x83 -#define ADD_IMM32_OP 0x81 -#define JMP_IMM8_OP 0xeb -#define JMP_IMM32_OP 0xe9 -#define JMP_IND_OP 0xff -#define LEA_OP 0x8d -#define REP_PREFIX 0xf3 -#define POP_OP 0x58 -#define RET_OP 0xc3 -#define RET_OP_2 0xc2 - -#define IS_REX_PREFIX(x) (((x) & 0xf0) == 0x40) - -HRESULT -DbsX64StackUnwinder::UnwindPrologue( - __in ULONG64 ImageBase, - __in ULONG64 ControlPc, - __in ULONG64 FrameBase, - __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __inout PAMD64_CONTEXT ContextRecord - ) - -/*++ - -Routine Description: - - This function processes unwind codes and reverses the state change - effects of a prologue. If the specified unwind information contains - chained unwind information, then that prologue is unwound recursively. - As the prologue is unwound state changes are recorded in the specified - context structure and optionally in the specified context pointers - structures. - -Arguments: - - ImageBase - Supplies the base address of the image that contains the - function being unwound. - - ControlPc - Supplies the address where control left the specified - function. - - FrameBase - Supplies the base of the stack frame subject function stack - frame. - - FunctionEntry - Supplies the address of the function table entry for the - specified function. - - ContextRecord - Supplies the address of a context record. - ---*/ - -{ - - HRESULT Status = E_UNEXPECTED; - ULONG64 FloatingAddress; - PAMD64_M128 FloatingRegister; - ULONG FrameOffset; - ULONG Index; - ULONG64 IntegerAddress; - PULONG64 IntegerRegister; - BOOLEAN MachineFrame; - ULONG OpInfo; - ULONG PrologOffset; - ULONG64 ReturnAddress; - ULONG64 StackAddress; - ULONG64 UnwindInfoBuffer[32]; - PAMD64_UNWIND_INFO UnwindInfo; - ULONG UnwindOp; - - // - // Process the unwind codes. - // - - FloatingRegister = &ContextRecord->Xmm0; - IntegerRegister = &ContextRecord->Rax; - Index = 0; - MachineFrame = FALSE; - PrologOffset = (ULONG)(ControlPc - (FunctionEntry->BeginAddress + ImageBase)); - - m_Services->Status(1, "Prol: RIP %I64X, 0x%X bytes in function at %I64X\n", - ControlPc, PrologOffset, - FunctionEntry->BeginAddress + ImageBase); - m_Services->Status(1, "Prol: Read unwind info at %I64X\n", - FunctionEntry->UnwindInfoAddress + ImageBase); - - if ((Status = - GetUnwindInfo(ImageBase, FunctionEntry->UnwindInfoAddress, - false, - UnwindInfoBuffer, sizeof(UnwindInfoBuffer), - (PVOID*)&UnwindInfo)) != S_OK) { - m_Services->Status(1, "Prol: Unable to read unwind info\n"); - return Status; - } - - m_Services->Status(1, " Unwind info has 0x%X codes\n", - UnwindInfo->CountOfCodes); - - while (Index < UnwindInfo->CountOfCodes) { - - m_Services->Status(1, " %02X: Code %X offs %03X, RSP %I64X\n", - Index, UnwindInfo->UnwindCode[Index].UnwindOp, - UnwindInfo->UnwindCode[Index].CodeOffset, - ContextRecord->Rsp); - - // - // If the prologue offset is greater than the next unwind code offset, - // then simulate the effect of the unwind code. - // - - UnwindOp = UnwindInfo->UnwindCode[Index].UnwindOp; - if (UnwindOp > AMD64_UWOP_PUSH_MACHFRAME) { - m_Services->Status(1, "Prol: Invalid unwind op %X at index %X\n", - UnwindOp, Index); - goto Fail; - } - - OpInfo = UnwindInfo->UnwindCode[Index].OpInfo; - if (PrologOffset >= UnwindInfo->UnwindCode[Index].CodeOffset) { - switch (UnwindOp) { - - // - // Push nonvolatile integer register. - // - // The operation information is the register number of the - // register than was pushed. - // - - case AMD64_UWOP_PUSH_NONVOL: - IntegerAddress = ContextRecord->Rsp; - if ((Status = m_Services-> - ReadAllMemory(IntegerAddress, - &IntegerRegister[OpInfo], - sizeof(ULONG64))) != S_OK) { - m_Services->Status(1, "Prol: Op %X memory " - "read failed at %I64X\n", - UnwindOp, IntegerAddress); - goto Fail; - } - - ContextRecord->Rsp += 8; - break; - - // - // Allocate a large sized area on the stack. - // - // The operation information determines if the size is - // 16- or 32-bits. - // - - case AMD64_UWOP_ALLOC_LARGE: - Index += 1; - FrameOffset = UnwindInfo->UnwindCode[Index].FrameOffset; - if (OpInfo != 0) { - Index += 1; - FrameOffset += (UnwindInfo->UnwindCode[Index].FrameOffset << 16); - } else { - // The 16-bit form is scaled. - FrameOffset *= 8; - } - - ContextRecord->Rsp += FrameOffset; - break; - - // - // Allocate a small sized area on the stack. - // - // The operation information is the size of the unscaled - // allocation size (8 is the scale factor) minus 8. - // - - case AMD64_UWOP_ALLOC_SMALL: - ContextRecord->Rsp += (OpInfo * 8) + 8; - break; - - // - // Establish the the frame pointer register. - // - // The operation information is not used. - // - - case AMD64_UWOP_SET_FPREG: - ContextRecord->Rsp = IntegerRegister[UnwindInfo->FrameRegister]; - ContextRecord->Rsp -= UnwindInfo->FrameOffset * 16; - break; - - // - // Save nonvolatile integer register on the stack using a - // 16-bit displacment. - // - // The operation information is the register number. - // - - case AMD64_UWOP_SAVE_NONVOL: - Index += 1; - FrameOffset = UnwindInfo->UnwindCode[Index].FrameOffset * 8; - IntegerAddress = FrameBase + FrameOffset; - if ((Status = m_Services-> - ReadAllMemory(IntegerAddress, - &IntegerRegister[OpInfo], - sizeof(ULONG64))) != S_OK) { - m_Services->Status(1, "Prol: Op %X memory read " - "failed at %I64X\n", - UnwindOp, IntegerAddress); - goto Fail; - } - break; - - // - // Save nonvolatile integer register on the stack using a - // 32-bit displacment. - // - // The operation information is the register number. - // - - case AMD64_UWOP_SAVE_NONVOL_FAR: - Index += 2; - FrameOffset = UnwindInfo->UnwindCode[Index - 1].FrameOffset; - FrameOffset += UnwindInfo->UnwindCode[Index].FrameOffset << 16; - IntegerAddress = FrameBase + FrameOffset; - if ((Status = m_Services-> - ReadAllMemory(IntegerAddress, - &IntegerRegister[OpInfo], - sizeof(ULONG64))) != S_OK) { - m_Services->Status(1, "Prol: Op %X memory read " - "failed at %I64X\n", - UnwindOp, IntegerAddress); - goto Fail; - } - break; - - // - // Save a nonvolatile XMM(64) register on the stack using a - // 16-bit displacement. - // - // The operation information is the register number. - // - - case AMD64_UWOP_SAVE_XMM: - Index += 1; - FrameOffset = UnwindInfo->UnwindCode[Index].FrameOffset * 8; - FloatingAddress = FrameBase + FrameOffset; - FloatingRegister[OpInfo].High = 0; - if ((Status = m_Services-> - ReadAllMemory(FloatingAddress, - &FloatingRegister[OpInfo].Low, - sizeof(ULONG64))) != S_OK) { - m_Services->Status(1, "Prol: Op %X memory read " - "failed at %I64X\n", - UnwindOp, FloatingAddress); - goto Fail; - } - break; - - // - // Save a nonvolatile XMM(64) register on the stack using a - // 32-bit displacement. - // - // The operation information is the register number. - // - - case AMD64_UWOP_SAVE_XMM_FAR: - Index += 2; - FrameOffset = UnwindInfo->UnwindCode[Index - 1].FrameOffset; - FrameOffset += UnwindInfo->UnwindCode[Index].FrameOffset << 16; - FloatingAddress = FrameBase + FrameOffset; - FloatingRegister[OpInfo].High = 0; - if ((Status = m_Services-> - ReadAllMemory(FloatingAddress, - &FloatingRegister[OpInfo].Low, - sizeof(ULONG64))) != S_OK) { - m_Services->Status(1, "Prol: Op %X memory read " - "failed at %I64X\n", - UnwindOp, FloatingAddress); - goto Fail; - } - break; - - // - // Save a nonvolatile XMM(128) register on the stack using a - // 16-bit displacement. - // - // The operation information is the register number. - // - - case AMD64_UWOP_SAVE_XMM128: - Index += 1; - FrameOffset = UnwindInfo->UnwindCode[Index].FrameOffset * 16; - FloatingAddress = FrameBase + FrameOffset; - if ((Status = m_Services-> - ReadAllMemory(FloatingAddress, - &FloatingRegister[OpInfo], - sizeof(AMD64_M128))) != S_OK) { - m_Services->Status(1, "Prol: Op %X memory read " - "failed at %I64X\n", - UnwindOp, FloatingAddress); - goto Fail; - } - break; - - // - // Save a nonvolatile XMM(128) register on the stack using a - // 32-bit displacement. - // - // The operation information is the register number. - // - - case AMD64_UWOP_SAVE_XMM128_FAR: - Index += 2; - FrameOffset = UnwindInfo->UnwindCode[Index - 1].FrameOffset; - FrameOffset += UnwindInfo->UnwindCode[Index].FrameOffset << 16; - FloatingAddress = FrameBase + FrameOffset; - if ((Status = m_Services-> - ReadAllMemory(FloatingAddress, - &FloatingRegister[OpInfo], - sizeof(AMD64_M128))) != S_OK) { - m_Services->Status(1, "Prol: Op %X memory read " - "failed at %I64X\n", - UnwindOp, FloatingAddress); - goto Fail; - } - break; - - // - // Push a machine frame on the stack. - // - // The operation information determines whether the machine - // frame contains an error code or not. - // - - case AMD64_UWOP_PUSH_MACHFRAME: - MachineFrame = TRUE; - ReturnAddress = ContextRecord->Rsp; - StackAddress = ContextRecord->Rsp + (3 * 8); - if (OpInfo != 0) { - ReturnAddress += 8; - StackAddress += 8; - } - - m_RestartFrame = true; - m_TrapAddr = ReturnAddress - - FIELD_OFFSET(AMD64_KTRAP_FRAME, Rip); - - if ((Status = m_Services-> - ReadAllMemory(ReturnAddress, - &ContextRecord->Rip, - sizeof(ULONG64))) != S_OK) { - m_Services->Status(1, "Prol: Op %X memory " - "read 1 failed at %I64X\n", - UnwindOp, ReturnAddress); - goto Fail; - } - if ((Status = m_Services-> - ReadAllMemory(StackAddress, - &ContextRecord->Rsp, - sizeof(ULONG64))) != S_OK) { - m_Services->Status(1, "Prol: Op %X memory " - "read 2 failed at %I64X\n", - UnwindOp, StackAddress); - goto Fail; - } - break; - - // - // Unused codes. - // - - default: - break; - } - - Index += 1; - - } else { - - // - // Skip this unwind operation by advancing the slot index by the - // number of slots consumed by this operation. - // - - Index += s_UnwindOpSlotTable[UnwindOp]; - - // - // Special case any unwind operations that can consume a variable - // number of slots. - // - - switch (UnwindOp) { - - // - // A non-zero operation information indicates that an - // additional slot is consumed. - // - - case AMD64_UWOP_ALLOC_LARGE: - if (OpInfo != 0) { - Index += 1; - } - - break; - - // - // No other special cases. - // - - default: - break; - } - } - } - - // - // If chained unwind information is specified, then recursively unwind - // the chained information. Otherwise, determine the return address if - // a machine frame was not encountered during the scan of the unwind - // codes. - // - - if ((UnwindInfo->Flags & AMD64_UNW_FLAG_CHAININFO) != 0) { - - _PIMAGE_RUNTIME_FUNCTION_ENTRY ChainEntry; - - Index = UnwindInfo->CountOfCodes; - if ((Index & 1) != 0) { - Index += 1; - } - - // GetUnwindInfo looks for CHAININFO and reads - // the trailing RUNTIME_FUNCTION so we can just - // directly use the data sitting in UnwindInfo. - ChainEntry = (_PIMAGE_RUNTIME_FUNCTION_ENTRY) - &UnwindInfo->UnwindCode[Index]; - - m_Services->Status(1, " Chain with entry at %I64X\n", - FunctionEntry->UnwindInfoAddress + ImageBase + - (ULONG64)((PUCHAR)&UnwindInfo->UnwindCode[Index] - - (PUCHAR)UnwindInfo)); - - Status = UnwindPrologue(ImageBase, - ControlPc, - FrameBase, - ChainEntry, - ContextRecord); - - FreeUnwindInfo(UnwindInfo, UnwindInfoBuffer); - return Status; - - } else { - FreeUnwindInfo(UnwindInfo, UnwindInfoBuffer); - - if (MachineFrame == FALSE) { - if ((Status = m_Services-> - ReadAllMemory(ContextRecord->Rsp, - &ContextRecord->Rip, - sizeof(ULONG64))) != S_OK) { - return Status; - } - ContextRecord->Rsp += 8; - } - - m_Services->Status(1, "Prol: Returning with RIP %I64X, RSP %I64X\n", - ContextRecord->Rip, ContextRecord->Rsp); - return S_OK; - } - - Fail: - FreeUnwindInfo(UnwindInfo, UnwindInfoBuffer); - m_Services->Status(1, "Prol: Unwind failed, 0x%08X\n", Status); - return Status; -} - -HRESULT -DbsX64StackUnwinder::VirtualUnwind( - __in ULONG64 ImageBase, - __in ULONG64 ControlPc, - __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __inout PAMD64_CONTEXT ContextRecord, - __out PULONG64 EstablisherFrame - ) - -/*++ - -Routine Description: - - This function virtually unwinds the specified function by executing its - prologue code backward or its epilogue code forward. - - If a context pointers record is specified, then the address where each - nonvolatile registers is restored from is recorded in the appropriate - element of the context pointers record. - -Arguments: - - ImageBase - Supplies the base address of the image that contains the - function being unwound. - - ControlPc - Supplies the address where control left the specified - function. - - FunctionEntry - Supplies the address of the function table entry for the - specified function. - - ContextRecord - Supplies the address of a context record. - - EstablisherFrame - Supplies a pointer to a variable that receives the - the establisher frame pointer value. - ---*/ - -{ - - HRESULT Status; - ULONG64 BranchTarget; - LONG Displacement; - ULONG FrameRegister; - ULONG Index; - LOGICAL InEpilogue; - PULONG64 IntegerRegister; - PUCHAR NextByte; - _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryFunctionEntry; - ULONG PrologOffset; - ULONG RegisterNumber; - PAMD64_UNWIND_INFO UnwindInfo; - ULONG64 UnwindInfoBuffer[8]; - ULONG Done; - UCHAR InstrBuffer[32]; - ULONG InstrBytes; - ULONG Bytes; - ULONG UnwindFrameReg; - - // - // If the specified function does not use a frame pointer, then the - // establisher frame is the contents of the stack pointer. This may - // not actually be the real establisher frame if control left the - // function from within the prologue. In this case the establisher - // frame may be not required since control has not actually entered - // the function and prologue entries cannot refer to the establisher - // frame before it has been established, i.e., if it has not been - // established, then no save unwind codes should be encountered during - // the unwind operation. - // - // If the specified function uses a frame pointer and control left the - // function outside of the prologue or the unwind information contains - // a chained information structure, then the establisher frame is the - // contents of the frame pointer. - // - // If the specified function uses a frame pointer and control left the - // function from within the prologue, then the set frame pointer unwind - // code must be looked up in the unwind codes to detetermine if the - // contents of the stack pointer or the contents of the frame pointer - // should be used for the establisher frame. This may not actually be - // the real establisher frame. In this case the establisher frame may - // not be required since control has not actually entered the function - // and prologue entries cannot refer to the establisher frame before it - // has been established, i.e., if it has not been established, then no - // save unwind codes should be encountered during the unwind operation. - // - // N.B. The correctness of these assumptions is based on the ordering of - // unwind codes. - // - - if ((Status = GetUnwindInfo(ImageBase, FunctionEntry->UnwindInfoAddress, - true, - UnwindInfoBuffer, sizeof(UnwindInfoBuffer), - (PVOID*)&UnwindInfo)) != S_OK) { - return Status; - } - - PrologOffset = (ULONG)(ControlPc - (FunctionEntry->BeginAddress + ImageBase)); - UnwindFrameReg = UnwindInfo->FrameRegister; - if (UnwindFrameReg == 0) { - *EstablisherFrame = ContextRecord->Rsp; - - } else if ((PrologOffset >= UnwindInfo->SizeOfProlog) || - ((UnwindInfo->Flags & AMD64_UNW_FLAG_CHAININFO) != 0)) { - *EstablisherFrame = (&ContextRecord->Rax)[UnwindFrameReg]; - *EstablisherFrame -= UnwindInfo->FrameOffset * 16; - - } else { - - // Read all the data. - if ((Status = GetUnwindInfo(ImageBase, - FunctionEntry->UnwindInfoAddress, - false, - UnwindInfoBuffer, - sizeof(UnwindInfoBuffer), - (PVOID*)&UnwindInfo)) != S_OK) { - return Status; - } - - Index = 0; - while (Index < UnwindInfo->CountOfCodes) { - if (UnwindInfo->UnwindCode[Index].UnwindOp == AMD64_UWOP_SET_FPREG) { - break; - } - - Index += 1; - } - - if (PrologOffset >= UnwindInfo->UnwindCode[Index].CodeOffset) { - *EstablisherFrame = (&ContextRecord->Rax)[UnwindFrameReg]; - *EstablisherFrame -= UnwindInfo->FrameOffset * 16; - - } else { - *EstablisherFrame = ContextRecord->Rsp; - } - - FreeUnwindInfo(UnwindInfo, UnwindInfoBuffer); - } - - if ((Status = m_Services-> - ReadMemory(ControlPc, InstrBuffer, sizeof(InstrBuffer), - &InstrBytes)) != S_OK) { - m_Services->Status(1, "Unable to read instruction stream at %I64X\n", - ControlPc); - - // We need the code to look for epilogue ops. - // It's very rare to be stopped in an epilogue when - // getting a stack trace, so if we can't read the - // code just assume we aren't in an epilogue. - InstrBytes = 0; - } - - // - // If the point at which control left the specified function is in an - // epilogue, then emulate the execution of the epilogue forward and - // return no exception handler. - // - - IntegerRegister = &ContextRecord->Rax; - NextByte = InstrBuffer; - Bytes = InstrBytes; - - // - // Check for one of: - // - // add rsp, imm8 - // or - // add rsp, imm32 - // or - // lea rsp, -disp8[fp] - // or - // lea rsp, -disp32[fp] - // - - if (Bytes >= 4 && - (NextByte[0] == SIZE64_PREFIX) && - (NextByte[1] == ADD_IMM8_OP) && - (NextByte[2] == 0xc4)) { - - // - // add rsp, imm8. - // - - NextByte += 4; - Bytes -= 4; - - } else if (Bytes >= 7 && - (NextByte[0] == SIZE64_PREFIX) && - (NextByte[1] == ADD_IMM32_OP) && - (NextByte[2] == 0xc4)) { - - // - // add rsp, imm32. - // - - NextByte += 7; - Bytes -= 7; - - } else if (Bytes >= 4 && - ((NextByte[0] & 0xf8) == SIZE64_PREFIX) && - (NextByte[1] == LEA_OP)) { - - FrameRegister = ((NextByte[0] & 0x7) << 3) | (NextByte[2] & 0x7); - if ((FrameRegister != 0) && - (FrameRegister == UnwindFrameReg)) { - if ((NextByte[2] & 0xf8) == 0x60) { - - // - // lea rsp, disp8[fp]. - // - - NextByte += 4; - Bytes -= 4; - - } else if (Bytes >= 7 && - (NextByte[2] &0xf8) == 0xa0) { - - // - // lea rsp, disp32[fp]. - // - - NextByte += 7; - Bytes -= 7; - } - } - } - - // - // Check for any number of: - // - // pop nonvolatile-integer-register[0..15]. - // - - while (TRUE) { - if (Bytes >= 1 && - (NextByte[0] & 0xf8) == POP_OP) { - NextByte += 1; - Bytes -= 1; - - } else if (Bytes >= 2 && - IS_REX_PREFIX(NextByte[0]) && - ((NextByte[1] & 0xf8) == POP_OP)) { - - NextByte += 2; - Bytes -= 2; - - } else { - break; - } - } - - // - // If the next instruction is a return or an appropriate jump, then - // control is currently in an epilogue and execution of the epilogue - // should be emulated. Otherwise, execution is not in an epilogue and - // the prologue should be unwound. - // - - InEpilogue = FALSE; - if ((Bytes >= 1 && - ((NextByte[0] == RET_OP) || - (NextByte[0] == RET_OP_2))) || - (Bytes >= 2 && - ((NextByte[0] == REP_PREFIX) && (NextByte[1] == RET_OP)))) { - - // - // A return is an unambiguous indication of an epilogue. - // - - InEpilogue = TRUE; - - } else if ((Bytes >= 2 && NextByte[0] == JMP_IMM8_OP) || - (Bytes >= 5 && NextByte[0] == JMP_IMM32_OP)) { - - // - // An unconditional branch to a target that is equal to the start of - // or outside of this routine is logically a call to another function. - // - - BranchTarget = (ULONG64)(NextByte - InstrBuffer) + ControlPc - ImageBase; - if (NextByte[0] == JMP_IMM8_OP) { - BranchTarget += 2 + (CHAR)NextByte[1]; - } else { - BranchTarget += 5 + *((LONG UNALIGNED *)&NextByte[1]); - } - - // - // Determine whether the branch target refers to code within this - // function. If not, then it is an epilogue indicator. - // - // A branch to the start of self implies a recursive call, so - // is treated as an epilogue. - // - - if (BranchTarget < FunctionEntry->BeginAddress || - BranchTarget >= FunctionEntry->EndAddress) { - - _IMAGE_RUNTIME_FUNCTION_ENTRY PrimaryEntryBuffer; - - // - // The branch target is outside of the region described by - // this function entry. See whether it is contained within - // an indirect function entry associated with this same - // function. - // - // If not, then the branch target really is outside of - // this function. - // - - PrimaryFunctionEntry = - SameFunction(FunctionEntry, - ImageBase, - BranchTarget + ImageBase, - &PrimaryEntryBuffer); - - if ((PrimaryFunctionEntry == NULL) || - (BranchTarget == PrimaryFunctionEntry->BeginAddress)) { - - InEpilogue = TRUE; - } - - } else if ((BranchTarget == FunctionEntry->BeginAddress) && - ((UnwindInfo->Flags & AMD64_UNW_FLAG_CHAININFO) == 0)) { - - InEpilogue = TRUE; - } - - } else if (Bytes >= 2 && - (NextByte[0] == JMP_IND_OP) && (NextByte[1] == 0x25)) { - - // - // An unconditional jump indirect. - // - // This is a jmp outside of the function, probably a tail call - // to an import function. - // - - InEpilogue = TRUE; - - } else if (Bytes >= 3 && - ((NextByte[0] & 0xf8) == SIZE64_PREFIX) && - (NextByte[1] == 0xff) && - (NextByte[2] & 0x38) == 0x20) { - - // - // This is an indirect jump opcode: 0x48 0xff /4. The 64-bit - // flag (REX.W) is always redundant here, so its presence is - // overloaded to indicate a branch out of the function - a tail - // call. - // - // Such an opcode is an unambiguous epilogue indication. - // - - InEpilogue = TRUE; - } - - if (InEpilogue != FALSE) { - NextByte = InstrBuffer; - Bytes = InstrBytes; - - // - // Emulate one of (if any): - // - // add rsp, imm8 - // or - // add rsp, imm32 - // or - // lea rsp, disp8[frame-register] - // or - // lea rsp, disp32[frame-register] - // - - if (Bytes >= 1 && - (NextByte[0] & 0xf8) == SIZE64_PREFIX) { - - if (Bytes >= 4 && - NextByte[1] == ADD_IMM8_OP) { - - // - // add rsp, imm8. - // - - ContextRecord->Rsp += (CHAR)NextByte[3]; - NextByte += 4; - Bytes -= 4; - - } else if (Bytes >= 7 && - NextByte[1] == ADD_IMM32_OP) { - - // - // add rsp, imm32. - // - - Displacement = NextByte[3] | (NextByte[4] << 8); - Displacement |= (NextByte[5] << 16) | (NextByte[6] << 24); - ContextRecord->Rsp += Displacement; - NextByte += 7; - Bytes -= 7; - - } else if (Bytes >= 4 && - NextByte[1] == LEA_OP) { - if ((NextByte[2] & 0xf8) == 0x60) { - - // - // lea rsp, disp8[frame-register]. - // - - ContextRecord->Rsp = IntegerRegister[FrameRegister]; - ContextRecord->Rsp += (CHAR)NextByte[3]; - NextByte += 4; - Bytes -= 4; - - } else if (Bytes >= 7 && - (NextByte[2] & 0xf8) == 0xa0) { - - // - // lea rsp, disp32[frame-register]. - // - - Displacement = NextByte[3] | (NextByte[4] << 8); - Displacement |= (NextByte[5] << 16) | (NextByte[6] << 24); - ContextRecord->Rsp = IntegerRegister[FrameRegister]; - ContextRecord->Rsp += Displacement; - NextByte += 7; - Bytes -= 7; - } - } - } - - // - // Emulate any number of (if any): - // - // pop nonvolatile-integer-register. - // - - while (TRUE) { - if (Bytes >= 1 && - (NextByte[0] & 0xf8) == POP_OP) { - - // - // pop nonvolatile-integer-register[0..7] - // - - RegisterNumber = NextByte[0] & 0x7; - if ((Status = m_Services-> - ReadAllMemory(ContextRecord->Rsp, - &IntegerRegister[RegisterNumber], - sizeof(ULONG64))) != S_OK) { - m_Services->Status(1, "Unable to read stack at %I64X\n", - ContextRecord->Rsp); - return Status; - } - ContextRecord->Rsp += 8; - NextByte += 1; - Bytes -= 1; - - } else if (Bytes >= 2 && - IS_REX_PREFIX(NextByte[0]) && - (NextByte[1] & 0xf8) == POP_OP) { - - // - // pop nonvolatile-integer-register[8..15] - // - - RegisterNumber = ((NextByte[0] & 1) << 3) | (NextByte[1] & 0x7); - if ((Status = m_Services-> - ReadAllMemory(ContextRecord->Rsp, - &IntegerRegister[RegisterNumber], - sizeof(ULONG64))) != S_OK) { - m_Services->Status(1, "Unable to read stack at %I64X\n", - ContextRecord->Rsp); - return Status; - } - ContextRecord->Rsp += 8; - NextByte += 2; - Bytes -= 2; - - } else { - break; - } - } - - // - // Emulate return and return null exception handler. - // - // Note: this instruction might in fact be a jmp, however - // we want to emulate a return regardless. - // - - if ((Status = m_Services-> - ReadAllMemory(ContextRecord->Rsp, - &ContextRecord->Rip, - sizeof(ULONG64))) != S_OK) { - m_Services->Status(1, "Unable to read stack at %I64X\n", - ContextRecord->Rsp); - return Status; - } - ContextRecord->Rsp += 8; - return S_OK; - } - - // - // Control left the specified function outside an epilogue. Unwind the - // subject function and any chained unwind information. - // - - return UnwindPrologue(ImageBase, - ControlPc, - *EstablisherFrame, - FunctionEntry, - ContextRecord); -} - -ULONG64 -DbsX64StackUnwinder::LookupPrimaryUnwindInfo( - __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __in ULONG64 ImageBase, - __out _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryEntry - ) - -/*++ - -Routine Description: - - This function determines whether the supplied function entry is a primary - function entry or a chained function entry. If it is a chained function - entry, the unwind information associated with the primary function entry - is returned. - -Arguments: - - FunctionEntry - Supplies a pointer to the function entry for which the - associated primary function entry will be located. - - ImageBase - Supplies the base address of the image containing the - supplied function entry. - - PrimaryEntry - Supplies the address of a variable that receives a pointer - to the primary function entry. - -Return Value: - - A pointer to the unwind information for the primary function entry is - returned as the function value. - ---*/ - -{ - - ULONG Index; - ULONG64 UnwindInfoBuffer[32]; - PAMD64_UNWIND_INFO UnwindInfo; - ULONG UnwindRel; - ULONG64 UnwindAbs; - - // - // Locate the unwind information and determine whether it is chained. - // If the unwind information is chained, then locate the parent function - // entry and loop again. - // - - UnwindRel = FunctionEntry->UnwindInfoAddress; - // Copy the function entry before it becomes invalid. - *PrimaryEntry = *FunctionEntry; - - do { - UnwindAbs = ImageBase + UnwindRel; - if (GetUnwindInfo(ImageBase, UnwindRel, - false, - UnwindInfoBuffer, sizeof(UnwindInfoBuffer), - (PVOID*)&UnwindInfo) != S_OK || - (UnwindInfo->Flags & AMD64_UNW_FLAG_CHAININFO) == 0) { - break; - } - - Index = UnwindInfo->CountOfCodes; - if ((Index & 1) != 0) { - Index += 1; - } - - FunctionEntry = (_PIMAGE_RUNTIME_FUNCTION_ENTRY) - &UnwindInfo->UnwindCode[Index]; - UnwindRel = FunctionEntry->UnwindInfoAddress; - - // Copy the function entry before it becomes invalid. - *PrimaryEntry = *FunctionEntry; - - FreeUnwindInfo(UnwindInfo, UnwindInfoBuffer); - - } while (TRUE); - - return UnwindAbs; -} - -_PIMAGE_RUNTIME_FUNCTION_ENTRY -DbsX64StackUnwinder::SameFunction( - __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __in ULONG64 ImageBase, - __in ULONG64 ControlPc, - __out _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionReturnBuffer - ) - -/*++ - -Routine Description: - - This function determines whether the address supplied by ControlPc lies - anywhere within the function associated with FunctionEntry. - -Arguments: - - FunctionEntry - Supplies a pointer to a function entry (primary or chained) - associated with the function. - - ImageBase - Supplies the base address of the image containing the supplied - function entry. - - ControlPc - Supplies the address that will be tested for inclusion within - the function associated with FunctionEntry. - -Return Value: - - If the address of the unwind information for the specified function is - equal to the address of the unwind information for the control PC, then - a pointer to a function table entry that describes the primary function - table entry is returned as the function value. Otherwise, NULL is returned. - ---*/ - -{ - - _IMAGE_RUNTIME_FUNCTION_ENTRY TargetFunctionEntry; - ULONG64 TargetImageBase; - ULONG64 UnwindInfo1; - ULONG64 UnwindInfo2; - - // - // Find the unwind information referenced by the primary function entry - // associated with the specified function entry. - // - - UnwindInfo1 = LookupPrimaryUnwindInfo(FunctionEntry, ImageBase, - FunctionReturnBuffer); - - // - // Determine the function entry containing the control Pc and similarly - // resolve it's primary function entry. - // - - if (m_Services->GetModuleBase(ControlPc, &TargetImageBase) != S_OK || - m_Services->GetFunctionEntry(ControlPc, - &TargetFunctionEntry, - sizeof(TargetFunctionEntry)) != S_OK) { - return NULL; - } - - UnwindInfo2 = LookupPrimaryUnwindInfo(&TargetFunctionEntry, - TargetImageBase, - FunctionReturnBuffer); - - // - // If the address of the two sets of unwind information are equal, then - // return the address of the primary function entry. Otherwise, return - // NULL. - // - - if (UnwindInfo1 == UnwindInfo2) { - return FunctionReturnBuffer; - - } else { - return NULL; - } -} - -//---------------------------------------------------------------------------- -// -// DbsX64StackUnwinder. -// -//---------------------------------------------------------------------------- - -#define DBHX64_SAVE_TRAP(_DbhFrame) ((_DbhFrame)->Reserved[0]) - -// -// Flags word. -// - -#define DBHX64_IS_RESTART_FLAG (0x1UI64) - -#define DBHX64_GET_IS_RESTART(_DbhFrame) \ - ((((_DbhFrame)->Reserved[2]) & DBHX64_IS_RESTART_FLAG) != 0) -#define DBHX64_SET_IS_RESTART(_DbhFrame, _IsRestart) \ - ((_DbhFrame)->Reserved[2] = \ - (((_DbhFrame)->Reserved[2]) & ~DBHX64_IS_RESTART_FLAG) | \ - ((_IsRestart) ? DBHX64_IS_RESTART_FLAG : 0)) - -DbsX64StackUnwinder::DbsX64StackUnwinder(__in_opt DbsStackServices* Services) - : DbsStackUnwinder(Services, "x64", IMAGE_FILE_MACHINE_AMD64, - sizeof(m_Context), - sizeof(_IMAGE_RUNTIME_FUNCTION_ENTRY), - sizeof(AMD64_UNWIND_INFO), 16, 8, 1) -{ - m_ContextBuffer = &m_Context; -} - -HRESULT -DbsX64StackUnwinder::Unwind(void) -{ - HRESULT Status; - - ClearUnwindDerived(); - - if (SUCCEEDED(Status = BaseUnwind())) - { - return Status; - } - - // - // Unable to do a normal unwind, so check for - // alternate transitions like kernel/user boundaries. - // If this fails just return the original error - // as that's more likely to be interesting. - // - - DWORD64 ImageBase; - _IMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry; - DWORD64 StackPointer; - - if (UnwindNtKernelCallback(&ImageBase, - &FunctionEntry, - sizeof(FunctionEntry), - &StackPointer) != S_OK) - { - return Status; - } - - m_InstructionPointer = ImageBase + FunctionEntry.BeginAddress; - m_CallPointer = m_InstructionPointer; - m_Context.Rip = m_InstructionPointer; - m_StackPointer = StackPointer; - m_Context.Rsp = m_StackPointer; - m_RestartFrame = true; - - return S_OK; -} - -DWORD -DbsX64StackUnwinder:: -GetFullUnwindInfoSize(__in PVOID InfoHeader) -{ - PAMD64_UNWIND_INFO UnwindInfo = (PAMD64_UNWIND_INFO)InfoHeader; - - DWORD UnwindInfoSize = FIELD_OFFSET(AMD64_UNWIND_INFO, UnwindCode) + - UnwindInfo->CountOfCodes * sizeof(AMD64_UNWIND_CODE); - - // An extra alignment code and function entry may be added on to handle - // the chained info case where the chain function entry is just - // beyond the end of the normal code array. - if ((UnwindInfo->Flags & AMD64_UNW_FLAG_CHAININFO) != 0) - { - if ((UnwindInfo->CountOfCodes & 1) != 0) - { - UnwindInfoSize += sizeof(AMD64_UNWIND_CODE); - } - UnwindInfoSize += sizeof(_IMAGE_RUNTIME_FUNCTION_ENTRY); - } - - return UnwindInfoSize; -} - -HRESULT -DbsX64StackUnwinder::DbhStart(__inout LPSTACKFRAME64 StackFrame, - __in DWORD DbhVersion, - __in_bcount(DbhStorageBytes) PVOID DbhStorage, - __in DWORD DbhStorageBytes, - __inout PVOID Context) -{ - HRESULT Status; - - if ((StackFrame->AddrPC.Offset && - StackFrame->AddrPC.Mode != AddrModeFlat) || - (StackFrame->AddrStack.Offset && - StackFrame->AddrStack.Mode != AddrModeFlat) || - (StackFrame->AddrFrame.Offset && - StackFrame->AddrFrame.Mode != AddrModeFlat)) - { - return E_INVALIDARG; - } - - if ((Status = DbsStackUnwinder:: - DbhStart(StackFrame, DbhVersion, DbhStorage, DbhStorageBytes, - Context)) != S_OK) - { - return Status; - } - - // dbghelp doesn't give a context size so we - // have to assume the buffer is large enough. - memcpy(&m_Context, Context, sizeof(m_Context)); - - // - // Override context values from the stack frame if necessary. - // - - if (StackFrame->AddrPC.Offset) - { - m_Context.Rip = StackFrame->AddrPC.Offset; - } - if (StackFrame->AddrStack.Offset) - { - m_Context.Rsp = StackFrame->AddrStack.Offset; - } - if (StackFrame->AddrFrame.Offset) - { - m_Context.Rbp = StackFrame->AddrFrame.Offset; - } - UpdateAbstractPointers(); - m_CallPointer = m_InstructionPointer; - - SetRestart(); - return S_OK; -} - -HRESULT -DbsX64StackUnwinder:: -DbhContinue(__inout LPSTACKFRAME64 StackFrame, - __in DWORD DbhVersion, - __in_bcount(DbhStorageBytes) PVOID DbhStorage, - __in DWORD DbhStorageBytes, - __inout PVOID Context) -{ - HRESULT Status; - - if ((Status = DbsStackUnwinder:: - DbhContinue(StackFrame, DbhVersion, - DbhStorage, DbhStorageBytes, - Context)) != S_OK) - { - return Status; - } - - if (DBHX64_GET_IS_RESTART(StackFrame)) - { - m_RestartFrame = true; - // The base DbhContinue always assumes it - // isn't a restart frame, so override it. - m_CallPointer = m_InstructionPointer; - } - - return Status; -} - -HRESULT -DbsX64StackUnwinder::DbhUpdatePreUnwind(__inout LPSTACKFRAME64 StackFrame) -{ - HRESULT Status; - - if ((Status = DbsStackUnwinder::DbhUpdatePreUnwind(StackFrame)) != S_OK) - { - return Status; - } - - DBHX64_SET_IS_RESTART(StackFrame, m_RestartFrame); - return S_OK; -} - -HRESULT -DbsX64StackUnwinder::DbhUpdatePostUnwind(__inout LPSTACKFRAME64 StackFrame, - __in HRESULT UnwindStatus) -{ - HRESULT Status; - - if ((Status = DbsStackUnwinder::DbhUpdatePostUnwind(StackFrame, - UnwindStatus)) != S_OK) - { - return Status; - } - - // The frame pointer is an artificial value set - // to a pointer below the return address. This - // matches an RBP-chain style of frame while - // also allowing easy access to the return - // address and homed arguments above it. - StackFrame->AddrFrame.Offset = m_FramePointer; - - DBHX64_SAVE_TRAP(StackFrame) = m_TrapAddr; - return S_OK; -} - -void -DbsX64StackUnwinder::UpdateAbstractPointers(void) -{ - m_InstructionPointer = m_Context.Rip; - m_StackPointer = m_Context.Rsp; - m_FramePointer = m_Context.Rbp; -} - -HRESULT -DbsX64StackUnwinder::BaseUnwind(void) -{ - HRESULT Status; - _IMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry; - - Status = m_Services->GetFunctionEntry(m_CallPointer, - &FunctionEntry, - sizeof(FunctionEntry)); - if (Status == S_OK) - { - DWORD64 ImageBase; - DWORD64 EstablisherFrame; - - // - // The return value coming out of mainCRTStartup is set by some - // run-time routine to be 0; this serves to cause an error if someone - // actually does a return from the mainCRTStartup frame. - // - - if ((Status = m_Services-> - GetModuleBase(m_Context.Rip, &ImageBase)) != S_OK || - (Status = VirtualUnwind(ImageBase, - m_Context.Rip, - &FunctionEntry, - &m_Context, - &EstablisherFrame)) != S_OK) - { - return Status; - } - - DWORD64 OldIp = m_InstructionPointer; - - UpdateAbstractPointers(); - UpdateCallPointer(); - m_FramePointer = m_StackPointer - 2 * sizeof(DWORD64); - - // Check for end frame. - if (m_Context.Rip == 0 || - (m_Context.Rip == OldIp && - EstablisherFrame == m_Context.Rsp)) - { - return S_FALSE; - } - } - else if (Status == E_NOINTERFACE) - { - // - // If there's no function entry for a function - // we assume that it's a leaf and that RSP points - // directly to the return address. - // - - m_Services->Status(1, "Leaf %I64X RSP %I64X\n", - m_Context.Rip, m_Context.Rsp); - - if ((Status = m_Services-> - ReadAllMemory(m_Context.Rsp, &m_Context.Rip, - sizeof(m_Context.Rip))) != S_OK) - { - return Status; - } - - // Update the context values to what they should be in - // the caller. - m_Context.Rsp += sizeof(m_Context.Rip); - UpdateAbstractPointers(); - m_CallPointer = m_InstructionPointer - 1; - m_FramePointer = m_StackPointer - 2 * sizeof(DWORD64); - } - else - { - return Status; - } - - m_FrameIndex++; - if (!m_RestartFrame) - { - AdjustForNoReturn(&m_InstructionPointer); - } - return S_OK; -} diff --git a/src/debug/daccess/amd64/unwinder_amd64.cpp b/src/debug/daccess/amd64/unwinder_amd64.cpp deleted file mode 100644 index de7f94f977..0000000000 --- a/src/debug/daccess/amd64/unwinder_amd64.cpp +++ /dev/null @@ -1,1225 +0,0 @@ -// -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -// - -#include "stdafx.h" -#include "unwinder_amd64.h" - -//--------------------------------------------------------------------------------------- -// -// Given the target address of an UNWIND_INFO structure, this function retrieves all the memory used for -// the UNWIND_INFO, including the variable size array of UNWIND_CODE. The function returns a host copy -// of the UNWIND_INFO. -// -// Arguments: -// taUnwindInfo - the target address of an UNWIND_INFO -// -// Return Value: -// Return a host copy of the UNWIND_INFO, including the array of UNWIND_CODE. -// -// Notes: -// The host copy of UNWIND_INFO is created from DAC memory, which will be flushed when the DAC cache -// is flushed (i.e. when the debugee is continued). Thus, the caller doesn't need to worry about freeing -// this memory. -// - -UNWIND_INFO * DacGetUnwindInfo(TADDR taUnwindInfo) -{ - PTR_UNWIND_INFO pUnwindInfo = PTR_UNWIND_INFO(taUnwindInfo); - DWORD cbUnwindInfo = offsetof(UNWIND_INFO, UnwindCode) + - pUnwindInfo->CountOfUnwindCodes * sizeof(UNWIND_CODE); - - // Check if there is a chained unwind info. If so, it has an extra RUNTIME_FUNCTION tagged to the end. - if ((pUnwindInfo->Flags & UNW_FLAG_CHAININFO) != 0) - { - // If there is an odd number of UNWIND_CODE, we need to adjust for alignment. - if ((pUnwindInfo->CountOfUnwindCodes & 1) != 0) - { - cbUnwindInfo += sizeof(UNWIND_CODE); - } - cbUnwindInfo += sizeof(RUNTIME_FUNCTION); - } - return reinterpret_cast(DacInstantiateTypeByAddress(taUnwindInfo, cbUnwindInfo, true)); -} - -//--------------------------------------------------------------------------------------- -// -// This function is just a wrapper over OOPStackUnwinder. The runtime can call this function to -// virtually unwind a CONTEXT out-of-process. -// -// Arguments: -// pContext - This is an in-out parameter. On entry, this is the CONTEXT to be unwound. -// On exit, this is the caller CONTEXT. -// -// Return Value: -// TRUE if the unwinding is successful -// -// Notes: -// This function overwrites the specified CONTEXT to store the caller CONTEXT. -// - -BOOL DacUnwindStackFrame(CONTEXT * pContext, KNONVOLATILE_CONTEXT_POINTERS* pContextPointers) -{ - OOPStackUnwinderAMD64 unwinder; - BOOL res = unwinder.Unwind(pContext); - - if (res && pContextPointers) - { - for (int i = 0; i < 16; i++) - { - *(&pContextPointers->Rax + i) = &pContext->Rax + i; - } - } - - return res; -} - -//--------------------------------------------------------------------------------------- -// -// Unwind the given CONTEXT to the caller CONTEXT. The given CONTEXT will be overwritten. -// -// Arguments: -// pContext - in-out parameter storing the specified CONTEXT on entry and the unwound CONTEXT on exit -// -// Return Value: -// TRUE if the unwinding is successful -// - -BOOL OOPStackUnwinderAMD64::Unwind(CONTEXT * pContext) -{ - HRESULT hr = E_FAIL; - - ULONG64 uControlPC = (DWORD64)dac_cast(::GetIP(pContext)); - - // get the module base - ULONG64 uImageBase; - hr = GetModuleBase(uControlPC, &uImageBase); - if (FAILED(hr)) - { - return FALSE; - } - - // get the function entry - IMAGE_RUNTIME_FUNCTION_ENTRY functionEntry; - hr = GetFunctionEntry(uControlPC, &functionEntry, sizeof(functionEntry)); - if (FAILED(hr)) - { - return FALSE; - } - - // call VirtualUnwind() to do the real work - ULONG64 EstablisherFrame; - hr = VirtualUnwind(uImageBase, uControlPC, &functionEntry, pContext, &EstablisherFrame); - - return (hr == S_OK); -} - - -// -// -// -// -// Everything below is borrowed from dbghelp.dll -// -// -// -// - - -//---------------------------------------------------------------------------- -// -// Copied OS code. -// -// This must be kept in sync with the system unwinder. -// base\ntos\rtl\amd64\exdsptch.c -// -//---------------------------------------------------------------------------- - -// -// Lookup table providing the number of slots used by each unwind code. -// - -UCHAR -OOPStackUnwinderAMD64::s_UnwindOpSlotTable[] = -{ - 1, // UWOP_PUSH_NONVOL - 2, // UWOP_ALLOC_LARGE (or 3, special cased in lookup code) - 1, // UWOP_ALLOC_SMALL - 1, // UWOP_SET_FPREG - 2, // UWOP_SAVE_NONVOL - 3, // UWOP_SAVE_NONVOL_FAR - 2, // UWOP_SAVE_XMM - 3, // UWOP_SAVE_XMM_FAR - 2, // UWOP_SAVE_XMM128 - 3, // UWOP_SAVE_XMM128_FAR - 1 // UWOP_PUSH_MACHFRAME -}; - -// -// ****** temp - defin elsewhere ****** -// - -#define SIZE64_PREFIX 0x48 -#define ADD_IMM8_OP 0x83 -#define ADD_IMM32_OP 0x81 -#define JMP_IMM8_OP 0xeb -#define JMP_IMM32_OP 0xe9 -#define JMP_IND_OP 0xff -#define LEA_OP 0x8d -#define REP_PREFIX 0xf3 -#define POP_OP 0x58 -#define RET_OP 0xc3 -#define RET_OP_2 0xc2 - -#define IS_REX_PREFIX(x) (((x) & 0xf0) == 0x40) - -HRESULT -OOPStackUnwinderAMD64::UnwindPrologue( - __in ULONG64 ImageBase, - __in ULONG64 ControlPc, - __in ULONG64 FrameBase, - __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __inout PCONTEXT ContextRecord - ) - -/*++ - -Routine Description: - - This function processes unwind codes and reverses the state change - effects of a prologue. If the specified unwind information contains - chained unwind information, then that prologue is unwound recursively. - As the prologue is unwound state changes are recorded in the specified - context structure and optionally in the specified context pointers - structures. - -Arguments: - - ImageBase - Supplies the base address of the image that contains the - function being unwound. - - ControlPc - Supplies the address where control left the specified - function. - - FrameBase - Supplies the base of the stack frame subject function stack - frame. - - FunctionEntry - Supplies the address of the function table entry for the - specified function. - - ContextRecord - Supplies the address of a context record. - ---*/ - -{ - - HRESULT Status = E_UNEXPECTED; - ULONG64 FloatingAddress; - M128A* FloatingRegister; - ULONG FrameOffset; - ULONG Index; - ULONG64 IntegerAddress; - PULONG64 IntegerRegister; - BOOLEAN MachineFrame; - ULONG OpInfo; - ULONG PrologOffset; - ULONG64 ReturnAddress; - ULONG64 StackAddress; - PUNWIND_INFO UnwindInfo; - ULONG UnwindOp; - - // - // Process the unwind codes. - // - - FloatingRegister = &ContextRecord->Xmm0; - IntegerRegister = &ContextRecord->Rax; - Index = 0; - MachineFrame = FALSE; - PrologOffset = (ULONG)(ControlPc - (FunctionEntry->BeginAddress + ImageBase)); - - UnwindInfo = DacGetUnwindInfo(ImageBase + FunctionEntry->UnwindInfoAddress); - if (UnwindInfo == NULL) - { - return HRESULT_FROM_WIN32(ERROR_READ_FAULT); - } - - while (Index < UnwindInfo->CountOfUnwindCodes) { - - // - // If the prologue offset is greater than the next unwind code offset, - // then simulate the effect of the unwind code. - // - - UnwindOp = UnwindInfo->UnwindCode[Index].UnwindOp; - if (UnwindOp > UWOP_PUSH_MACHFRAME) { - goto Fail; - } - - OpInfo = UnwindInfo->UnwindCode[Index].OpInfo; - if (PrologOffset >= UnwindInfo->UnwindCode[Index].CodeOffset) { - switch (UnwindOp) { - - // - // Push nonvolatile integer register. - // - // The operation information is the register number of the - // register than was pushed. - // - - case UWOP_PUSH_NONVOL: - IntegerAddress = ContextRecord->Rsp; - if ((Status = - ReadAllMemory(IntegerAddress, - &IntegerRegister[OpInfo], - sizeof(ULONG64))) != S_OK) { - goto Fail; - } - - ContextRecord->Rsp += 8; - break; - - // - // Allocate a large sized area on the stack. - // - // The operation information determines if the size is - // 16- or 32-bits. - // - - case UWOP_ALLOC_LARGE: - Index += 1; - FrameOffset = UnwindInfo->UnwindCode[Index].FrameOffset; - if (OpInfo != 0) { - Index += 1; - FrameOffset += (UnwindInfo->UnwindCode[Index].FrameOffset << 16); - } else { - // The 16-bit form is scaled. - FrameOffset *= 8; - } - - ContextRecord->Rsp += FrameOffset; - break; - - // - // Allocate a small sized area on the stack. - // - // The operation information is the size of the unscaled - // allocation size (8 is the scale factor) minus 8. - // - - case UWOP_ALLOC_SMALL: - ContextRecord->Rsp += (OpInfo * 8) + 8; - break; - - // - // Establish the the frame pointer register. - // - // The operation information is not used. - // - - case UWOP_SET_FPREG: - ContextRecord->Rsp = IntegerRegister[UnwindInfo->FrameRegister]; - ContextRecord->Rsp -= UnwindInfo->FrameOffset * 16; - break; - - // - // Save nonvolatile integer register on the stack using a - // 16-bit displacment. - // - // The operation information is the register number. - // - - case UWOP_SAVE_NONVOL: - Index += 1; - FrameOffset = UnwindInfo->UnwindCode[Index].FrameOffset * 8; - IntegerAddress = FrameBase + FrameOffset; - if ((Status = - ReadAllMemory(IntegerAddress, - &IntegerRegister[OpInfo], - sizeof(ULONG64))) != S_OK) { - goto Fail; - } - break; - - // - // Save nonvolatile integer register on the stack using a - // 32-bit displacment. - // - // The operation information is the register number. - // - - case UWOP_SAVE_NONVOL_FAR: - Index += 2; - FrameOffset = UnwindInfo->UnwindCode[Index - 1].FrameOffset; - FrameOffset += UnwindInfo->UnwindCode[Index].FrameOffset << 16; - IntegerAddress = FrameBase + FrameOffset; - if ((Status = - ReadAllMemory(IntegerAddress, - &IntegerRegister[OpInfo], - sizeof(ULONG64))) != S_OK) { - goto Fail; - } - break; - - // - // Save a nonvolatile XMM(64) register on the stack using a - // 16-bit displacement. - // - // The operation information is the register number. - // - - case UWOP_SAVE_XMM: - Index += 1; - FrameOffset = UnwindInfo->UnwindCode[Index].FrameOffset * 8; - FloatingAddress = FrameBase + FrameOffset; - FloatingRegister[OpInfo].High = 0; - if ((Status = - ReadAllMemory(FloatingAddress, - &FloatingRegister[OpInfo].Low, - sizeof(ULONG64))) != S_OK) { - goto Fail; - } - break; - - // - // Save a nonvolatile XMM(64) register on the stack using a - // 32-bit displacement. - // - // The operation information is the register number. - // - - case UWOP_SAVE_XMM_FAR: - Index += 2; - FrameOffset = UnwindInfo->UnwindCode[Index - 1].FrameOffset; - FrameOffset += UnwindInfo->UnwindCode[Index].FrameOffset << 16; - FloatingAddress = FrameBase + FrameOffset; - FloatingRegister[OpInfo].High = 0; - if ((Status = - ReadAllMemory(FloatingAddress, - &FloatingRegister[OpInfo].Low, - sizeof(ULONG64))) != S_OK) { - goto Fail; - } - break; - - // - // Save a nonvolatile XMM(128) register on the stack using a - // 16-bit displacement. - // - // The operation information is the register number. - // - - case UWOP_SAVE_XMM128: - Index += 1; - FrameOffset = UnwindInfo->UnwindCode[Index].FrameOffset * 16; - FloatingAddress = FrameBase + FrameOffset; - if ((Status = - ReadAllMemory(FloatingAddress, - &FloatingRegister[OpInfo], - sizeof(M128A))) != S_OK) { - goto Fail; - } - break; - - // - // Save a nonvolatile XMM(128) register on the stack using a - // 32-bit displacement. - // - // The operation information is the register number. - // - - case UWOP_SAVE_XMM128_FAR: - Index += 2; - FrameOffset = UnwindInfo->UnwindCode[Index - 1].FrameOffset; - FrameOffset += UnwindInfo->UnwindCode[Index].FrameOffset << 16; - FloatingAddress = FrameBase + FrameOffset; - if ((Status = - ReadAllMemory(FloatingAddress, - &FloatingRegister[OpInfo], - sizeof(M128A))) != S_OK) { - goto Fail; - } - break; - - // - // Push a machine frame on the stack. - // - // The operation information determines whether the machine - // frame contains an error code or not. - // - - case UWOP_PUSH_MACHFRAME: - MachineFrame = TRUE; - ReturnAddress = ContextRecord->Rsp; - StackAddress = ContextRecord->Rsp + (3 * 8); - if (OpInfo != 0) { - ReturnAddress += 8; - StackAddress += 8; - } - - if ((Status = - ReadAllMemory(ReturnAddress, - &ContextRecord->Rip, - sizeof(ULONG64))) != S_OK) { - goto Fail; - } - if ((Status = - ReadAllMemory(StackAddress, - &ContextRecord->Rsp, - sizeof(ULONG64))) != S_OK) { - goto Fail; - } - break; - - // - // Unused codes. - // - - default: - break; - } - - Index += 1; - - } else { - - // - // Skip this unwind operation by advancing the slot index by the - // number of slots consumed by this operation. - // - - Index += s_UnwindOpSlotTable[UnwindOp]; - - // - // Special case any unwind operations that can consume a variable - // number of slots. - // - - switch (UnwindOp) { - - // - // A non-zero operation information indicates that an - // additional slot is consumed. - // - - case UWOP_ALLOC_LARGE: - if (OpInfo != 0) { - Index += 1; - } - - break; - - // - // No other special cases. - // - - default: - break; - } - } - } - - // - // If chained unwind information is specified, then recursively unwind - // the chained information. Otherwise, determine the return address if - // a machine frame was not encountered during the scan of the unwind - // codes. - // - - if ((UnwindInfo->Flags & UNW_FLAG_CHAININFO) != 0) { - - _PIMAGE_RUNTIME_FUNCTION_ENTRY ChainEntry; - - Index = UnwindInfo->CountOfUnwindCodes; - if ((Index & 1) != 0) { - Index += 1; - } - - // GetUnwindInfo looks for CHAININFO and reads - // the trailing RUNTIME_FUNCTION so we can just - // directly use the data sitting in UnwindInfo. - ChainEntry = (_PIMAGE_RUNTIME_FUNCTION_ENTRY) - &UnwindInfo->UnwindCode[Index]; - - Status = UnwindPrologue(ImageBase, - ControlPc, - FrameBase, - ChainEntry, - ContextRecord); - - return Status; - - } else { - - if (MachineFrame == FALSE) { - if ((Status = - ReadAllMemory(ContextRecord->Rsp, - &ContextRecord->Rip, - sizeof(ULONG64))) != S_OK) { - return Status; - } - ContextRecord->Rsp += 8; - } - - return S_OK; - } - - Fail: - return Status; -} - -HRESULT -OOPStackUnwinderAMD64::VirtualUnwind( - __in ULONG64 ImageBase, - __in ULONG64 ControlPc, - __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __inout PCONTEXT ContextRecord, - __out PULONG64 EstablisherFrame - ) - -/*++ - -Routine Description: - - This function virtually unwinds the specified function by executing its - prologue code backward or its epilogue code forward. - - If a context pointers record is specified, then the address where each - nonvolatile registers is restored from is recorded in the appropriate - element of the context pointers record. - -Arguments: - - ImageBase - Supplies the base address of the image that contains the - function being unwound. - - ControlPc - Supplies the address where control left the specified - function. - - FunctionEntry - Supplies the address of the function table entry for the - specified function. - - ContextRecord - Supplies the address of a context record. - - EstablisherFrame - Supplies a pointer to a variable that receives the - the establisher frame pointer value. - ---*/ - -{ - - HRESULT Status; - ULONG64 BranchTarget; - LONG Displacement; - ULONG FrameRegister = 0; - ULONG Index; - BOOL InEpilogue; - PULONG64 IntegerRegister; - PUCHAR NextByte; - _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryFunctionEntry; - ULONG PrologOffset; - ULONG RegisterNumber; - PUNWIND_INFO UnwindInfo; - UCHAR InstrBuffer[32]; - ULONG InstrBytes; - ULONG Bytes; - ULONG UnwindFrameReg; - - // - // If the specified function does not use a frame pointer, then the - // establisher frame is the contents of the stack pointer. This may - // not actually be the real establisher frame if control left the - // function from within the prologue. In this case the establisher - // frame may be not required since control has not actually entered - // the function and prologue entries cannot refer to the establisher - // frame before it has been established, i.e., if it has not been - // established, then no save unwind codes should be encountered during - // the unwind operation. - // - // If the specified function uses a frame pointer and control left the - // function outside of the prologue or the unwind information contains - // a chained information structure, then the establisher frame is the - // contents of the frame pointer. - // - // If the specified function uses a frame pointer and control left the - // function from within the prologue, then the set frame pointer unwind - // code must be looked up in the unwind codes to detetermine if the - // contents of the stack pointer or the contents of the frame pointer - // should be used for the establisher frame. This may not actually be - // the real establisher frame. In this case the establisher frame may - // not be required since control has not actually entered the function - // and prologue entries cannot refer to the establisher frame before it - // has been established, i.e., if it has not been established, then no - // save unwind codes should be encountered during the unwind operation. - // - // N.B. The correctness of these assumptions is based on the ordering of - // unwind codes. - // - - UnwindInfo = DacGetUnwindInfo(ImageBase + FunctionEntry->UnwindInfoAddress); - if (UnwindInfo == NULL) - { - return HRESULT_FROM_WIN32(ERROR_READ_FAULT); - } - - PrologOffset = (ULONG)(ControlPc - (FunctionEntry->BeginAddress + ImageBase)); - UnwindFrameReg = UnwindInfo->FrameRegister; - if (UnwindFrameReg == 0) { - *EstablisherFrame = ContextRecord->Rsp; - - } else if ((PrologOffset >= UnwindInfo->SizeOfProlog) || - ((UnwindInfo->Flags & UNW_FLAG_CHAININFO) != 0)) { - *EstablisherFrame = (&ContextRecord->Rax)[UnwindFrameReg]; - *EstablisherFrame -= UnwindInfo->FrameOffset * 16; - - } else { - - // Read all the data. - UnwindInfo = DacGetUnwindInfo(ImageBase + FunctionEntry->UnwindInfoAddress); - if (UnwindInfo == NULL) - { - return HRESULT_FROM_WIN32(ERROR_READ_FAULT); - } - - Index = 0; - while (Index < UnwindInfo->CountOfUnwindCodes) { - if (UnwindInfo->UnwindCode[Index].UnwindOp == UWOP_SET_FPREG) { - break; - } - - Index += 1; - } - - if (PrologOffset >= UnwindInfo->UnwindCode[Index].CodeOffset) { - *EstablisherFrame = (&ContextRecord->Rax)[UnwindFrameReg]; - *EstablisherFrame -= UnwindInfo->FrameOffset * 16; - - } else { - *EstablisherFrame = ContextRecord->Rsp; - } - } - - if ((Status = - ReadMemory(ControlPc, InstrBuffer, sizeof(InstrBuffer), - &InstrBytes)) != S_OK) { - - // We need the code to look for epilogue ops. - // It's very rare to be stopped in an epilogue when - // getting a stack trace, so if we can't read the - // code just assume we aren't in an epilogue. - InstrBytes = 0; - } - - // - // If the point at which control left the specified function is in an - // epilogue, then emulate the execution of the epilogue forward and - // return no exception handler. - // - - IntegerRegister = &ContextRecord->Rax; - NextByte = InstrBuffer; - Bytes = InstrBytes; - - // - // Check for one of: - // - // add rsp, imm8 - // or - // add rsp, imm32 - // or - // lea rsp, -disp8[fp] - // or - // lea rsp, -disp32[fp] - // - - if (Bytes >= 4 && - (NextByte[0] == SIZE64_PREFIX) && - (NextByte[1] == ADD_IMM8_OP) && - (NextByte[2] == 0xc4)) { - - // - // add rsp, imm8. - // - - NextByte += 4; - Bytes -= 4; - - } else if (Bytes >= 7 && - (NextByte[0] == SIZE64_PREFIX) && - (NextByte[1] == ADD_IMM32_OP) && - (NextByte[2] == 0xc4)) { - - // - // add rsp, imm32. - // - - NextByte += 7; - Bytes -= 7; - - } else if (Bytes >= 4 && - ((NextByte[0] & 0xf8) == SIZE64_PREFIX) && - (NextByte[1] == LEA_OP)) { - - FrameRegister = ((NextByte[0] & 0x7) << 3) | (NextByte[2] & 0x7); - if ((FrameRegister != 0) && - (FrameRegister == UnwindFrameReg)) { - if ((NextByte[2] & 0xf8) == 0x60) { - - // - // lea rsp, disp8[fp]. - // - - NextByte += 4; - Bytes -= 4; - - } else if (Bytes >= 7 && - (NextByte[2] &0xf8) == 0xa0) { - - // - // lea rsp, disp32[fp]. - // - - NextByte += 7; - Bytes -= 7; - } - } - } - - // - // Check for any number of: - // - // pop nonvolatile-integer-register[0..15]. - // - - while (TRUE) { - if (Bytes >= 1 && - (NextByte[0] & 0xf8) == POP_OP) { - NextByte += 1; - Bytes -= 1; - - } else if (Bytes >= 2 && - IS_REX_PREFIX(NextByte[0]) && - ((NextByte[1] & 0xf8) == POP_OP)) { - - NextByte += 2; - Bytes -= 2; - - } else { - break; - } - } - - // - // If the next instruction is a return or an appropriate jump, then - // control is currently in an epilogue and execution of the epilogue - // should be emulated. Otherwise, execution is not in an epilogue and - // the prologue should be unwound. - // - - InEpilogue = FALSE; - if ((Bytes >= 1 && - ((NextByte[0] == RET_OP) || - (NextByte[0] == RET_OP_2))) || - (Bytes >= 2 && - ((NextByte[0] == REP_PREFIX) && (NextByte[1] == RET_OP)))) { - - // - // A return is an unambiguous indication of an epilogue. - // - - InEpilogue = TRUE; - - } else if ((Bytes >= 2 && NextByte[0] == JMP_IMM8_OP) || - (Bytes >= 5 && NextByte[0] == JMP_IMM32_OP)) { - - // - // An unconditional branch to a target that is equal to the start of - // or outside of this routine is logically a call to another function. - // - - BranchTarget = (ULONG64)(NextByte - InstrBuffer) + ControlPc - ImageBase; - if (NextByte[0] == JMP_IMM8_OP) { - BranchTarget += 2 + (CHAR)NextByte[1]; - } else { - BranchTarget += 5 + *((LONG UNALIGNED *)&NextByte[1]); - } - - // - // Determine whether the branch target refers to code within this - // function. If not, then it is an epilogue indicator. - // - // A branch to the start of self implies a recursive call, so - // is treated as an epilogue. - // - - if (BranchTarget < FunctionEntry->BeginAddress || - BranchTarget >= FunctionEntry->EndAddress) { - - _IMAGE_RUNTIME_FUNCTION_ENTRY PrimaryEntryBuffer; - - // - // The branch target is outside of the region described by - // this function entry. See whether it is contained within - // an indirect function entry associated with this same - // function. - // - // If not, then the branch target really is outside of - // this function. - // - - PrimaryFunctionEntry = - SameFunction(FunctionEntry, - ImageBase, - BranchTarget + ImageBase, - &PrimaryEntryBuffer); - - if ((PrimaryFunctionEntry == NULL) || - (BranchTarget == PrimaryFunctionEntry->BeginAddress)) { - - InEpilogue = TRUE; - } - - } else if ((BranchTarget == FunctionEntry->BeginAddress) && - ((UnwindInfo->Flags & UNW_FLAG_CHAININFO) == 0)) { - - InEpilogue = TRUE; - } - - } else if (Bytes >= 2 && - (NextByte[0] == JMP_IND_OP) && (NextByte[1] == 0x25)) { - - // - // An unconditional jump indirect. - // - // This is a jmp outside of the function, probably a tail call - // to an import function. - // - - InEpilogue = TRUE; - - } else if (Bytes >= 3 && - ((NextByte[0] & 0xf8) == SIZE64_PREFIX) && - (NextByte[1] == 0xff) && - (NextByte[2] & 0x38) == 0x20) { - - // - // This is an indirect jump opcode: 0x48 0xff /4. The 64-bit - // flag (REX.W) is always redundant here, so its presence is - // overloaded to indicate a branch out of the function - a tail - // call. - // - // Such an opcode is an unambiguous epilogue indication. - // - - InEpilogue = TRUE; - } - - if (InEpilogue != FALSE) { - NextByte = InstrBuffer; - Bytes = InstrBytes; - - // - // Emulate one of (if any): - // - // add rsp, imm8 - // or - // add rsp, imm32 - // or - // lea rsp, disp8[frame-register] - // or - // lea rsp, disp32[frame-register] - // - - if (Bytes >= 1 && - (NextByte[0] & 0xf8) == SIZE64_PREFIX) { - - if (Bytes >= 4 && - NextByte[1] == ADD_IMM8_OP) { - - // - // add rsp, imm8. - // - - ContextRecord->Rsp += (CHAR)NextByte[3]; - NextByte += 4; - Bytes -= 4; - - } else if (Bytes >= 7 && - NextByte[1] == ADD_IMM32_OP) { - - // - // add rsp, imm32. - // - - Displacement = NextByte[3] | (NextByte[4] << 8); - Displacement |= (NextByte[5] << 16) | (NextByte[6] << 24); - ContextRecord->Rsp += Displacement; - NextByte += 7; - Bytes -= 7; - - } else if (Bytes >= 4 && - NextByte[1] == LEA_OP) { - if ((NextByte[2] & 0xf8) == 0x60) { - - // - // lea rsp, disp8[frame-register]. - // - - ContextRecord->Rsp = IntegerRegister[FrameRegister]; - ContextRecord->Rsp += (CHAR)NextByte[3]; - NextByte += 4; - Bytes -= 4; - - } else if (Bytes >= 7 && - (NextByte[2] & 0xf8) == 0xa0) { - - // - // lea rsp, disp32[frame-register]. - // - - Displacement = NextByte[3] | (NextByte[4] << 8); - Displacement |= (NextByte[5] << 16) | (NextByte[6] << 24); - ContextRecord->Rsp = IntegerRegister[FrameRegister]; - ContextRecord->Rsp += Displacement; - NextByte += 7; - Bytes -= 7; - } - } - } - - // - // Emulate any number of (if any): - // - // pop nonvolatile-integer-register. - // - - while (TRUE) { - if (Bytes >= 1 && - (NextByte[0] & 0xf8) == POP_OP) { - - // - // pop nonvolatile-integer-register[0..7] - // - - RegisterNumber = NextByte[0] & 0x7; - if ((Status = - ReadAllMemory(ContextRecord->Rsp, - &IntegerRegister[RegisterNumber], - sizeof(ULONG64))) != S_OK) { - return Status; - } - ContextRecord->Rsp += 8; - NextByte += 1; - Bytes -= 1; - - } else if (Bytes >= 2 && - IS_REX_PREFIX(NextByte[0]) && - (NextByte[1] & 0xf8) == POP_OP) { - - // - // pop nonvolatile-integer-register[8..15] - // - - RegisterNumber = ((NextByte[0] & 1) << 3) | (NextByte[1] & 0x7); - if ((Status = - ReadAllMemory(ContextRecord->Rsp, - &IntegerRegister[RegisterNumber], - sizeof(ULONG64))) != S_OK) { - return Status; - } - ContextRecord->Rsp += 8; - NextByte += 2; - Bytes -= 2; - - } else { - break; - } - } - - // - // Emulate return and return null exception handler. - // - // Note: this instruction might in fact be a jmp, however - // we want to emulate a return regardless. - // - - if ((Status = - ReadAllMemory(ContextRecord->Rsp, - &ContextRecord->Rip, - sizeof(ULONG64))) != S_OK) { - return Status; - } - ContextRecord->Rsp += 8; - return S_OK; - } - - // - // Control left the specified function outside an epilogue. Unwind the - // subject function and any chained unwind information. - // - - return UnwindPrologue(ImageBase, - ControlPc, - *EstablisherFrame, - FunctionEntry, - ContextRecord); -} - -ULONG64 -OOPStackUnwinderAMD64::LookupPrimaryUnwindInfo( - __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __in ULONG64 ImageBase, - __out _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryEntry - ) - -/*++ - -Routine Description: - - This function determines whether the supplied function entry is a primary - function entry or a chained function entry. If it is a chained function - entry, the unwind information associated with the primary function entry - is returned. - -Arguments: - - FunctionEntry - Supplies a pointer to the function entry for which the - associated primary function entry will be located. - - ImageBase - Supplies the base address of the image containing the - supplied function entry. - - PrimaryEntry - Supplies the address of a variable that receives a pointer - to the primary function entry. - -Return Value: - - A pointer to the unwind information for the primary function entry is - returned as the function value. - ---*/ - -{ - - ULONG Index; - PUNWIND_INFO UnwindInfo; - ULONG UnwindRel; - ULONG64 UnwindAbs; - - // - // Locate the unwind information and determine whether it is chained. - // If the unwind information is chained, then locate the parent function - // entry and loop again. - // - - UnwindRel = FunctionEntry->UnwindInfoAddress; - // Copy the function entry before it becomes invalid. - *PrimaryEntry = *FunctionEntry; - - do { - UnwindAbs = ImageBase + UnwindRel; - UnwindInfo = DacGetUnwindInfo(ImageBase + UnwindRel); - if ((UnwindInfo == NULL) || ((UnwindInfo->Flags & UNW_FLAG_CHAININFO) == 0)) - { - break; - } - - Index = UnwindInfo->CountOfUnwindCodes; - if ((Index & 1) != 0) { - Index += 1; - } - - FunctionEntry = (_PIMAGE_RUNTIME_FUNCTION_ENTRY) - &UnwindInfo->UnwindCode[Index]; - UnwindRel = FunctionEntry->UnwindInfoAddress; - - // Copy the function entry before it becomes invalid. - *PrimaryEntry = *FunctionEntry; - - } while (TRUE); - - return UnwindAbs; -} - -_PIMAGE_RUNTIME_FUNCTION_ENTRY -OOPStackUnwinderAMD64::SameFunction( - __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __in ULONG64 ImageBase, - __in ULONG64 ControlPc, - __out _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionReturnBuffer - ) - -/*++ - -Routine Description: - - This function determines whether the address supplied by ControlPc lies - anywhere within the function associated with FunctionEntry. - -Arguments: - - FunctionEntry - Supplies a pointer to a function entry (primary or chained) - associated with the function. - - ImageBase - Supplies the base address of the image containing the supplied - function entry. - - ControlPc - Supplies the address that will be tested for inclusion within - the function associated with FunctionEntry. - -Return Value: - - If the address of the unwind information for the specified function is - equal to the address of the unwind information for the control PC, then - a pointer to a function table entry that describes the primary function - table entry is returned as the function value. Otherwise, NULL is returned. - ---*/ - -{ - - _IMAGE_RUNTIME_FUNCTION_ENTRY TargetFunctionEntry; - ULONG64 TargetImageBase; - ULONG64 UnwindInfo1; - ULONG64 UnwindInfo2; - - // - // Find the unwind information referenced by the primary function entry - // associated with the specified function entry. - // - - UnwindInfo1 = LookupPrimaryUnwindInfo(FunctionEntry, ImageBase, - FunctionReturnBuffer); - - // - // Determine the function entry containing the control Pc and similarly - // resolve it's primary function entry. - // - - if (GetModuleBase(ControlPc, &TargetImageBase) != S_OK || - GetFunctionEntry(ControlPc, - &TargetFunctionEntry, - sizeof(TargetFunctionEntry)) != S_OK) { - return NULL; - } - - UnwindInfo2 = LookupPrimaryUnwindInfo(&TargetFunctionEntry, - TargetImageBase, - FunctionReturnBuffer); - - // - // If the address of the two sets of unwind information are equal, then - // return the address of the primary function entry. Otherwise, return - // NULL. - // - - if (UnwindInfo1 == UnwindInfo2) { - return FunctionReturnBuffer; - - } else { - return NULL; - } -} diff --git a/src/debug/daccess/amd64/unwinder_amd64.h b/src/debug/daccess/amd64/unwinder_amd64.h deleted file mode 100644 index 4c0bd46529..0000000000 --- a/src/debug/daccess/amd64/unwinder_amd64.h +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -// - -#ifndef __unwinder_amd64_h__ -#define __unwinder_amd64_h__ - -#include "unwinder.h" - - -//--------------------------------------------------------------------------------------- -// -// See the comment for the base class code:OOPStackUnwinder. -// - -class OOPStackUnwinderAMD64 : public OOPStackUnwinder -{ -public: - // Unwind the given CONTEXT to the caller CONTEXT. The CONTEXT will be overwritten. - BOOL Unwind(CONTEXT * pContext); - - // - // Everything below comes from dbghelp.dll. - // - -protected: - static BYTE s_UnwindOpSlotTable[]; - - HRESULT UnwindPrologue(__in DWORD64 ImageBase, - __in DWORD64 ControlPc, - __in DWORD64 FrameBase, - __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __inout PCONTEXT ContextRecord); - - HRESULT VirtualUnwind(__in DWORD64 ImageBase, - __in DWORD64 ControlPc, - __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __inout PCONTEXT ContextRecord, - __out PDWORD64 EstablisherFrame); - - DWORD64 LookupPrimaryUnwindInfo - (__in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __in DWORD64 ImageBase, - __out _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryEntry); - - _PIMAGE_RUNTIME_FUNCTION_ENTRY SameFunction - (__in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __in DWORD64 ImageBase, - __in DWORD64 ControlPc, - __out _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionReturnBuffer); -}; - -#endif // __unwinder_amd64_h__ - diff --git a/src/debug/daccess/arm/unwinder_arm.cpp b/src/debug/daccess/arm/unwinder_arm.cpp deleted file mode 100644 index c2463f58f7..0000000000 --- a/src/debug/daccess/arm/unwinder_arm.cpp +++ /dev/null @@ -1,1469 +0,0 @@ -// -// 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(addr)) -#define MEMORY_READ_DWORD(params, addr) (*dac_cast(addr)) -#define MEMORY_READ_QWORD(params, addr) (*dac_cast(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, - // 0xf9: 4-byte stack adjust ... add sp, sp, - // - - 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, - // 0xfa: 4-byte stack adjust ... add sp, sp, - // - - case 0xf8: - case 0xfa: - if (UnwindCodePtr + 3 > UnwindCodesEndPtr) { - Status = E_FAIL; - break; - } - ContextRecord->Sp += 4 * 256 * 256 * MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); - ContextRecord->Sp += 4 * 256 * MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr + 1); - ContextRecord->Sp += 4 * MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr + 2); - UnwindCodePtr += 3; - break; - - // - // 0xfb: 2-byte no-op/misc instruction - // 0xfc: 4-byte no-op/misc instruction - // - - case 0xfb: - case 0xfc: - break; - - // - // 0xfd: 2-byte end (epilog) - // 0xfe: 4-byte end (epilog) - // 0xff: generic end - // - - case 0xfd: - case 0xfe: - case 0xff: - goto finished; - - default: - Status = E_FAIL; - break; - } - } - } - - // - // If we succeeded, post-process the results a bit - // -finished: - if (Status == STATUS_SUCCESS) { - - // - // Since we always POP to the LR, recover the final PC from there, unless - // it was overwritten due to a special case custom unwinding operation. - // Also set the establisher frame equal to the final stack pointer. - // - - if ((ContextRecord->ContextFlags & CONTEXT_UNWOUND_TO_CALL) != 0) { - ContextRecord->Pc = ContextRecord->Lr; - } - *EstablisherFrame = ContextRecord->Sp; - - if (ARGUMENT_PRESENT(HandlerRoutine)) { - *HandlerRoutine = ExceptionHandler; - } - *HandlerData = ExceptionHandlerData; - } - - return Status; -} - - -BOOL OOPStackUnwinderArm::Unwind(T_CONTEXT * pContext) -{ - DWORD64 ImageBase = 0; - HRESULT hr = GetModuleBase(DBS_EXTEND64(pContext->Pc), &ImageBase); - if (hr != S_OK) - return FALSE; - - PEXCEPTION_ROUTINE DummyHandlerRoutine; - PVOID DummyHandlerData; - ULONG DummyEstablisherFrame; - - DWORD startingPc = pContext->Pc; - DWORD startingSp = pContext->Sp; - - IMAGE_ARM_RUNTIME_FUNCTION_ENTRY Rfe; - if (FAILED(GetFunctionEntry(DBS_EXTEND64(pContext->Pc), &Rfe, sizeof(Rfe)))) - return FALSE; - - if ((Rfe.UnwindData & 3) != 0) - { - hr = RtlpUnwindFunctionCompact(pContext->Pc - (ULONG)ImageBase, - &Rfe, - pContext, - &DummyEstablisherFrame, - &DummyHandlerRoutine, - &DummyHandlerData, - NULL); - - } - else - { - hr = RtlpUnwindFunctionFull(pContext->Pc - (ULONG)ImageBase, - (ULONG)ImageBase, - &Rfe, - pContext, - &DummyEstablisherFrame, - &DummyHandlerRoutine, - &DummyHandlerData, - NULL); - } - - // PC == 0 means unwinding is finished. - // Same if no forward progress is made - if (pContext->Pc == 0 || (startingPc == pContext->Pc && startingSp == pContext->Sp)) - return FALSE; - - return TRUE; -} - - -BOOL DacUnwindStackFrame(T_CONTEXT *pContext, T_KNONVOLATILE_CONTEXT_POINTERS* pContextPointers) -{ - OOPStackUnwinderArm unwinder; - BOOL res = unwinder.Unwind(pContext); - - if (res && pContextPointers) - { - for (int i = 0; i < 8; i++) - { - *(&pContextPointers->R4 + i) = &pContext->R4 + i; - } - } - - return res; -} diff --git a/src/debug/daccess/arm/unwinder_arm.h b/src/debug/daccess/arm/unwinder_arm.h deleted file mode 100644 index 7eb5d7a065..0000000000 --- a/src/debug/daccess/arm/unwinder_arm.h +++ /dev/null @@ -1,55 +0,0 @@ -// -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -// - -#ifndef __unwinder_arm__ -#define __unwinder_arm__ - -#include "unwinder.h" - - -//--------------------------------------------------------------------------------------- -// -// See the comment for the base class code:OOPStackUnwinder. -// - -class OOPStackUnwinderArm : public OOPStackUnwinder -{ -public: - // Unwind the given CONTEXT to the caller CONTEXT. The CONTEXT will be overwritten. - BOOL Unwind(T_CONTEXT * pContext); - - // - // Everything below comes from dbghelp.dll. - // - -protected: - HRESULT UnwindPrologue(__in DWORD64 ImageBase, - __in DWORD64 ControlPc, - __in DWORD64 FrameBase, - __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __inout PT_CONTEXT ContextRecord); - - HRESULT VirtualUnwind(__in DWORD64 ImageBase, - __in DWORD64 ControlPc, - __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __inout PT_CONTEXT ContextRecord, - __out PDWORD64 EstablisherFrame); - - DWORD64 LookupPrimaryUnwindInfo - (__in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __in DWORD64 ImageBase, - __out _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryEntry); - - _PIMAGE_RUNTIME_FUNCTION_ENTRY SameFunction - (__in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __in DWORD64 ImageBase, - __in DWORD64 ControlPc, - __out _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionReturnBuffer); -}; - -#endif // __unwinder_arm__ - diff --git a/src/debug/daccess/arm64/unwinder_arm64.cpp b/src/debug/daccess/arm64/unwinder_arm64.cpp deleted file mode 100644 index a56e42469e..0000000000 --- a/src/debug/daccess/arm64/unwinder_arm64.cpp +++ /dev/null @@ -1,1580 +0,0 @@ -// -// 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 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 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 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 at [sp+#Z*8], offset <= 504 - // - - else if (CurCode <= 0xd7) { - if (AccumulatedSaveNexts != 0) { - return STATUS_UNWIND_INVALID_SEQUENCE; - } - NextCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); - UnwindCodePtr++; - Status = RtlpUnwindRestoreRegisterRange( - ContextRecord, - 8 * (NextCode & 0x3f), - 19 + 2 * (((CurCode & 1) << 2) + (NextCode >> 6)), - 1, - UnwindParams); - if (Status == STATUS_SUCCESS) { - RtlpUnwindRestoreRegisterRange( - ContextRecord, - 8 * (NextCode & 0x3f) + 8, - 30, - 1, - UnwindParams); - } - } - - // - // save_fregp (1101100x|xxzzzzzz): save pair d(8+#X) at [sp+#Z*8], offset <= 504 - // - - else if (CurCode <= 0xd9) { - NextCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); - UnwindCodePtr++; - Status = RtlpUnwindRestoreFpRegisterRange( - ContextRecord, - 8 * (NextCode & 0x3f), - 8 + ((CurCode & 1) << 2) + (NextCode >> 6), - 2 + AccumulatedSaveNexts, - UnwindParams); - AccumulatedSaveNexts = 0; - } - - // - // save_fregp_x (1101101x|xxzzzzzz): save pair d(8+#X), at [sp-(#Z+1)*8]!, pre-indexed offset >= -512 - // - - else if (CurCode <= 0xdb) { - NextCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); - UnwindCodePtr++; - Status = RtlpUnwindRestoreFpRegisterRange( - ContextRecord, - -8 * ((NextCode & 0x3f) + 1), - 8 + ((CurCode & 1) << 2) + (NextCode >> 6), - 2 + AccumulatedSaveNexts, - UnwindParams); - AccumulatedSaveNexts = 0; - } - - // - // save_freg (1101110x|xxzzzzzz): save reg d(9+#X) at [sp+#Z*8], offset <= 504 - // - - else if (CurCode <= 0xdd) { - if (AccumulatedSaveNexts != 0) { - return STATUS_UNWIND_INVALID_SEQUENCE; - } - NextCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); - UnwindCodePtr++; - Status = RtlpUnwindRestoreFpRegisterRange( - ContextRecord, - 8 * (NextCode & 0x3f), - 8 + ((CurCode & 1) << 2) + (NextCode >> 6), - 1, - UnwindParams); - } - - // - // save_freg_x (11011110|xxxzzzzz): save reg d(8+#X) at [sp-(#Z+1)*8]!, pre-indexed offset >= -256 - // - - else if (CurCode == 0xde) { - if (AccumulatedSaveNexts != 0) { - return STATUS_UNWIND_INVALID_SEQUENCE; - } - NextCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); - UnwindCodePtr++; - Status = RtlpUnwindRestoreFpRegisterRange( - ContextRecord, - -8 * ((NextCode & 0x1f) + 1), - 8 + (NextCode >> 5), - 1, - UnwindParams); - } - - // - // alloc_l (11100000|xxxxxxxx|xxxxxxxx|xxxxxxxx): allocate large stack with size < 256M - // - - else if (CurCode == 0xe0) { - if (AccumulatedSaveNexts != 0) { - return STATUS_UNWIND_INVALID_SEQUENCE; - } - ContextRecord->Sp += 16 * (MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr) << 16); - UnwindCodePtr++; - ContextRecord->Sp += 16 * (MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr) << 8); - UnwindCodePtr++; - ContextRecord->Sp += 16 * MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); - UnwindCodePtr++; - } - - // - // set_fp (11100001): set up r29: with: mov r29,sp - // - - else if (CurCode == 0xe1) { - if (AccumulatedSaveNexts != 0) { - return STATUS_UNWIND_INVALID_SEQUENCE; - } - ContextRecord->Sp = ContextRecord->Fp; - } - - // - // add_fp (11100010|xxxxxxxx): set up r29 with: add r29,sp,#x*8 - // - - else if (CurCode == 0xe2) { - if (AccumulatedSaveNexts != 0) { - return STATUS_UNWIND_INVALID_SEQUENCE; - } - ContextRecord->Sp = ContextRecord->Fp - 8 * MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); - UnwindCodePtr++; - } - - // - // nop (11100011): no unwind operation is required - // - - else if (CurCode == 0xe3) { - if (AccumulatedSaveNexts != 0) { - return STATUS_UNWIND_INVALID_SEQUENCE; - } - } - - // - // end (11100100): end of unwind code - // - - else if (CurCode == 0xe4) { - if (AccumulatedSaveNexts != 0) { - return STATUS_UNWIND_INVALID_SEQUENCE; - } - goto finished; - } - - // - // end_c (11100101): end of unwind code in current chained scope - // - - else if (CurCode == 0xe5) { - if (AccumulatedSaveNexts != 0) { - return STATUS_UNWIND_INVALID_SEQUENCE; - } - goto finished; - } - - // - // save_next (11100110): save next non-volatile Int or FP register pair. - // - - else if (CurCode == 0xe6) { - AccumulatedSaveNexts++; - } - - // - // custom_0 (111010xx): restore custom structure - // - - else if (CurCode >= 0xe8 && CurCode <= 0xeb) { - if (AccumulatedSaveNexts != 0) { - return STATUS_UNWIND_INVALID_SEQUENCE; - } - Status = RtlpUnwindCustom(ContextRecord, (BYTE) CurCode, UnwindParams); - FinalPcFromLr = FALSE; - } - - // - // Anything else is invalid - // - - else { - return STATUS_UNWIND_INVALID_SEQUENCE; - } - } - - // - // If we succeeded, post-process the results a bit - // -finished: - if (Status == STATUS_SUCCESS) { - - // - // Since we always POP to the LR, recover the final PC from there, unless - // it was overwritten due to a special case custom unwinding operation. - // Also set the establisher frame equal to the final stack pointer. - // - - if (FinalPcFromLr) { - ContextRecord->Pc = ContextRecord->Lr; - } - *EstablisherFrame = ContextRecord->Sp; - - if (ARGUMENT_PRESENT(HandlerRoutine)) { - *HandlerRoutine = ExceptionHandler; - } - *HandlerData = ExceptionHandlerData; - } - - return Status; -} - -NTSTATUS -RtlpUnwindFunctionCompact( - __in DWORD64 ControlPcRva, - __in PT_RUNTIME_FUNCTION FunctionEntry, - __inout T_CONTEXT *ContextRecord, - __out PDWORD64 EstablisherFrame, - __deref_opt_out_opt PEXCEPTION_ROUTINE *HandlerRoutine, - __out PVOID *HandlerData, - __in PARM64_UNWIND_PARAMS UnwindParams - ) - -/*++ - -Routine Description: - - This function virtually unwinds the specified function by parsing the - compact .pdata record to determine where in the function the provided - ControlPc is, and then executing a standard, well-defined set of - operations. - - If a context pointers record is specified (in the UnwindParams), then - the address where each nonvolatile register is restored from is recorded - in the appropriate element of the context pointers record. - -Arguments: - - ControlPcRva - Supplies the address where control left the specified - function, as an offset relative to the IamgeBase. - - FunctionEntry - Supplies the address of the function table entry for the - specified function. If appropriate, this should have already been - probed. - - ContextRecord - Supplies the address of a context record. - - EstablisherFrame - Supplies a pointer to a variable that receives the - the establisher frame pointer value. - - HandlerRoutine - Supplies an optional pointer to a variable that receives - the handler routine address. If control did not leave the specified - function in either the prolog or an epilog and a handler of the - proper type is associated with the function, then the address of the - language specific exception handler is returned. Otherwise, NULL is - returned. - - HandlerData - Supplies a pointer to a variable that receives a pointer - the the language handler data. - - UnwindParams - Additional parameters shared with caller. - -Return Value: - - STATUS_SUCCESS if the unwind could be completed, a failure status otherwise. - Unwind can only fail when validation bounds are specified. - ---*/ - -{ - ULONG Count; - ULONG Cr; - ULONG CurrentOffset; - ULONG EpilogLength; - ULONG Flag; - ULONG FloatSize; - ULONG FrameSize; - ULONG FRegOpcodes; - ULONG FunctionLength; - ULONG HBit; - ULONG HOpcodes; - ULONG IRegOpcodes; - ULONG InstCount; - ULONG IntSize; - ULONG LocalSize; - DWORD64 OffsetInFunction; - DWORD64 OffsetInScope; - ULONG PrologLength; - ULONG RegF; - ULONG RegI; - ULONG RegSize; - ULONG ScopeStart; - ULONG StackAdjustOpcodes; - NTSTATUS Status; - ULONG UnwindData; - - UnwindData = FunctionEntry->UnwindData; - Status = STATUS_SUCCESS; - - // - // Compact records always describe an unwind to a call. - // - - ContextRecord->ContextFlags |= CONTEXT_UNWOUND_TO_CALL; - - // - // Extract the basic information about how to do a full unwind. - // - - Flag = UnwindData & 3; - FunctionLength = (UnwindData >> 2) & 0x7ff; - RegF = (UnwindData >> 13) & 7; - RegI = (UnwindData >> 16) & 0xf; - HBit = (UnwindData >> 20) & 1; - Cr = (UnwindData >> 21) & 3; - FrameSize = (UnwindData >> 23) & 0x1ff; - - if (Flag == 3) { - return STATUS_UNWIND_INVALID_SEQUENCE; - } - if (Cr == 2) { - return STATUS_UNWIND_INVALID_SEQUENCE; - } - - // - // Determine the size of the locals - // - - IntSize = RegI * 8; - if (Cr == 1) { - IntSize += 8; - } - FloatSize = (RegF == 0) ? 0 : (RegF + 1) * 8; - RegSize = (IntSize + FloatSize + 8*8 * HBit + 0xf) & ~0xf; - if (RegSize > 16 * FrameSize) { - return STATUS_UNWIND_INVALID_SEQUENCE; - } - LocalSize = 16 * FrameSize - RegSize; - - // - // If we're near the start of the function (within 17 words), - // see if we are within the prolog. - // - // N.B. If the low 2 bits of the UnwindData are 2, then we have - // no prolog. - // - - OffsetInFunction = (ControlPcRva - FunctionEntry->BeginAddress) / 4; - OffsetInScope = 0; - if (OffsetInFunction < 17 && Flag != 2) { - - // - // Compute sizes for each opcode in the prolog. - // - - IRegOpcodes = (IntSize + 8) / 16; - FRegOpcodes = (FloatSize + 8) / 16; - HOpcodes = 4 * HBit; - StackAdjustOpcodes = (Cr == 3) ? 1 : 0; - if (Cr != 3 || LocalSize > 512) { - StackAdjustOpcodes += (LocalSize > 4088) ? 2 : (LocalSize > 0) ? 1 : 0; - } - - // - // Compute the total prolog length and determine if we are within - // its scope. - // - // N.B. We must execute prolog operations backwards to unwind, so - // our final scope offset in this case is the distance from the end. - // - - PrologLength = IRegOpcodes + FRegOpcodes + HOpcodes + StackAdjustOpcodes; - - if (OffsetInFunction < PrologLength) { - OffsetInScope = PrologLength - OffsetInFunction; - } - } - - // - // If we're near the end of the function (within 15 words), see if - // we are within the epilog. - // - // N.B. If the low 2 bits of the UnwindData are 2, then we have - // no epilog. - // - - if (OffsetInScope == 0 && OffsetInFunction + 15 >= FunctionLength && Flag != 2) { - - // - // Compute sizes for each opcode in the epilog. - // - - IRegOpcodes = (IntSize + 8) / 16; - FRegOpcodes = (FloatSize + 8) / 16; - HOpcodes = HBit; - StackAdjustOpcodes = (Cr == 3) ? 1 : 0; - if (Cr != 3 || LocalSize > 512) { - StackAdjustOpcodes += (LocalSize > 4088) ? 2 : (LocalSize > 0) ? 1 : 0; - } - - // - // Compute the total epilog length and determine if we are within - // its scope. - // - - EpilogLength = IRegOpcodes + FRegOpcodes + HOpcodes + StackAdjustOpcodes + 1; - - ScopeStart = FunctionLength - EpilogLength; - if (OffsetInFunction > ScopeStart) { - OffsetInScope = OffsetInFunction - ScopeStart; - } - } - - // - // Process operations backwards, in the order: stack/frame deallocation, - // VFP register popping, integer register popping, parameter home - // area recovery. - // - // First case is simple: we process everything with no regard for - // the current offset within the scope. - // - - Status = STATUS_SUCCESS; - if (OffsetInScope == 0) { - - if (Cr == 3) { - Status = RtlpUnwindRestoreRegisterRange(ContextRecord, 0, 29, 2, UnwindParams); - } - ContextRecord->Sp += LocalSize; - - if (RegF != 0 && Status == STATUS_SUCCESS) { - Status = RtlpUnwindRestoreFpRegisterRange(ContextRecord, IntSize, 8, RegF + 1, UnwindParams); - } - - if (Cr == 1 && Status == STATUS_SUCCESS) { - Status = RtlpUnwindRestoreRegisterRange(ContextRecord, IntSize - 8, 30, 1, UnwindParams); - } - if (RegI > 0 && Status == STATUS_SUCCESS) { - Status = RtlpUnwindRestoreRegisterRange(ContextRecord, 0, 19, RegI, UnwindParams); - } - ContextRecord->Sp += RegSize; - } - - // - // Second case is more complex: we must step along each operation - // to ensure it should be executed. - // - - else { - - CurrentOffset = 0; - if (Cr == 3) { - if (LocalSize <= 512) { - if (CurrentOffset++ >= OffsetInScope) { - Status = RtlpUnwindRestoreRegisterRange(ContextRecord, -(LONG)LocalSize, 29, 2, UnwindParams); - } - LocalSize = 0; - } - } - while (LocalSize != 0) { - Count = (LocalSize + 4087) % 4088 + 1; - if (CurrentOffset++ >= OffsetInScope) { - ContextRecord->Sp += Count; - } - LocalSize -= Count; - } - - if (HBit != 0) { - CurrentOffset += 4; - } - - if (RegF != 0 && Status == STATUS_SUCCESS) { - RegF++; - while (RegF != 0) { - Count = 2 - (RegF & 1); - RegF -= Count; - if (CurrentOffset++ >= OffsetInScope) { - Status = RtlpUnwindRestoreFpRegisterRange( - ContextRecord, - (RegF == 0 && RegI == 0) ? (-(LONG)RegSize) : (IntSize + 8 * RegF), - 8 + RegF, - Count, - UnwindParams); - } - } - } - - if (Cr == 1 && Status == STATUS_SUCCESS) { - if (RegI % 2 == 0) { - if (CurrentOffset++ >= OffsetInScope) { - Status = RtlpUnwindRestoreRegisterRange(ContextRecord, IntSize - 8, 30, 1, UnwindParams); - } - } else { - if (CurrentOffset++ >= OffsetInScope) { - RegI--; - Status = RtlpUnwindRestoreRegisterRange(ContextRecord, IntSize - 8, 30, 1, UnwindParams); - if (Status == STATUS_SUCCESS) { - Status = RtlpUnwindRestoreRegisterRange(ContextRecord, IntSize - 16, 19 + RegI, 1, UnwindParams); - } - } - } - } - - while (RegI != 0 && Status == STATUS_SUCCESS) { - Count = 2 - (RegI & 1); - RegI -= Count; - if (CurrentOffset++ >= OffsetInScope) { - Status = RtlpUnwindRestoreRegisterRange( - ContextRecord, - (RegI == 0) ? (-(LONG)RegSize) : (8 * RegI), - 19 + RegI, - Count, - UnwindParams); - } - } - } - - // - // If we succeeded, post-process the results a bit - // - - if (Status == STATUS_SUCCESS) { - - ContextRecord->Pc = ContextRecord->Lr; - *EstablisherFrame = ContextRecord->Sp; - - if (ARGUMENT_PRESENT(HandlerRoutine)) { - *HandlerRoutine = NULL; - } - *HandlerData = NULL; - } - - return Status; -} - -BOOL OOPStackUnwinderArm64::Unwind(T_CONTEXT * pContext) -{ - DWORD64 ImageBase = 0; - HRESULT hr = GetModuleBase(pContext->Pc, &ImageBase); - if (hr != S_OK) - return FALSE; - - PEXCEPTION_ROUTINE DummyHandlerRoutine; - PVOID DummyHandlerData; - DWORD64 DummyEstablisherFrame; - - DWORD64 startingPc = pContext->Pc; - DWORD64 startingSp = pContext->Sp; - - T_RUNTIME_FUNCTION Rfe; - if (FAILED(GetFunctionEntry(pContext->Pc, &Rfe, sizeof(Rfe)))) - return FALSE; - - if ((Rfe.UnwindData & 3) != 0) - { - hr = RtlpUnwindFunctionCompact(pContext->Pc - (ULONG)ImageBase, - &Rfe, - pContext, - &DummyEstablisherFrame, - &DummyHandlerRoutine, - &DummyHandlerData, - NULL); - - } - else - { - hr = RtlpUnwindFunctionFull(pContext->Pc - (ULONG)ImageBase, - (ULONG)ImageBase, - &Rfe, - pContext, - &DummyEstablisherFrame, - &DummyHandlerRoutine, - &DummyHandlerData, - NULL); - } - - // PC == 0 means unwinding is finished. - // Same if no forward progress is made - if (pContext->Pc == 0 || (startingPc == pContext->Pc && startingSp == pContext->Sp)) - return FALSE; - - return TRUE; -} - -BOOL DacUnwindStackFrame(T_CONTEXT *pContext, T_KNONVOLATILE_CONTEXT_POINTERS* pContextPointers) -{ - OOPStackUnwinderArm64 unwinder; - BOOL res = unwinder.Unwind(pContext); - - if (res && pContextPointers) - { - for (int i = 0; i < 12; i++) - { - *(&pContextPointers->X19 + i) = &pContext->X19 + i; - } - } - - return res; -} diff --git a/src/debug/daccess/arm64/unwinder_arm64.h b/src/debug/daccess/arm64/unwinder_arm64.h deleted file mode 100644 index 7396e85e26..0000000000 --- a/src/debug/daccess/arm64/unwinder_arm64.h +++ /dev/null @@ -1,55 +0,0 @@ -// -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -// - -#ifndef __unwinder_arm64__ -#define __unwinder_arm64__ - -#include "unwinder.h" - - -//--------------------------------------------------------------------------------------- -// -// See the comment for the base class code:OOPStackUnwinder. -// - -class OOPStackUnwinderArm64 : public OOPStackUnwinder -{ -public: - // Unwind the given CONTEXT to the caller CONTEXT. The CONTEXT will be overwritten. - BOOL Unwind(T_CONTEXT * pContext); - - // - // Everything below comes from dbghelp.dll. - // - -protected: - HRESULT UnwindPrologue(__in DWORD64 ImageBase, - __in DWORD64 ControlPc, - __in DWORD64 FrameBase, - __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __inout PT_CONTEXT ContextRecord); - - HRESULT VirtualUnwind(__in DWORD64 ImageBase, - __in DWORD64 ControlPc, - __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __inout PT_CONTEXT ContextRecord, - __out PDWORD64 EstablisherFrame); - - DWORD64 LookupPrimaryUnwindInfo - (__in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __in DWORD64 ImageBase, - __out _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryEntry); - - _PIMAGE_RUNTIME_FUNCTION_ENTRY SameFunction - (__in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __in DWORD64 ImageBase, - __in DWORD64 ControlPc, - __out _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionReturnBuffer); -}; - -#endif // __unwinder_arm64__ - diff --git a/src/debug/daccess/daccess.targets b/src/debug/daccess/daccess.targets index 7aacdc302c..a7d9e41554 100644 --- a/src/debug/daccess/daccess.targets +++ b/src/debug/daccess/daccess.targets @@ -58,15 +58,9 @@ - - - - - - diff --git a/src/debug/daccess/unwinder.cpp b/src/debug/daccess/unwinder.cpp deleted file mode 100644 index 6431a83ea8..0000000000 --- a/src/debug/daccess/unwinder.cpp +++ /dev/null @@ -1,163 +0,0 @@ -// -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -// - -#include "stdafx.h" -#include "unwinder.h" - -EXTERN_C void GetRuntimeStackWalkInfo(IN ULONG64 ControlPc, - OUT UINT_PTR* pModuleBase, - OUT UINT_PTR* pFuncEntry); - -#if !defined(_TARGET_ARM_) && !defined(_TARGET_ARM64_) -//--------------------------------------------------------------------------------------- -// -// Read an UNWIND_INFO structure given its address. The UNWIND_INFO structure is variable sized. -// This is just a simple wrapper over the platform-specific DAC function which does the real work. -// -// Arguments: -// taUnwindInfo - target address of the beginning of the UNWIND_INFO structure -// -// Return Value: -// Return the specified UNWIND_INFO. It lives in the DAC cache, so the caller doesn't need to explicitly -// free the memory. It'll get flushed when the DAC cache is flushed (i.e. when we continue). -// - -UNWIND_INFO * OOPStackUnwinder::GetUnwindInfo(TADDR taUnwindInfo) -{ - return DacGetUnwindInfo(taUnwindInfo); -} -#endif // !_TARGET_ARM_ && !_TARGET_ARM64_ - -//--------------------------------------------------------------------------------------- -// -// This is a simple wrapper over code:OOPStackUnwinder::ReadMemory(). Unlike ReadMemory(), -// it fails if we don't successfully read all the specified bytes. -// -// Arguments: -// address - the address to be read -// pbBuffer - the buffer to store the read memory -// cbRequest - the number of bytes requested -// -// Return Value: -// S_OK if all the memory is read successfully. -// HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY) if only part of the memory is read. -// Failure HRs otherwise. -// - -HRESULT OOPStackUnwinder::ReadAllMemory( DWORD64 address, - __in_ecount(cbRequest) PVOID pbBuffer, - DWORD cbRequest) -{ - DWORD cbDone = 0; - HRESULT hr = ReadMemory(address, pbBuffer, cbRequest, &cbDone); - if (SUCCEEDED(hr) && (cbDone != cbRequest)) - { - return HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY); - } - else - { - return hr; - } -} - -//--------------------------------------------------------------------------------------- -// -// Read the specified memory from out-of-process. This function uses DacReadAll() to do the trick. -// -// Arguments: -// address - the address to be read -// pbBuffer - the buffer to store the read memory -// cbRequest - the number of bytes requested -// pcbDone - out parameter; returns the number of bytes read -// -// Return Value: -// S_OK if all the memory is read successfully -// - -HRESULT OOPStackUnwinder::ReadMemory( DWORD64 address, - __in_ecount(cbRequest) PVOID pbBuffer, - DWORD cbRequest, - __out_opt PDWORD pcbDone) -{ - _ASSERTE(pcbDone != NULL); - - HRESULT hr = DacReadAll(TO_TADDR(address), pbBuffer, cbRequest, false); - if (SUCCEEDED(hr)) - { - *pcbDone = cbRequest; - - // On X64, we need to replace any patches which are within the requested memory range. - // This is because the X64 unwinder needs to disassemble the native instructions in order to determine - // whether the IP is in an epilog. -#if defined(_TARGET_AMD64_) - MemoryRange range(dac_cast((TADDR)address), cbRequest); - hr = DacReplacePatchesInHostMemory(range, pbBuffer); -#endif // _TARGET_AMD64_ - } - else - { - *pcbDone = 0; - } - - return hr; -} - -//--------------------------------------------------------------------------------------- -// -// Given a control PC, return the base of the module it is in. For jitted managed code, this is the -// start of the code heap. -// -// Arguments: -// address - the specified address -// pdwBase - out parameter; returns the module base -// -// Return Value: -// S_OK if we retrieve the module base successfully; -// E_FAIL otherwise -// - -HRESULT OOPStackUnwinder::GetModuleBase( DWORD64 address, - __out PDWORD64 pdwBase) -{ - GetRuntimeStackWalkInfo(address, reinterpret_cast(pdwBase), NULL); - return ((*pdwBase == NULL) ? E_FAIL : S_OK); -} - -//--------------------------------------------------------------------------------------- -// -// Given a control PC, return the function entry of the functoin it is in. -// -// Arguments: -// address - the specified IP -// pBuffer - the buffer to store the retrieved function entry -// cbBuffer - the size of the buffer -// -// Return Value: -// S_OK if we retrieve the function entry successfully; -// E_INVALIDARG if the buffer is too small; -// E_FAIL otherwise -// - -HRESULT OOPStackUnwinder::GetFunctionEntry( DWORD64 address, - __out_ecount(cbBuffer) PVOID pBuffer, - DWORD cbBuffer) -{ - if (cbBuffer < sizeof(RUNTIME_FUNCTION)) - { - return E_INVALIDARG; - } - - PVOID pFuncEntry = NULL; - GetRuntimeStackWalkInfo(address, NULL, reinterpret_cast(&pFuncEntry)); - if (pFuncEntry == NULL) - { - return E_FAIL; - } - - memcpy(pBuffer, pFuncEntry, cbBuffer); - return S_OK; -} diff --git a/src/debug/daccess/unwinder.h b/src/debug/daccess/unwinder.h deleted file mode 100644 index 21d64eae3f..0000000000 --- a/src/debug/daccess/unwinder.h +++ /dev/null @@ -1,64 +0,0 @@ -// -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -// - -#ifndef __unwinder_h__ -#define __unwinder_h__ - - -//--------------------------------------------------------------------------------------- -// -// OOPStackUnwinder is the abstract base class for unwinding stack frames. Each of the two 64-bit platforms -// has its own derived class. Although the name of this class and its derived classes have changed, they -// are actually borrowed from dbghelp.dll. (StackWalk64() is built on top of these classes.) We have ripped -// out everything we don't need such as symbol lookup and various state, and keep just enough code to support -// VirtualUnwind(). The managed debugging infrastructure can't call RtlVirtualUnwind() because it doesn't -// work from out-of-processr -// -// Notes: -// To see what we have changed in the borrowed source, you can diff the original version and our version. -// For example, on X64, you can diff clr\src\Debug\daccess\amd64\dbs_stack_x64.cpp (the original) and -// clr\src\Debug\daccess\amd64\unwinder_amd64.cpp. -// - -class OOPStackUnwinder -{ -public: - // Unwind the given CONTEXT to the caller CONTEXT. The CONTEXT will be overwritten. - // Derived classes are expected to override this function for each processor architecture. - virtual BOOL Unwind(T_CONTEXT * pContext) = 0; - -protected: -#if !defined(_TARGET_ARM_) && !defined(_TARGET_ARM64_) - // the unwind info on ARM is significantly different than AMD64, as such we don't need this function. - // Read an UNWIND_INFO structure given its address. The UNWIND_INFO structure is variable sized. - virtual UNWIND_INFO * GetUnwindInfo(TADDR taUnwindInfo); -#endif // !_TARGET_ARM_ && !_TARGET_ARM64_ - - // This is a simple wrapper over ReadMemory(). Unlike ReadMemory(), it fails if we don't successfully - // read all the specified bytes. - virtual HRESULT ReadAllMemory( DWORD64 address, - __in_ecount(cbRequest) PVOID pbBuffer, - DWORD cbRequest); - - // Read the specified memory. - virtual HRESULT ReadMemory( DWORD64 address, - __in_ecount(cbRequest) PVOID pbBuffer, - DWORD cbRequest, - __out_opt PDWORD pcbDone); - - // Given a control PC, return the base of the module it is in. For jitted managed code, this is the - // start of the code heap. - virtual HRESULT GetModuleBase( DWORD64 address, - __out PDWORD64 pdwBase); - - // Given a control PC, return the function entry of the functoin it is in. - virtual HRESULT GetFunctionEntry( DWORD64 address, - __out_ecount(cbBuffer) PVOID pBuffer, - DWORD cbBuffer); -}; - -#endif // __unwinder_h__ diff --git a/src/dirs.proj b/src/dirs.proj index d5df279b42..3599b7912b 100644 --- a/src/dirs.proj +++ b/src/dirs.proj @@ -40,6 +40,7 @@ + diff --git a/src/dlls/mscordac/CMakeLists.txt b/src/dlls/mscordac/CMakeLists.txt index 057288c747..527cf341b5 100644 --- a/src/dlls/mscordac/CMakeLists.txt +++ b/src/dlls/mscordac/CMakeLists.txt @@ -37,6 +37,7 @@ set(COREDAC_LIBRARIES mdruntimerw_dac strongname_dac utilcode_dac + unwinder_dac ) if(WIN32) diff --git a/src/dlls/mscordac/mscordac.targets b/src/dlls/mscordac/mscordac.targets index 8c41f106f7..3e0474f9a8 100644 --- a/src/dlls/mscordac/mscordac.targets +++ b/src/dlls/mscordac/mscordac.targets @@ -77,6 +77,9 @@ $(ClrSrcDirectory)StrongName\api\dac\$(XPlatHostLibBuildDir)\strongname_dac.nativeproj + + $(ClrSrcDirectory)unwinder\dac\$(XPlatHostLibBuildDir)\unwinder_dac.nativeproj + $(ClrSrcDirectory)debug\dbgutil\$(XPlatHostLibBuildDir)\dbgutil.nativeproj diff --git a/src/unwinder/.gitmirror b/src/unwinder/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/unwinder/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file diff --git a/src/unwinder/CMakeLists.txt b/src/unwinder/CMakeLists.txt new file mode 100644 index 0000000000..0ed0160b8a --- /dev/null +++ b/src/unwinder/CMakeLists.txt @@ -0,0 +1,22 @@ +include_directories(BEFORE ${VM_DIR}) +include_directories(BEFORE ${VM_DIR}/${ARCH_SOURCES_DIR}) +include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(BEFORE ${CLR_DIR}/src/unwinder) +include_directories(${CLR_DIR}/src/debug/ee) +include_directories(${CLR_DIR}/src/gc) +include_directories(${CLR_DIR}/src/gcdump) +include_directories(${CLR_DIR}/src/debug/daccess) + +if(IS_64BIT_BUILD EQUAL 1) + include_directories(amd64) + + set(UNWINDER_SOURCES + unwinder.cpp + amd64/unwinder_amd64.cpp + ) + + convert_to_absolute_path(UNWINDER_SOURCES ${UNWINDER_SOURCES}) + + add_subdirectory(dac) +endif(IS_64BIT_BUILD EQUAL 1) + diff --git a/src/unwinder/amd64/.gitmirror b/src/unwinder/amd64/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/unwinder/amd64/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file diff --git a/src/unwinder/amd64/dbs_stack_x64.cpp b/src/unwinder/amd64/dbs_stack_x64.cpp new file mode 100644 index 0000000000..83f2a00abf --- /dev/null +++ b/src/unwinder/amd64/dbs_stack_x64.cpp @@ -0,0 +1,1500 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +//---------------------------------------------------------------------------- +// + +// +// Stack unwinding implementation for x64. +// + +// +//---------------------------------------------------------------------------- + +#include "pch.cpp" +#pragma hdrstop + +//---------------------------------------------------------------------------- +// +// Copied OS code. +// +// This must be kept in sync with the system unwinder. +// base\ntos\rtl\amd64\exdsptch.c +// +//---------------------------------------------------------------------------- + +// +// Lookup table providing the number of slots used by each unwind code. +// + +UCHAR +DbsX64StackUnwinder::s_UnwindOpSlotTable[] = +{ + 1, // UWOP_PUSH_NONVOL + 2, // UWOP_ALLOC_LARGE (or 3, special cased in lookup code) + 1, // UWOP_ALLOC_SMALL + 1, // UWOP_SET_FPREG + 2, // UWOP_SAVE_NONVOL + 3, // UWOP_SAVE_NONVOL_FAR + 2, // UWOP_SAVE_XMM + 3, // UWOP_SAVE_XMM_FAR + 2, // UWOP_SAVE_XMM128 + 3, // UWOP_SAVE_XMM128_FAR + 1 // UWOP_PUSH_MACHFRAME +}; + +// +// ****** temp - defin elsewhere ****** +// + +#define SIZE64_PREFIX 0x48 +#define ADD_IMM8_OP 0x83 +#define ADD_IMM32_OP 0x81 +#define JMP_IMM8_OP 0xeb +#define JMP_IMM32_OP 0xe9 +#define JMP_IND_OP 0xff +#define LEA_OP 0x8d +#define REP_PREFIX 0xf3 +#define POP_OP 0x58 +#define RET_OP 0xc3 +#define RET_OP_2 0xc2 + +#define IS_REX_PREFIX(x) (((x) & 0xf0) == 0x40) + +HRESULT +DbsX64StackUnwinder::UnwindPrologue( + __in ULONG64 ImageBase, + __in ULONG64 ControlPc, + __in ULONG64 FrameBase, + __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __inout PAMD64_CONTEXT ContextRecord + ) + +/*++ + +Routine Description: + + This function processes unwind codes and reverses the state change + effects of a prologue. If the specified unwind information contains + chained unwind information, then that prologue is unwound recursively. + As the prologue is unwound state changes are recorded in the specified + context structure and optionally in the specified context pointers + structures. + +Arguments: + + ImageBase - Supplies the base address of the image that contains the + function being unwound. + + ControlPc - Supplies the address where control left the specified + function. + + FrameBase - Supplies the base of the stack frame subject function stack + frame. + + FunctionEntry - Supplies the address of the function table entry for the + specified function. + + ContextRecord - Supplies the address of a context record. + +--*/ + +{ + + HRESULT Status = E_UNEXPECTED; + ULONG64 FloatingAddress; + PAMD64_M128 FloatingRegister; + ULONG FrameOffset; + ULONG Index; + ULONG64 IntegerAddress; + PULONG64 IntegerRegister; + BOOLEAN MachineFrame; + ULONG OpInfo; + ULONG PrologOffset; + ULONG64 ReturnAddress; + ULONG64 StackAddress; + ULONG64 UnwindInfoBuffer[32]; + PAMD64_UNWIND_INFO UnwindInfo; + ULONG UnwindOp; + + // + // Process the unwind codes. + // + + FloatingRegister = &ContextRecord->Xmm0; + IntegerRegister = &ContextRecord->Rax; + Index = 0; + MachineFrame = FALSE; + PrologOffset = (ULONG)(ControlPc - (FunctionEntry->BeginAddress + ImageBase)); + + m_Services->Status(1, "Prol: RIP %I64X, 0x%X bytes in function at %I64X\n", + ControlPc, PrologOffset, + FunctionEntry->BeginAddress + ImageBase); + m_Services->Status(1, "Prol: Read unwind info at %I64X\n", + FunctionEntry->UnwindInfoAddress + ImageBase); + + if ((Status = + GetUnwindInfo(ImageBase, FunctionEntry->UnwindInfoAddress, + false, + UnwindInfoBuffer, sizeof(UnwindInfoBuffer), + (PVOID*)&UnwindInfo)) != S_OK) { + m_Services->Status(1, "Prol: Unable to read unwind info\n"); + return Status; + } + + m_Services->Status(1, " Unwind info has 0x%X codes\n", + UnwindInfo->CountOfCodes); + + while (Index < UnwindInfo->CountOfCodes) { + + m_Services->Status(1, " %02X: Code %X offs %03X, RSP %I64X\n", + Index, UnwindInfo->UnwindCode[Index].UnwindOp, + UnwindInfo->UnwindCode[Index].CodeOffset, + ContextRecord->Rsp); + + // + // If the prologue offset is greater than the next unwind code offset, + // then simulate the effect of the unwind code. + // + + UnwindOp = UnwindInfo->UnwindCode[Index].UnwindOp; + if (UnwindOp > AMD64_UWOP_PUSH_MACHFRAME) { + m_Services->Status(1, "Prol: Invalid unwind op %X at index %X\n", + UnwindOp, Index); + goto Fail; + } + + OpInfo = UnwindInfo->UnwindCode[Index].OpInfo; + if (PrologOffset >= UnwindInfo->UnwindCode[Index].CodeOffset) { + switch (UnwindOp) { + + // + // Push nonvolatile integer register. + // + // The operation information is the register number of the + // register than was pushed. + // + + case AMD64_UWOP_PUSH_NONVOL: + IntegerAddress = ContextRecord->Rsp; + if ((Status = m_Services-> + ReadAllMemory(IntegerAddress, + &IntegerRegister[OpInfo], + sizeof(ULONG64))) != S_OK) { + m_Services->Status(1, "Prol: Op %X memory " + "read failed at %I64X\n", + UnwindOp, IntegerAddress); + goto Fail; + } + + ContextRecord->Rsp += 8; + break; + + // + // Allocate a large sized area on the stack. + // + // The operation information determines if the size is + // 16- or 32-bits. + // + + case AMD64_UWOP_ALLOC_LARGE: + Index += 1; + FrameOffset = UnwindInfo->UnwindCode[Index].FrameOffset; + if (OpInfo != 0) { + Index += 1; + FrameOffset += (UnwindInfo->UnwindCode[Index].FrameOffset << 16); + } else { + // The 16-bit form is scaled. + FrameOffset *= 8; + } + + ContextRecord->Rsp += FrameOffset; + break; + + // + // Allocate a small sized area on the stack. + // + // The operation information is the size of the unscaled + // allocation size (8 is the scale factor) minus 8. + // + + case AMD64_UWOP_ALLOC_SMALL: + ContextRecord->Rsp += (OpInfo * 8) + 8; + break; + + // + // Establish the the frame pointer register. + // + // The operation information is not used. + // + + case AMD64_UWOP_SET_FPREG: + ContextRecord->Rsp = IntegerRegister[UnwindInfo->FrameRegister]; + ContextRecord->Rsp -= UnwindInfo->FrameOffset * 16; + break; + + // + // Save nonvolatile integer register on the stack using a + // 16-bit displacment. + // + // The operation information is the register number. + // + + case AMD64_UWOP_SAVE_NONVOL: + Index += 1; + FrameOffset = UnwindInfo->UnwindCode[Index].FrameOffset * 8; + IntegerAddress = FrameBase + FrameOffset; + if ((Status = m_Services-> + ReadAllMemory(IntegerAddress, + &IntegerRegister[OpInfo], + sizeof(ULONG64))) != S_OK) { + m_Services->Status(1, "Prol: Op %X memory read " + "failed at %I64X\n", + UnwindOp, IntegerAddress); + goto Fail; + } + break; + + // + // Save nonvolatile integer register on the stack using a + // 32-bit displacment. + // + // The operation information is the register number. + // + + case AMD64_UWOP_SAVE_NONVOL_FAR: + Index += 2; + FrameOffset = UnwindInfo->UnwindCode[Index - 1].FrameOffset; + FrameOffset += UnwindInfo->UnwindCode[Index].FrameOffset << 16; + IntegerAddress = FrameBase + FrameOffset; + if ((Status = m_Services-> + ReadAllMemory(IntegerAddress, + &IntegerRegister[OpInfo], + sizeof(ULONG64))) != S_OK) { + m_Services->Status(1, "Prol: Op %X memory read " + "failed at %I64X\n", + UnwindOp, IntegerAddress); + goto Fail; + } + break; + + // + // Save a nonvolatile XMM(64) register on the stack using a + // 16-bit displacement. + // + // The operation information is the register number. + // + + case AMD64_UWOP_SAVE_XMM: + Index += 1; + FrameOffset = UnwindInfo->UnwindCode[Index].FrameOffset * 8; + FloatingAddress = FrameBase + FrameOffset; + FloatingRegister[OpInfo].High = 0; + if ((Status = m_Services-> + ReadAllMemory(FloatingAddress, + &FloatingRegister[OpInfo].Low, + sizeof(ULONG64))) != S_OK) { + m_Services->Status(1, "Prol: Op %X memory read " + "failed at %I64X\n", + UnwindOp, FloatingAddress); + goto Fail; + } + break; + + // + // Save a nonvolatile XMM(64) register on the stack using a + // 32-bit displacement. + // + // The operation information is the register number. + // + + case AMD64_UWOP_SAVE_XMM_FAR: + Index += 2; + FrameOffset = UnwindInfo->UnwindCode[Index - 1].FrameOffset; + FrameOffset += UnwindInfo->UnwindCode[Index].FrameOffset << 16; + FloatingAddress = FrameBase + FrameOffset; + FloatingRegister[OpInfo].High = 0; + if ((Status = m_Services-> + ReadAllMemory(FloatingAddress, + &FloatingRegister[OpInfo].Low, + sizeof(ULONG64))) != S_OK) { + m_Services->Status(1, "Prol: Op %X memory read " + "failed at %I64X\n", + UnwindOp, FloatingAddress); + goto Fail; + } + break; + + // + // Save a nonvolatile XMM(128) register on the stack using a + // 16-bit displacement. + // + // The operation information is the register number. + // + + case AMD64_UWOP_SAVE_XMM128: + Index += 1; + FrameOffset = UnwindInfo->UnwindCode[Index].FrameOffset * 16; + FloatingAddress = FrameBase + FrameOffset; + if ((Status = m_Services-> + ReadAllMemory(FloatingAddress, + &FloatingRegister[OpInfo], + sizeof(AMD64_M128))) != S_OK) { + m_Services->Status(1, "Prol: Op %X memory read " + "failed at %I64X\n", + UnwindOp, FloatingAddress); + goto Fail; + } + break; + + // + // Save a nonvolatile XMM(128) register on the stack using a + // 32-bit displacement. + // + // The operation information is the register number. + // + + case AMD64_UWOP_SAVE_XMM128_FAR: + Index += 2; + FrameOffset = UnwindInfo->UnwindCode[Index - 1].FrameOffset; + FrameOffset += UnwindInfo->UnwindCode[Index].FrameOffset << 16; + FloatingAddress = FrameBase + FrameOffset; + if ((Status = m_Services-> + ReadAllMemory(FloatingAddress, + &FloatingRegister[OpInfo], + sizeof(AMD64_M128))) != S_OK) { + m_Services->Status(1, "Prol: Op %X memory read " + "failed at %I64X\n", + UnwindOp, FloatingAddress); + goto Fail; + } + break; + + // + // Push a machine frame on the stack. + // + // The operation information determines whether the machine + // frame contains an error code or not. + // + + case AMD64_UWOP_PUSH_MACHFRAME: + MachineFrame = TRUE; + ReturnAddress = ContextRecord->Rsp; + StackAddress = ContextRecord->Rsp + (3 * 8); + if (OpInfo != 0) { + ReturnAddress += 8; + StackAddress += 8; + } + + m_RestartFrame = true; + m_TrapAddr = ReturnAddress - + FIELD_OFFSET(AMD64_KTRAP_FRAME, Rip); + + if ((Status = m_Services-> + ReadAllMemory(ReturnAddress, + &ContextRecord->Rip, + sizeof(ULONG64))) != S_OK) { + m_Services->Status(1, "Prol: Op %X memory " + "read 1 failed at %I64X\n", + UnwindOp, ReturnAddress); + goto Fail; + } + if ((Status = m_Services-> + ReadAllMemory(StackAddress, + &ContextRecord->Rsp, + sizeof(ULONG64))) != S_OK) { + m_Services->Status(1, "Prol: Op %X memory " + "read 2 failed at %I64X\n", + UnwindOp, StackAddress); + goto Fail; + } + break; + + // + // Unused codes. + // + + default: + break; + } + + Index += 1; + + } else { + + // + // Skip this unwind operation by advancing the slot index by the + // number of slots consumed by this operation. + // + + Index += s_UnwindOpSlotTable[UnwindOp]; + + // + // Special case any unwind operations that can consume a variable + // number of slots. + // + + switch (UnwindOp) { + + // + // A non-zero operation information indicates that an + // additional slot is consumed. + // + + case AMD64_UWOP_ALLOC_LARGE: + if (OpInfo != 0) { + Index += 1; + } + + break; + + // + // No other special cases. + // + + default: + break; + } + } + } + + // + // If chained unwind information is specified, then recursively unwind + // the chained information. Otherwise, determine the return address if + // a machine frame was not encountered during the scan of the unwind + // codes. + // + + if ((UnwindInfo->Flags & AMD64_UNW_FLAG_CHAININFO) != 0) { + + _PIMAGE_RUNTIME_FUNCTION_ENTRY ChainEntry; + + Index = UnwindInfo->CountOfCodes; + if ((Index & 1) != 0) { + Index += 1; + } + + // GetUnwindInfo looks for CHAININFO and reads + // the trailing RUNTIME_FUNCTION so we can just + // directly use the data sitting in UnwindInfo. + ChainEntry = (_PIMAGE_RUNTIME_FUNCTION_ENTRY) + &UnwindInfo->UnwindCode[Index]; + + m_Services->Status(1, " Chain with entry at %I64X\n", + FunctionEntry->UnwindInfoAddress + ImageBase + + (ULONG64)((PUCHAR)&UnwindInfo->UnwindCode[Index] - + (PUCHAR)UnwindInfo)); + + Status = UnwindPrologue(ImageBase, + ControlPc, + FrameBase, + ChainEntry, + ContextRecord); + + FreeUnwindInfo(UnwindInfo, UnwindInfoBuffer); + return Status; + + } else { + FreeUnwindInfo(UnwindInfo, UnwindInfoBuffer); + + if (MachineFrame == FALSE) { + if ((Status = m_Services-> + ReadAllMemory(ContextRecord->Rsp, + &ContextRecord->Rip, + sizeof(ULONG64))) != S_OK) { + return Status; + } + ContextRecord->Rsp += 8; + } + + m_Services->Status(1, "Prol: Returning with RIP %I64X, RSP %I64X\n", + ContextRecord->Rip, ContextRecord->Rsp); + return S_OK; + } + + Fail: + FreeUnwindInfo(UnwindInfo, UnwindInfoBuffer); + m_Services->Status(1, "Prol: Unwind failed, 0x%08X\n", Status); + return Status; +} + +HRESULT +DbsX64StackUnwinder::VirtualUnwind( + __in ULONG64 ImageBase, + __in ULONG64 ControlPc, + __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __inout PAMD64_CONTEXT ContextRecord, + __out PULONG64 EstablisherFrame + ) + +/*++ + +Routine Description: + + This function virtually unwinds the specified function by executing its + prologue code backward or its epilogue code forward. + + If a context pointers record is specified, then the address where each + nonvolatile registers is restored from is recorded in the appropriate + element of the context pointers record. + +Arguments: + + ImageBase - Supplies the base address of the image that contains the + function being unwound. + + ControlPc - Supplies the address where control left the specified + function. + + FunctionEntry - Supplies the address of the function table entry for the + specified function. + + ContextRecord - Supplies the address of a context record. + + EstablisherFrame - Supplies a pointer to a variable that receives the + the establisher frame pointer value. + +--*/ + +{ + + HRESULT Status; + ULONG64 BranchTarget; + LONG Displacement; + ULONG FrameRegister; + ULONG Index; + LOGICAL InEpilogue; + PULONG64 IntegerRegister; + PUCHAR NextByte; + _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryFunctionEntry; + ULONG PrologOffset; + ULONG RegisterNumber; + PAMD64_UNWIND_INFO UnwindInfo; + ULONG64 UnwindInfoBuffer[8]; + ULONG Done; + UCHAR InstrBuffer[32]; + ULONG InstrBytes; + ULONG Bytes; + ULONG UnwindFrameReg; + + // + // If the specified function does not use a frame pointer, then the + // establisher frame is the contents of the stack pointer. This may + // not actually be the real establisher frame if control left the + // function from within the prologue. In this case the establisher + // frame may be not required since control has not actually entered + // the function and prologue entries cannot refer to the establisher + // frame before it has been established, i.e., if it has not been + // established, then no save unwind codes should be encountered during + // the unwind operation. + // + // If the specified function uses a frame pointer and control left the + // function outside of the prologue or the unwind information contains + // a chained information structure, then the establisher frame is the + // contents of the frame pointer. + // + // If the specified function uses a frame pointer and control left the + // function from within the prologue, then the set frame pointer unwind + // code must be looked up in the unwind codes to detetermine if the + // contents of the stack pointer or the contents of the frame pointer + // should be used for the establisher frame. This may not actually be + // the real establisher frame. In this case the establisher frame may + // not be required since control has not actually entered the function + // and prologue entries cannot refer to the establisher frame before it + // has been established, i.e., if it has not been established, then no + // save unwind codes should be encountered during the unwind operation. + // + // N.B. The correctness of these assumptions is based on the ordering of + // unwind codes. + // + + if ((Status = GetUnwindInfo(ImageBase, FunctionEntry->UnwindInfoAddress, + true, + UnwindInfoBuffer, sizeof(UnwindInfoBuffer), + (PVOID*)&UnwindInfo)) != S_OK) { + return Status; + } + + PrologOffset = (ULONG)(ControlPc - (FunctionEntry->BeginAddress + ImageBase)); + UnwindFrameReg = UnwindInfo->FrameRegister; + if (UnwindFrameReg == 0) { + *EstablisherFrame = ContextRecord->Rsp; + + } else if ((PrologOffset >= UnwindInfo->SizeOfProlog) || + ((UnwindInfo->Flags & AMD64_UNW_FLAG_CHAININFO) != 0)) { + *EstablisherFrame = (&ContextRecord->Rax)[UnwindFrameReg]; + *EstablisherFrame -= UnwindInfo->FrameOffset * 16; + + } else { + + // Read all the data. + if ((Status = GetUnwindInfo(ImageBase, + FunctionEntry->UnwindInfoAddress, + false, + UnwindInfoBuffer, + sizeof(UnwindInfoBuffer), + (PVOID*)&UnwindInfo)) != S_OK) { + return Status; + } + + Index = 0; + while (Index < UnwindInfo->CountOfCodes) { + if (UnwindInfo->UnwindCode[Index].UnwindOp == AMD64_UWOP_SET_FPREG) { + break; + } + + Index += 1; + } + + if (PrologOffset >= UnwindInfo->UnwindCode[Index].CodeOffset) { + *EstablisherFrame = (&ContextRecord->Rax)[UnwindFrameReg]; + *EstablisherFrame -= UnwindInfo->FrameOffset * 16; + + } else { + *EstablisherFrame = ContextRecord->Rsp; + } + + FreeUnwindInfo(UnwindInfo, UnwindInfoBuffer); + } + + if ((Status = m_Services-> + ReadMemory(ControlPc, InstrBuffer, sizeof(InstrBuffer), + &InstrBytes)) != S_OK) { + m_Services->Status(1, "Unable to read instruction stream at %I64X\n", + ControlPc); + + // We need the code to look for epilogue ops. + // It's very rare to be stopped in an epilogue when + // getting a stack trace, so if we can't read the + // code just assume we aren't in an epilogue. + InstrBytes = 0; + } + + // + // If the point at which control left the specified function is in an + // epilogue, then emulate the execution of the epilogue forward and + // return no exception handler. + // + + IntegerRegister = &ContextRecord->Rax; + NextByte = InstrBuffer; + Bytes = InstrBytes; + + // + // Check for one of: + // + // add rsp, imm8 + // or + // add rsp, imm32 + // or + // lea rsp, -disp8[fp] + // or + // lea rsp, -disp32[fp] + // + + if (Bytes >= 4 && + (NextByte[0] == SIZE64_PREFIX) && + (NextByte[1] == ADD_IMM8_OP) && + (NextByte[2] == 0xc4)) { + + // + // add rsp, imm8. + // + + NextByte += 4; + Bytes -= 4; + + } else if (Bytes >= 7 && + (NextByte[0] == SIZE64_PREFIX) && + (NextByte[1] == ADD_IMM32_OP) && + (NextByte[2] == 0xc4)) { + + // + // add rsp, imm32. + // + + NextByte += 7; + Bytes -= 7; + + } else if (Bytes >= 4 && + ((NextByte[0] & 0xf8) == SIZE64_PREFIX) && + (NextByte[1] == LEA_OP)) { + + FrameRegister = ((NextByte[0] & 0x7) << 3) | (NextByte[2] & 0x7); + if ((FrameRegister != 0) && + (FrameRegister == UnwindFrameReg)) { + if ((NextByte[2] & 0xf8) == 0x60) { + + // + // lea rsp, disp8[fp]. + // + + NextByte += 4; + Bytes -= 4; + + } else if (Bytes >= 7 && + (NextByte[2] &0xf8) == 0xa0) { + + // + // lea rsp, disp32[fp]. + // + + NextByte += 7; + Bytes -= 7; + } + } + } + + // + // Check for any number of: + // + // pop nonvolatile-integer-register[0..15]. + // + + while (TRUE) { + if (Bytes >= 1 && + (NextByte[0] & 0xf8) == POP_OP) { + NextByte += 1; + Bytes -= 1; + + } else if (Bytes >= 2 && + IS_REX_PREFIX(NextByte[0]) && + ((NextByte[1] & 0xf8) == POP_OP)) { + + NextByte += 2; + Bytes -= 2; + + } else { + break; + } + } + + // + // If the next instruction is a return or an appropriate jump, then + // control is currently in an epilogue and execution of the epilogue + // should be emulated. Otherwise, execution is not in an epilogue and + // the prologue should be unwound. + // + + InEpilogue = FALSE; + if ((Bytes >= 1 && + ((NextByte[0] == RET_OP) || + (NextByte[0] == RET_OP_2))) || + (Bytes >= 2 && + ((NextByte[0] == REP_PREFIX) && (NextByte[1] == RET_OP)))) { + + // + // A return is an unambiguous indication of an epilogue. + // + + InEpilogue = TRUE; + + } else if ((Bytes >= 2 && NextByte[0] == JMP_IMM8_OP) || + (Bytes >= 5 && NextByte[0] == JMP_IMM32_OP)) { + + // + // An unconditional branch to a target that is equal to the start of + // or outside of this routine is logically a call to another function. + // + + BranchTarget = (ULONG64)(NextByte - InstrBuffer) + ControlPc - ImageBase; + if (NextByte[0] == JMP_IMM8_OP) { + BranchTarget += 2 + (CHAR)NextByte[1]; + } else { + BranchTarget += 5 + *((LONG UNALIGNED *)&NextByte[1]); + } + + // + // Determine whether the branch target refers to code within this + // function. If not, then it is an epilogue indicator. + // + // A branch to the start of self implies a recursive call, so + // is treated as an epilogue. + // + + if (BranchTarget < FunctionEntry->BeginAddress || + BranchTarget >= FunctionEntry->EndAddress) { + + _IMAGE_RUNTIME_FUNCTION_ENTRY PrimaryEntryBuffer; + + // + // The branch target is outside of the region described by + // this function entry. See whether it is contained within + // an indirect function entry associated with this same + // function. + // + // If not, then the branch target really is outside of + // this function. + // + + PrimaryFunctionEntry = + SameFunction(FunctionEntry, + ImageBase, + BranchTarget + ImageBase, + &PrimaryEntryBuffer); + + if ((PrimaryFunctionEntry == NULL) || + (BranchTarget == PrimaryFunctionEntry->BeginAddress)) { + + InEpilogue = TRUE; + } + + } else if ((BranchTarget == FunctionEntry->BeginAddress) && + ((UnwindInfo->Flags & AMD64_UNW_FLAG_CHAININFO) == 0)) { + + InEpilogue = TRUE; + } + + } else if (Bytes >= 2 && + (NextByte[0] == JMP_IND_OP) && (NextByte[1] == 0x25)) { + + // + // An unconditional jump indirect. + // + // This is a jmp outside of the function, probably a tail call + // to an import function. + // + + InEpilogue = TRUE; + + } else if (Bytes >= 3 && + ((NextByte[0] & 0xf8) == SIZE64_PREFIX) && + (NextByte[1] == 0xff) && + (NextByte[2] & 0x38) == 0x20) { + + // + // This is an indirect jump opcode: 0x48 0xff /4. The 64-bit + // flag (REX.W) is always redundant here, so its presence is + // overloaded to indicate a branch out of the function - a tail + // call. + // + // Such an opcode is an unambiguous epilogue indication. + // + + InEpilogue = TRUE; + } + + if (InEpilogue != FALSE) { + NextByte = InstrBuffer; + Bytes = InstrBytes; + + // + // Emulate one of (if any): + // + // add rsp, imm8 + // or + // add rsp, imm32 + // or + // lea rsp, disp8[frame-register] + // or + // lea rsp, disp32[frame-register] + // + + if (Bytes >= 1 && + (NextByte[0] & 0xf8) == SIZE64_PREFIX) { + + if (Bytes >= 4 && + NextByte[1] == ADD_IMM8_OP) { + + // + // add rsp, imm8. + // + + ContextRecord->Rsp += (CHAR)NextByte[3]; + NextByte += 4; + Bytes -= 4; + + } else if (Bytes >= 7 && + NextByte[1] == ADD_IMM32_OP) { + + // + // add rsp, imm32. + // + + Displacement = NextByte[3] | (NextByte[4] << 8); + Displacement |= (NextByte[5] << 16) | (NextByte[6] << 24); + ContextRecord->Rsp += Displacement; + NextByte += 7; + Bytes -= 7; + + } else if (Bytes >= 4 && + NextByte[1] == LEA_OP) { + if ((NextByte[2] & 0xf8) == 0x60) { + + // + // lea rsp, disp8[frame-register]. + // + + ContextRecord->Rsp = IntegerRegister[FrameRegister]; + ContextRecord->Rsp += (CHAR)NextByte[3]; + NextByte += 4; + Bytes -= 4; + + } else if (Bytes >= 7 && + (NextByte[2] & 0xf8) == 0xa0) { + + // + // lea rsp, disp32[frame-register]. + // + + Displacement = NextByte[3] | (NextByte[4] << 8); + Displacement |= (NextByte[5] << 16) | (NextByte[6] << 24); + ContextRecord->Rsp = IntegerRegister[FrameRegister]; + ContextRecord->Rsp += Displacement; + NextByte += 7; + Bytes -= 7; + } + } + } + + // + // Emulate any number of (if any): + // + // pop nonvolatile-integer-register. + // + + while (TRUE) { + if (Bytes >= 1 && + (NextByte[0] & 0xf8) == POP_OP) { + + // + // pop nonvolatile-integer-register[0..7] + // + + RegisterNumber = NextByte[0] & 0x7; + if ((Status = m_Services-> + ReadAllMemory(ContextRecord->Rsp, + &IntegerRegister[RegisterNumber], + sizeof(ULONG64))) != S_OK) { + m_Services->Status(1, "Unable to read stack at %I64X\n", + ContextRecord->Rsp); + return Status; + } + ContextRecord->Rsp += 8; + NextByte += 1; + Bytes -= 1; + + } else if (Bytes >= 2 && + IS_REX_PREFIX(NextByte[0]) && + (NextByte[1] & 0xf8) == POP_OP) { + + // + // pop nonvolatile-integer-register[8..15] + // + + RegisterNumber = ((NextByte[0] & 1) << 3) | (NextByte[1] & 0x7); + if ((Status = m_Services-> + ReadAllMemory(ContextRecord->Rsp, + &IntegerRegister[RegisterNumber], + sizeof(ULONG64))) != S_OK) { + m_Services->Status(1, "Unable to read stack at %I64X\n", + ContextRecord->Rsp); + return Status; + } + ContextRecord->Rsp += 8; + NextByte += 2; + Bytes -= 2; + + } else { + break; + } + } + + // + // Emulate return and return null exception handler. + // + // Note: this instruction might in fact be a jmp, however + // we want to emulate a return regardless. + // + + if ((Status = m_Services-> + ReadAllMemory(ContextRecord->Rsp, + &ContextRecord->Rip, + sizeof(ULONG64))) != S_OK) { + m_Services->Status(1, "Unable to read stack at %I64X\n", + ContextRecord->Rsp); + return Status; + } + ContextRecord->Rsp += 8; + return S_OK; + } + + // + // Control left the specified function outside an epilogue. Unwind the + // subject function and any chained unwind information. + // + + return UnwindPrologue(ImageBase, + ControlPc, + *EstablisherFrame, + FunctionEntry, + ContextRecord); +} + +ULONG64 +DbsX64StackUnwinder::LookupPrimaryUnwindInfo( + __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __in ULONG64 ImageBase, + __out _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryEntry + ) + +/*++ + +Routine Description: + + This function determines whether the supplied function entry is a primary + function entry or a chained function entry. If it is a chained function + entry, the unwind information associated with the primary function entry + is returned. + +Arguments: + + FunctionEntry - Supplies a pointer to the function entry for which the + associated primary function entry will be located. + + ImageBase - Supplies the base address of the image containing the + supplied function entry. + + PrimaryEntry - Supplies the address of a variable that receives a pointer + to the primary function entry. + +Return Value: + + A pointer to the unwind information for the primary function entry is + returned as the function value. + +--*/ + +{ + + ULONG Index; + ULONG64 UnwindInfoBuffer[32]; + PAMD64_UNWIND_INFO UnwindInfo; + ULONG UnwindRel; + ULONG64 UnwindAbs; + + // + // Locate the unwind information and determine whether it is chained. + // If the unwind information is chained, then locate the parent function + // entry and loop again. + // + + UnwindRel = FunctionEntry->UnwindInfoAddress; + // Copy the function entry before it becomes invalid. + *PrimaryEntry = *FunctionEntry; + + do { + UnwindAbs = ImageBase + UnwindRel; + if (GetUnwindInfo(ImageBase, UnwindRel, + false, + UnwindInfoBuffer, sizeof(UnwindInfoBuffer), + (PVOID*)&UnwindInfo) != S_OK || + (UnwindInfo->Flags & AMD64_UNW_FLAG_CHAININFO) == 0) { + break; + } + + Index = UnwindInfo->CountOfCodes; + if ((Index & 1) != 0) { + Index += 1; + } + + FunctionEntry = (_PIMAGE_RUNTIME_FUNCTION_ENTRY) + &UnwindInfo->UnwindCode[Index]; + UnwindRel = FunctionEntry->UnwindInfoAddress; + + // Copy the function entry before it becomes invalid. + *PrimaryEntry = *FunctionEntry; + + FreeUnwindInfo(UnwindInfo, UnwindInfoBuffer); + + } while (TRUE); + + return UnwindAbs; +} + +_PIMAGE_RUNTIME_FUNCTION_ENTRY +DbsX64StackUnwinder::SameFunction( + __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __in ULONG64 ImageBase, + __in ULONG64 ControlPc, + __out _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionReturnBuffer + ) + +/*++ + +Routine Description: + + This function determines whether the address supplied by ControlPc lies + anywhere within the function associated with FunctionEntry. + +Arguments: + + FunctionEntry - Supplies a pointer to a function entry (primary or chained) + associated with the function. + + ImageBase - Supplies the base address of the image containing the supplied + function entry. + + ControlPc - Supplies the address that will be tested for inclusion within + the function associated with FunctionEntry. + +Return Value: + + If the address of the unwind information for the specified function is + equal to the address of the unwind information for the control PC, then + a pointer to a function table entry that describes the primary function + table entry is returned as the function value. Otherwise, NULL is returned. + +--*/ + +{ + + _IMAGE_RUNTIME_FUNCTION_ENTRY TargetFunctionEntry; + ULONG64 TargetImageBase; + ULONG64 UnwindInfo1; + ULONG64 UnwindInfo2; + + // + // Find the unwind information referenced by the primary function entry + // associated with the specified function entry. + // + + UnwindInfo1 = LookupPrimaryUnwindInfo(FunctionEntry, ImageBase, + FunctionReturnBuffer); + + // + // Determine the function entry containing the control Pc and similarly + // resolve it's primary function entry. + // + + if (m_Services->GetModuleBase(ControlPc, &TargetImageBase) != S_OK || + m_Services->GetFunctionEntry(ControlPc, + &TargetFunctionEntry, + sizeof(TargetFunctionEntry)) != S_OK) { + return NULL; + } + + UnwindInfo2 = LookupPrimaryUnwindInfo(&TargetFunctionEntry, + TargetImageBase, + FunctionReturnBuffer); + + // + // If the address of the two sets of unwind information are equal, then + // return the address of the primary function entry. Otherwise, return + // NULL. + // + + if (UnwindInfo1 == UnwindInfo2) { + return FunctionReturnBuffer; + + } else { + return NULL; + } +} + +//---------------------------------------------------------------------------- +// +// DbsX64StackUnwinder. +// +//---------------------------------------------------------------------------- + +#define DBHX64_SAVE_TRAP(_DbhFrame) ((_DbhFrame)->Reserved[0]) + +// +// Flags word. +// + +#define DBHX64_IS_RESTART_FLAG (0x1UI64) + +#define DBHX64_GET_IS_RESTART(_DbhFrame) \ + ((((_DbhFrame)->Reserved[2]) & DBHX64_IS_RESTART_FLAG) != 0) +#define DBHX64_SET_IS_RESTART(_DbhFrame, _IsRestart) \ + ((_DbhFrame)->Reserved[2] = \ + (((_DbhFrame)->Reserved[2]) & ~DBHX64_IS_RESTART_FLAG) | \ + ((_IsRestart) ? DBHX64_IS_RESTART_FLAG : 0)) + +DbsX64StackUnwinder::DbsX64StackUnwinder(__in_opt DbsStackServices* Services) + : DbsStackUnwinder(Services, "x64", IMAGE_FILE_MACHINE_AMD64, + sizeof(m_Context), + sizeof(_IMAGE_RUNTIME_FUNCTION_ENTRY), + sizeof(AMD64_UNWIND_INFO), 16, 8, 1) +{ + m_ContextBuffer = &m_Context; +} + +HRESULT +DbsX64StackUnwinder::Unwind(void) +{ + HRESULT Status; + + ClearUnwindDerived(); + + if (SUCCEEDED(Status = BaseUnwind())) + { + return Status; + } + + // + // Unable to do a normal unwind, so check for + // alternate transitions like kernel/user boundaries. + // If this fails just return the original error + // as that's more likely to be interesting. + // + + DWORD64 ImageBase; + _IMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry; + DWORD64 StackPointer; + + if (UnwindNtKernelCallback(&ImageBase, + &FunctionEntry, + sizeof(FunctionEntry), + &StackPointer) != S_OK) + { + return Status; + } + + m_InstructionPointer = ImageBase + FunctionEntry.BeginAddress; + m_CallPointer = m_InstructionPointer; + m_Context.Rip = m_InstructionPointer; + m_StackPointer = StackPointer; + m_Context.Rsp = m_StackPointer; + m_RestartFrame = true; + + return S_OK; +} + +DWORD +DbsX64StackUnwinder:: +GetFullUnwindInfoSize(__in PVOID InfoHeader) +{ + PAMD64_UNWIND_INFO UnwindInfo = (PAMD64_UNWIND_INFO)InfoHeader; + + DWORD UnwindInfoSize = FIELD_OFFSET(AMD64_UNWIND_INFO, UnwindCode) + + UnwindInfo->CountOfCodes * sizeof(AMD64_UNWIND_CODE); + + // An extra alignment code and function entry may be added on to handle + // the chained info case where the chain function entry is just + // beyond the end of the normal code array. + if ((UnwindInfo->Flags & AMD64_UNW_FLAG_CHAININFO) != 0) + { + if ((UnwindInfo->CountOfCodes & 1) != 0) + { + UnwindInfoSize += sizeof(AMD64_UNWIND_CODE); + } + UnwindInfoSize += sizeof(_IMAGE_RUNTIME_FUNCTION_ENTRY); + } + + return UnwindInfoSize; +} + +HRESULT +DbsX64StackUnwinder::DbhStart(__inout LPSTACKFRAME64 StackFrame, + __in DWORD DbhVersion, + __in_bcount(DbhStorageBytes) PVOID DbhStorage, + __in DWORD DbhStorageBytes, + __inout PVOID Context) +{ + HRESULT Status; + + if ((StackFrame->AddrPC.Offset && + StackFrame->AddrPC.Mode != AddrModeFlat) || + (StackFrame->AddrStack.Offset && + StackFrame->AddrStack.Mode != AddrModeFlat) || + (StackFrame->AddrFrame.Offset && + StackFrame->AddrFrame.Mode != AddrModeFlat)) + { + return E_INVALIDARG; + } + + if ((Status = DbsStackUnwinder:: + DbhStart(StackFrame, DbhVersion, DbhStorage, DbhStorageBytes, + Context)) != S_OK) + { + return Status; + } + + // dbghelp doesn't give a context size so we + // have to assume the buffer is large enough. + memcpy(&m_Context, Context, sizeof(m_Context)); + + // + // Override context values from the stack frame if necessary. + // + + if (StackFrame->AddrPC.Offset) + { + m_Context.Rip = StackFrame->AddrPC.Offset; + } + if (StackFrame->AddrStack.Offset) + { + m_Context.Rsp = StackFrame->AddrStack.Offset; + } + if (StackFrame->AddrFrame.Offset) + { + m_Context.Rbp = StackFrame->AddrFrame.Offset; + } + UpdateAbstractPointers(); + m_CallPointer = m_InstructionPointer; + + SetRestart(); + return S_OK; +} + +HRESULT +DbsX64StackUnwinder:: +DbhContinue(__inout LPSTACKFRAME64 StackFrame, + __in DWORD DbhVersion, + __in_bcount(DbhStorageBytes) PVOID DbhStorage, + __in DWORD DbhStorageBytes, + __inout PVOID Context) +{ + HRESULT Status; + + if ((Status = DbsStackUnwinder:: + DbhContinue(StackFrame, DbhVersion, + DbhStorage, DbhStorageBytes, + Context)) != S_OK) + { + return Status; + } + + if (DBHX64_GET_IS_RESTART(StackFrame)) + { + m_RestartFrame = true; + // The base DbhContinue always assumes it + // isn't a restart frame, so override it. + m_CallPointer = m_InstructionPointer; + } + + return Status; +} + +HRESULT +DbsX64StackUnwinder::DbhUpdatePreUnwind(__inout LPSTACKFRAME64 StackFrame) +{ + HRESULT Status; + + if ((Status = DbsStackUnwinder::DbhUpdatePreUnwind(StackFrame)) != S_OK) + { + return Status; + } + + DBHX64_SET_IS_RESTART(StackFrame, m_RestartFrame); + return S_OK; +} + +HRESULT +DbsX64StackUnwinder::DbhUpdatePostUnwind(__inout LPSTACKFRAME64 StackFrame, + __in HRESULT UnwindStatus) +{ + HRESULT Status; + + if ((Status = DbsStackUnwinder::DbhUpdatePostUnwind(StackFrame, + UnwindStatus)) != S_OK) + { + return Status; + } + + // The frame pointer is an artificial value set + // to a pointer below the return address. This + // matches an RBP-chain style of frame while + // also allowing easy access to the return + // address and homed arguments above it. + StackFrame->AddrFrame.Offset = m_FramePointer; + + DBHX64_SAVE_TRAP(StackFrame) = m_TrapAddr; + return S_OK; +} + +void +DbsX64StackUnwinder::UpdateAbstractPointers(void) +{ + m_InstructionPointer = m_Context.Rip; + m_StackPointer = m_Context.Rsp; + m_FramePointer = m_Context.Rbp; +} + +HRESULT +DbsX64StackUnwinder::BaseUnwind(void) +{ + HRESULT Status; + _IMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry; + + Status = m_Services->GetFunctionEntry(m_CallPointer, + &FunctionEntry, + sizeof(FunctionEntry)); + if (Status == S_OK) + { + DWORD64 ImageBase; + DWORD64 EstablisherFrame; + + // + // The return value coming out of mainCRTStartup is set by some + // run-time routine to be 0; this serves to cause an error if someone + // actually does a return from the mainCRTStartup frame. + // + + if ((Status = m_Services-> + GetModuleBase(m_Context.Rip, &ImageBase)) != S_OK || + (Status = VirtualUnwind(ImageBase, + m_Context.Rip, + &FunctionEntry, + &m_Context, + &EstablisherFrame)) != S_OK) + { + return Status; + } + + DWORD64 OldIp = m_InstructionPointer; + + UpdateAbstractPointers(); + UpdateCallPointer(); + m_FramePointer = m_StackPointer - 2 * sizeof(DWORD64); + + // Check for end frame. + if (m_Context.Rip == 0 || + (m_Context.Rip == OldIp && + EstablisherFrame == m_Context.Rsp)) + { + return S_FALSE; + } + } + else if (Status == E_NOINTERFACE) + { + // + // If there's no function entry for a function + // we assume that it's a leaf and that RSP points + // directly to the return address. + // + + m_Services->Status(1, "Leaf %I64X RSP %I64X\n", + m_Context.Rip, m_Context.Rsp); + + if ((Status = m_Services-> + ReadAllMemory(m_Context.Rsp, &m_Context.Rip, + sizeof(m_Context.Rip))) != S_OK) + { + return Status; + } + + // Update the context values to what they should be in + // the caller. + m_Context.Rsp += sizeof(m_Context.Rip); + UpdateAbstractPointers(); + m_CallPointer = m_InstructionPointer - 1; + m_FramePointer = m_StackPointer - 2 * sizeof(DWORD64); + } + else + { + return Status; + } + + m_FrameIndex++; + if (!m_RestartFrame) + { + AdjustForNoReturn(&m_InstructionPointer); + } + return S_OK; +} diff --git a/src/unwinder/amd64/unwinder_amd64.cpp b/src/unwinder/amd64/unwinder_amd64.cpp new file mode 100644 index 0000000000..de7f94f977 --- /dev/null +++ b/src/unwinder/amd64/unwinder_amd64.cpp @@ -0,0 +1,1225 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// + +#include "stdafx.h" +#include "unwinder_amd64.h" + +//--------------------------------------------------------------------------------------- +// +// Given the target address of an UNWIND_INFO structure, this function retrieves all the memory used for +// the UNWIND_INFO, including the variable size array of UNWIND_CODE. The function returns a host copy +// of the UNWIND_INFO. +// +// Arguments: +// taUnwindInfo - the target address of an UNWIND_INFO +// +// Return Value: +// Return a host copy of the UNWIND_INFO, including the array of UNWIND_CODE. +// +// Notes: +// The host copy of UNWIND_INFO is created from DAC memory, which will be flushed when the DAC cache +// is flushed (i.e. when the debugee is continued). Thus, the caller doesn't need to worry about freeing +// this memory. +// + +UNWIND_INFO * DacGetUnwindInfo(TADDR taUnwindInfo) +{ + PTR_UNWIND_INFO pUnwindInfo = PTR_UNWIND_INFO(taUnwindInfo); + DWORD cbUnwindInfo = offsetof(UNWIND_INFO, UnwindCode) + + pUnwindInfo->CountOfUnwindCodes * sizeof(UNWIND_CODE); + + // Check if there is a chained unwind info. If so, it has an extra RUNTIME_FUNCTION tagged to the end. + if ((pUnwindInfo->Flags & UNW_FLAG_CHAININFO) != 0) + { + // If there is an odd number of UNWIND_CODE, we need to adjust for alignment. + if ((pUnwindInfo->CountOfUnwindCodes & 1) != 0) + { + cbUnwindInfo += sizeof(UNWIND_CODE); + } + cbUnwindInfo += sizeof(RUNTIME_FUNCTION); + } + return reinterpret_cast(DacInstantiateTypeByAddress(taUnwindInfo, cbUnwindInfo, true)); +} + +//--------------------------------------------------------------------------------------- +// +// This function is just a wrapper over OOPStackUnwinder. The runtime can call this function to +// virtually unwind a CONTEXT out-of-process. +// +// Arguments: +// pContext - This is an in-out parameter. On entry, this is the CONTEXT to be unwound. +// On exit, this is the caller CONTEXT. +// +// Return Value: +// TRUE if the unwinding is successful +// +// Notes: +// This function overwrites the specified CONTEXT to store the caller CONTEXT. +// + +BOOL DacUnwindStackFrame(CONTEXT * pContext, KNONVOLATILE_CONTEXT_POINTERS* pContextPointers) +{ + OOPStackUnwinderAMD64 unwinder; + BOOL res = unwinder.Unwind(pContext); + + if (res && pContextPointers) + { + for (int i = 0; i < 16; i++) + { + *(&pContextPointers->Rax + i) = &pContext->Rax + i; + } + } + + return res; +} + +//--------------------------------------------------------------------------------------- +// +// Unwind the given CONTEXT to the caller CONTEXT. The given CONTEXT will be overwritten. +// +// Arguments: +// pContext - in-out parameter storing the specified CONTEXT on entry and the unwound CONTEXT on exit +// +// Return Value: +// TRUE if the unwinding is successful +// + +BOOL OOPStackUnwinderAMD64::Unwind(CONTEXT * pContext) +{ + HRESULT hr = E_FAIL; + + ULONG64 uControlPC = (DWORD64)dac_cast(::GetIP(pContext)); + + // get the module base + ULONG64 uImageBase; + hr = GetModuleBase(uControlPC, &uImageBase); + if (FAILED(hr)) + { + return FALSE; + } + + // get the function entry + IMAGE_RUNTIME_FUNCTION_ENTRY functionEntry; + hr = GetFunctionEntry(uControlPC, &functionEntry, sizeof(functionEntry)); + if (FAILED(hr)) + { + return FALSE; + } + + // call VirtualUnwind() to do the real work + ULONG64 EstablisherFrame; + hr = VirtualUnwind(uImageBase, uControlPC, &functionEntry, pContext, &EstablisherFrame); + + return (hr == S_OK); +} + + +// +// +// +// +// Everything below is borrowed from dbghelp.dll +// +// +// +// + + +//---------------------------------------------------------------------------- +// +// Copied OS code. +// +// This must be kept in sync with the system unwinder. +// base\ntos\rtl\amd64\exdsptch.c +// +//---------------------------------------------------------------------------- + +// +// Lookup table providing the number of slots used by each unwind code. +// + +UCHAR +OOPStackUnwinderAMD64::s_UnwindOpSlotTable[] = +{ + 1, // UWOP_PUSH_NONVOL + 2, // UWOP_ALLOC_LARGE (or 3, special cased in lookup code) + 1, // UWOP_ALLOC_SMALL + 1, // UWOP_SET_FPREG + 2, // UWOP_SAVE_NONVOL + 3, // UWOP_SAVE_NONVOL_FAR + 2, // UWOP_SAVE_XMM + 3, // UWOP_SAVE_XMM_FAR + 2, // UWOP_SAVE_XMM128 + 3, // UWOP_SAVE_XMM128_FAR + 1 // UWOP_PUSH_MACHFRAME +}; + +// +// ****** temp - defin elsewhere ****** +// + +#define SIZE64_PREFIX 0x48 +#define ADD_IMM8_OP 0x83 +#define ADD_IMM32_OP 0x81 +#define JMP_IMM8_OP 0xeb +#define JMP_IMM32_OP 0xe9 +#define JMP_IND_OP 0xff +#define LEA_OP 0x8d +#define REP_PREFIX 0xf3 +#define POP_OP 0x58 +#define RET_OP 0xc3 +#define RET_OP_2 0xc2 + +#define IS_REX_PREFIX(x) (((x) & 0xf0) == 0x40) + +HRESULT +OOPStackUnwinderAMD64::UnwindPrologue( + __in ULONG64 ImageBase, + __in ULONG64 ControlPc, + __in ULONG64 FrameBase, + __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __inout PCONTEXT ContextRecord + ) + +/*++ + +Routine Description: + + This function processes unwind codes and reverses the state change + effects of a prologue. If the specified unwind information contains + chained unwind information, then that prologue is unwound recursively. + As the prologue is unwound state changes are recorded in the specified + context structure and optionally in the specified context pointers + structures. + +Arguments: + + ImageBase - Supplies the base address of the image that contains the + function being unwound. + + ControlPc - Supplies the address where control left the specified + function. + + FrameBase - Supplies the base of the stack frame subject function stack + frame. + + FunctionEntry - Supplies the address of the function table entry for the + specified function. + + ContextRecord - Supplies the address of a context record. + +--*/ + +{ + + HRESULT Status = E_UNEXPECTED; + ULONG64 FloatingAddress; + M128A* FloatingRegister; + ULONG FrameOffset; + ULONG Index; + ULONG64 IntegerAddress; + PULONG64 IntegerRegister; + BOOLEAN MachineFrame; + ULONG OpInfo; + ULONG PrologOffset; + ULONG64 ReturnAddress; + ULONG64 StackAddress; + PUNWIND_INFO UnwindInfo; + ULONG UnwindOp; + + // + // Process the unwind codes. + // + + FloatingRegister = &ContextRecord->Xmm0; + IntegerRegister = &ContextRecord->Rax; + Index = 0; + MachineFrame = FALSE; + PrologOffset = (ULONG)(ControlPc - (FunctionEntry->BeginAddress + ImageBase)); + + UnwindInfo = DacGetUnwindInfo(ImageBase + FunctionEntry->UnwindInfoAddress); + if (UnwindInfo == NULL) + { + return HRESULT_FROM_WIN32(ERROR_READ_FAULT); + } + + while (Index < UnwindInfo->CountOfUnwindCodes) { + + // + // If the prologue offset is greater than the next unwind code offset, + // then simulate the effect of the unwind code. + // + + UnwindOp = UnwindInfo->UnwindCode[Index].UnwindOp; + if (UnwindOp > UWOP_PUSH_MACHFRAME) { + goto Fail; + } + + OpInfo = UnwindInfo->UnwindCode[Index].OpInfo; + if (PrologOffset >= UnwindInfo->UnwindCode[Index].CodeOffset) { + switch (UnwindOp) { + + // + // Push nonvolatile integer register. + // + // The operation information is the register number of the + // register than was pushed. + // + + case UWOP_PUSH_NONVOL: + IntegerAddress = ContextRecord->Rsp; + if ((Status = + ReadAllMemory(IntegerAddress, + &IntegerRegister[OpInfo], + sizeof(ULONG64))) != S_OK) { + goto Fail; + } + + ContextRecord->Rsp += 8; + break; + + // + // Allocate a large sized area on the stack. + // + // The operation information determines if the size is + // 16- or 32-bits. + // + + case UWOP_ALLOC_LARGE: + Index += 1; + FrameOffset = UnwindInfo->UnwindCode[Index].FrameOffset; + if (OpInfo != 0) { + Index += 1; + FrameOffset += (UnwindInfo->UnwindCode[Index].FrameOffset << 16); + } else { + // The 16-bit form is scaled. + FrameOffset *= 8; + } + + ContextRecord->Rsp += FrameOffset; + break; + + // + // Allocate a small sized area on the stack. + // + // The operation information is the size of the unscaled + // allocation size (8 is the scale factor) minus 8. + // + + case UWOP_ALLOC_SMALL: + ContextRecord->Rsp += (OpInfo * 8) + 8; + break; + + // + // Establish the the frame pointer register. + // + // The operation information is not used. + // + + case UWOP_SET_FPREG: + ContextRecord->Rsp = IntegerRegister[UnwindInfo->FrameRegister]; + ContextRecord->Rsp -= UnwindInfo->FrameOffset * 16; + break; + + // + // Save nonvolatile integer register on the stack using a + // 16-bit displacment. + // + // The operation information is the register number. + // + + case UWOP_SAVE_NONVOL: + Index += 1; + FrameOffset = UnwindInfo->UnwindCode[Index].FrameOffset * 8; + IntegerAddress = FrameBase + FrameOffset; + if ((Status = + ReadAllMemory(IntegerAddress, + &IntegerRegister[OpInfo], + sizeof(ULONG64))) != S_OK) { + goto Fail; + } + break; + + // + // Save nonvolatile integer register on the stack using a + // 32-bit displacment. + // + // The operation information is the register number. + // + + case UWOP_SAVE_NONVOL_FAR: + Index += 2; + FrameOffset = UnwindInfo->UnwindCode[Index - 1].FrameOffset; + FrameOffset += UnwindInfo->UnwindCode[Index].FrameOffset << 16; + IntegerAddress = FrameBase + FrameOffset; + if ((Status = + ReadAllMemory(IntegerAddress, + &IntegerRegister[OpInfo], + sizeof(ULONG64))) != S_OK) { + goto Fail; + } + break; + + // + // Save a nonvolatile XMM(64) register on the stack using a + // 16-bit displacement. + // + // The operation information is the register number. + // + + case UWOP_SAVE_XMM: + Index += 1; + FrameOffset = UnwindInfo->UnwindCode[Index].FrameOffset * 8; + FloatingAddress = FrameBase + FrameOffset; + FloatingRegister[OpInfo].High = 0; + if ((Status = + ReadAllMemory(FloatingAddress, + &FloatingRegister[OpInfo].Low, + sizeof(ULONG64))) != S_OK) { + goto Fail; + } + break; + + // + // Save a nonvolatile XMM(64) register on the stack using a + // 32-bit displacement. + // + // The operation information is the register number. + // + + case UWOP_SAVE_XMM_FAR: + Index += 2; + FrameOffset = UnwindInfo->UnwindCode[Index - 1].FrameOffset; + FrameOffset += UnwindInfo->UnwindCode[Index].FrameOffset << 16; + FloatingAddress = FrameBase + FrameOffset; + FloatingRegister[OpInfo].High = 0; + if ((Status = + ReadAllMemory(FloatingAddress, + &FloatingRegister[OpInfo].Low, + sizeof(ULONG64))) != S_OK) { + goto Fail; + } + break; + + // + // Save a nonvolatile XMM(128) register on the stack using a + // 16-bit displacement. + // + // The operation information is the register number. + // + + case UWOP_SAVE_XMM128: + Index += 1; + FrameOffset = UnwindInfo->UnwindCode[Index].FrameOffset * 16; + FloatingAddress = FrameBase + FrameOffset; + if ((Status = + ReadAllMemory(FloatingAddress, + &FloatingRegister[OpInfo], + sizeof(M128A))) != S_OK) { + goto Fail; + } + break; + + // + // Save a nonvolatile XMM(128) register on the stack using a + // 32-bit displacement. + // + // The operation information is the register number. + // + + case UWOP_SAVE_XMM128_FAR: + Index += 2; + FrameOffset = UnwindInfo->UnwindCode[Index - 1].FrameOffset; + FrameOffset += UnwindInfo->UnwindCode[Index].FrameOffset << 16; + FloatingAddress = FrameBase + FrameOffset; + if ((Status = + ReadAllMemory(FloatingAddress, + &FloatingRegister[OpInfo], + sizeof(M128A))) != S_OK) { + goto Fail; + } + break; + + // + // Push a machine frame on the stack. + // + // The operation information determines whether the machine + // frame contains an error code or not. + // + + case UWOP_PUSH_MACHFRAME: + MachineFrame = TRUE; + ReturnAddress = ContextRecord->Rsp; + StackAddress = ContextRecord->Rsp + (3 * 8); + if (OpInfo != 0) { + ReturnAddress += 8; + StackAddress += 8; + } + + if ((Status = + ReadAllMemory(ReturnAddress, + &ContextRecord->Rip, + sizeof(ULONG64))) != S_OK) { + goto Fail; + } + if ((Status = + ReadAllMemory(StackAddress, + &ContextRecord->Rsp, + sizeof(ULONG64))) != S_OK) { + goto Fail; + } + break; + + // + // Unused codes. + // + + default: + break; + } + + Index += 1; + + } else { + + // + // Skip this unwind operation by advancing the slot index by the + // number of slots consumed by this operation. + // + + Index += s_UnwindOpSlotTable[UnwindOp]; + + // + // Special case any unwind operations that can consume a variable + // number of slots. + // + + switch (UnwindOp) { + + // + // A non-zero operation information indicates that an + // additional slot is consumed. + // + + case UWOP_ALLOC_LARGE: + if (OpInfo != 0) { + Index += 1; + } + + break; + + // + // No other special cases. + // + + default: + break; + } + } + } + + // + // If chained unwind information is specified, then recursively unwind + // the chained information. Otherwise, determine the return address if + // a machine frame was not encountered during the scan of the unwind + // codes. + // + + if ((UnwindInfo->Flags & UNW_FLAG_CHAININFO) != 0) { + + _PIMAGE_RUNTIME_FUNCTION_ENTRY ChainEntry; + + Index = UnwindInfo->CountOfUnwindCodes; + if ((Index & 1) != 0) { + Index += 1; + } + + // GetUnwindInfo looks for CHAININFO and reads + // the trailing RUNTIME_FUNCTION so we can just + // directly use the data sitting in UnwindInfo. + ChainEntry = (_PIMAGE_RUNTIME_FUNCTION_ENTRY) + &UnwindInfo->UnwindCode[Index]; + + Status = UnwindPrologue(ImageBase, + ControlPc, + FrameBase, + ChainEntry, + ContextRecord); + + return Status; + + } else { + + if (MachineFrame == FALSE) { + if ((Status = + ReadAllMemory(ContextRecord->Rsp, + &ContextRecord->Rip, + sizeof(ULONG64))) != S_OK) { + return Status; + } + ContextRecord->Rsp += 8; + } + + return S_OK; + } + + Fail: + return Status; +} + +HRESULT +OOPStackUnwinderAMD64::VirtualUnwind( + __in ULONG64 ImageBase, + __in ULONG64 ControlPc, + __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __inout PCONTEXT ContextRecord, + __out PULONG64 EstablisherFrame + ) + +/*++ + +Routine Description: + + This function virtually unwinds the specified function by executing its + prologue code backward or its epilogue code forward. + + If a context pointers record is specified, then the address where each + nonvolatile registers is restored from is recorded in the appropriate + element of the context pointers record. + +Arguments: + + ImageBase - Supplies the base address of the image that contains the + function being unwound. + + ControlPc - Supplies the address where control left the specified + function. + + FunctionEntry - Supplies the address of the function table entry for the + specified function. + + ContextRecord - Supplies the address of a context record. + + EstablisherFrame - Supplies a pointer to a variable that receives the + the establisher frame pointer value. + +--*/ + +{ + + HRESULT Status; + ULONG64 BranchTarget; + LONG Displacement; + ULONG FrameRegister = 0; + ULONG Index; + BOOL InEpilogue; + PULONG64 IntegerRegister; + PUCHAR NextByte; + _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryFunctionEntry; + ULONG PrologOffset; + ULONG RegisterNumber; + PUNWIND_INFO UnwindInfo; + UCHAR InstrBuffer[32]; + ULONG InstrBytes; + ULONG Bytes; + ULONG UnwindFrameReg; + + // + // If the specified function does not use a frame pointer, then the + // establisher frame is the contents of the stack pointer. This may + // not actually be the real establisher frame if control left the + // function from within the prologue. In this case the establisher + // frame may be not required since control has not actually entered + // the function and prologue entries cannot refer to the establisher + // frame before it has been established, i.e., if it has not been + // established, then no save unwind codes should be encountered during + // the unwind operation. + // + // If the specified function uses a frame pointer and control left the + // function outside of the prologue or the unwind information contains + // a chained information structure, then the establisher frame is the + // contents of the frame pointer. + // + // If the specified function uses a frame pointer and control left the + // function from within the prologue, then the set frame pointer unwind + // code must be looked up in the unwind codes to detetermine if the + // contents of the stack pointer or the contents of the frame pointer + // should be used for the establisher frame. This may not actually be + // the real establisher frame. In this case the establisher frame may + // not be required since control has not actually entered the function + // and prologue entries cannot refer to the establisher frame before it + // has been established, i.e., if it has not been established, then no + // save unwind codes should be encountered during the unwind operation. + // + // N.B. The correctness of these assumptions is based on the ordering of + // unwind codes. + // + + UnwindInfo = DacGetUnwindInfo(ImageBase + FunctionEntry->UnwindInfoAddress); + if (UnwindInfo == NULL) + { + return HRESULT_FROM_WIN32(ERROR_READ_FAULT); + } + + PrologOffset = (ULONG)(ControlPc - (FunctionEntry->BeginAddress + ImageBase)); + UnwindFrameReg = UnwindInfo->FrameRegister; + if (UnwindFrameReg == 0) { + *EstablisherFrame = ContextRecord->Rsp; + + } else if ((PrologOffset >= UnwindInfo->SizeOfProlog) || + ((UnwindInfo->Flags & UNW_FLAG_CHAININFO) != 0)) { + *EstablisherFrame = (&ContextRecord->Rax)[UnwindFrameReg]; + *EstablisherFrame -= UnwindInfo->FrameOffset * 16; + + } else { + + // Read all the data. + UnwindInfo = DacGetUnwindInfo(ImageBase + FunctionEntry->UnwindInfoAddress); + if (UnwindInfo == NULL) + { + return HRESULT_FROM_WIN32(ERROR_READ_FAULT); + } + + Index = 0; + while (Index < UnwindInfo->CountOfUnwindCodes) { + if (UnwindInfo->UnwindCode[Index].UnwindOp == UWOP_SET_FPREG) { + break; + } + + Index += 1; + } + + if (PrologOffset >= UnwindInfo->UnwindCode[Index].CodeOffset) { + *EstablisherFrame = (&ContextRecord->Rax)[UnwindFrameReg]; + *EstablisherFrame -= UnwindInfo->FrameOffset * 16; + + } else { + *EstablisherFrame = ContextRecord->Rsp; + } + } + + if ((Status = + ReadMemory(ControlPc, InstrBuffer, sizeof(InstrBuffer), + &InstrBytes)) != S_OK) { + + // We need the code to look for epilogue ops. + // It's very rare to be stopped in an epilogue when + // getting a stack trace, so if we can't read the + // code just assume we aren't in an epilogue. + InstrBytes = 0; + } + + // + // If the point at which control left the specified function is in an + // epilogue, then emulate the execution of the epilogue forward and + // return no exception handler. + // + + IntegerRegister = &ContextRecord->Rax; + NextByte = InstrBuffer; + Bytes = InstrBytes; + + // + // Check for one of: + // + // add rsp, imm8 + // or + // add rsp, imm32 + // or + // lea rsp, -disp8[fp] + // or + // lea rsp, -disp32[fp] + // + + if (Bytes >= 4 && + (NextByte[0] == SIZE64_PREFIX) && + (NextByte[1] == ADD_IMM8_OP) && + (NextByte[2] == 0xc4)) { + + // + // add rsp, imm8. + // + + NextByte += 4; + Bytes -= 4; + + } else if (Bytes >= 7 && + (NextByte[0] == SIZE64_PREFIX) && + (NextByte[1] == ADD_IMM32_OP) && + (NextByte[2] == 0xc4)) { + + // + // add rsp, imm32. + // + + NextByte += 7; + Bytes -= 7; + + } else if (Bytes >= 4 && + ((NextByte[0] & 0xf8) == SIZE64_PREFIX) && + (NextByte[1] == LEA_OP)) { + + FrameRegister = ((NextByte[0] & 0x7) << 3) | (NextByte[2] & 0x7); + if ((FrameRegister != 0) && + (FrameRegister == UnwindFrameReg)) { + if ((NextByte[2] & 0xf8) == 0x60) { + + // + // lea rsp, disp8[fp]. + // + + NextByte += 4; + Bytes -= 4; + + } else if (Bytes >= 7 && + (NextByte[2] &0xf8) == 0xa0) { + + // + // lea rsp, disp32[fp]. + // + + NextByte += 7; + Bytes -= 7; + } + } + } + + // + // Check for any number of: + // + // pop nonvolatile-integer-register[0..15]. + // + + while (TRUE) { + if (Bytes >= 1 && + (NextByte[0] & 0xf8) == POP_OP) { + NextByte += 1; + Bytes -= 1; + + } else if (Bytes >= 2 && + IS_REX_PREFIX(NextByte[0]) && + ((NextByte[1] & 0xf8) == POP_OP)) { + + NextByte += 2; + Bytes -= 2; + + } else { + break; + } + } + + // + // If the next instruction is a return or an appropriate jump, then + // control is currently in an epilogue and execution of the epilogue + // should be emulated. Otherwise, execution is not in an epilogue and + // the prologue should be unwound. + // + + InEpilogue = FALSE; + if ((Bytes >= 1 && + ((NextByte[0] == RET_OP) || + (NextByte[0] == RET_OP_2))) || + (Bytes >= 2 && + ((NextByte[0] == REP_PREFIX) && (NextByte[1] == RET_OP)))) { + + // + // A return is an unambiguous indication of an epilogue. + // + + InEpilogue = TRUE; + + } else if ((Bytes >= 2 && NextByte[0] == JMP_IMM8_OP) || + (Bytes >= 5 && NextByte[0] == JMP_IMM32_OP)) { + + // + // An unconditional branch to a target that is equal to the start of + // or outside of this routine is logically a call to another function. + // + + BranchTarget = (ULONG64)(NextByte - InstrBuffer) + ControlPc - ImageBase; + if (NextByte[0] == JMP_IMM8_OP) { + BranchTarget += 2 + (CHAR)NextByte[1]; + } else { + BranchTarget += 5 + *((LONG UNALIGNED *)&NextByte[1]); + } + + // + // Determine whether the branch target refers to code within this + // function. If not, then it is an epilogue indicator. + // + // A branch to the start of self implies a recursive call, so + // is treated as an epilogue. + // + + if (BranchTarget < FunctionEntry->BeginAddress || + BranchTarget >= FunctionEntry->EndAddress) { + + _IMAGE_RUNTIME_FUNCTION_ENTRY PrimaryEntryBuffer; + + // + // The branch target is outside of the region described by + // this function entry. See whether it is contained within + // an indirect function entry associated with this same + // function. + // + // If not, then the branch target really is outside of + // this function. + // + + PrimaryFunctionEntry = + SameFunction(FunctionEntry, + ImageBase, + BranchTarget + ImageBase, + &PrimaryEntryBuffer); + + if ((PrimaryFunctionEntry == NULL) || + (BranchTarget == PrimaryFunctionEntry->BeginAddress)) { + + InEpilogue = TRUE; + } + + } else if ((BranchTarget == FunctionEntry->BeginAddress) && + ((UnwindInfo->Flags & UNW_FLAG_CHAININFO) == 0)) { + + InEpilogue = TRUE; + } + + } else if (Bytes >= 2 && + (NextByte[0] == JMP_IND_OP) && (NextByte[1] == 0x25)) { + + // + // An unconditional jump indirect. + // + // This is a jmp outside of the function, probably a tail call + // to an import function. + // + + InEpilogue = TRUE; + + } else if (Bytes >= 3 && + ((NextByte[0] & 0xf8) == SIZE64_PREFIX) && + (NextByte[1] == 0xff) && + (NextByte[2] & 0x38) == 0x20) { + + // + // This is an indirect jump opcode: 0x48 0xff /4. The 64-bit + // flag (REX.W) is always redundant here, so its presence is + // overloaded to indicate a branch out of the function - a tail + // call. + // + // Such an opcode is an unambiguous epilogue indication. + // + + InEpilogue = TRUE; + } + + if (InEpilogue != FALSE) { + NextByte = InstrBuffer; + Bytes = InstrBytes; + + // + // Emulate one of (if any): + // + // add rsp, imm8 + // or + // add rsp, imm32 + // or + // lea rsp, disp8[frame-register] + // or + // lea rsp, disp32[frame-register] + // + + if (Bytes >= 1 && + (NextByte[0] & 0xf8) == SIZE64_PREFIX) { + + if (Bytes >= 4 && + NextByte[1] == ADD_IMM8_OP) { + + // + // add rsp, imm8. + // + + ContextRecord->Rsp += (CHAR)NextByte[3]; + NextByte += 4; + Bytes -= 4; + + } else if (Bytes >= 7 && + NextByte[1] == ADD_IMM32_OP) { + + // + // add rsp, imm32. + // + + Displacement = NextByte[3] | (NextByte[4] << 8); + Displacement |= (NextByte[5] << 16) | (NextByte[6] << 24); + ContextRecord->Rsp += Displacement; + NextByte += 7; + Bytes -= 7; + + } else if (Bytes >= 4 && + NextByte[1] == LEA_OP) { + if ((NextByte[2] & 0xf8) == 0x60) { + + // + // lea rsp, disp8[frame-register]. + // + + ContextRecord->Rsp = IntegerRegister[FrameRegister]; + ContextRecord->Rsp += (CHAR)NextByte[3]; + NextByte += 4; + Bytes -= 4; + + } else if (Bytes >= 7 && + (NextByte[2] & 0xf8) == 0xa0) { + + // + // lea rsp, disp32[frame-register]. + // + + Displacement = NextByte[3] | (NextByte[4] << 8); + Displacement |= (NextByte[5] << 16) | (NextByte[6] << 24); + ContextRecord->Rsp = IntegerRegister[FrameRegister]; + ContextRecord->Rsp += Displacement; + NextByte += 7; + Bytes -= 7; + } + } + } + + // + // Emulate any number of (if any): + // + // pop nonvolatile-integer-register. + // + + while (TRUE) { + if (Bytes >= 1 && + (NextByte[0] & 0xf8) == POP_OP) { + + // + // pop nonvolatile-integer-register[0..7] + // + + RegisterNumber = NextByte[0] & 0x7; + if ((Status = + ReadAllMemory(ContextRecord->Rsp, + &IntegerRegister[RegisterNumber], + sizeof(ULONG64))) != S_OK) { + return Status; + } + ContextRecord->Rsp += 8; + NextByte += 1; + Bytes -= 1; + + } else if (Bytes >= 2 && + IS_REX_PREFIX(NextByte[0]) && + (NextByte[1] & 0xf8) == POP_OP) { + + // + // pop nonvolatile-integer-register[8..15] + // + + RegisterNumber = ((NextByte[0] & 1) << 3) | (NextByte[1] & 0x7); + if ((Status = + ReadAllMemory(ContextRecord->Rsp, + &IntegerRegister[RegisterNumber], + sizeof(ULONG64))) != S_OK) { + return Status; + } + ContextRecord->Rsp += 8; + NextByte += 2; + Bytes -= 2; + + } else { + break; + } + } + + // + // Emulate return and return null exception handler. + // + // Note: this instruction might in fact be a jmp, however + // we want to emulate a return regardless. + // + + if ((Status = + ReadAllMemory(ContextRecord->Rsp, + &ContextRecord->Rip, + sizeof(ULONG64))) != S_OK) { + return Status; + } + ContextRecord->Rsp += 8; + return S_OK; + } + + // + // Control left the specified function outside an epilogue. Unwind the + // subject function and any chained unwind information. + // + + return UnwindPrologue(ImageBase, + ControlPc, + *EstablisherFrame, + FunctionEntry, + ContextRecord); +} + +ULONG64 +OOPStackUnwinderAMD64::LookupPrimaryUnwindInfo( + __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __in ULONG64 ImageBase, + __out _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryEntry + ) + +/*++ + +Routine Description: + + This function determines whether the supplied function entry is a primary + function entry or a chained function entry. If it is a chained function + entry, the unwind information associated with the primary function entry + is returned. + +Arguments: + + FunctionEntry - Supplies a pointer to the function entry for which the + associated primary function entry will be located. + + ImageBase - Supplies the base address of the image containing the + supplied function entry. + + PrimaryEntry - Supplies the address of a variable that receives a pointer + to the primary function entry. + +Return Value: + + A pointer to the unwind information for the primary function entry is + returned as the function value. + +--*/ + +{ + + ULONG Index; + PUNWIND_INFO UnwindInfo; + ULONG UnwindRel; + ULONG64 UnwindAbs; + + // + // Locate the unwind information and determine whether it is chained. + // If the unwind information is chained, then locate the parent function + // entry and loop again. + // + + UnwindRel = FunctionEntry->UnwindInfoAddress; + // Copy the function entry before it becomes invalid. + *PrimaryEntry = *FunctionEntry; + + do { + UnwindAbs = ImageBase + UnwindRel; + UnwindInfo = DacGetUnwindInfo(ImageBase + UnwindRel); + if ((UnwindInfo == NULL) || ((UnwindInfo->Flags & UNW_FLAG_CHAININFO) == 0)) + { + break; + } + + Index = UnwindInfo->CountOfUnwindCodes; + if ((Index & 1) != 0) { + Index += 1; + } + + FunctionEntry = (_PIMAGE_RUNTIME_FUNCTION_ENTRY) + &UnwindInfo->UnwindCode[Index]; + UnwindRel = FunctionEntry->UnwindInfoAddress; + + // Copy the function entry before it becomes invalid. + *PrimaryEntry = *FunctionEntry; + + } while (TRUE); + + return UnwindAbs; +} + +_PIMAGE_RUNTIME_FUNCTION_ENTRY +OOPStackUnwinderAMD64::SameFunction( + __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __in ULONG64 ImageBase, + __in ULONG64 ControlPc, + __out _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionReturnBuffer + ) + +/*++ + +Routine Description: + + This function determines whether the address supplied by ControlPc lies + anywhere within the function associated with FunctionEntry. + +Arguments: + + FunctionEntry - Supplies a pointer to a function entry (primary or chained) + associated with the function. + + ImageBase - Supplies the base address of the image containing the supplied + function entry. + + ControlPc - Supplies the address that will be tested for inclusion within + the function associated with FunctionEntry. + +Return Value: + + If the address of the unwind information for the specified function is + equal to the address of the unwind information for the control PC, then + a pointer to a function table entry that describes the primary function + table entry is returned as the function value. Otherwise, NULL is returned. + +--*/ + +{ + + _IMAGE_RUNTIME_FUNCTION_ENTRY TargetFunctionEntry; + ULONG64 TargetImageBase; + ULONG64 UnwindInfo1; + ULONG64 UnwindInfo2; + + // + // Find the unwind information referenced by the primary function entry + // associated with the specified function entry. + // + + UnwindInfo1 = LookupPrimaryUnwindInfo(FunctionEntry, ImageBase, + FunctionReturnBuffer); + + // + // Determine the function entry containing the control Pc and similarly + // resolve it's primary function entry. + // + + if (GetModuleBase(ControlPc, &TargetImageBase) != S_OK || + GetFunctionEntry(ControlPc, + &TargetFunctionEntry, + sizeof(TargetFunctionEntry)) != S_OK) { + return NULL; + } + + UnwindInfo2 = LookupPrimaryUnwindInfo(&TargetFunctionEntry, + TargetImageBase, + FunctionReturnBuffer); + + // + // If the address of the two sets of unwind information are equal, then + // return the address of the primary function entry. Otherwise, return + // NULL. + // + + if (UnwindInfo1 == UnwindInfo2) { + return FunctionReturnBuffer; + + } else { + return NULL; + } +} diff --git a/src/unwinder/amd64/unwinder_amd64.h b/src/unwinder/amd64/unwinder_amd64.h new file mode 100644 index 0000000000..4c0bd46529 --- /dev/null +++ b/src/unwinder/amd64/unwinder_amd64.h @@ -0,0 +1,57 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// + +#ifndef __unwinder_amd64_h__ +#define __unwinder_amd64_h__ + +#include "unwinder.h" + + +//--------------------------------------------------------------------------------------- +// +// See the comment for the base class code:OOPStackUnwinder. +// + +class OOPStackUnwinderAMD64 : public OOPStackUnwinder +{ +public: + // Unwind the given CONTEXT to the caller CONTEXT. The CONTEXT will be overwritten. + BOOL Unwind(CONTEXT * pContext); + + // + // Everything below comes from dbghelp.dll. + // + +protected: + static BYTE s_UnwindOpSlotTable[]; + + HRESULT UnwindPrologue(__in DWORD64 ImageBase, + __in DWORD64 ControlPc, + __in DWORD64 FrameBase, + __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __inout PCONTEXT ContextRecord); + + HRESULT VirtualUnwind(__in DWORD64 ImageBase, + __in DWORD64 ControlPc, + __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __inout PCONTEXT ContextRecord, + __out PDWORD64 EstablisherFrame); + + DWORD64 LookupPrimaryUnwindInfo + (__in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __in DWORD64 ImageBase, + __out _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryEntry); + + _PIMAGE_RUNTIME_FUNCTION_ENTRY SameFunction + (__in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __in DWORD64 ImageBase, + __in DWORD64 ControlPc, + __out _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionReturnBuffer); +}; + +#endif // __unwinder_amd64_h__ + diff --git a/src/unwinder/arm/.gitmirror b/src/unwinder/arm/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/unwinder/arm/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file diff --git a/src/unwinder/arm/unwinder_arm.cpp b/src/unwinder/arm/unwinder_arm.cpp new file mode 100644 index 0000000000..c2463f58f7 --- /dev/null +++ b/src/unwinder/arm/unwinder_arm.cpp @@ -0,0 +1,1469 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// + +#include "stdafx.h" +#include "utilcode.h" + +#include "unwinder_arm.h" + +#define DBS_EXTEND64(x) ((DWORD64)x) +#define MEMORY_READ_BYTE(params, addr) (*dac_cast(addr)) +#define MEMORY_READ_DWORD(params, addr) (*dac_cast(addr)) +#define MEMORY_READ_QWORD(params, addr) (*dac_cast(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, + // 0xf9: 4-byte stack adjust ... add sp, sp, + // + + 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, + // 0xfa: 4-byte stack adjust ... add sp, sp, + // + + case 0xf8: + case 0xfa: + if (UnwindCodePtr + 3 > UnwindCodesEndPtr) { + Status = E_FAIL; + break; + } + ContextRecord->Sp += 4 * 256 * 256 * MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + ContextRecord->Sp += 4 * 256 * MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr + 1); + ContextRecord->Sp += 4 * MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr + 2); + UnwindCodePtr += 3; + break; + + // + // 0xfb: 2-byte no-op/misc instruction + // 0xfc: 4-byte no-op/misc instruction + // + + case 0xfb: + case 0xfc: + break; + + // + // 0xfd: 2-byte end (epilog) + // 0xfe: 4-byte end (epilog) + // 0xff: generic end + // + + case 0xfd: + case 0xfe: + case 0xff: + goto finished; + + default: + Status = E_FAIL; + break; + } + } + } + + // + // If we succeeded, post-process the results a bit + // +finished: + if (Status == STATUS_SUCCESS) { + + // + // Since we always POP to the LR, recover the final PC from there, unless + // it was overwritten due to a special case custom unwinding operation. + // Also set the establisher frame equal to the final stack pointer. + // + + if ((ContextRecord->ContextFlags & CONTEXT_UNWOUND_TO_CALL) != 0) { + ContextRecord->Pc = ContextRecord->Lr; + } + *EstablisherFrame = ContextRecord->Sp; + + if (ARGUMENT_PRESENT(HandlerRoutine)) { + *HandlerRoutine = ExceptionHandler; + } + *HandlerData = ExceptionHandlerData; + } + + return Status; +} + + +BOOL OOPStackUnwinderArm::Unwind(T_CONTEXT * pContext) +{ + DWORD64 ImageBase = 0; + HRESULT hr = GetModuleBase(DBS_EXTEND64(pContext->Pc), &ImageBase); + if (hr != S_OK) + return FALSE; + + PEXCEPTION_ROUTINE DummyHandlerRoutine; + PVOID DummyHandlerData; + ULONG DummyEstablisherFrame; + + DWORD startingPc = pContext->Pc; + DWORD startingSp = pContext->Sp; + + IMAGE_ARM_RUNTIME_FUNCTION_ENTRY Rfe; + if (FAILED(GetFunctionEntry(DBS_EXTEND64(pContext->Pc), &Rfe, sizeof(Rfe)))) + return FALSE; + + if ((Rfe.UnwindData & 3) != 0) + { + hr = RtlpUnwindFunctionCompact(pContext->Pc - (ULONG)ImageBase, + &Rfe, + pContext, + &DummyEstablisherFrame, + &DummyHandlerRoutine, + &DummyHandlerData, + NULL); + + } + else + { + hr = RtlpUnwindFunctionFull(pContext->Pc - (ULONG)ImageBase, + (ULONG)ImageBase, + &Rfe, + pContext, + &DummyEstablisherFrame, + &DummyHandlerRoutine, + &DummyHandlerData, + NULL); + } + + // PC == 0 means unwinding is finished. + // Same if no forward progress is made + if (pContext->Pc == 0 || (startingPc == pContext->Pc && startingSp == pContext->Sp)) + return FALSE; + + return TRUE; +} + + +BOOL DacUnwindStackFrame(T_CONTEXT *pContext, T_KNONVOLATILE_CONTEXT_POINTERS* pContextPointers) +{ + OOPStackUnwinderArm unwinder; + BOOL res = unwinder.Unwind(pContext); + + if (res && pContextPointers) + { + for (int i = 0; i < 8; i++) + { + *(&pContextPointers->R4 + i) = &pContext->R4 + i; + } + } + + return res; +} diff --git a/src/unwinder/arm/unwinder_arm.h b/src/unwinder/arm/unwinder_arm.h new file mode 100644 index 0000000000..7eb5d7a065 --- /dev/null +++ b/src/unwinder/arm/unwinder_arm.h @@ -0,0 +1,55 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// + +#ifndef __unwinder_arm__ +#define __unwinder_arm__ + +#include "unwinder.h" + + +//--------------------------------------------------------------------------------------- +// +// See the comment for the base class code:OOPStackUnwinder. +// + +class OOPStackUnwinderArm : public OOPStackUnwinder +{ +public: + // Unwind the given CONTEXT to the caller CONTEXT. The CONTEXT will be overwritten. + BOOL Unwind(T_CONTEXT * pContext); + + // + // Everything below comes from dbghelp.dll. + // + +protected: + HRESULT UnwindPrologue(__in DWORD64 ImageBase, + __in DWORD64 ControlPc, + __in DWORD64 FrameBase, + __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __inout PT_CONTEXT ContextRecord); + + HRESULT VirtualUnwind(__in DWORD64 ImageBase, + __in DWORD64 ControlPc, + __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __inout PT_CONTEXT ContextRecord, + __out PDWORD64 EstablisherFrame); + + DWORD64 LookupPrimaryUnwindInfo + (__in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __in DWORD64 ImageBase, + __out _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryEntry); + + _PIMAGE_RUNTIME_FUNCTION_ENTRY SameFunction + (__in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __in DWORD64 ImageBase, + __in DWORD64 ControlPc, + __out _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionReturnBuffer); +}; + +#endif // __unwinder_arm__ + diff --git a/src/unwinder/arm64/.gitmirror b/src/unwinder/arm64/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/unwinder/arm64/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file diff --git a/src/unwinder/arm64/unwinder_arm64.cpp b/src/unwinder/arm64/unwinder_arm64.cpp new file mode 100644 index 0000000000..a56e42469e --- /dev/null +++ b/src/unwinder/arm64/unwinder_arm64.cpp @@ -0,0 +1,1580 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// + +#include "stdafx.h" +#include "utilcode.h" +#include "crosscomp.h" + +#include "unwinder_arm64.h" + +typedef struct _ARM64_KTRAP_FRAME { + +// +// Exception active indicator. +// +// 0 - interrupt frame. +// 1 - exception frame. +// 2 - service frame. +// + + /* +0x000 */ UCHAR ExceptionActive; // always valid + /* +0x001 */ UCHAR ContextFromKFramesUnwound; // set if KeContextFromKFrames created this frame + /* +0x002 */ UCHAR DebugRegistersValid; // always valid + /* +0x003 */ union { + UCHAR PreviousMode; // system services only + UCHAR PreviousIrql; // interrupts only + }; + +// +// Page fault information (page faults only) +// Previous trap frame address (system services only) +// +// Organized this way to allow first couple words to be used +// for scratch space in the general case +// + + /* +0x004 */ ULONG FaultStatus; // page faults only + /* +0x008 */ union { + ULONG64 FaultAddress; // page faults only + ULONG64 TrapFrame; // system services only + }; + +// +// The ARM architecture does not have an architectural trap frame. On +// an exception or interrupt, the processor switches to an +// exception-specific processor mode in which at least the LR and SP +// registers are banked. Software is responsible for preserving +// registers which reflect the processor state in which the +// exception occurred rather than any intermediate processor modes. +// + +// +// Volatile floating point state is dynamically allocated; this +// pointer may be NULL if the FPU was not enabled at the time the +// trap was taken. +// + + /* +0x010 */ PVOID VfpState; + +// +// Debug registers +// + + /* +0x018 */ ULONG Bcr[ARM64_MAX_BREAKPOINTS]; + /* +0x038 */ ULONG64 Bvr[ARM64_MAX_BREAKPOINTS]; + /* +0x078 */ ULONG Wcr[ARM64_MAX_WATCHPOINTS]; + /* +0x080 */ ULONG64 Wvr[ARM64_MAX_WATCHPOINTS]; + +// +// Volatile registers X0-X17, and the FP, SP, LR +// + + /* +0x090 */ ULONG Spsr; + /* +0x094 */ ULONG Esr; + /* +0x098 */ ULONG64 Sp; + /* +0x0A0 */ ULONG64 X[19]; + /* +0x138 */ ULONG64 Lr; + /* +0x140 */ ULONG64 Fp; + /* +0x148 */ ULONG64 Pc; + /* +0x150 */ + +} ARM64_KTRAP_FRAME, *PARM64_KTRAP_FRAME; + +typedef struct _ARM64_VFP_STATE +{ + struct _ARM64_VFP_STATE *Link; // link to next state entry + ULONG Fpcr; // FPCR register + ULONG Fpsr; // FPSR register + NEON128 V[32]; // All V registers (0-31) +} ARM64_VFP_STATE, *PARM64_VFP_STATE, KARM64_VFP_STATE, *PKARM64_VFP_STATE; + +// +// Parameters describing the unwind codes. +// + +#define STATUS_UNWIND_UNSUPPORTED_VERSION STATUS_UNSUCCESSFUL +#define STATUS_UNWIND_NOT_IN_FUNCTION STATUS_UNSUCCESSFUL +#define STATUS_UNWIND_INVALID_SEQUENCE STATUS_UNSUCCESSFUL + +// +// Macros for accessing memory. These can be overridden if other code +// (in particular the debugger) needs to use them. + +#define MEMORY_READ_BYTE(params, addr) (*(const BYTE *)(addr)) +#define MEMORY_READ_DWORD(params, addr) (*(const DWORD *)(addr)) +#define MEMORY_READ_QWORD(params, addr) (*(const UINT64 *)(addr)) + +typedef struct _ARM64_UNWIND_PARAMS +{ + ULONG_PTR ControlPc; + PULONG_PTR LowLimit; + PULONG_PTR HighLimit; + PKNONVOLATILE_CONTEXT_POINTERS ContextPointers; +} ARM64_UNWIND_PARAMS, *PARM64_UNWIND_PARAMS; + +#define UNWIND_PARAMS_SET_TRAP_FRAME(Params, Address, Size) + +#define UPDATE_CONTEXT_POINTERS(Params, RegisterNumber, Address) +#define UPDATE_FP_CONTEXT_POINTERS(Params, RegisterNumber, Address) +#define VALIDATE_STACK_ADDRESS_EX(Params, Context, Address, DataSize, Alignment, OutStatus) +#define VALIDATE_STACK_ADDRESS(Params, Context, DataSize, Alignment, OutStatus) + +// +// Macros to clarify opcode parsing +// + +#define OPCODE_IS_END(Op) (((Op) & 0xfe) == 0xe4) + +// +// This table describes the size of each unwind code, in bytes +// + +static const BYTE UnwindCodeSizeTable[256] = +{ + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, + 4,1,2,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1 +}; + +NTSTATUS +RtlpUnwindCustom( + __inout PT_CONTEXT ContextRecord, + __in BYTE Opcode, + __in PARM64_UNWIND_PARAMS UnwindParams + ) + +/*++ + +Routine Description: + + Handles custom unwinding operations involving machine-specific + frames. + +Arguments: + + ContextRecord - Supplies the address of a context record. + + Opcode - The opcode to decode. + + UnwindParams - Additional parameters shared with caller. + +Return Value: + + An NTSTATUS indicating either STATUS_SUCCESS if everything went ok, or + another status code if there were problems. + +--*/ + +{ + ULONG Fpcr; + ULONG Fpsr; + ULONG RegIndex; + ULONG_PTR SourceAddress; + ULONG_PTR StartingSp; + NTSTATUS Status; + ULONG_PTR VfpStateAddress; + + StartingSp = ContextRecord->Sp; + Status = STATUS_SUCCESS; + + // + // The opcode describes the special-case stack + // + + switch (Opcode) + { + + // + // Trap frame case + // + + case 0xe8: // MSFT_OP_TRAP_FRAME: + + // + // Ensure there is enough valid space for the trap frame + // + + VALIDATE_STACK_ADDRESS(UnwindParams, ContextRecord, sizeof(ARM64_KTRAP_FRAME), 16, &Status); + if (!NT_SUCCESS(Status)) { + return Status; + } + + // + // Restore X0-X17, and D0-D7 + // + + SourceAddress = StartingSp + FIELD_OFFSET(ARM64_KTRAP_FRAME, X); + for (RegIndex = 0; RegIndex < 18; RegIndex++) { + UPDATE_CONTEXT_POINTERS(UnwindParams, RegIndex, SourceAddress); + ContextRecord->X[RegIndex] = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + SourceAddress += sizeof(ULONG_PTR); + } + + SourceAddress = StartingSp + FIELD_OFFSET(ARM64_KTRAP_FRAME, VfpState); + VfpStateAddress = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + if (VfpStateAddress != 0) { + + SourceAddress = VfpStateAddress + FIELD_OFFSET(KARM64_VFP_STATE, Fpcr); + Fpcr = MEMORY_READ_DWORD(UnwindParams, SourceAddress); + SourceAddress = VfpStateAddress + FIELD_OFFSET(KARM64_VFP_STATE, Fpsr); + Fpsr = MEMORY_READ_DWORD(UnwindParams, SourceAddress); + if (Fpcr != -1 && Fpsr != -1) { + + ContextRecord->Fpcr = Fpcr; + ContextRecord->Fpsr = Fpsr; + + SourceAddress = VfpStateAddress + FIELD_OFFSET(KARM64_VFP_STATE, V); + for (RegIndex = 0; RegIndex < 32; RegIndex++) { + UPDATE_FP_CONTEXT_POINTERS(UnwindParams, RegIndex, SourceAddress); + ContextRecord->V[RegIndex].Low = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + ContextRecord->V[RegIndex].High = MEMORY_READ_QWORD(UnwindParams, SourceAddress + 8); + SourceAddress += 2 * sizeof(ULONGLONG); + } + } + } + + // + // Restore R11, R12, SP, LR, PC, and the status registers + // + + SourceAddress = StartingSp + FIELD_OFFSET(ARM64_KTRAP_FRAME, Spsr); + ContextRecord->Cpsr = MEMORY_READ_DWORD(UnwindParams, SourceAddress); + + SourceAddress = StartingSp + FIELD_OFFSET(ARM64_KTRAP_FRAME, Sp); + ContextRecord->Sp = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + + SourceAddress = StartingSp + FIELD_OFFSET(ARM64_KTRAP_FRAME, Lr); + ContextRecord->Lr = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + + SourceAddress = StartingSp + FIELD_OFFSET(ARM64_KTRAP_FRAME, Fp); + ContextRecord->Fp = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + + SourceAddress = StartingSp + FIELD_OFFSET(ARM64_KTRAP_FRAME, Pc); + ContextRecord->Pc = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + + // + // Set the trap frame and clear the unwound-to-call flag + // + + UNWIND_PARAMS_SET_TRAP_FRAME(UnwindParams, StartingSp, sizeof(ARM64_KTRAP_FRAME)); + ContextRecord->ContextFlags &= ~CONTEXT_UNWOUND_TO_CALL; + break; + + // + // Context case + // + + case 0xea: // MSFT_OP_CONTEXT: + + // + // Ensure there is enough valid space for the full CONTEXT structure + // + + VALIDATE_STACK_ADDRESS(UnwindParams, ContextRecord, sizeof(CONTEXT), 16, &Status); + if (!NT_SUCCESS(Status)) { + return Status; + } + + // + // Restore X0-X28, and D0-D31 + // + + SourceAddress = StartingSp + FIELD_OFFSET(T_CONTEXT, X); + for (RegIndex = 0; RegIndex < 29; RegIndex++) { + UPDATE_CONTEXT_POINTERS(UnwindParams, RegIndex, SourceAddress); + ContextRecord->X[RegIndex] = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + SourceAddress += sizeof(ULONG_PTR); + } + + SourceAddress = StartingSp + FIELD_OFFSET(T_CONTEXT, V); + for (RegIndex = 0; RegIndex < 32; RegIndex++) { + UPDATE_FP_CONTEXT_POINTERS(UnwindParams, RegIndex, SourceAddress); + ContextRecord->V[RegIndex].Low = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + ContextRecord->V[RegIndex].High = MEMORY_READ_QWORD(UnwindParams, SourceAddress + 8); + SourceAddress += 2 * sizeof(ULONGLONG); + } + + // + // Restore SP, LR, PC, and the status registers + // + + SourceAddress = StartingSp + FIELD_OFFSET(T_CONTEXT, Cpsr); + ContextRecord->Cpsr = MEMORY_READ_DWORD(UnwindParams, SourceAddress); + + SourceAddress = StartingSp + FIELD_OFFSET(T_CONTEXT, Fp); + ContextRecord->Fp = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + + SourceAddress = StartingSp + FIELD_OFFSET(T_CONTEXT, Lr); + ContextRecord->Lr = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + + SourceAddress = StartingSp + FIELD_OFFSET(T_CONTEXT, Sp); + ContextRecord->Sp = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + + SourceAddress = StartingSp + FIELD_OFFSET(T_CONTEXT, Pc); + ContextRecord->Pc = MEMORY_READ_QWORD(UnwindParams, SourceAddress); + + SourceAddress = StartingSp + FIELD_OFFSET(T_CONTEXT, Fpcr); + ContextRecord->Fpcr = MEMORY_READ_DWORD(UnwindParams, SourceAddress); + + SourceAddress = StartingSp + FIELD_OFFSET(T_CONTEXT, Fpsr); + ContextRecord->Fpsr = MEMORY_READ_DWORD(UnwindParams, SourceAddress); + + // + // Inherit the unwound-to-call flag from this context + // + + SourceAddress = StartingSp + FIELD_OFFSET(T_CONTEXT, ContextFlags); + ContextRecord->ContextFlags &= ~CONTEXT_UNWOUND_TO_CALL; + ContextRecord->ContextFlags |= + MEMORY_READ_DWORD(UnwindParams, SourceAddress) & CONTEXT_UNWOUND_TO_CALL; + break; + + default: + return STATUS_UNSUCCESSFUL; + } + + return STATUS_SUCCESS; +} + +ULONG +RtlpComputeScopeSize( + __in ULONG_PTR UnwindCodePtr, + __in ULONG_PTR UnwindCodesEndPtr, + __in BOOLEAN IsEpilog, + __in PARM64_UNWIND_PARAMS UnwindParams + ) + +/*++ + +Routine Description: + + Computes the size of an prolog or epilog, in words. + +Arguments: + + UnwindCodePtr - Supplies a pointer to the start of the unwind + code sequence. + + UnwindCodesEndPtr - Supplies a pointer to the byte immediately + following the unwind code table, as described by the header. + + IsEpilog - Specifies TRUE if the scope describes an epilog, + or FALSE if it describes a prolog. + + UnwindParams - Additional parameters shared with caller. + +Return Value: + + The size of the scope described by the unwind codes, in halfword units. + +--*/ + +{ + ULONG ScopeSize; + BYTE Opcode; + + // + // Iterate through the unwind codes until we hit an end marker. + // While iterating, accumulate the total scope size. + // + + ScopeSize = 0; + Opcode = 0; + while (UnwindCodePtr < UnwindCodesEndPtr) { + Opcode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + if (OPCODE_IS_END(Opcode)) { + break; + } + + UnwindCodePtr += UnwindCodeSizeTable[Opcode]; + ScopeSize++; + } + + // + // Epilogs have one extra instruction at the end that needs to be + // accounted for. + // + + if (IsEpilog) { + ScopeSize++; + } + + return ScopeSize; +} + +NTSTATUS +RtlpUnwindRestoreRegisterRange( + __inout PT_CONTEXT ContextRecord, + __in LONG SpOffset, + __in ULONG FirstRegister, + __in ULONG RegisterCount, + __in PARM64_UNWIND_PARAMS UnwindParams + ) + +/*++ + +Routine Description: + + Restores a series of integer registers from the stack. + +Arguments: + + ContextRecord - Supplies the address of a context record. + + SpOffset - Specifies a stack offset. Positive values are simply used + as a base offset. Negative values assume a predecrement behavior: + a 0 offset is used for restoration, but the absoute value of the + offset is added to the final Sp. + + FirstRegister - Specifies the index of the first register to restore. + + RegisterCount - Specifies the number of registers to restore. + + UnwindParams - Additional parameters shared with caller. + +Return Value: + + None. + +--*/ + +{ + ULONG_PTR CurAddress; + ULONG RegIndex; + NTSTATUS Status; + + // + // Compute the source address and validate it. + // + + CurAddress = ContextRecord->Sp; + if (SpOffset >= 0) { + CurAddress += SpOffset; + } + + Status = STATUS_SUCCESS; + VALIDATE_STACK_ADDRESS(UnwindParams, ContextRecord, 8 * RegisterCount, 8, &Status); + if (Status != STATUS_SUCCESS) { + return Status; + } + + // + // Restore the registers + // + + for (RegIndex = 0; RegIndex < RegisterCount; RegIndex++) { + UPDATE_CONTEXT_POINTERS(UnwindParams, RegIndex, CurAddress); + ContextRecord->X[FirstRegister + RegIndex] = MEMORY_READ_QWORD(UnwindParams, CurAddress); + CurAddress += 8; + } + if (SpOffset < 0) { + ContextRecord->Sp -= SpOffset; + } + + return STATUS_SUCCESS; +} + +NTSTATUS +RtlpUnwindRestoreFpRegisterRange( + __inout PT_CONTEXT ContextRecord, + __in LONG SpOffset, + __in ULONG FirstRegister, + __in ULONG RegisterCount, + __in PARM64_UNWIND_PARAMS UnwindParams + ) + +/*++ + +Routine Description: + + Restores a series of floating-point registers from the stack. + +Arguments: + + ContextRecord - Supplies the address of a context record. + + SpOffset - Specifies a stack offset. Positive values are simply used + as a base offset. Negative values assume a predecrement behavior: + a 0 offset is used for restoration, but the absoute value of the + offset is added to the final Sp. + + FirstRegister - Specifies the index of the first register to restore. + + RegisterCount - Specifies the number of registers to restore. + + UnwindParams - Additional parameters shared with caller. + +Return Value: + + None. + +--*/ + +{ + ULONG_PTR CurAddress; + ULONG RegIndex; + NTSTATUS Status; + + // + // Compute the source address and validate it. + // + + CurAddress = ContextRecord->Sp; + if (SpOffset >= 0) { + CurAddress += SpOffset; + } + + Status = STATUS_SUCCESS; + VALIDATE_STACK_ADDRESS(UnwindParams, ContextRecord, 8 * RegisterCount, 8, &Status); + if (Status != STATUS_SUCCESS) { + return Status; + } + + // + // Restore the registers + // + + for (RegIndex = 0; RegIndex < RegisterCount; RegIndex++) { + UPDATE_FP_CONTEXT_POINTERS(UnwindParams, RegIndex, CurAddress); + ContextRecord->V[FirstRegister + RegIndex].Low = MEMORY_READ_QWORD(UnwindParams, CurAddress); + CurAddress += 8; + } + if (SpOffset < 0) { + ContextRecord->Sp -= SpOffset; + } + + return STATUS_SUCCESS; +} + +NTSTATUS +RtlpUnwindFunctionFull( + __in DWORD64 ControlPcRva, + __in ULONG_PTR ImageBase, + __in PT_RUNTIME_FUNCTION FunctionEntry, + __inout T_CONTEXT *ContextRecord, + __out PDWORD64 EstablisherFrame, + __deref_opt_out_opt PEXCEPTION_ROUTINE *HandlerRoutine, + __out PVOID *HandlerData, + __in PARM64_UNWIND_PARAMS UnwindParams + ) + +/*++ + +Routine Description: + + This function virtually unwinds the specified function by parsing the + .xdata record to determine where in the function the provided ControlPc + is, and then executing unwind codes that map to the function's prolog + or epilog behavior. + + If a context pointers record is specified (in the UnwindParams), then + the address where each nonvolatile register is restored from is recorded + in the appropriate element of the context pointers record. + +Arguments: + + ControlPcRva - Supplies the address where control left the specified + function, as an offset relative to the IamgeBase. + + ImageBase - Supplies the base address of the image that contains the + function being unwound. + + FunctionEntry - Supplies the address of the function table entry for the + specified function. If appropriate, this should have already been + probed. + + ContextRecord - Supplies the address of a context record. + + EstablisherFrame - Supplies a pointer to a variable that receives the + the establisher frame pointer value. + + HandlerRoutine - Supplies an optional pointer to a variable that receives + the handler routine address. If control did not leave the specified + function in either the prolog or an epilog and a handler of the + proper type is associated with the function, then the address of the + language specific exception handler is returned. Otherwise, NULL is + returned. + + HandlerData - Supplies a pointer to a variable that receives a pointer + the the language handler data. + + UnwindParams - Additional parameters shared with caller. + +Return Value: + + STATUS_SUCCESS if the unwind could be completed, a failure status otherwise. + Unwind can only fail when validation bounds are specified. + +--*/ + +{ + ULONG AccumulatedSaveNexts; + ULONG CurCode; + ULONG EpilogScopeCount; + PEXCEPTION_ROUTINE ExceptionHandler; + PVOID ExceptionHandlerData; + BOOLEAN FinalPcFromLr; + ULONG FunctionLength; + ULONG HeaderWord; + ULONG NextCode; + ULONG Offset; + DWORD64 OffsetInFunction; + ULONG ScopeNum; + ULONG ScopeSize; + ULONG ScopeStart; + DWORD64 SkipWords; + NTSTATUS Status; + ULONG_PTR UnwindCodePtr; + ULONG_PTR UnwindCodesEndPtr; + ULONG_PTR UnwindDataPtr; + ULONG UnwindIndex; + ULONG UnwindWords; + + // + // Unless a special frame is enountered, assume that any unwinding + // will return us to the return address of a call and set the flag + // appropriately (it will be cleared again if the special cases apply). + // + + ContextRecord->ContextFlags |= CONTEXT_UNWOUND_TO_CALL; + + // + // By default, unwinding is done by popping to the LR, then copying + // that LR to the PC. However, some special opcodes require different + // behavior. + // + + FinalPcFromLr = TRUE; + + // + // Fetch the header word from the .xdata blob + // + + UnwindDataPtr = ImageBase + FunctionEntry->UnwindData; + HeaderWord = MEMORY_READ_DWORD(UnwindParams, UnwindDataPtr); + UnwindDataPtr += 4; + + // + // Verify the version before we do anything else + // + + if (((HeaderWord >> 18) & 3) != 0) { + return STATUS_UNWIND_UNSUPPORTED_VERSION; + } + + FunctionLength = HeaderWord & 0x3ffff; + OffsetInFunction = (ControlPcRva - FunctionEntry->BeginAddress) / 4; + + // + // Determine the number of epilog scope records and the maximum number + // of unwind codes. + // + + UnwindWords = (HeaderWord >> 27) & 31; + EpilogScopeCount = (HeaderWord >> 22) & 31; + if (EpilogScopeCount == 0 && UnwindWords == 0) { + EpilogScopeCount = MEMORY_READ_DWORD(UnwindParams, UnwindDataPtr); + UnwindDataPtr += 4; + UnwindWords = (EpilogScopeCount >> 16) & 0xff; + EpilogScopeCount &= 0xffff; + } + if ((HeaderWord & (1 << 21)) != 0) { + UnwindIndex = EpilogScopeCount; + EpilogScopeCount = 0; + } + + // + // If exception data is present, extract it now. + // + + ExceptionHandler = NULL; + ExceptionHandlerData = NULL; + if ((HeaderWord & (1 << 20)) != 0) { + ExceptionHandler = (PEXCEPTION_ROUTINE)(ImageBase + + MEMORY_READ_DWORD(UnwindParams, UnwindDataPtr + 4 * (EpilogScopeCount + UnwindWords))); + ExceptionHandlerData = (PVOID)(UnwindDataPtr + 4 * (EpilogScopeCount + UnwindWords + 1)); + } + + // + // Unless we are in a prolog/epilog, we execute the unwind codes + // that immediately follow the epilog scope list. + // + + UnwindCodePtr = UnwindDataPtr + 4 * EpilogScopeCount; + UnwindCodesEndPtr = UnwindCodePtr + 4 * UnwindWords; + SkipWords = 0; + + // + // If we're near the start of the function, and this function has a prolog, + // compute the size of the prolog from the unwind codes. If we're in the + // midst of it, we still execute starting at unwind code index 0, but we may + // need to skip some to account for partial execution of the prolog. + // + // N.B. As an optimization here, note that each byte of unwind codes can + // describe at most one 32-bit instruction. Thus, the largest prologue + // that could possibly be described by UnwindWords (which is 4 * the + // number of unwind code bytes) is 4 * UnwindWords words. If + // OffsetInFunction is larger than this value, it is guaranteed to be + // in the body of the function. + // + + if (OffsetInFunction < 4 * UnwindWords) { + ScopeSize = RtlpComputeScopeSize(UnwindCodePtr, UnwindCodesEndPtr, FALSE, UnwindParams); + + if (OffsetInFunction < ScopeSize) { + SkipWords = ScopeSize - OffsetInFunction; + ExceptionHandler = NULL; + ExceptionHandlerData = NULL; + goto ExecuteCodes; + } + } + + // + // We're not in the prolog, now check to see if we are in the epilog. + // In the simple case, the 'E' bit is set indicating there is a single + // epilog that lives at the end of the function. If we're near the end + // of the function, compute the actual size of the epilog from the + // unwind codes. If we're in the midst of it, adjust the unwind code + // pointer to the start of the codes and determine how many we need to skip. + // + // N.B. Similar to the prolog case above, the maximum number of halfwords + // that an epilog can cover is limited by UnwindWords. In the epilog + // case, however, the starting index within the unwind code table is + // non-zero, and so the maximum number of unwind codes that can pertain + // to an epilog is (UnwindWords * 4 - UnwindIndex), thus further + // constraining the bounds of the epilog. + // + + if ((HeaderWord & (1 << 21)) != 0) { + if (OffsetInFunction + (4 * UnwindWords - UnwindIndex) >= FunctionLength) { + ScopeSize = RtlpComputeScopeSize(UnwindCodePtr + UnwindIndex, UnwindCodesEndPtr, TRUE, UnwindParams); + ScopeStart = FunctionLength - ScopeSize; + + if (OffsetInFunction >= ScopeStart) { + UnwindCodePtr += UnwindIndex; + SkipWords = OffsetInFunction - ScopeStart; + ExceptionHandler = NULL; + ExceptionHandlerData = NULL; + } + } + } + + // + // In the multiple-epilog case, we scan forward to see if we are within + // shooting distance of any of the epilogs. If we are, we compute the + // actual size of the epilog from the unwind codes and proceed like the + // simple case above. + // + + else { + for (ScopeNum = 0; ScopeNum < EpilogScopeCount; ScopeNum++) { + HeaderWord = MEMORY_READ_DWORD(UnwindParams, UnwindDataPtr); + UnwindDataPtr += 4; + + // + // The scope records are stored in order. If we hit a record that + // starts after our current position, we must not be in an epilog. + // + + ScopeStart = HeaderWord & 0x3ffff; + if (OffsetInFunction < ScopeStart) { + break; + } + + UnwindIndex = HeaderWord >> 22; + if (OffsetInFunction < ScopeStart + (4 * UnwindWords - UnwindIndex)) { + ScopeSize = RtlpComputeScopeSize(UnwindCodePtr + UnwindIndex, UnwindCodesEndPtr, TRUE, UnwindParams); + + if (OffsetInFunction < ScopeStart + ScopeSize) { + + UnwindCodePtr += UnwindIndex; + SkipWords = OffsetInFunction - ScopeStart; + ExceptionHandler = NULL; + ExceptionHandlerData = NULL; + break; + } + } + } + } + +ExecuteCodes: + + // + // Skip over unwind codes until we account for the number of halfwords + // to skip. + // + + while (UnwindCodePtr < UnwindCodesEndPtr && SkipWords > 0) { + CurCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + if (OPCODE_IS_END(CurCode)) { + break; + } + UnwindCodePtr += UnwindCodeSizeTable[CurCode]; + SkipWords--; + } + + // + // Now execute codes until we hit the end. + // + + Status = STATUS_SUCCESS; + AccumulatedSaveNexts = 0; + while (UnwindCodePtr < UnwindCodesEndPtr && Status == STATUS_SUCCESS) { + + CurCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + UnwindCodePtr += 1; + + // + // alloc_s (000xxxxx): allocate small stack with size < 1024 (2^5 * 16) + // + + if (CurCode <= 0x1f) { + if (AccumulatedSaveNexts != 0) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + ContextRecord->Sp += 16 * (CurCode & 0x1f); + } + + // + // save_r19r20_x (001zzzzz): save 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 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 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 at [sp+#Z*8], offset <= 504 + // + + else if (CurCode <= 0xd7) { + if (AccumulatedSaveNexts != 0) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + NextCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + UnwindCodePtr++; + Status = RtlpUnwindRestoreRegisterRange( + ContextRecord, + 8 * (NextCode & 0x3f), + 19 + 2 * (((CurCode & 1) << 2) + (NextCode >> 6)), + 1, + UnwindParams); + if (Status == STATUS_SUCCESS) { + RtlpUnwindRestoreRegisterRange( + ContextRecord, + 8 * (NextCode & 0x3f) + 8, + 30, + 1, + UnwindParams); + } + } + + // + // save_fregp (1101100x|xxzzzzzz): save pair d(8+#X) at [sp+#Z*8], offset <= 504 + // + + else if (CurCode <= 0xd9) { + NextCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + UnwindCodePtr++; + Status = RtlpUnwindRestoreFpRegisterRange( + ContextRecord, + 8 * (NextCode & 0x3f), + 8 + ((CurCode & 1) << 2) + (NextCode >> 6), + 2 + AccumulatedSaveNexts, + UnwindParams); + AccumulatedSaveNexts = 0; + } + + // + // save_fregp_x (1101101x|xxzzzzzz): save pair d(8+#X), at [sp-(#Z+1)*8]!, pre-indexed offset >= -512 + // + + else if (CurCode <= 0xdb) { + NextCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + UnwindCodePtr++; + Status = RtlpUnwindRestoreFpRegisterRange( + ContextRecord, + -8 * ((NextCode & 0x3f) + 1), + 8 + ((CurCode & 1) << 2) + (NextCode >> 6), + 2 + AccumulatedSaveNexts, + UnwindParams); + AccumulatedSaveNexts = 0; + } + + // + // save_freg (1101110x|xxzzzzzz): save reg d(9+#X) at [sp+#Z*8], offset <= 504 + // + + else if (CurCode <= 0xdd) { + if (AccumulatedSaveNexts != 0) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + NextCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + UnwindCodePtr++; + Status = RtlpUnwindRestoreFpRegisterRange( + ContextRecord, + 8 * (NextCode & 0x3f), + 8 + ((CurCode & 1) << 2) + (NextCode >> 6), + 1, + UnwindParams); + } + + // + // save_freg_x (11011110|xxxzzzzz): save reg d(8+#X) at [sp-(#Z+1)*8]!, pre-indexed offset >= -256 + // + + else if (CurCode == 0xde) { + if (AccumulatedSaveNexts != 0) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + NextCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + UnwindCodePtr++; + Status = RtlpUnwindRestoreFpRegisterRange( + ContextRecord, + -8 * ((NextCode & 0x1f) + 1), + 8 + (NextCode >> 5), + 1, + UnwindParams); + } + + // + // alloc_l (11100000|xxxxxxxx|xxxxxxxx|xxxxxxxx): allocate large stack with size < 256M + // + + else if (CurCode == 0xe0) { + if (AccumulatedSaveNexts != 0) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + ContextRecord->Sp += 16 * (MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr) << 16); + UnwindCodePtr++; + ContextRecord->Sp += 16 * (MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr) << 8); + UnwindCodePtr++; + ContextRecord->Sp += 16 * MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + UnwindCodePtr++; + } + + // + // set_fp (11100001): set up r29: with: mov r29,sp + // + + else if (CurCode == 0xe1) { + if (AccumulatedSaveNexts != 0) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + ContextRecord->Sp = ContextRecord->Fp; + } + + // + // add_fp (11100010|xxxxxxxx): set up r29 with: add r29,sp,#x*8 + // + + else if (CurCode == 0xe2) { + if (AccumulatedSaveNexts != 0) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + ContextRecord->Sp = ContextRecord->Fp - 8 * MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr); + UnwindCodePtr++; + } + + // + // nop (11100011): no unwind operation is required + // + + else if (CurCode == 0xe3) { + if (AccumulatedSaveNexts != 0) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + } + + // + // end (11100100): end of unwind code + // + + else if (CurCode == 0xe4) { + if (AccumulatedSaveNexts != 0) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + goto finished; + } + + // + // end_c (11100101): end of unwind code in current chained scope + // + + else if (CurCode == 0xe5) { + if (AccumulatedSaveNexts != 0) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + goto finished; + } + + // + // save_next (11100110): save next non-volatile Int or FP register pair. + // + + else if (CurCode == 0xe6) { + AccumulatedSaveNexts++; + } + + // + // custom_0 (111010xx): restore custom structure + // + + else if (CurCode >= 0xe8 && CurCode <= 0xeb) { + if (AccumulatedSaveNexts != 0) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + Status = RtlpUnwindCustom(ContextRecord, (BYTE) CurCode, UnwindParams); + FinalPcFromLr = FALSE; + } + + // + // Anything else is invalid + // + + else { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + } + + // + // If we succeeded, post-process the results a bit + // +finished: + if (Status == STATUS_SUCCESS) { + + // + // Since we always POP to the LR, recover the final PC from there, unless + // it was overwritten due to a special case custom unwinding operation. + // Also set the establisher frame equal to the final stack pointer. + // + + if (FinalPcFromLr) { + ContextRecord->Pc = ContextRecord->Lr; + } + *EstablisherFrame = ContextRecord->Sp; + + if (ARGUMENT_PRESENT(HandlerRoutine)) { + *HandlerRoutine = ExceptionHandler; + } + *HandlerData = ExceptionHandlerData; + } + + return Status; +} + +NTSTATUS +RtlpUnwindFunctionCompact( + __in DWORD64 ControlPcRva, + __in PT_RUNTIME_FUNCTION FunctionEntry, + __inout T_CONTEXT *ContextRecord, + __out PDWORD64 EstablisherFrame, + __deref_opt_out_opt PEXCEPTION_ROUTINE *HandlerRoutine, + __out PVOID *HandlerData, + __in PARM64_UNWIND_PARAMS UnwindParams + ) + +/*++ + +Routine Description: + + This function virtually unwinds the specified function by parsing the + compact .pdata record to determine where in the function the provided + ControlPc is, and then executing a standard, well-defined set of + operations. + + If a context pointers record is specified (in the UnwindParams), then + the address where each nonvolatile register is restored from is recorded + in the appropriate element of the context pointers record. + +Arguments: + + ControlPcRva - Supplies the address where control left the specified + function, as an offset relative to the IamgeBase. + + FunctionEntry - Supplies the address of the function table entry for the + specified function. If appropriate, this should have already been + probed. + + ContextRecord - Supplies the address of a context record. + + EstablisherFrame - Supplies a pointer to a variable that receives the + the establisher frame pointer value. + + HandlerRoutine - Supplies an optional pointer to a variable that receives + the handler routine address. If control did not leave the specified + function in either the prolog or an epilog and a handler of the + proper type is associated with the function, then the address of the + language specific exception handler is returned. Otherwise, NULL is + returned. + + HandlerData - Supplies a pointer to a variable that receives a pointer + the the language handler data. + + UnwindParams - Additional parameters shared with caller. + +Return Value: + + STATUS_SUCCESS if the unwind could be completed, a failure status otherwise. + Unwind can only fail when validation bounds are specified. + +--*/ + +{ + ULONG Count; + ULONG Cr; + ULONG CurrentOffset; + ULONG EpilogLength; + ULONG Flag; + ULONG FloatSize; + ULONG FrameSize; + ULONG FRegOpcodes; + ULONG FunctionLength; + ULONG HBit; + ULONG HOpcodes; + ULONG IRegOpcodes; + ULONG InstCount; + ULONG IntSize; + ULONG LocalSize; + DWORD64 OffsetInFunction; + DWORD64 OffsetInScope; + ULONG PrologLength; + ULONG RegF; + ULONG RegI; + ULONG RegSize; + ULONG ScopeStart; + ULONG StackAdjustOpcodes; + NTSTATUS Status; + ULONG UnwindData; + + UnwindData = FunctionEntry->UnwindData; + Status = STATUS_SUCCESS; + + // + // Compact records always describe an unwind to a call. + // + + ContextRecord->ContextFlags |= CONTEXT_UNWOUND_TO_CALL; + + // + // Extract the basic information about how to do a full unwind. + // + + Flag = UnwindData & 3; + FunctionLength = (UnwindData >> 2) & 0x7ff; + RegF = (UnwindData >> 13) & 7; + RegI = (UnwindData >> 16) & 0xf; + HBit = (UnwindData >> 20) & 1; + Cr = (UnwindData >> 21) & 3; + FrameSize = (UnwindData >> 23) & 0x1ff; + + if (Flag == 3) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + if (Cr == 2) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + + // + // Determine the size of the locals + // + + IntSize = RegI * 8; + if (Cr == 1) { + IntSize += 8; + } + FloatSize = (RegF == 0) ? 0 : (RegF + 1) * 8; + RegSize = (IntSize + FloatSize + 8*8 * HBit + 0xf) & ~0xf; + if (RegSize > 16 * FrameSize) { + return STATUS_UNWIND_INVALID_SEQUENCE; + } + LocalSize = 16 * FrameSize - RegSize; + + // + // If we're near the start of the function (within 17 words), + // see if we are within the prolog. + // + // N.B. If the low 2 bits of the UnwindData are 2, then we have + // no prolog. + // + + OffsetInFunction = (ControlPcRva - FunctionEntry->BeginAddress) / 4; + OffsetInScope = 0; + if (OffsetInFunction < 17 && Flag != 2) { + + // + // Compute sizes for each opcode in the prolog. + // + + IRegOpcodes = (IntSize + 8) / 16; + FRegOpcodes = (FloatSize + 8) / 16; + HOpcodes = 4 * HBit; + StackAdjustOpcodes = (Cr == 3) ? 1 : 0; + if (Cr != 3 || LocalSize > 512) { + StackAdjustOpcodes += (LocalSize > 4088) ? 2 : (LocalSize > 0) ? 1 : 0; + } + + // + // Compute the total prolog length and determine if we are within + // its scope. + // + // N.B. We must execute prolog operations backwards to unwind, so + // our final scope offset in this case is the distance from the end. + // + + PrologLength = IRegOpcodes + FRegOpcodes + HOpcodes + StackAdjustOpcodes; + + if (OffsetInFunction < PrologLength) { + OffsetInScope = PrologLength - OffsetInFunction; + } + } + + // + // If we're near the end of the function (within 15 words), see if + // we are within the epilog. + // + // N.B. If the low 2 bits of the UnwindData are 2, then we have + // no epilog. + // + + if (OffsetInScope == 0 && OffsetInFunction + 15 >= FunctionLength && Flag != 2) { + + // + // Compute sizes for each opcode in the epilog. + // + + IRegOpcodes = (IntSize + 8) / 16; + FRegOpcodes = (FloatSize + 8) / 16; + HOpcodes = HBit; + StackAdjustOpcodes = (Cr == 3) ? 1 : 0; + if (Cr != 3 || LocalSize > 512) { + StackAdjustOpcodes += (LocalSize > 4088) ? 2 : (LocalSize > 0) ? 1 : 0; + } + + // + // Compute the total epilog length and determine if we are within + // its scope. + // + + EpilogLength = IRegOpcodes + FRegOpcodes + HOpcodes + StackAdjustOpcodes + 1; + + ScopeStart = FunctionLength - EpilogLength; + if (OffsetInFunction > ScopeStart) { + OffsetInScope = OffsetInFunction - ScopeStart; + } + } + + // + // Process operations backwards, in the order: stack/frame deallocation, + // VFP register popping, integer register popping, parameter home + // area recovery. + // + // First case is simple: we process everything with no regard for + // the current offset within the scope. + // + + Status = STATUS_SUCCESS; + if (OffsetInScope == 0) { + + if (Cr == 3) { + Status = RtlpUnwindRestoreRegisterRange(ContextRecord, 0, 29, 2, UnwindParams); + } + ContextRecord->Sp += LocalSize; + + if (RegF != 0 && Status == STATUS_SUCCESS) { + Status = RtlpUnwindRestoreFpRegisterRange(ContextRecord, IntSize, 8, RegF + 1, UnwindParams); + } + + if (Cr == 1 && Status == STATUS_SUCCESS) { + Status = RtlpUnwindRestoreRegisterRange(ContextRecord, IntSize - 8, 30, 1, UnwindParams); + } + if (RegI > 0 && Status == STATUS_SUCCESS) { + Status = RtlpUnwindRestoreRegisterRange(ContextRecord, 0, 19, RegI, UnwindParams); + } + ContextRecord->Sp += RegSize; + } + + // + // Second case is more complex: we must step along each operation + // to ensure it should be executed. + // + + else { + + CurrentOffset = 0; + if (Cr == 3) { + if (LocalSize <= 512) { + if (CurrentOffset++ >= OffsetInScope) { + Status = RtlpUnwindRestoreRegisterRange(ContextRecord, -(LONG)LocalSize, 29, 2, UnwindParams); + } + LocalSize = 0; + } + } + while (LocalSize != 0) { + Count = (LocalSize + 4087) % 4088 + 1; + if (CurrentOffset++ >= OffsetInScope) { + ContextRecord->Sp += Count; + } + LocalSize -= Count; + } + + if (HBit != 0) { + CurrentOffset += 4; + } + + if (RegF != 0 && Status == STATUS_SUCCESS) { + RegF++; + while (RegF != 0) { + Count = 2 - (RegF & 1); + RegF -= Count; + if (CurrentOffset++ >= OffsetInScope) { + Status = RtlpUnwindRestoreFpRegisterRange( + ContextRecord, + (RegF == 0 && RegI == 0) ? (-(LONG)RegSize) : (IntSize + 8 * RegF), + 8 + RegF, + Count, + UnwindParams); + } + } + } + + if (Cr == 1 && Status == STATUS_SUCCESS) { + if (RegI % 2 == 0) { + if (CurrentOffset++ >= OffsetInScope) { + Status = RtlpUnwindRestoreRegisterRange(ContextRecord, IntSize - 8, 30, 1, UnwindParams); + } + } else { + if (CurrentOffset++ >= OffsetInScope) { + RegI--; + Status = RtlpUnwindRestoreRegisterRange(ContextRecord, IntSize - 8, 30, 1, UnwindParams); + if (Status == STATUS_SUCCESS) { + Status = RtlpUnwindRestoreRegisterRange(ContextRecord, IntSize - 16, 19 + RegI, 1, UnwindParams); + } + } + } + } + + while (RegI != 0 && Status == STATUS_SUCCESS) { + Count = 2 - (RegI & 1); + RegI -= Count; + if (CurrentOffset++ >= OffsetInScope) { + Status = RtlpUnwindRestoreRegisterRange( + ContextRecord, + (RegI == 0) ? (-(LONG)RegSize) : (8 * RegI), + 19 + RegI, + Count, + UnwindParams); + } + } + } + + // + // If we succeeded, post-process the results a bit + // + + if (Status == STATUS_SUCCESS) { + + ContextRecord->Pc = ContextRecord->Lr; + *EstablisherFrame = ContextRecord->Sp; + + if (ARGUMENT_PRESENT(HandlerRoutine)) { + *HandlerRoutine = NULL; + } + *HandlerData = NULL; + } + + return Status; +} + +BOOL OOPStackUnwinderArm64::Unwind(T_CONTEXT * pContext) +{ + DWORD64 ImageBase = 0; + HRESULT hr = GetModuleBase(pContext->Pc, &ImageBase); + if (hr != S_OK) + return FALSE; + + PEXCEPTION_ROUTINE DummyHandlerRoutine; + PVOID DummyHandlerData; + DWORD64 DummyEstablisherFrame; + + DWORD64 startingPc = pContext->Pc; + DWORD64 startingSp = pContext->Sp; + + T_RUNTIME_FUNCTION Rfe; + if (FAILED(GetFunctionEntry(pContext->Pc, &Rfe, sizeof(Rfe)))) + return FALSE; + + if ((Rfe.UnwindData & 3) != 0) + { + hr = RtlpUnwindFunctionCompact(pContext->Pc - (ULONG)ImageBase, + &Rfe, + pContext, + &DummyEstablisherFrame, + &DummyHandlerRoutine, + &DummyHandlerData, + NULL); + + } + else + { + hr = RtlpUnwindFunctionFull(pContext->Pc - (ULONG)ImageBase, + (ULONG)ImageBase, + &Rfe, + pContext, + &DummyEstablisherFrame, + &DummyHandlerRoutine, + &DummyHandlerData, + NULL); + } + + // PC == 0 means unwinding is finished. + // Same if no forward progress is made + if (pContext->Pc == 0 || (startingPc == pContext->Pc && startingSp == pContext->Sp)) + return FALSE; + + return TRUE; +} + +BOOL DacUnwindStackFrame(T_CONTEXT *pContext, T_KNONVOLATILE_CONTEXT_POINTERS* pContextPointers) +{ + OOPStackUnwinderArm64 unwinder; + BOOL res = unwinder.Unwind(pContext); + + if (res && pContextPointers) + { + for (int i = 0; i < 12; i++) + { + *(&pContextPointers->X19 + i) = &pContext->X19 + i; + } + } + + return res; +} diff --git a/src/unwinder/arm64/unwinder_arm64.h b/src/unwinder/arm64/unwinder_arm64.h new file mode 100644 index 0000000000..7396e85e26 --- /dev/null +++ b/src/unwinder/arm64/unwinder_arm64.h @@ -0,0 +1,55 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// + +#ifndef __unwinder_arm64__ +#define __unwinder_arm64__ + +#include "unwinder.h" + + +//--------------------------------------------------------------------------------------- +// +// See the comment for the base class code:OOPStackUnwinder. +// + +class OOPStackUnwinderArm64 : public OOPStackUnwinder +{ +public: + // Unwind the given CONTEXT to the caller CONTEXT. The CONTEXT will be overwritten. + BOOL Unwind(T_CONTEXT * pContext); + + // + // Everything below comes from dbghelp.dll. + // + +protected: + HRESULT UnwindPrologue(__in DWORD64 ImageBase, + __in DWORD64 ControlPc, + __in DWORD64 FrameBase, + __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __inout PT_CONTEXT ContextRecord); + + HRESULT VirtualUnwind(__in DWORD64 ImageBase, + __in DWORD64 ControlPc, + __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __inout PT_CONTEXT ContextRecord, + __out PDWORD64 EstablisherFrame); + + DWORD64 LookupPrimaryUnwindInfo + (__in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __in DWORD64 ImageBase, + __out _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryEntry); + + _PIMAGE_RUNTIME_FUNCTION_ENTRY SameFunction + (__in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __in DWORD64 ImageBase, + __in DWORD64 ControlPc, + __out _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionReturnBuffer); +}; + +#endif // __unwinder_arm64__ + diff --git a/src/unwinder/dac/.gitmirror b/src/unwinder/dac/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/unwinder/dac/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file diff --git a/src/unwinder/dac/CMakeLists.txt b/src/unwinder/dac/CMakeLists.txt new file mode 100644 index 0000000000..e82046197c --- /dev/null +++ b/src/unwinder/dac/CMakeLists.txt @@ -0,0 +1,10 @@ +include(${CLR_DIR}/dac.cmake) + +add_definitions(-DFEATURE_NO_HOST) +add_definitions(-D_TARGET_AMD64_=1) +add_definitions(-DDBG_TARGET_64BIT=1) +add_definitions(-DDBG_TARGET_AMD64=1) +add_definitions(-DDBG_TARGET_WIN64=1) +add_definitions(-D_WIN64=1) + +add_library(unwinder_dac ${UNWINDER_SOURCES}) diff --git a/src/unwinder/dac/dirs.proj b/src/unwinder/dac/dirs.proj new file mode 100644 index 0000000000..77738732ed --- /dev/null +++ b/src/unwinder/dac/dirs.proj @@ -0,0 +1,18 @@ + + + + + + true + false + true + true + + + + + + + + + diff --git a/src/unwinder/dac/hostlocal/.gitmirror b/src/unwinder/dac/hostlocal/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/unwinder/dac/hostlocal/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file diff --git a/src/unwinder/dac/hostlocal/unwinder_dac.nativeproj b/src/unwinder/dac/hostlocal/unwinder_dac.nativeproj new file mode 100644 index 0000000000..3b562e07f9 --- /dev/null +++ b/src/unwinder/dac/hostlocal/unwinder_dac.nativeproj @@ -0,0 +1,13 @@ + + + + + true + true + unwinder_dac + + + + + + diff --git a/src/unwinder/dac/hostwinamd64/.gitmirror b/src/unwinder/dac/hostwinamd64/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/unwinder/dac/hostwinamd64/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file diff --git a/src/unwinder/dac/hostwinamd64/CMakeLists.txt b/src/unwinder/dac/hostwinamd64/CMakeLists.txt new file mode 100644 index 0000000000..66a595f033 --- /dev/null +++ b/src/unwinder/dac/hostwinamd64/CMakeLists.txt @@ -0,0 +1,5 @@ +remove_definitions(-DPROFILING_SUPPORTED) +add_definitions(-DPROFILING_SUPPORTED_DATA) +add_definitions(-DDACCESS_COMPILE) + +add_library(unwinder_dac_amd64 ${UNWINDER_SOURCES}) diff --git a/src/unwinder/dac/hostwinamd64/unwinder_dac.nativeproj b/src/unwinder/dac/hostwinamd64/unwinder_dac.nativeproj new file mode 100644 index 0000000000..e0dff469a4 --- /dev/null +++ b/src/unwinder/dac/hostwinamd64/unwinder_dac.nativeproj @@ -0,0 +1,15 @@ + + + + + + + + true + unwinder_dac_amd64 + + + + + + diff --git a/src/unwinder/dac/hostwinx86/.gitmirror b/src/unwinder/dac/hostwinx86/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/unwinder/dac/hostwinx86/.gitmirror @@ -0,0 +1 @@ +Only contents of this folder, excluding subfolders, will be mirrored by the Git-TFS Mirror. \ No newline at end of file diff --git a/src/unwinder/dac/hostwinx86/unwinder_dac.nativeproj b/src/unwinder/dac/hostwinx86/unwinder_dac.nativeproj new file mode 100644 index 0000000000..a3538c3383 --- /dev/null +++ b/src/unwinder/dac/hostwinx86/unwinder_dac.nativeproj @@ -0,0 +1,15 @@ + + + + + + + + true + unwinder_dac_x86 + + + + + + diff --git a/src/unwinder/dirs.proj b/src/unwinder/dirs.proj new file mode 100644 index 0000000000..8b511971e3 --- /dev/null +++ b/src/unwinder/dirs.proj @@ -0,0 +1,19 @@ + + + + + + true + false + true + true + + + + + + + + + + diff --git a/src/unwinder/stdafx.cpp b/src/unwinder/stdafx.cpp new file mode 100644 index 0000000000..184cc8de11 --- /dev/null +++ b/src/unwinder/stdafx.cpp @@ -0,0 +1,13 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// File: stdafx.cpp +// + +// +// Host for precompiled headers. +// +//***************************************************************************** +#include "stdafx.h" // Precompiled header key. diff --git a/src/unwinder/stdafx.h b/src/unwinder/stdafx.h new file mode 100644 index 0000000000..840877ea5b --- /dev/null +++ b/src/unwinder/stdafx.h @@ -0,0 +1,19 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// File: stdafx.h +// + +// Prevent the inclusion of Random.h from disabling rand(). rand() is used by some other headers we include +// and there's no reason why DAC should be forbidden from using it. +#define DO_NOT_DISABLE_RAND + +#define USE_COM_CONTEXT_DEF + +#include +#include +#include +#include +#include diff --git a/src/unwinder/unwinder.cpp b/src/unwinder/unwinder.cpp new file mode 100644 index 0000000000..6431a83ea8 --- /dev/null +++ b/src/unwinder/unwinder.cpp @@ -0,0 +1,163 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// + +#include "stdafx.h" +#include "unwinder.h" + +EXTERN_C void GetRuntimeStackWalkInfo(IN ULONG64 ControlPc, + OUT UINT_PTR* pModuleBase, + OUT UINT_PTR* pFuncEntry); + +#if !defined(_TARGET_ARM_) && !defined(_TARGET_ARM64_) +//--------------------------------------------------------------------------------------- +// +// Read an UNWIND_INFO structure given its address. The UNWIND_INFO structure is variable sized. +// This is just a simple wrapper over the platform-specific DAC function which does the real work. +// +// Arguments: +// taUnwindInfo - target address of the beginning of the UNWIND_INFO structure +// +// Return Value: +// Return the specified UNWIND_INFO. It lives in the DAC cache, so the caller doesn't need to explicitly +// free the memory. It'll get flushed when the DAC cache is flushed (i.e. when we continue). +// + +UNWIND_INFO * OOPStackUnwinder::GetUnwindInfo(TADDR taUnwindInfo) +{ + return DacGetUnwindInfo(taUnwindInfo); +} +#endif // !_TARGET_ARM_ && !_TARGET_ARM64_ + +//--------------------------------------------------------------------------------------- +// +// This is a simple wrapper over code:OOPStackUnwinder::ReadMemory(). Unlike ReadMemory(), +// it fails if we don't successfully read all the specified bytes. +// +// Arguments: +// address - the address to be read +// pbBuffer - the buffer to store the read memory +// cbRequest - the number of bytes requested +// +// Return Value: +// S_OK if all the memory is read successfully. +// HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY) if only part of the memory is read. +// Failure HRs otherwise. +// + +HRESULT OOPStackUnwinder::ReadAllMemory( DWORD64 address, + __in_ecount(cbRequest) PVOID pbBuffer, + DWORD cbRequest) +{ + DWORD cbDone = 0; + HRESULT hr = ReadMemory(address, pbBuffer, cbRequest, &cbDone); + if (SUCCEEDED(hr) && (cbDone != cbRequest)) + { + return HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY); + } + else + { + return hr; + } +} + +//--------------------------------------------------------------------------------------- +// +// Read the specified memory from out-of-process. This function uses DacReadAll() to do the trick. +// +// Arguments: +// address - the address to be read +// pbBuffer - the buffer to store the read memory +// cbRequest - the number of bytes requested +// pcbDone - out parameter; returns the number of bytes read +// +// Return Value: +// S_OK if all the memory is read successfully +// + +HRESULT OOPStackUnwinder::ReadMemory( DWORD64 address, + __in_ecount(cbRequest) PVOID pbBuffer, + DWORD cbRequest, + __out_opt PDWORD pcbDone) +{ + _ASSERTE(pcbDone != NULL); + + HRESULT hr = DacReadAll(TO_TADDR(address), pbBuffer, cbRequest, false); + if (SUCCEEDED(hr)) + { + *pcbDone = cbRequest; + + // On X64, we need to replace any patches which are within the requested memory range. + // This is because the X64 unwinder needs to disassemble the native instructions in order to determine + // whether the IP is in an epilog. +#if defined(_TARGET_AMD64_) + MemoryRange range(dac_cast((TADDR)address), cbRequest); + hr = DacReplacePatchesInHostMemory(range, pbBuffer); +#endif // _TARGET_AMD64_ + } + else + { + *pcbDone = 0; + } + + return hr; +} + +//--------------------------------------------------------------------------------------- +// +// Given a control PC, return the base of the module it is in. For jitted managed code, this is the +// start of the code heap. +// +// Arguments: +// address - the specified address +// pdwBase - out parameter; returns the module base +// +// Return Value: +// S_OK if we retrieve the module base successfully; +// E_FAIL otherwise +// + +HRESULT OOPStackUnwinder::GetModuleBase( DWORD64 address, + __out PDWORD64 pdwBase) +{ + GetRuntimeStackWalkInfo(address, reinterpret_cast(pdwBase), NULL); + return ((*pdwBase == NULL) ? E_FAIL : S_OK); +} + +//--------------------------------------------------------------------------------------- +// +// Given a control PC, return the function entry of the functoin it is in. +// +// Arguments: +// address - the specified IP +// pBuffer - the buffer to store the retrieved function entry +// cbBuffer - the size of the buffer +// +// Return Value: +// S_OK if we retrieve the function entry successfully; +// E_INVALIDARG if the buffer is too small; +// E_FAIL otherwise +// + +HRESULT OOPStackUnwinder::GetFunctionEntry( DWORD64 address, + __out_ecount(cbBuffer) PVOID pBuffer, + DWORD cbBuffer) +{ + if (cbBuffer < sizeof(RUNTIME_FUNCTION)) + { + return E_INVALIDARG; + } + + PVOID pFuncEntry = NULL; + GetRuntimeStackWalkInfo(address, NULL, reinterpret_cast(&pFuncEntry)); + if (pFuncEntry == NULL) + { + return E_FAIL; + } + + memcpy(pBuffer, pFuncEntry, cbBuffer); + return S_OK; +} diff --git a/src/unwinder/unwinder.h b/src/unwinder/unwinder.h new file mode 100644 index 0000000000..21d64eae3f --- /dev/null +++ b/src/unwinder/unwinder.h @@ -0,0 +1,64 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// + +#ifndef __unwinder_h__ +#define __unwinder_h__ + + +//--------------------------------------------------------------------------------------- +// +// OOPStackUnwinder is the abstract base class for unwinding stack frames. Each of the two 64-bit platforms +// has its own derived class. Although the name of this class and its derived classes have changed, they +// are actually borrowed from dbghelp.dll. (StackWalk64() is built on top of these classes.) We have ripped +// out everything we don't need such as symbol lookup and various state, and keep just enough code to support +// VirtualUnwind(). The managed debugging infrastructure can't call RtlVirtualUnwind() because it doesn't +// work from out-of-processr +// +// Notes: +// To see what we have changed in the borrowed source, you can diff the original version and our version. +// For example, on X64, you can diff clr\src\Debug\daccess\amd64\dbs_stack_x64.cpp (the original) and +// clr\src\Debug\daccess\amd64\unwinder_amd64.cpp. +// + +class OOPStackUnwinder +{ +public: + // Unwind the given CONTEXT to the caller CONTEXT. The CONTEXT will be overwritten. + // Derived classes are expected to override this function for each processor architecture. + virtual BOOL Unwind(T_CONTEXT * pContext) = 0; + +protected: +#if !defined(_TARGET_ARM_) && !defined(_TARGET_ARM64_) + // the unwind info on ARM is significantly different than AMD64, as such we don't need this function. + // Read an UNWIND_INFO structure given its address. The UNWIND_INFO structure is variable sized. + virtual UNWIND_INFO * GetUnwindInfo(TADDR taUnwindInfo); +#endif // !_TARGET_ARM_ && !_TARGET_ARM64_ + + // This is a simple wrapper over ReadMemory(). Unlike ReadMemory(), it fails if we don't successfully + // read all the specified bytes. + virtual HRESULT ReadAllMemory( DWORD64 address, + __in_ecount(cbRequest) PVOID pbBuffer, + DWORD cbRequest); + + // Read the specified memory. + virtual HRESULT ReadMemory( DWORD64 address, + __in_ecount(cbRequest) PVOID pbBuffer, + DWORD cbRequest, + __out_opt PDWORD pcbDone); + + // Given a control PC, return the base of the module it is in. For jitted managed code, this is the + // start of the code heap. + virtual HRESULT GetModuleBase( DWORD64 address, + __out PDWORD64 pdwBase); + + // Given a control PC, return the function entry of the functoin it is in. + virtual HRESULT GetFunctionEntry( DWORD64 address, + __out_ecount(cbBuffer) PVOID pBuffer, + DWORD cbBuffer); +}; + +#endif // __unwinder_h__ diff --git a/src/unwinder/unwinder.targets b/src/unwinder/unwinder.targets new file mode 100644 index 0000000000..18cd2bfa7c --- /dev/null +++ b/src/unwinder/unwinder.targets @@ -0,0 +1,60 @@ + + + + + + + + + true + + + $(UserIncludes); + $(ClrSrcDirectory)\unwinder; + $(ClrSrcDirectory)\debug\daccess; + $(ClrSrcDirectory)\vm; + $(ClrSrcDirectory)\vm\$(TargetCpu); + $(ClrSrcDirectory)\debug\inc; + $(ClrSrcDirectory)\debug\inc\$(TargetCpu); + $(ClrSrcDirectory)\debug\inc\dump; + $(ClrSrcDirectory)\debug\ee; + $(ClrSrcDirectory)\inc; + $(VCToolsIncPath); + + + $(CDefines);UNICODE;_UNICODE;$(USER_SPECIFIC_C_DEFINES);FEATURE_NO_HOST + + unwinder_dac + $(ClrLibDest) + LIBRARY + stdafx.h + true + $(ClrSrcDirectory)\unwinder\stdafx.cpp + + $(ClrSrcDirectory)\unwinder + $(ClrSrcDirectory)\unwinder\amd64 + $(ClrSrcDirectory)\unwinder\arm + $(ClrSrcDirectory)\unwinder\arm64 + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.3