summaryrefslogtreecommitdiff
path: root/src/unwinder
diff options
context:
space:
mode:
authorJan Vorlicek <janvorli@microsoft.com>2015-02-13 11:33:23 +0100
committerJan Vorlicek <janvorli@microsoft.com>2015-02-13 11:33:23 +0100
commit5720457962860915e42b8ee0fd9fb52904b21423 (patch)
tree81a4bda602941b1da84dc986600bc1b639070688 /src/unwinder
parentd92129870bcdeb1ed04cb73cce14874a905848d8 (diff)
downloadcoreclr-5720457962860915e42b8ee0fd9fb52904b21423.tar.gz
coreclr-5720457962860915e42b8ee0fd9fb52904b21423.tar.bz2
coreclr-5720457962860915e42b8ee0fd9fb52904b21423.zip
Modify the windows amd64 unwinder to work as jitted code unwinder on Unix
Update the windows amd64 unwinder from its source to the latest version and add context pointers updating that was excluded before. Change methods of the OOPStackUnwinder and OOPStackUnwinderAMD64 to be static, since the base class has no data members and the unwinding is always using the processor specific subclass. Modify the code so that it is built in two flavors. The first one is the DAC flavor which is used by the debugger. The second one will be used for jitted code stack frames unwinding on non-Windows systems.
Diffstat (limited to 'src/unwinder')
-rw-r--r--src/unwinder/CMakeLists.txt10
-rw-r--r--src/unwinder/amd64/unwinder_amd64.cpp1986
-rw-r--r--src/unwinder/amd64/unwinder_amd64.h50
-rw-r--r--src/unwinder/stdafx.h2
-rw-r--r--src/unwinder/unwinder.cpp94
-rw-r--r--src/unwinder/unwinder.h32
-rw-r--r--src/unwinder/wks/.gitmirror1
-rw-r--r--src/unwinder/wks/CMakeLists.txt1
8 files changed, 1330 insertions, 846 deletions
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})