diff options
author | Jan Vorlicek <janvorli@microsoft.com> | 2015-02-13 16:31:38 +0100 |
---|---|---|
committer | Jan Vorlicek <janvorli@microsoft.com> | 2015-02-13 16:31:38 +0100 |
commit | 0e20a9e34ad2b861460c2772865653082ca06dfd (patch) | |
tree | 4758e7aea1039b5d1b389bd1a27397c265c27371 | |
parent | 7672677442f918422b4a53ec63e4ba803caa860a (diff) | |
parent | 5720457962860915e42b8ee0fd9fb52904b21423 (diff) | |
download | coreclr-0e20a9e34ad2b861460c2772865653082ca06dfd.tar.gz coreclr-0e20a9e34ad2b861460c2772865653082ca06dfd.tar.bz2 coreclr-0e20a9e34ad2b861460c2772865653082ca06dfd.zip |
Merge pull request #259 from janvorli/linux-issue177
Modify the windows amd64 unwinder to work as jitted code unwinder on Uni...
-rw-r--r-- | src/inc/clrnt.h | 30 | ||||
-rw-r--r-- | src/jit/unwindamd64.cpp | 4 | ||||
-rw-r--r-- | src/unwinder/CMakeLists.txt | 10 | ||||
-rw-r--r-- | src/unwinder/amd64/unwinder_amd64.cpp | 1986 | ||||
-rw-r--r-- | src/unwinder/amd64/unwinder_amd64.h | 50 | ||||
-rw-r--r-- | src/unwinder/stdafx.h | 2 | ||||
-rw-r--r-- | src/unwinder/unwinder.cpp | 94 | ||||
-rw-r--r-- | src/unwinder/unwinder.h | 32 | ||||
-rw-r--r-- | src/unwinder/wks/.gitmirror | 1 | ||||
-rw-r--r-- | src/unwinder/wks/CMakeLists.txt | 1 |
10 files changed, 1357 insertions, 853 deletions
diff --git a/src/inc/clrnt.h b/src/inc/clrnt.h index 37940f712a..e477b524dc 100644 --- a/src/inc/clrnt.h +++ b/src/inc/clrnt.h @@ -816,8 +816,8 @@ typedef enum _UNWIND_OP_CODES { UWOP_SET_FPREG, UWOP_SAVE_NONVOL, UWOP_SAVE_NONVOL_FAR, - UWOP_SAVE_XMM, - UWOP_SAVE_XMM_FAR, + UWOP_EPILOG, + UWOP_SPARE_CODE, UWOP_SAVE_XMM128, UWOP_SAVE_XMM128_FAR, UWOP_PUSH_MACHFRAME @@ -825,13 +825,13 @@ typedef enum _UNWIND_OP_CODES { static const UCHAR UnwindOpExtraSlotTable[] = { 0, // UWOP_PUSH_NONVOL - 1, // UWOP_ALLOC_LARGE (special cased) + 1, // UWOP_ALLOC_LARGE (or 3, special cased in lookup code) 0, // UWOP_ALLOC_SMALL 0, // UWOP_SET_FPREG 1, // UWOP_SAVE_NONVOL 2, // UWOP_SAVE_NONVOL_FAR - 1, // UWOP_SAVE_XMM - 2, // UWOP_SAVE_XMM_FAR + 1, // UWOP_EPILOG + 2, // UWOP_SPARE_CODE // previously 64-bit UWOP_SAVE_XMM_FAR 1, // UWOP_SAVE_XMM128 2, // UWOP_SAVE_XMM128_FAR 0 // UWOP_PUSH_MACHFRAME @@ -848,6 +848,12 @@ typedef union _UNWIND_CODE { UCHAR OpInfo : 4; }; + struct { + UCHAR OffsetLow; + UCHAR UnwindOp : 4; + UCHAR OffsetHigh : 4; + } EpilogueCode; + USHORT FrameOffset; } UNWIND_CODE, *PUNWIND_CODE; @@ -898,7 +904,21 @@ PEXCEPTION_ROUTINE IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL ); +#ifndef FEATURE_PAL extern RtlVirtualUnwindFn* RtlVirtualUnwind_Unsafe; +#else // !FEATURE_PAL +PEXCEPTION_ROUTINE +RtlVirtualUnwind_Unsafe( + IN ULONG HandlerType, + IN ULONG64 ImageBase, + IN ULONG64 ControlPc, + IN PRUNTIME_FUNCTION FunctionEntry, + IN OUT PCONTEXT ContextRecord, + OUT PVOID *HandlerData, + OUT PULONG64 EstablisherFrame, + IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL + ); +#endif // !FEATURE_PAL #endif // _TARGET_AMD64_ diff --git a/src/jit/unwindamd64.cpp b/src/jit/unwindamd64.cpp index 7fc680ed5e..cf83ed3015 100644 --- a/src/jit/unwindamd64.cpp +++ b/src/jit/unwindamd64.cpp @@ -392,8 +392,8 @@ void DumpUnwindInfo(bool isHotCode, UNATIVE_OFFSET startOffset, UNATIVE_OFFSET e i++; break; - case UWOP_SAVE_XMM: - case UWOP_SAVE_XMM_FAR: + case UWOP_EPILOG: + case UWOP_SPARE_CODE: case UWOP_PUSH_MACHFRAME: default: printf(" Unrecognized UNWIND_CODE: 0x%04X\n", *(USHORT*)pCode); diff --git a/src/unwinder/CMakeLists.txt b/src/unwinder/CMakeLists.txt index 0ed0160b8a..418920b6e9 100644 --- a/src/unwinder/CMakeLists.txt +++ b/src/unwinder/CMakeLists.txt @@ -10,13 +10,21 @@ include_directories(${CLR_DIR}/src/debug/daccess) if(IS_64BIT_BUILD EQUAL 1) include_directories(amd64) +if(CLR_CMAKE_PLATFORM_UNIX) + add_compile_options(-fPIC) +endif(CLR_CMAKE_PLATFORM_UNIX) + set(UNWINDER_SOURCES unwinder.cpp amd64/unwinder_amd64.cpp ) convert_to_absolute_path(UNWINDER_SOURCES ${UNWINDER_SOURCES}) - + add_subdirectory(dac) +if(CLR_CMAKE_PLATFORM_UNIX) + add_subdirectory(wks) +endif(CLR_CMAKE_PLATFORM_UNIX) + endif(IS_64BIT_BUILD EQUAL 1) diff --git a/src/unwinder/amd64/unwinder_amd64.cpp b/src/unwinder/amd64/unwinder_amd64.cpp index de7f94f977..e22bc91be2 100644 --- a/src/unwinder/amd64/unwinder_amd64.cpp +++ b/src/unwinder/amd64/unwinder_amd64.cpp @@ -8,6 +8,138 @@ #include "stdafx.h" #include "unwinder_amd64.h" +typedef DPTR(M128A) PTR_M128A; + +//--------------------------------------------------------------------------------------- +// +// Read 64 bit unsigned value from the specified address. When the unwinder is built +// for jitted code unwinding on non-Windows systems, this is just a plain memory read. +// When the unwinder is built for DAC though, this reads data from the target debugged +// process. +// +// Arguments: +// addr - address to read from +// +// Return Value: +// The value that was read +// +// Notes: +// If the memory read fails in the DAC mode, the failure is reported as an exception +// via the DacError function. +// +static ULONG64 MemoryRead64(PULONG64 addr) +{ + return *dac_cast<PTR_ULONG64>((TADDR)addr); +} + +//--------------------------------------------------------------------------------------- +// +// Read 128 bit value from the specified address. When the unwinder is built +// for jitted code unwinding on non-Windows systems, this is just a plain memory read. +// When the unwinder is built for DAC though, this reads data from the target debugged +// process. +// +// Arguments: +// addr - address to read from +// +// Return Value: +// The value that was read +// +// Notes: +// If the memory read fails in the DAC mode, the failure is reported as an exception +// via the DacError function. +// +static M128A MemoryRead128(PM128A addr) +{ + return *dac_cast<PTR_M128A>((TADDR)addr); +} + +#ifdef DACCESS_COMPILE + +// Report failure in the unwinder if the condition is FALSE +#define UNWINDER_ASSERT(Condition) if (!(Condition)) DacError(CORDBG_E_TARGET_INCONSISTENT) + +//--------------------------------------------------------------------------------------- +// +// The InstructionBuffer class abstracts accessing assembler instructions in the function +// being unwound. It behaves as a memory byte pointer, but it reads the instruction codes +// from the target process being debugged and removes all changes that the debugger +// may have made to the code, e.g. breakpoint instructions. +// +class InstructionBuffer +{ + UINT m_offset; + SIZE_T m_address; + UCHAR m_buffer[32]; + + // Load the instructions from the target process being debugged + HRESULT Load() + { + HRESULT hr = DacReadAll(TO_TADDR(m_address), m_buffer, sizeof(m_buffer), false); + if (SUCCEEDED(hr)) + { + // 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. + MemoryRange range(dac_cast<PTR_VOID>((TADDR)m_address), sizeof(m_buffer)); + hr = DacReplacePatchesInHostMemory(range, m_buffer); + } + + return hr; + } + +public: + + // Construct the InstructionBuffer for the given address in the target process + InstructionBuffer(SIZE_T address) + : m_address(address), + m_offset(0) + { + HRESULT hr = Load(); + if (FAILED(hr)) + { + // If we have failed to read from the target process, just pretend + // we've read zeros. + // The InstructionBuffer is used in code driven epilogue unwinding + // when we read processor instructions and simulate them. + // 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 instead of failing + // the unwind. + memset(m_buffer, 0, sizeof(m_buffer)); + } + } + + // Move to the next byte in the buffer + InstructionBuffer& operator++() + { + m_offset++; + return *this; + } + + // Skip delta bytes in the buffer + InstructionBuffer& operator+=(INT delta) + { + m_offset += delta; + return *this; + } + + // Return address of the current byte in the buffer + explicit operator ULONG64() + { + return m_address + m_offset; + } + + // Get the byte at the given index from the current position + // Invoke DacError if the index is out of the buffer + UCHAR operator[](int index) + { + int realIndex = m_offset + index; + UNWINDER_ASSERT(realIndex < sizeof(m_buffer)); + return m_buffer[realIndex]; + } +}; + //--------------------------------------------------------------------------------------- // // Given the target address of an UNWIND_INFO structure, this function retrieves all the memory used for @@ -25,12 +157,11 @@ // 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); + 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) @@ -47,6 +178,18 @@ UNWIND_INFO * DacGetUnwindInfo(TADDR taUnwindInfo) //--------------------------------------------------------------------------------------- // +// This function just wraps the DacGetUnwindInfo. +// The DacGetUnwindInfo is called from other places outside of the unwinder, so it +// cannot be merged into the body of this method. +// +UNWIND_INFO * OOPStackUnwinderAMD64::GetUnwindInfo(TADDR taUnwindInfo) +{ + return DacGetUnwindInfo(taUnwindInfo); +} + + +//--------------------------------------------------------------------------------------- +// // This function is just a wrapper over OOPStackUnwinder. The runtime can call this function to // virtually unwind a CONTEXT out-of-process. // @@ -63,8 +206,7 @@ UNWIND_INFO * DacGetUnwindInfo(TADDR taUnwindInfo) BOOL DacUnwindStackFrame(CONTEXT * pContext, KNONVOLATILE_CONTEXT_POINTERS* pContextPointers) { - OOPStackUnwinderAMD64 unwinder; - BOOL res = unwinder.Unwind(pContext); + BOOL res = OOPStackUnwinderAMD64::Unwind(pContext); if (res && pContextPointers) { @@ -112,17 +254,111 @@ BOOL OOPStackUnwinderAMD64::Unwind(CONTEXT * pContext) // call VirtualUnwind() to do the real work ULONG64 EstablisherFrame; - hr = VirtualUnwind(uImageBase, uControlPC, &functionEntry, pContext, &EstablisherFrame); + hr = VirtualUnwind(0, uImageBase, uControlPC, &functionEntry, pContext, NULL, &EstablisherFrame, NULL, NULL); return (hr == S_OK); } +#else // DACCESS_COMPILE + +// Report failure in the unwinder if the condition is FALSE +#define UNWINDER_ASSERT _ASSERTE + +// For unwinding of the jitted code on non-Windows platforms, the Instruction buffer is +// just a plain pointer to the instruction data. +typedef UCHAR * InstructionBuffer; + +//--------------------------------------------------------------------------------------- +// +// Return UNWIND_INFO pointer for the given address. +// +UNWIND_INFO * OOPStackUnwinderAMD64::GetUnwindInfo(TADDR taUnwindInfo) +{ + return (UNWIND_INFO *)taUnwindInfo; +} + +//--------------------------------------------------------------------------------------- +// +// This function behaves like the RtlVirtualUnwind in Windows. +// It 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: +// +// HandlerType - Supplies the handler type expected for the virtual unwind. +// This may be either an exception or an unwind handler. A flag may +// optionally be supplied to avoid epilogue detection if it is known +// the specified control PC is not located inside a function epilogue. +// +// 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. +// +// HandlerData - Supplies a pointer to a variable that receives a pointer +// the the language handler data. +// +// EstablisherFrame - Supplies a pointer to a variable that receives the +// the establisher frame pointer value. +// +// ContextPointers - Supplies an optional pointer to a context pointers +// record. +// +// Return value: +// +// The handler routine address. If control did not leave the specified +// function in either the prologue or an epilogue 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. +// +PEXCEPTION_ROUTINE RtlVirtualUnwind_Unsafe( + __in ULONG HandlerType, + __in ULONG64 ImageBase, + __in ULONG64 ControlPc, + __in PRUNTIME_FUNCTION FunctionEntry, + __in OUT PCONTEXT ContextRecord, + __out PVOID *HandlerData, + __out PULONG64 EstablisherFrame, + __inout_opt PKNONVOLATILE_CONTEXT_POINTERS ContextPointers + ) +{ + PEXCEPTION_ROUTINE handlerRoutine; + + HRESULT res = OOPStackUnwinderAMD64::VirtualUnwind( + HandlerType, + ImageBase, + ControlPc, + (_PIMAGE_RUNTIME_FUNCTION_ENTRY)FunctionEntry, + ContextRecord, + HandlerData, + EstablisherFrame, + ContextPointers, + &handlerRoutine); + + _ASSERTE(SUCCEEDED(res)); + + return handlerRoutine; +} + + +#endif // DACCESS_COMPILE // // // <NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE> // -// Everything below is borrowed from dbghelp.dll +// Everything below is borrowed from minkernel\ntos\rtl\amd64\exdsptch.c file from Windows // // <NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE> // @@ -134,31 +370,11 @@ BOOL OOPStackUnwinderAMD64::Unwind(CONTEXT * pContext) // Copied OS code. // // This must be kept in sync with the system unwinder. -// base\ntos\rtl\amd64\exdsptch.c +// minkernel\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 ****** // @@ -169,6 +385,7 @@ OOPStackUnwinderAMD64::s_UnwindOpSlotTable[] = #define JMP_IMM32_OP 0xe9 #define JMP_IND_OP 0xff #define LEA_OP 0x8d +#define REPNE_PREFIX 0xf2 #define REP_PREFIX 0xf3 #define POP_OP 0x58 #define RET_OP 0xc3 @@ -176,408 +393,612 @@ OOPStackUnwinderAMD64::s_UnwindOpSlotTable[] = #define IS_REX_PREFIX(x) (((x) & 0xf0) == 0x40) +#define UNWIND_CHAIN_LIMIT 32 + HRESULT -OOPStackUnwinderAMD64::UnwindPrologue( +OOPStackUnwinderAMD64::UnwindEpilogue( __in ULONG64 ImageBase, __in ULONG64 ControlPc, - __in ULONG64 FrameBase, + __in ULONG EpilogueOffset, __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __inout PCONTEXT ContextRecord - ) + __inout PCONTEXT ContextRecord, + __inout_opt PKNONVOLATILE_CONTEXT_POINTERS ContextPointers +) /*++ 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. + This function emulates the state change associated with a function + epilogue by using the corresponding prologue unwind codes of the + primary function entry corresponding to the specified function. + + The prologue unwind codes can be used to reverse the epilogue since + the epilogue operations are structured as a mirror-image of the initial + prologue instructions prior to the establishment of the frame. 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. + ControlPc - Supplies the address where control left the specified function. - FrameBase - Supplies the base of the stack frame subject function stack - frame. + EpilogueOffset - Supplies the offset within an epilogue of the specified + instruction pointer address. - FunctionEntry - Supplies the address of the function table entry for the - specified function. + FunctionEntry - Supplies a pointer to the function table entry for the + specified function. If appropriate, this has already been probed. - ContextRecord - Supplies the address of a context record. + ContextRecord - Supplies a pointer to a context record. + + ContextPointers - Supplies an optional pointer to a context pointers record. + + +Return Value: + +HRESULT. --*/ { - HRESULT Status = E_UNEXPECTED; - ULONG64 FloatingAddress; - M128A* FloatingRegister; - ULONG FrameOffset; + ULONG ChainCount; + ULONG CountOfCodes; + ULONG CurrentOffset; + ULONG FirstPushIndex; ULONG Index; - ULONG64 IntegerAddress; + PULONG64 IntegerAddress; PULONG64 IntegerRegister; - BOOLEAN MachineFrame; ULONG OpInfo; - ULONG PrologOffset; - ULONG64 ReturnAddress; - ULONG64 StackAddress; + PULONG64 ReturnAddress; + PULONG64 StackAddress; + NTSTATUS Status; PUNWIND_INFO UnwindInfo; - ULONG UnwindOp; + UNWIND_CODE UnwindOp; // - // Process the unwind codes. + // A canonical epilogue sequence consists of the following operations: + // + // 1. Optional cleanup of fixed and dynamic stack allocations, which is + // considered to be outside of the epilogue region. + // + // add rsp, imm + // or + // lea rsp, disp[fp] + // + // 2. Zero or more pop nonvolatile-integer-register[0..15] instructions, + // which are unwound using the corresponding UWOP_PUSH_NONVOL opcodes. + // + // pop r64 + // or + // REX.R pop r64 + // + // 3. An optional one-byte pop r64 to a volatile register to clean up an + // RFLAGS register pushed with pushfq. This is marked with a + // UWOP_ALLOC_SMALL 8 opcode. + // + // pop rcx + // + // 4. A control transfer instruction (ret or jump). In both cases, there + // will be no prologue unwind codes remaining after the previous set of + // recognized operations are emulated. + // + // ret 0 + // or + // jmp imm + // or + // jmp [target] + // or + // iretq + // + // N.B. The correctness of these assumptions is based on the ordering + // of unwind codes and the mirroring of epilogue and prologue + // regions. + // + // Find the function's primary entry, which contains the relevant frame + // adjustment unwind codes. + // + // Locate the first push unwind code. This code requires that all pushes + // occur within a single function entry, though not necessarily within the + // root function entry of a chained function. // - FloatingRegister = &ContextRecord->Xmm0; - IntegerRegister = &ContextRecord->Rax; - Index = 0; - MachineFrame = FALSE; - PrologOffset = (ULONG)(ControlPc - (FunctionEntry->BeginAddress + ImageBase)); + ChainCount = 0; + for (;;) { + UnwindInfo = GetUnwindInfo(FunctionEntry->UnwindInfoAddress + ImageBase); + if (UnwindInfo == NULL) + { + return HRESULT_FROM_WIN32(ERROR_READ_FAULT); + } + CountOfCodes = UnwindInfo->CountOfUnwindCodes; + FirstPushIndex = 0; + while (FirstPushIndex < CountOfCodes) { + UnwindOp = UnwindInfo->UnwindCode[FirstPushIndex]; + if ((UnwindOp.UnwindOp == UWOP_PUSH_NONVOL) || + (UnwindOp.UnwindOp == UWOP_PUSH_MACHFRAME)) { - UnwindInfo = DacGetUnwindInfo(ImageBase + FunctionEntry->UnwindInfoAddress); - if (UnwindInfo == NULL) - { - return HRESULT_FROM_WIN32(ERROR_READ_FAULT); - } + break; + } + + FirstPushIndex += UnwindOpSlots(UnwindOp); + } - while (Index < UnwindInfo->CountOfUnwindCodes) { + if (FirstPushIndex < CountOfCodes) { + break; + } // - // If the prologue offset is greater than the next unwind code offset, - // then simulate the effect of the unwind code. + // If a chained parent function entry exists, continue looking for + // push opcodes in the parent. // - UnwindOp = UnwindInfo->UnwindCode[Index].UnwindOp; - if (UnwindOp > UWOP_PUSH_MACHFRAME) { - goto Fail; + if ((UnwindInfo->Flags & UNW_FLAG_CHAININFO) == 0) { + break; } - - 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. - // + ChainCount += 1; + if (ChainCount > UNWIND_CHAIN_LIMIT) { + return E_FAIL; + } - case UWOP_PUSH_NONVOL: - IntegerAddress = ContextRecord->Rsp; - if ((Status = - ReadAllMemory(IntegerAddress, - &IntegerRegister[OpInfo], - sizeof(ULONG64))) != S_OK) { - goto Fail; - } + Index = CountOfCodes; + if (Index % 2 != 0) { + Index += 1; + } - ContextRecord->Rsp += 8; - break; + FunctionEntry = (_PIMAGE_RUNTIME_FUNCTION_ENTRY)&UnwindInfo->UnwindCode[Index]; + } - // - // Allocate a large sized area on the stack. - // - // The operation information determines if the size is - // 16- or 32-bits. - // + // + // Unwind any push codes that have not already been reversed by the + // epilogue. + // - 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; - } + CurrentOffset = 0; + IntegerRegister = &ContextRecord->Rax; + for (Index = FirstPushIndex; Index < CountOfCodes; Index += 1) { + UnwindOp = UnwindInfo->UnwindCode[Index]; + OpInfo = UnwindOp.OpInfo; - ContextRecord->Rsp += FrameOffset; - break; + if (UnwindOp.UnwindOp != UWOP_PUSH_NONVOL) { + 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. - // + if (CurrentOffset >= EpilogueOffset) { + IntegerAddress = (PULONG64)(ContextRecord->Rsp); - case UWOP_ALLOC_SMALL: - ContextRecord->Rsp += (OpInfo * 8) + 8; - break; + ContextRecord->Rsp += 8; + IntegerRegister[OpInfo] = MemoryRead64(IntegerAddress); + if (ARGUMENT_PRESENT(ContextPointers)) { + ContextPointers->IntegerContext[OpInfo] = IntegerAddress; + } + } - // - // Establish the the frame pointer register. - // - // The operation information is not used. - // + // + // POP r64 is encoded as (58h + r64) for the lower 8 general-purpose + // registers and REX.R, (58h + r64) for r8 - r15. + // - case UWOP_SET_FPREG: - ContextRecord->Rsp = IntegerRegister[UnwindInfo->FrameRegister]; - ContextRecord->Rsp -= UnwindInfo->FrameOffset * 16; - break; + CurrentOffset += 1; + if (OpInfo >= 8) { + CurrentOffset += 1; + } + } - // - // Save nonvolatile integer register on the stack using a - // 16-bit displacment. - // - // The operation information is the register number. - // + // + // Check for an UWOP_ALLOC_SMALL 8 directive, which corresponds to a push + // of the FLAGS register. + // - 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; + if ((Index < CountOfCodes) && + (UnwindOp.UnwindOp == UWOP_ALLOC_SMALL) && (OpInfo == 0)) { - // - // Save nonvolatile integer register on the stack using a - // 32-bit displacment. - // - // The operation information is the register number. - // + if (CurrentOffset >= EpilogueOffset) { + ContextRecord->Rsp += 8; + } - 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; + CurrentOffset += 1; + Index += 1; + } - // - // Save a nonvolatile XMM(64) register on the stack using a - // 16-bit displacement. - // - // The operation information is the register number. - // + // + // Check for a machine frame. + // - 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; + if (Index < CountOfCodes) { + UnwindOp = UnwindInfo->UnwindCode[Index]; + if (UnwindOp.UnwindOp == UWOP_PUSH_MACHFRAME) { + ReturnAddress = (PULONG64)(ContextRecord->Rsp); + StackAddress = (PULONG64)(ContextRecord->Rsp + (3 * 8)); - // - // Save a nonvolatile XMM(64) register on the stack using a - // 32-bit displacement. - // - // The operation information is the register number. - // + ContextRecord->Rip = MemoryRead64(ReturnAddress); + ContextRecord->Rsp = MemoryRead64(StackAddress); + return S_OK; + } - 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; + // + // Any remaining operation must be a machine frame. + // - // - // Save a nonvolatile XMM(128) register on the stack using a - // 16-bit displacement. - // - // The operation information is the register number. - // + UNWINDER_ASSERT(FALSE); + } - 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; + // + // Emulate a return operation. + // - // - // Save a nonvolatile XMM(128) register on the stack using a - // 32-bit displacement. - // - // The operation information is the register number. - // + IntegerAddress = (PULONG64)(ContextRecord->Rsp); - 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; + ContextRecord->Rip = MemoryRead64(IntegerAddress); + ContextRecord->Rsp += 8; + return S_OK; +} - // - // Push a machine frame on the stack. - // - // The operation information determines whether the machine - // frame contains an error code or not. - // +HRESULT +OOPStackUnwinderAMD64::UnwindPrologue( + __in ULONG64 ImageBase, + __in ULONG64 ControlPc, + __in ULONG64 FrameBase, + __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __inout PCONTEXT ContextRecord, + __inout_opt PKNONVOLATILE_CONTEXT_POINTERS ContextPointers, + __deref_out _PIMAGE_RUNTIME_FUNCTION_ENTRY *FinalFunctionEntry + ) - 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; +Routine Description: - // - // Unused codes. - // + 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. - default: - break; - } +Arguments: - Index += 1; - - } else { + ImageBase - Supplies the base address of the image that contains the + function being unwound. - // - // Skip this unwind operation by advancing the slot index by the - // number of slots consumed by this operation. - // + ControlPc - Supplies the address where control left the specified + function. - Index += s_UnwindOpSlotTable[UnwindOp]; + FrameBase - Supplies the base of the stack frame subject function stack + frame. - // - // Special case any unwind operations that can consume a variable - // number of slots. - // + FunctionEntry - Supplies the address of the function table entry for the + specified function. - switch (UnwindOp) { + ContextRecord - Supplies the address of a context record. - // - // A non-zero operation information indicates that an - // additional slot is consumed. - // + ContextPointers - Supplies an optional pointer to a context pointers + record. - case UWOP_ALLOC_LARGE: - if (OpInfo != 0) { - Index += 1; - } + FinalFunctionEntry - Supplies a pointer to a variable that receives the + final function entry after the specified function entry and all + descendent chained entries have been unwound. This will have been + probed as appropriate. - break; +Return Value: - // - // No other special cases. - // + HRESULT. - default: - break; - } - } - } +--*/ + +{ + + ULONG ChainCount; + PM128A FloatingAddress; + PM128A FloatingRegister; + ULONG FrameOffset; + ULONG Index; + PULONG64 IntegerAddress; + PULONG64 IntegerRegister; + BOOLEAN MachineFrame; + ULONG OpInfo; + ULONG PrologOffset; + PULONG64 ReturnAddress; + PULONG64 StackAddress; + PUNWIND_INFO UnwindInfo; + ULONG UnwindOp; // - // 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. + // Process the unwind codes for the specified function entry and all its + // descendent chained function entries. // - if ((UnwindInfo->Flags & UNW_FLAG_CHAININFO) != 0) { - - _PIMAGE_RUNTIME_FUNCTION_ENTRY ChainEntry; - - Index = UnwindInfo->CountOfUnwindCodes; - if ((Index & 1) != 0) { - Index += 1; + ChainCount = 0; + FloatingRegister = &ContextRecord->Xmm0; + IntegerRegister = &ContextRecord->Rax; + do { + Index = 0; + MachineFrame = FALSE; + PrologOffset = (ULONG)(ControlPc - (FunctionEntry->BeginAddress + ImageBase)); + + UnwindInfo = GetUnwindInfo(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) { + return E_UNEXPECTED; + } + + 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 = (PULONG64)ContextRecord->Rsp; + IntegerRegister[OpInfo] = MemoryRead64(IntegerAddress); + + if (ARGUMENT_PRESENT(ContextPointers)) { + ContextPointers->IntegerContext[OpInfo] = IntegerAddress; + } + + 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 = (PULONG64)(FrameBase + FrameOffset); + IntegerRegister[OpInfo] = MemoryRead64(IntegerAddress); + + if (ARGUMENT_PRESENT(ContextPointers)) { + ContextPointers->IntegerContext[OpInfo] = IntegerAddress; + } + + 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 = (PULONG64)(FrameBase + FrameOffset); + IntegerRegister[OpInfo] = MemoryRead64(IntegerAddress); + + if (ARGUMENT_PRESENT(ContextPointers)) { + ContextPointers->IntegerContext[OpInfo] = IntegerAddress; + } + + break; + + // + // Function epilog marker (ignored for prologue unwind). + // - // 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); + case UWOP_EPILOG: + Index += 1; + break; + + // + // Spare unused codes. + // + + + case UWOP_SPARE_CODE: + + UNWINDER_ASSERT(FALSE); + + Index += 2; + 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 = (PM128A)(FrameBase + FrameOffset); + FloatingRegister[OpInfo] = MemoryRead128(FloatingAddress); + + if (ARGUMENT_PRESENT(ContextPointers)) { + ContextPointers->FloatingContext[OpInfo] = FloatingAddress; + } + + 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 = (PM128A)(FrameBase + FrameOffset); + FloatingRegister[OpInfo] = MemoryRead128(FloatingAddress); + + if (ARGUMENT_PRESENT(ContextPointers)) { + ContextPointers->FloatingContext[OpInfo] = FloatingAddress; + } + + 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 = (PULONG64)ContextRecord->Rsp; + StackAddress = (PULONG64)(ContextRecord->Rsp + (3 * 8)); + if (OpInfo != 0) { + ReturnAddress += 1; + StackAddress += 1; + } + + ContextRecord->Rip = MemoryRead64(ReturnAddress); + ContextRecord->Rsp = MemoryRead64(StackAddress); + + break; + + // + // Unused codes. + // + + default: + //RtlRaiseStatus(STATUS_BAD_FUNCTION_TABLE); + break; + } + + Index += 1; + + } else { + + // + // Skip this unwind operation by advancing the slot index + // by the number of slots consumed by this operation. + // + + Index += UnwindOpSlots(UnwindInfo->UnwindCode[Index]); + } + } + + // + // If chained unwind information is specified, then set the function + // entry address to the chained function entry and continue the scan. + // Otherwise, determine the return address if a machine frame was not + // encountered during the scan of the unwind codes and terminate the + // scan. + // + + 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. + FunctionEntry = (_PIMAGE_RUNTIME_FUNCTION_ENTRY) + &UnwindInfo->UnwindCode[Index]; + } else { + + if (MachineFrame == FALSE) { + ContextRecord->Rip = MemoryRead64((PULONG64)ContextRecord->Rsp); + ContextRecord->Rsp += 8; + } + + break; + } - return Status; + // + // Limit the number of iterations possible for chained function table + // entries. + // - } else { + ChainCount += 1; + UNWINDER_ASSERT(ChainCount <= UNWIND_CHAIN_LIMIT); - if (MachineFrame == FALSE) { - if ((Status = - ReadAllMemory(ContextRecord->Rsp, - &ContextRecord->Rip, - sizeof(ULONG64))) != S_OK) { - return Status; - } - ContextRecord->Rsp += 8; - } - - return S_OK; - } + } while (TRUE); - Fail: - return Status; + *FinalFunctionEntry = FunctionEntry; + return S_OK; } HRESULT OOPStackUnwinderAMD64::VirtualUnwind( + __in DWORD HandlerType, __in ULONG64 ImageBase, __in ULONG64 ControlPc, __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, __inout PCONTEXT ContextRecord, - __out PULONG64 EstablisherFrame + __out PVOID *HandlerData, + __out PULONG64 EstablisherFrame, + __inout_opt PKNONVOLATILE_CONTEXT_POINTERS ContextPointers, + __deref_opt_out_opt PEXCEPTION_ROUTINE *HandlerRoutine ) /*++ @@ -593,6 +1014,11 @@ Routine Description: Arguments: + HandlerType - Supplies the handler type expected for the virtual unwind. + This may be either an exception or an unwind handler. A flag may + optionally be supplied to avoid epilogue detection if it is known + the specified control PC is not located inside a function epilogue. + ImageBase - Supplies the base address of the image that contains the function being unwound. @@ -604,29 +1030,53 @@ Arguments: ContextRecord - Supplies the address of a context record. + + HandlerData - Supplies a pointer to a variable that receives a pointer + the the language handler data. + EstablisherFrame - Supplies a pointer to a variable that receives the the establisher frame pointer value. + ContextPointers - Supplies an optional pointer to a context pointers + record. + + 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 prologue or an epilogue 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. --*/ { - HRESULT Status; ULONG64 BranchTarget; LONG Displacement; - ULONG FrameRegister = 0; + ULONG EpilogueOffset; + ULONG EpilogueSize; + PEXCEPTION_ROUTINE FoundHandler; + ULONG FrameRegister; ULONG Index; BOOL InEpilogue; + PULONG64 IntegerAddress; PULONG64 IntegerRegister; - PUCHAR NextByte; _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryFunctionEntry; ULONG PrologOffset; ULONG RegisterNumber; + ULONG RelativePc; + HRESULT Status; PUNWIND_INFO UnwindInfo; - UCHAR InstrBuffer[32]; - ULONG InstrBytes; - ULONG Bytes; - ULONG UnwindFrameReg; + ULONG UnwindVersion; + UNWIND_CODE UnwindOp; + + FoundHandler = NULL; + UnwindInfo = GetUnwindInfo(ImageBase + FunctionEntry->UnwindInfoAddress); + if (UnwindInfo == NULL) + { + return HRESULT_FROM_WIN32(ERROR_READ_FAULT); + } + + UnwindVersion = UnwindInfo->Version; // // If the specified function does not use a frame pointer, then the @@ -646,7 +1096,7 @@ Arguments: // // 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 + // code must be looked up in the unwind codes to determine 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 @@ -659,42 +1109,29 @@ Arguments: // 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) { + if (UnwindInfo->FrameRegister == 0) { *EstablisherFrame = ContextRecord->Rsp; } else if ((PrologOffset >= UnwindInfo->SizeOfProlog) || ((UnwindInfo->Flags & UNW_FLAG_CHAININFO) != 0)) { - *EstablisherFrame = (&ContextRecord->Rax)[UnwindFrameReg]; + + *EstablisherFrame = (&ContextRecord->Rax)[UnwindInfo->FrameRegister]; *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) { + UnwindOp = UnwindInfo->UnwindCode[Index]; + if (UnwindOp.UnwindOp == UWOP_SET_FPREG) { break; } - Index += 1; + Index += UnwindOpSlots(UnwindOp); } if (PrologOffset >= UnwindInfo->UnwindCode[Index].CodeOffset) { - *EstablisherFrame = (&ContextRecord->Rax)[UnwindFrameReg]; + *EstablisherFrame = (&ContextRecord->Rax)[UnwindInfo->FrameRegister]; *EstablisherFrame -= UnwindInfo->FrameOffset * 16; } else { @@ -702,358 +1139,419 @@ Arguments: } } - 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 + // Check if control left the specified function during an epilogue + // sequence and emulate the execution of the epilogue forward and // return no exception handler. // - - IntegerRegister = &ContextRecord->Rax; - NextByte = InstrBuffer; - Bytes = InstrBytes; - - // - // Check for one of: + // If the unwind version indicates the absence of epilogue unwind codes + // this is done by emulating the instruction stream. Otherwise, epilogue + // detection and emulation is performed using the function unwind codes. // - // 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)) { - + + InEpilogue = FALSE; + if (UnwindVersion < 2) { + InstructionBuffer InstrBuffer = (InstructionBuffer)ControlPc; + InstructionBuffer NextByte = InstrBuffer; + // - // add rsp, imm8. + // Check for one of: // - - NextByte += 4; - Bytes -= 4; - - } else if (Bytes >= 7 && - (NextByte[0] == SIZE64_PREFIX) && - (NextByte[1] == ADD_IMM32_OP) && - (NextByte[2] == 0xc4)) { + // add rsp, imm8 + // or + // add rsp, imm32 + // or + // lea rsp, -disp8[fp] + // or + // lea rsp, -disp32[fp] + // + + if ((NextByte[0] == SIZE64_PREFIX) && + (NextByte[1] == ADD_IMM8_OP) && + (NextByte[2] == 0xc4)) { + + // + // add rsp, imm8. + // + + NextByte += 4; + + } else if ((NextByte[0] == SIZE64_PREFIX) && + (NextByte[1] == ADD_IMM32_OP) && + (NextByte[2] == 0xc4)) { + + // + // add rsp, imm32. + // + + NextByte += 7; + + } else if (((NextByte[0] & 0xfe) == SIZE64_PREFIX) && + (NextByte[1] == LEA_OP)) { + + FrameRegister = ((NextByte[0] & 0x1) << 3) | (NextByte[2] & 0x7); + if ((FrameRegister != 0) && + (FrameRegister == UnwindInfo->FrameRegister)) { + + if ((NextByte[2] & 0xf8) == 0x60) { + + // + // lea rsp, disp8[fp]. + // + + NextByte += 4; + + } else if ((NextByte[2] &0xf8) == 0xa0) { + + // + // lea rsp, disp32[fp]. + // + + NextByte += 7; + } + } + } // - // add rsp, imm32. + // Check for any number of: + // + // pop nonvolatile-integer-register[0..15]. // - 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; + while (TRUE) { + if ((NextByte[0] & 0xf8) == POP_OP) { + NextByte += 1; - } else if (Bytes >= 7 && - (NextByte[2] &0xf8) == 0xa0) { + } else if (IS_REX_PREFIX(NextByte[0]) && + ((NextByte[1] & 0xf8) == POP_OP)) { - // - // lea rsp, disp32[fp]. - // + NextByte += 2; - NextByte += 7; - Bytes -= 7; + } else { + break; } } - } - - // - // 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)) { - + // A REPNE prefix may optionally precede a control transfer + // instruction with no effect on unwinding. // - // 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]); + + if (NextByte[0] == REPNE_PREFIX) { + 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 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. // - if (BranchTarget < FunctionEntry->BeginAddress || - BranchTarget >= FunctionEntry->EndAddress) { - - _IMAGE_RUNTIME_FUNCTION_ENTRY PrimaryEntryBuffer; - + InEpilogue = FALSE; + if ( ((NextByte[0] == RET_OP) || + (NextByte[0] == RET_OP_2)) || + (((NextByte[0] == REP_PREFIX) && (NextByte[1] == RET_OP)))) { + // - // 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. + // A return is an unambiguous indication of an epilogue. // - // If not, then the branch target really is outside of - // this function. + + InEpilogue = TRUE; + + } else if ((NextByte[0] == JMP_IMM8_OP) || + (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 - ImageBase; + if (NextByte[0] == JMP_IMM8_OP) { + BranchTarget += 2 + (CHAR)NextByte[1]; + + } else { + LONG32 delta = NextByte[1] | (NextByte[2] << 8) | + (NextByte[3] << 16) | (NextByte[4] << 24); + BranchTarget += 5 + delta; - PrimaryFunctionEntry = - SameFunction(FunctionEntry, - ImageBase, - BranchTarget + ImageBase, - &PrimaryEntryBuffer); - - if ((PrimaryFunctionEntry == NULL) || - (BranchTarget == PrimaryFunctionEntry->BeginAddress)) { - + } + + // + // 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) { + + // + // 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); + + if ((PrimaryFunctionEntry == NULL) || + (BranchTarget == PrimaryFunctionEntry->BeginAddress)) { + + InEpilogue = TRUE; + } + + } else if ((BranchTarget == FunctionEntry->BeginAddress) && + ((UnwindInfo->Flags & UNW_FLAG_CHAININFO) == 0)) { + InEpilogue = TRUE; } - - } else if ((BranchTarget == FunctionEntry->BeginAddress) && - ((UnwindInfo->Flags & UNW_FLAG_CHAININFO) == 0)) { - + + } else if ((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 (((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; } - } 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; + if (InEpilogue != FALSE) { + IntegerRegister = &ContextRecord->Rax; + NextByte = InstrBuffer; - } else if (Bytes >= 3 && - ((NextByte[0] & 0xf8) == SIZE64_PREFIX) && - (NextByte[1] == 0xff) && - (NextByte[2] & 0x38) == 0x20) { + // + // Emulate one of (if any): + // + // add rsp, imm8 + // or + // add rsp, imm32 + // or + // lea rsp, disp8[frame-register] + // or + // lea rsp, disp32[frame-register] + // - // - // 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. - // + if ((NextByte[0] & 0xf8) == SIZE64_PREFIX) { - InEpilogue = TRUE; - } - - if (InEpilogue != FALSE) { - NextByte = InstrBuffer; - Bytes = InstrBytes; + if (NextByte[1] == ADD_IMM8_OP) { - // - // 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]. + // add rsp, imm8. // - - ContextRecord->Rsp = IntegerRegister[FrameRegister]; + ContextRecord->Rsp += (CHAR)NextByte[3]; NextByte += 4; - Bytes -= 4; - - } else if (Bytes >= 7 && - (NextByte[2] & 0xf8) == 0xa0) { - + + } + else if (NextByte[1] == ADD_IMM32_OP) { + // - // lea rsp, disp32[frame-register]. + // add rsp, imm32. // - + Displacement = NextByte[3] | (NextByte[4] << 8); Displacement |= (NextByte[5] << 16) | (NextByte[6] << 24); - ContextRecord->Rsp = IntegerRegister[FrameRegister]; ContextRecord->Rsp += Displacement; NextByte += 7; - Bytes -= 7; + + } + else if (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; + + } + else if ((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; + } } } - } - - // - // 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; + + // + // Emulate any number of (if any): + // + // pop nonvolatile-integer-register. + // + + while (TRUE) { + if ((NextByte[0] & 0xf8) == POP_OP) { + + // + // pop nonvolatile-integer-register[0..7] + // + + RegisterNumber = NextByte[0] & 0x7; + IntegerAddress = (PULONG64)ContextRecord->Rsp; + IntegerRegister[RegisterNumber] = MemoryRead64(IntegerAddress); + + if (ARGUMENT_PRESENT(ContextPointers)) { + ContextPointers->IntegerContext[RegisterNumber] = IntegerAddress; + } + + ContextRecord->Rsp += 8; + NextByte += 1; + } - 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; + else if (IS_REX_PREFIX(NextByte[0]) && + (NextByte[1] & 0xf8) == POP_OP) { + + // + // pop nonvolatile-integer-register[8..15] + // + + RegisterNumber = ((NextByte[0] & 1) << 3) | (NextByte[1] & 0x7); + IntegerAddress = (PULONG64)ContextRecord->Rsp; + IntegerRegister[RegisterNumber] = MemoryRead64(IntegerAddress); + + if (ARGUMENT_PRESENT(ContextPointers)) { + ContextPointers->IntegerContext[RegisterNumber] = IntegerAddress; + } + + ContextRecord->Rsp += 8; + NextByte += 2; + + } + else { + break; } - 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. + // + + ContextRecord->Rip = MemoryRead64((PULONG64)ContextRecord->Rsp); + ContextRecord->Rsp += 8; + goto ExitSetHandler; } + } else if (UnwindInfo->CountOfUnwindCodes != 0) { + + UNWINDER_ASSERT(UnwindVersion >= 2); + // - // Emulate return and return null exception handler. - // - // Note: this instruction might in fact be a jmp, however - // we want to emulate a return regardless. + // Capture the first unwind code and check if it is an epilogue code. + // If it is not an epilogue code, the current function entry does not + // contain any epilogues (it could represent a body region of a + // separated function or it could represent a function which never + // returns). // - - if ((Status = - ReadAllMemory(ContextRecord->Rsp, - &ContextRecord->Rip, - sizeof(ULONG64))) != S_OK) { - return Status; + + UnwindOp = UnwindInfo->UnwindCode[0]; + if (UnwindOp.UnwindOp == UWOP_EPILOG) { + EpilogueSize = UnwindOp.CodeOffset; + + UNWINDER_ASSERT(EpilogueSize != 0); + // + // If the low bit of the OpInfo field of the first epilogue code + // is set, the function has a single epilogue at the end of the + // function. Otherwise, subsequent epilogue unwind codes indicate + // the offset of the epilogue(s) from the function end and the + // relative PC must be compared against each epilogue record. + // + // N.B. The relative instruction pointer may not be within the + // bounds of the runtime function entry if control left the + // function in a region described by an indirect function + // entry. Such a region cannot contain any epilogues. + // + + RelativePc = (ULONG)(ControlPc - ImageBase); + if ((UnwindOp.OpInfo & 1) != 0) { + EpilogueOffset = FunctionEntry->EndAddress - EpilogueSize; + if (RelativePc - EpilogueOffset < EpilogueSize) { + InEpilogue = TRUE; + } + } + + if (InEpilogue == FALSE) { + for (Index = 1; Index < UnwindInfo->CountOfUnwindCodes; Index += 1) { + UnwindOp = UnwindInfo->UnwindCode[Index]; + + if (UnwindOp.UnwindOp == UWOP_EPILOG) { + EpilogueOffset = UnwindOp.EpilogueCode.OffsetLow + + UnwindOp.EpilogueCode.OffsetHigh * 256; + + // + // An epilogue offset of 0 indicates that this is + // a padding entry (the number of epilogue codes + // is a multiple of 2). + // + + if (EpilogueOffset == 0) { + break; + } + + EpilogueOffset = FunctionEntry->EndAddress - EpilogueOffset; + if (RelativePc - EpilogueOffset < EpilogueSize) { + + UNWINDER_ASSERT(EpilogueOffset != FunctionEntry->EndAddress); + InEpilogue = TRUE; + break; + } + + } else { + break; + } + } + } + + if (InEpilogue != FALSE) { + Status = UnwindEpilogue(ImageBase, + ControlPc, + RelativePc - EpilogueOffset, + FunctionEntry, + ContextRecord, + ContextPointers); + + goto ExitSetHandler; + } } - ContextRecord->Rsp += 8; - return S_OK; } // @@ -1061,18 +1559,58 @@ Arguments: // subject function and any chained unwind information. // - return UnwindPrologue(ImageBase, - ControlPc, - *EstablisherFrame, - FunctionEntry, - ContextRecord); + Status = UnwindPrologue(ImageBase, + ControlPc, + *EstablisherFrame, + FunctionEntry, + ContextRecord, + ContextPointers, + &FunctionEntry); + + if (Status != S_OK) { + return Status; + } + + // + // If control left the specified function outside of the prologue and + // the function has a handler that matches the specified type, then + // return the address of the language specific exception handler. + // Otherwise, return NULL. + // + + if (HandlerType != 0) { + PrologOffset = (ULONG)(ControlPc - (FunctionEntry->BeginAddress + ImageBase)); + UnwindInfo = GetUnwindInfo(FunctionEntry->UnwindInfoAddress + ImageBase); + if (UnwindInfo == NULL) + { + return HRESULT_FROM_WIN32(ERROR_READ_FAULT); + } + if ((PrologOffset >= UnwindInfo->SizeOfProlog) && + ((UnwindInfo->Flags & HandlerType) != 0)) { + + Index = UnwindInfo->CountOfUnwindCodes; + if ((Index & 1) != 0) { + Index += 1; + } + + *HandlerData = &UnwindInfo->UnwindCode[Index + 2]; + FoundHandler = (PEXCEPTION_ROUTINE)(*((PULONG)&UnwindInfo->UnwindCode[Index]) + ImageBase); + } + } + +ExitSetHandler: + if (ARGUMENT_PRESENT(HandlerRoutine)) { + *HandlerRoutine = FoundHandler; + } + + return S_OK; } -ULONG64 -OOPStackUnwinderAMD64::LookupPrimaryUnwindInfo( +_PIMAGE_RUNTIME_FUNCTION_ENTRY +OOPStackUnwinderAMD64::LookupPrimaryFunctionEntry( __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __in ULONG64 ImageBase, - __out _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryEntry + __in ULONG64 ImageBase + ) /*++ @@ -1081,8 +1619,7 @@ 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. + entry, then the primary function entry is returned. Arguments: @@ -1092,22 +1629,17 @@ Arguments: 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. + A pointer to the primary function entry is returned as the function value. --*/ { + ULONG ChainCount; ULONG Index; PUNWIND_INFO UnwindInfo; - ULONG UnwindRel; - ULONG64 UnwindAbs; // // Locate the unwind information and determine whether it is chained. @@ -1115,13 +1647,9 @@ Return Value: // entry and loop again. // - UnwindRel = FunctionEntry->UnwindInfoAddress; - // Copy the function entry before it becomes invalid. - *PrimaryEntry = *FunctionEntry; - + ChainCount = 0; do { - UnwindAbs = ImageBase + UnwindRel; - UnwindInfo = DacGetUnwindInfo(ImageBase + UnwindRel); + UnwindInfo = GetUnwindInfo(FunctionEntry->UnwindInfoAddress + ImageBase); if ((UnwindInfo == NULL) || ((UnwindInfo->Flags & UNW_FLAG_CHAININFO) == 0)) { break; @@ -1132,24 +1660,26 @@ Return Value: Index += 1; } - FunctionEntry = (_PIMAGE_RUNTIME_FUNCTION_ENTRY) - &UnwindInfo->UnwindCode[Index]; - UnwindRel = FunctionEntry->UnwindInfoAddress; + FunctionEntry = (_PIMAGE_RUNTIME_FUNCTION_ENTRY)&UnwindInfo->UnwindCode[Index]; + + // + // Limit the number of iterations possible for chained function table + // entries. + // - // Copy the function entry before it becomes invalid. - *PrimaryEntry = *FunctionEntry; + ChainCount += 1; + UNWINDER_ASSERT(ChainCount <= UNWIND_CHAIN_LIMIT); } while (TRUE); - return UnwindAbs; + return FunctionEntry; } _PIMAGE_RUNTIME_FUNCTION_ENTRY OOPStackUnwinderAMD64::SameFunction( __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, __in ULONG64 ImageBase, - __in ULONG64 ControlPc, - __out _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionReturnBuffer + __in ULONG64 ControlPc ) /*++ @@ -1180,23 +1710,22 @@ Return Value: --*/ { - + _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryFunctionEntry; _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); + PrimaryFunctionEntry = LookupPrimaryFunctionEntry(FunctionEntry, + ImageBase); // // Determine the function entry containing the control Pc and similarly - // resolve it's primary function entry. + // resolve its primary function entry. If no function entry can be + // found then the control pc resides in a different function. // if (GetModuleBase(ControlPc, &TargetImageBase) != S_OK || @@ -1206,20 +1735,65 @@ Return Value: return NULL; } - UnwindInfo2 = LookupPrimaryUnwindInfo(&TargetFunctionEntry, - TargetImageBase, - FunctionReturnBuffer); + // + // Lookup the primary function entry associated with the target function + // entry. + // + + TargetFunctionEntry = *LookupPrimaryFunctionEntry(&TargetFunctionEntry, + TargetImageBase); // - // If the address of the two sets of unwind information are equal, then + // If the beginning offset of the two function entries are equal, then // return the address of the primary function entry. Otherwise, return // NULL. // - if (UnwindInfo1 == UnwindInfo2) { - return FunctionReturnBuffer; + if (PrimaryFunctionEntry->BeginAddress == TargetFunctionEntry.BeginAddress) { + return PrimaryFunctionEntry; } else { return NULL; } } + +ULONG OOPStackUnwinderAMD64::UnwindOpSlots(__in UNWIND_CODE UnwindCode) +/*++ + +Routine Description: + + This routine determines the number of unwind code slots ultimately + consumed by an unwind code sequence. + +Arguments: + + UnwindCode - Supplies the first unwind code in the sequence. + +Return Value: + + Returns the total count of the number of slots consumed by the unwind + code sequence. + +--*/ +{ + + ULONG Slots; + ULONG UnwindOp; + + // + // UWOP_SPARE_CODE may be found in very old x64 images. + // + + UnwindOp = UnwindCode.UnwindOp; + + UNWINDER_ASSERT(UnwindOp != UWOP_SPARE_CODE); + UNWINDER_ASSERT(UnwindOp < sizeof(UnwindOpExtraSlotTable)); + + Slots = UnwindOpExtraSlotTable[UnwindOp] + 1; + if ((UnwindOp == UWOP_ALLOC_LARGE) && (UnwindCode.OpInfo != 0)) { + Slots += 1; + } + + return Slots; +} + diff --git a/src/unwinder/amd64/unwinder_amd64.h b/src/unwinder/amd64/unwinder_amd64.h index 4c0bd46529..2d90492654 100644 --- a/src/unwinder/amd64/unwinder_amd64.h +++ b/src/unwinder/amd64/unwinder_amd64.h @@ -20,37 +20,51 @@ class OOPStackUnwinderAMD64 : public OOPStackUnwinder { public: // Unwind the given CONTEXT to the caller CONTEXT. The CONTEXT will be overwritten. - BOOL Unwind(CONTEXT * pContext); + static BOOL Unwind(CONTEXT * pContext); // // Everything below comes from dbghelp.dll. // + static HRESULT VirtualUnwind(__in DWORD HandlerType, + __in DWORD64 ImageBase, + __in DWORD64 ControlPc, + __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __inout PCONTEXT ContextRecord, + __out PVOID *HandlerData, + __out PDWORD64 EstablisherFrame, + __inout_opt PKNONVOLATILE_CONTEXT_POINTERS ContextPointers, + __deref_opt_out_opt PEXCEPTION_ROUTINE *HandlerRoutine); + 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); + static ULONG UnwindOpSlots(__in UNWIND_CODE UnwindCode); + + static HRESULT UnwindEpilogue(__in ULONG64 ImageBase, + __in ULONG64 ControlPc, + __in ULONG EpilogueOffset, + __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __inout PCONTEXT ContextRecord, + __inout_opt PKNONVOLATILE_CONTEXT_POINTERS ContextPointers); - HRESULT VirtualUnwind(__in DWORD64 ImageBase, - __in DWORD64 ControlPc, - __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __inout PCONTEXT ContextRecord, - __out PDWORD64 EstablisherFrame); + static HRESULT UnwindPrologue(__in DWORD64 ImageBase, + __in DWORD64 ControlPc, + __in DWORD64 FrameBase, + __in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, + __inout PCONTEXT ContextRecord, + __inout_opt PKNONVOLATILE_CONTEXT_POINTERS ContextPointers, + __deref_out _PIMAGE_RUNTIME_FUNCTION_ENTRY *FinalFunctionEntry); - DWORD64 LookupPrimaryUnwindInfo + static _PIMAGE_RUNTIME_FUNCTION_ENTRY LookupPrimaryFunctionEntry (__in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, - __in DWORD64 ImageBase, - __out _PIMAGE_RUNTIME_FUNCTION_ENTRY PrimaryEntry); + __in DWORD64 ImageBase); - _PIMAGE_RUNTIME_FUNCTION_ENTRY SameFunction + static _PIMAGE_RUNTIME_FUNCTION_ENTRY SameFunction (__in _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry, __in DWORD64 ImageBase, - __in DWORD64 ControlPc, - __out _PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionReturnBuffer); + __in DWORD64 ControlPc); + + static UNWIND_INFO * GetUnwindInfo(TADDR taUnwindInfo); }; #endif // __unwinder_amd64_h__ diff --git a/src/unwinder/stdafx.h b/src/unwinder/stdafx.h index 840877ea5b..2e6330d846 100644 --- a/src/unwinder/stdafx.h +++ b/src/unwinder/stdafx.h @@ -15,5 +15,7 @@ #include <common.h> #include <debugger.h> #include <methoditer.h> +#ifdef DACCESS_COMPILE #include <dacprivate.h> #include <dacimpl.h> +#endif // DACCESS_COMPILE diff --git a/src/unwinder/unwinder.cpp b/src/unwinder/unwinder.cpp index 6431a83ea8..e42cf21d1c 100644 --- a/src/unwinder/unwinder.cpp +++ b/src/unwinder/unwinder.cpp @@ -12,100 +12,6 @@ EXTERN_C void GetRuntimeStackWalkInfo(IN ULONG64 ControlPc, OUT UINT_PTR* pModuleBase, OUT UINT_PTR* pFuncEntry); -#if !defined(_TARGET_ARM_) && !defined(_TARGET_ARM64_) -//--------------------------------------------------------------------------------------- -// -// Read an UNWIND_INFO structure given its address. The UNWIND_INFO structure is variable sized. -// This is just a simple wrapper over the platform-specific DAC function which does the real work. -// -// Arguments: -// taUnwindInfo - target address of the beginning of the UNWIND_INFO structure -// -// Return Value: -// Return the specified UNWIND_INFO. It lives in the DAC cache, so the caller doesn't need to explicitly -// free the memory. It'll get flushed when the DAC cache is flushed (i.e. when we continue). -// - -UNWIND_INFO * OOPStackUnwinder::GetUnwindInfo(TADDR taUnwindInfo) -{ - return DacGetUnwindInfo(taUnwindInfo); -} -#endif // !_TARGET_ARM_ && !_TARGET_ARM64_ - -//--------------------------------------------------------------------------------------- -// -// This is a simple wrapper over code:OOPStackUnwinder::ReadMemory(). Unlike ReadMemory(), -// it fails if we don't successfully read all the specified bytes. -// -// Arguments: -// address - the address to be read -// pbBuffer - the buffer to store the read memory -// cbRequest - the number of bytes requested -// -// Return Value: -// S_OK if all the memory is read successfully. -// HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY) if only part of the memory is read. -// Failure HRs otherwise. -// - -HRESULT OOPStackUnwinder::ReadAllMemory( DWORD64 address, - __in_ecount(cbRequest) PVOID pbBuffer, - DWORD cbRequest) -{ - DWORD cbDone = 0; - HRESULT hr = ReadMemory(address, pbBuffer, cbRequest, &cbDone); - if (SUCCEEDED(hr) && (cbDone != cbRequest)) - { - return HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY); - } - else - { - return hr; - } -} - -//--------------------------------------------------------------------------------------- -// -// Read the specified memory from out-of-process. This function uses DacReadAll() to do the trick. -// -// Arguments: -// address - the address to be read -// pbBuffer - the buffer to store the read memory -// cbRequest - the number of bytes requested -// pcbDone - out parameter; returns the number of bytes read -// -// Return Value: -// S_OK if all the memory is read successfully -// - -HRESULT OOPStackUnwinder::ReadMemory( DWORD64 address, - __in_ecount(cbRequest) PVOID pbBuffer, - DWORD cbRequest, - __out_opt PDWORD pcbDone) -{ - _ASSERTE(pcbDone != NULL); - - HRESULT hr = DacReadAll(TO_TADDR(address), pbBuffer, cbRequest, false); - if (SUCCEEDED(hr)) - { - *pcbDone = cbRequest; - - // On X64, we need to replace any patches which are within the requested memory range. - // This is because the X64 unwinder needs to disassemble the native instructions in order to determine - // whether the IP is in an epilog. -#if defined(_TARGET_AMD64_) - MemoryRange range(dac_cast<PTR_VOID>((TADDR)address), cbRequest); - hr = DacReplacePatchesInHostMemory(range, pbBuffer); -#endif // _TARGET_AMD64_ - } - else - { - *pcbDone = 0; - } - - return hr; -} - //--------------------------------------------------------------------------------------- // // Given a control PC, return the base of the module it is in. For jitted managed code, this is the diff --git a/src/unwinder/unwinder.h b/src/unwinder/unwinder.h index 21d64eae3f..c771857726 100644 --- a/src/unwinder/unwinder.h +++ b/src/unwinder/unwinder.h @@ -26,39 +26,17 @@ 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); + static 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); + static HRESULT GetFunctionEntry( DWORD64 address, + __out_ecount(cbBuffer) PVOID pBuffer, + DWORD cbBuffer); }; #endif // __unwinder_h__ diff --git a/src/unwinder/wks/.gitmirror b/src/unwinder/wks/.gitmirror new file mode 100644 index 0000000000..f507630f94 --- /dev/null +++ b/src/unwinder/wks/.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/wks/CMakeLists.txt b/src/unwinder/wks/CMakeLists.txt new file mode 100644 index 0000000000..91000b368f --- /dev/null +++ b/src/unwinder/wks/CMakeLists.txt @@ -0,0 +1 @@ +add_library(unwinder_wks ${UNWINDER_SOURCES}) |