You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1699 lines
66 KiB
1699 lines
66 KiB
/*++
|
|
|
|
Copyright (c) 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
walkalpha.c
|
|
|
|
Abstract:
|
|
|
|
This file implements the ALPHA stack walking api.
|
|
|
|
Author:
|
|
|
|
Wesley Witt (wesw) 1-Oct-1993
|
|
|
|
Environment:
|
|
|
|
User Mode
|
|
|
|
--*/
|
|
|
|
#define _IMAGEHLP_SOURCE_
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include "private.h"
|
|
#define NOEXTAPI
|
|
#include "wdbgexts.h"
|
|
#include "ntdbg.h"
|
|
#include "symbols.h"
|
|
#include "alphaops.h"
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
#include <globals.h>
|
|
#include "fecache.hpp"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C"
|
|
#endif
|
|
|
|
BOOL
|
|
WalkAlphaInit(
|
|
HANDLE hProcess,
|
|
LPSTACKFRAME64 StackFrame,
|
|
PALPHA_NT5_CONTEXT Context,
|
|
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
|
|
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
|
|
PFUNCTION_TABLE_ACCESS_ROUTINE64 GetFunctionEntry,
|
|
BOOL Use64
|
|
);
|
|
|
|
BOOL
|
|
WalkAlphaNext(
|
|
HANDLE hProcess,
|
|
LPSTACKFRAME64 StackFrame,
|
|
PALPHA_NT5_CONTEXT Context,
|
|
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
|
|
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
|
|
PFUNCTION_TABLE_ACCESS_ROUTINE64 GetFunctionEntry,
|
|
BOOL Use64
|
|
);
|
|
|
|
BOOL
|
|
WalkAlphaGetStackFrame(
|
|
HANDLE hProcess,
|
|
PULONG64 ReturnAddress,
|
|
PULONG64 FramePointer,
|
|
PALPHA_NT5_CONTEXT Context,
|
|
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
|
|
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
|
|
PFUNCTION_TABLE_ACCESS_ROUTINE64 GetFunctionEntry,
|
|
PKDHELP64 KdHelp,
|
|
BOOL Use64
|
|
);
|
|
|
|
VOID
|
|
GetUnwindFunctionEntry(
|
|
HANDLE hProcess,
|
|
ULONG64 ControlPc,
|
|
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
|
|
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
|
|
PFUNCTION_TABLE_ACCESS_ROUTINE64 GetFunctionEntry,
|
|
BOOL Use64,
|
|
FeCacheEntry* FunctionEntry,
|
|
PIMAGE_ALPHA64_RUNTIME_FUNCTION_ENTRY UnwindFunctionEntry,
|
|
PULONG StackAdjust,
|
|
PULONG64 FixedReturn
|
|
);
|
|
|
|
#define ZERO 0x0 /* integer register 0 */
|
|
#define SP 0x1d /* integer register 29 */
|
|
#define RA 0x1f /* integer register 31 */
|
|
#define SAVED_FLOATING_MASK 0xfff00000 /* saved floating registers */
|
|
#define SAVED_INTEGER_MASK 0xf3ffff02 /* saved integer registers */
|
|
#define IS_FLOATING_SAVED(Register) ((SAVED_FLOATING_MASK >> Register) & 1L)
|
|
#define IS_INTEGER_SAVED(Register) ((SAVED_INTEGER_MASK >> Register) & 1L)
|
|
|
|
#define IS_HANDLER_DEFINED(FunctionEntry) \
|
|
(RF_EXCEPTION_HANDLER(FunctionEntry) != 0)
|
|
|
|
BOOL
|
|
WalkAlpha(
|
|
HANDLE hProcess,
|
|
LPSTACKFRAME64 StackFrame,
|
|
PVOID ContextRecord,
|
|
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
|
|
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
|
|
PFUNCTION_TABLE_ACCESS_ROUTINE64 GetFunctionEntry,
|
|
BOOL Use64
|
|
)
|
|
{
|
|
BOOL rval;
|
|
PALPHA_NT5_CONTEXT Context = (PALPHA_NT5_CONTEXT)ContextRecord;
|
|
|
|
if (StackFrame->Virtual) {
|
|
|
|
rval = WalkAlphaNext( hProcess,
|
|
StackFrame,
|
|
Context,
|
|
ReadMemory,
|
|
GetModuleBase,
|
|
GetFunctionEntry,
|
|
Use64
|
|
);
|
|
|
|
} else {
|
|
|
|
rval = WalkAlphaInit( hProcess,
|
|
StackFrame,
|
|
Context,
|
|
ReadMemory,
|
|
GetModuleBase,
|
|
GetFunctionEntry,
|
|
Use64
|
|
);
|
|
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
|
|
ULONG64
|
|
VirtualUnwind (
|
|
HANDLE hProcess,
|
|
ULONG64 ControlPc,
|
|
PIMAGE_ALPHA64_RUNTIME_FUNCTION_ENTRY FunctionEntry,
|
|
ULONG StackAdjust,
|
|
ULONG64 FixedReturn,
|
|
PALPHA_NT5_CONTEXT Context,
|
|
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory
|
|
// PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function virtually unwinds the specified function by executing its
|
|
prologue code backwards. Given the current context and the instructions
|
|
that preserve registers in the prologue, it is possible to recreate the
|
|
nonvolatile context at the point the function was called.
|
|
|
|
If the function is a leaf function, then the address where control left
|
|
the previous frame is obtained from the context record. If the function
|
|
is a nested function, but not an exception or interrupt frame, then the
|
|
prologue code is executed backwards and the address where control left
|
|
the previous frame is obtained from the updated context record.
|
|
|
|
Otherwise, an exception or interrupt entry to the system is being unwound
|
|
and a specially coded prologue restores the return address twice. Once
|
|
from the fault instruction address and once from the saved return address
|
|
register. The first restore is returned as the function value and the
|
|
second restore is placed in the updated context record.
|
|
|
|
During the unwind, the virtual and real frame pointers for the function
|
|
are calculated and returned in the given frame pointers structure.
|
|
|
|
If a context pointers record is specified, then the address where each
|
|
register is restored from is recorded in the appropriate element of the
|
|
context pointers record.
|
|
|
|
Arguments:
|
|
|
|
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.
|
|
|
|
ContextPointers - Supplies an optional pointer to a context pointers
|
|
record.
|
|
|
|
Return Value:
|
|
|
|
The address where control left the previous frame is returned as the
|
|
function value.
|
|
|
|
Implementation Notes:
|
|
|
|
N.B. "where control left" is not the "return address" of the call in the
|
|
previous frame. For normal frames, NextPc points to the last instruction
|
|
that completed in the previous frame (the JSR/BSR). The difference between
|
|
NextPc and NextPc + 4 (return address) is important for correct behavior
|
|
in boundary cases of exception addresses and scope tables.
|
|
|
|
For exception and interrupt frames, NextPc is obtained from the trap frame
|
|
contination address (Fir). For faults and synchronous traps, NextPc is both
|
|
the last instruction to execute in the previous frame and the next
|
|
instruction to execute if the function were to return. For asynchronous
|
|
traps, NextPc is the continuation address. It is the responsibility of the
|
|
compiler to insert TRAPB instructions to insure asynchronous traps do not
|
|
occur outside the scope from the instruction(s) that caused them.
|
|
|
|
N.B. in this and other files where RtlVirtualUnwind is used, the variable
|
|
named NextPc is perhaps more accurately, LastPc - the last PC value in
|
|
the previous frame, or CallPc - the address of the call instruction, or
|
|
ControlPc - the address where control left the previous frame. Instead
|
|
think of NextPc as the next PC to use in another call to virtual unwind.
|
|
|
|
The Alpha version of virtual unwind is similar in design, but slightly
|
|
more complex than the Mips version. This is because Alpha compilers
|
|
are given more flexibility to optimize generated code and instruction
|
|
sequences, including within procedure prologues. And also because of
|
|
compiler design issues, the function must manage both virtual and real
|
|
frame pointers.
|
|
|
|
Version Information: This version was taken from exdspatch.c@v37 (Feb 1993)
|
|
|
|
--*/
|
|
|
|
{
|
|
ALPHA_INSTRUCTION FollowingInstruction;
|
|
ALPHA_INSTRUCTION Instruction;
|
|
ULONGLONG Address;
|
|
ULONG DecrementOffset;
|
|
ULONG DecrementRegister;
|
|
PULONGLONG FloatingRegister;
|
|
ULONG FrameSize;
|
|
ULONG Function;
|
|
PULONGLONG IntegerRegister;
|
|
ULONG Literal8;
|
|
ULONGLONG NextPc;
|
|
LONG Offset16;
|
|
ULONG Opcode;
|
|
ULONG Ra;
|
|
ULONG Rb;
|
|
ULONG Rc;
|
|
BOOLEAN RestoredRa;
|
|
BOOLEAN RestoredSp;
|
|
DWORD cb;
|
|
PVOID Prolog;
|
|
|
|
|
|
//
|
|
// perf hack: fill cache with prolog
|
|
// skip it if this is a secondary function entry
|
|
//
|
|
|
|
if (FunctionEntry &&
|
|
(ALPHA_RF_PROLOG_END_ADDRESS(FunctionEntry) > ALPHA_RF_BEGIN_ADDRESS(FunctionEntry)) &&
|
|
(ALPHA_RF_PROLOG_END_ADDRESS(FunctionEntry) < ALPHA_RF_END_ADDRESS(FunctionEntry)) ) {
|
|
|
|
cb = (ULONG)(ALPHA_RF_PROLOG_END_ADDRESS(FunctionEntry) - ALPHA_RF_BEGIN_ADDRESS(FunctionEntry));
|
|
|
|
//
|
|
// If the function is a leaf it doesn't have a prolog, skip this
|
|
// optimization.
|
|
//
|
|
|
|
if (cb != 0) {
|
|
Prolog = (PVOID) MemAlloc( cb );
|
|
if (!ReadMemory( hProcess,
|
|
ALPHA_RF_BEGIN_ADDRESS(FunctionEntry),
|
|
Prolog,
|
|
cb,
|
|
&cb )) {
|
|
return 0;
|
|
}
|
|
MemFree(Prolog);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the base address of the integer and floating register arrays within
|
|
// the context record. Each set of 32 registers is known to be contiguous.
|
|
//
|
|
|
|
// assuming that quad values are together in context.
|
|
|
|
IntegerRegister = &Context->IntV0;
|
|
FloatingRegister = &Context->FltF0;
|
|
|
|
//
|
|
// Handle the epilogue case where the next instruction is a return.
|
|
//
|
|
// Exception handlers cannot be called if the ControlPc is within the
|
|
// epilogue because exception handlers expect to operate with a current
|
|
// stack frame. The value of SP is not current within the epilogue.
|
|
//
|
|
|
|
if (!ReadMemory(hProcess, ControlPc, &Instruction.Long, 4, &cb)) {
|
|
return(0);
|
|
}
|
|
|
|
if (IS_RETURN_0001_INSTRUCTION(Instruction.Long)) {
|
|
Rb = Instruction.Jump.Rb;
|
|
NextPc = IntegerRegister[Rb] - 4;
|
|
|
|
//
|
|
// The instruction at the point where control left the specified
|
|
// function is a return, so any saved registers have already been
|
|
// restored, and the stack pointer has already been adjusted. The
|
|
// stack does not need to be unwound in this case and the saved
|
|
// return address register is returned as the function value.
|
|
//
|
|
// In fact, reverse execution of the prologue is not possible in
|
|
// this case: the stack pointer has already been incremented and
|
|
// so, for this frame, neither a valid stack pointer nor frame
|
|
// pointer exists from which to begin reverse execution of the
|
|
// prologue. In addition, the integrity of any data on the stack
|
|
// below the stack pointer is never guaranteed (due to interrupts
|
|
// and exceptions).
|
|
//
|
|
// The epilogue instruction sequence is:
|
|
//
|
|
// ==> ret zero, (Ra), 1 // return
|
|
// or
|
|
//
|
|
// mov ra, Rx // save return address
|
|
// ...
|
|
// ==> ret zero, (Rx), 1 // return
|
|
//
|
|
|
|
return NextPc;
|
|
}
|
|
|
|
//
|
|
// Handle the epilogue case where the next two instructions are a stack
|
|
// frame deallocation and a return.
|
|
//
|
|
|
|
if (!ReadMemory(hProcess,(ControlPc+4),&FollowingInstruction.Long,4,&cb)) {
|
|
return 0;
|
|
}
|
|
|
|
if (IS_RETURN_0001_INSTRUCTION(FollowingInstruction.Long)) {
|
|
Rb = FollowingInstruction.Jump.Rb;
|
|
NextPc = IntegerRegister[Rb] - 4;
|
|
|
|
//
|
|
// The second instruction following the point where control
|
|
// left the specified function is a return. If the instruction
|
|
// before the return is a stack increment instruction, then all
|
|
// saved registers have already been restored except for SP.
|
|
// The value of the stack pointer register cannot be recovered
|
|
// through reverse execution of the prologue because in order
|
|
// to begin reverse execution either the stack pointer or the
|
|
// frame pointer (if any) must still be valid.
|
|
//
|
|
// Instead, the effect that the stack increment instruction
|
|
// would have had on the context is manually applied to the
|
|
// current context. This is forward execution of the epilogue
|
|
// rather than reverse execution of the prologue.
|
|
//
|
|
// In an epilogue, as in a prologue, the stack pointer is always
|
|
// adjusted with a single instruction: either an immediate-value
|
|
// (lda) or a register-value (addq) add instruction.
|
|
//
|
|
|
|
Function = Instruction.OpReg.Function;
|
|
Offset16 = Instruction.Memory.MemDisp;
|
|
Opcode = Instruction.OpReg.Opcode;
|
|
Ra = Instruction.OpReg.Ra;
|
|
Rb = Instruction.OpReg.Rb;
|
|
Rc = Instruction.OpReg.Rc;
|
|
|
|
if ((Opcode == LDA_OP) && (Ra == SP_REG)) {
|
|
|
|
//
|
|
// Load Address instruction.
|
|
//
|
|
// Since the destination (Ra) register is SP, an immediate-
|
|
// value stack deallocation operation is being performed. The
|
|
// displacement value should be added to SP. The displacement
|
|
// value is assumed to be positive. The amount of stack
|
|
// deallocation possible using this instruction ranges from
|
|
// 16 to 32752 (32768 - 16) bytes. The base register (Rb) is
|
|
// usually SP, but may be another register.
|
|
//
|
|
// The epilogue instruction sequence is:
|
|
//
|
|
// ==> lda sp, +N(sp) // deallocate stack frame
|
|
// ret zero, (ra) // return
|
|
// or
|
|
//
|
|
// ==> lda sp, +N(Rx) // restore SP and deallocate frame
|
|
// ret zero, (ra) // return
|
|
//
|
|
|
|
Context->IntSp = Offset16 + IntegerRegister[Rb];
|
|
return NextPc;
|
|
|
|
} else if ((Opcode == ARITH_OP) && (Function == ADDQ_FUNC) &&
|
|
(Rc == SP_REG) &&
|
|
(Instruction.OpReg.RbvType == RBV_REGISTER_FORMAT)) {
|
|
|
|
//
|
|
// Add Quadword instruction.
|
|
//
|
|
// Since both source operands are registers, and the
|
|
// destination register is SP, a register-value stack
|
|
// deallocation is being performed. The value of the two
|
|
// source registers should be added and this is the new
|
|
// value of SP. One of the source registers is usually SP,
|
|
// but may be another register.
|
|
//
|
|
// The epilogue instruction sequence is:
|
|
//
|
|
// ldiq Rx, N // set [large] frame size
|
|
// ...
|
|
// ==> addq sp, Rx, sp // deallocate stack frame
|
|
// ret zero, (ra) // return
|
|
// or
|
|
//
|
|
// ==> addq Rx, Ry, sp // restore SP and deallocate frame
|
|
// ret zero, (ra) // return
|
|
//
|
|
|
|
Context->IntSp = IntegerRegister[Ra] + IntegerRegister[Rb];
|
|
return NextPc;
|
|
}
|
|
}
|
|
|
|
//
|
|
// By default set the frame pointers to the current value of SP.
|
|
//
|
|
// When a procedure is called, the value of SP before the stack
|
|
// allocation instruction is the virtual frame pointer. When reverse
|
|
// executing instructions in the prologue, the value of SP before the
|
|
// stack allocation instruction is encountered is the real frame
|
|
// pointer. This is the current value of SP unless the procedure uses
|
|
// a frame pointer (e.g., FP_REG).
|
|
//
|
|
|
|
//
|
|
// If the address where control left the specified function is beyond
|
|
// the end of the prologue, then the control PC is considered to be
|
|
// within the function and the control address is set to the end of
|
|
// the prologue. Otherwise, the control PC is not considered to be
|
|
// within the function (i.e., the prologue).
|
|
//
|
|
// N.B. PrologEndAddress is equal to BeginAddress for a leaf function.
|
|
//
|
|
// The low-order two bits of PrologEndAddress are reserved for the IEEE
|
|
// exception mode and so must be masked out.
|
|
//
|
|
|
|
if ((ControlPc < ALPHA_RF_BEGIN_ADDRESS(FunctionEntry)) ||
|
|
(ControlPc >= ALPHA_RF_PROLOG_END_ADDRESS(FunctionEntry))) {
|
|
ControlPc = ALPHA_RF_PROLOG_END_ADDRESS(FunctionEntry);
|
|
}
|
|
|
|
//
|
|
// Scan backward through the prologue to reload callee saved registers
|
|
// that were stored or copied and to increment the stack pointer if it
|
|
// was decremented.
|
|
//
|
|
|
|
DecrementRegister = ZERO_REG;
|
|
NextPc = Context->IntRa - 4;
|
|
RestoredRa = FALSE;
|
|
RestoredSp = FALSE;
|
|
while (ControlPc > ALPHA_RF_BEGIN_ADDRESS(FunctionEntry)) {
|
|
|
|
//
|
|
// Get instruction value, decode fields, case on opcode value, and
|
|
// reverse register store and stack decrement operations.
|
|
// N.B. The location of Opcode, Ra, Rb, and Rc is the same across
|
|
// all opcode formats. The same is not true for Function.
|
|
//
|
|
|
|
ControlPc -= 4;
|
|
if (!ReadMemory(hProcess, ControlPc, &Instruction.Long, 4, &cb)) {
|
|
return 0;
|
|
}
|
|
Function = Instruction.OpReg.Function;
|
|
Literal8 = Instruction.OpLit.Literal;
|
|
Offset16 = Instruction.Memory.MemDisp;
|
|
Opcode = Instruction.OpReg.Opcode;
|
|
Ra = Instruction.OpReg.Ra;
|
|
Rb = Instruction.OpReg.Rb;
|
|
Rc = Instruction.OpReg.Rc;
|
|
|
|
//
|
|
// Compare against each instruction type that will affect the context
|
|
// and that is allowed in a prologue. Any other instructions found
|
|
// in the prologue will be ignored since they are assumed to have no
|
|
// effect on the context.
|
|
//
|
|
|
|
switch (Opcode) {
|
|
|
|
case STQ_OP :
|
|
|
|
//
|
|
// Store Quad instruction.
|
|
//
|
|
// If the base register is SP, then reload the source register
|
|
// value from the value stored on the stack.
|
|
//
|
|
// The prologue instruction sequence is:
|
|
//
|
|
// ==> stq Rx, N(sp) // save integer register Rx
|
|
//
|
|
|
|
if ((Rb == SP_REG) && (Ra != ZERO_REG)) {
|
|
|
|
//
|
|
// Reload the register by retrieving the value previously
|
|
// stored on the stack.
|
|
//
|
|
|
|
Address = (Offset16 + Context->IntSp);
|
|
if (!ReadMemory(hProcess, Address, &IntegerRegister[Ra], 8L, &cb)) {
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// If the destination register is RA and this is the first
|
|
// time that RA is being restored, then set the address of
|
|
// where control left the previous frame. Otherwise, if this
|
|
// is the second time RA is being restored, then the first
|
|
// one was an interrupt or exception address and the return
|
|
// PC should not have been biased by 4.
|
|
//
|
|
|
|
if (Ra == RA_REG) {
|
|
if (RestoredRa == FALSE) {
|
|
NextPc = Context->IntRa - 4;
|
|
RestoredRa = TRUE;
|
|
|
|
} else {
|
|
NextPc += 4;
|
|
}
|
|
|
|
//
|
|
// Otherwise, if the destination register is SP and this is
|
|
// the first time that SP is being restored, then set the
|
|
// establisher frame pointers.
|
|
//
|
|
|
|
} else if ((Ra == SP_REG) && (RestoredSp == FALSE)) {
|
|
RestoredSp = TRUE;
|
|
}
|
|
|
|
//
|
|
// If a context pointer record is specified, then record
|
|
// the address where the destination register contents
|
|
// are stored.
|
|
//
|
|
|
|
//if (ContextPointers != (PKNONVOLATILE_CONTEXT_POINTERS) NULL) {
|
|
// ContextPointers->IntegerContext[Ra] = (PULONGLONG)Address;
|
|
//}
|
|
}
|
|
break;
|
|
|
|
case LDAH_OP :
|
|
Offset16 <<= 16;
|
|
|
|
case LDA_OP :
|
|
|
|
//
|
|
// Load Address High, Load Address instruction.
|
|
//
|
|
// There are several cases where the lda and/or ldah instructions
|
|
// are used: one to decrement the stack pointer directly, and the
|
|
// others to load immediate values into another register and that
|
|
// register is then used to decrement the stack pointer.
|
|
//
|
|
// In the examples below, as a single instructions or as a pair,
|
|
// a lda may be substituted for a ldah and visa-versa.
|
|
//
|
|
|
|
if (Ra == SP_REG) {
|
|
if (Rb == SP_REG) {
|
|
|
|
//
|
|
// If both the destination (Ra) and base (Rb) registers
|
|
// are SP, then a standard stack allocation was performed
|
|
// and the negated displacement value is the stack frame
|
|
// size. The amount of stack allocation possible using
|
|
// the lda instruction ranges from 16 to 32768 bytes and
|
|
// the amount of stack allocation possible using the ldah
|
|
// instruction ranges from 65536 to 2GB in multiples of
|
|
// 65536 bytes. It is rare for the ldah instruction to be
|
|
// used in this manner.
|
|
//
|
|
// The prologue instruction sequence is:
|
|
//
|
|
// ==> lda sp, -N(sp) // allocate stack frame
|
|
//
|
|
|
|
FrameSize = -Offset16;
|
|
goto StackAllocation;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The destination register is SP and the base register
|
|
// is not SP, so this instruction must be the second
|
|
// half of an instruction pair to allocate a large size
|
|
// (>32768 bytes) stack frame. Save the displacement value
|
|
// as the partial decrement value and postpone adjusting
|
|
// the value of SP until the first instruction of the pair
|
|
// is encountered.
|
|
//
|
|
// The prologue instruction sequence is:
|
|
//
|
|
// ldah Rx, -N(sp) // prepare new SP (upper)
|
|
// ==> lda sp, sN(Rx) // allocate stack frame
|
|
//
|
|
|
|
DecrementRegister = Rb;
|
|
DecrementOffset = Offset16;
|
|
}
|
|
|
|
} else if (Ra == DecrementRegister) {
|
|
if (Rb == DecrementRegister) {
|
|
|
|
//
|
|
// Both the destination and base registers are the
|
|
// decrement register, so this instruction exists as the
|
|
// second half of a two instruction pair to load a
|
|
// 31-bit immediate value into the decrement register.
|
|
// Save the displacement value as the partial decrement
|
|
// value.
|
|
//
|
|
// The prologue instruction sequence is:
|
|
//
|
|
// ldah Rx, +N(zero) // set frame size (upper)
|
|
// ==> lda Rx, sN(Rx) // set frame size (+lower)
|
|
// ...
|
|
// subq sp, Rx, sp // allocate stack frame
|
|
//
|
|
|
|
DecrementOffset += Offset16;
|
|
|
|
} else if (Rb == ZERO_REG) {
|
|
|
|
//
|
|
// The destination register is the decrement register and
|
|
// the base register is zero, so this instruction exists
|
|
// to load an immediate value into the decrement register.
|
|
// The stack frame size is the new displacement value added
|
|
// to the previous displacement value, if any.
|
|
//
|
|
// The prologue instruction sequence is:
|
|
//
|
|
// ==> lda Rx, +N(zero) // set frame size
|
|
// ...
|
|
// subq sp, Rx, sp // allocate stack frame
|
|
// or
|
|
//
|
|
// ==> ldah Rx, +N(zero) // set frame size (upper)
|
|
// lda Rx, sN(Rx) // set frame size (+lower)
|
|
// ...
|
|
// subq sp, Rx, sp // allocate stack frame
|
|
//
|
|
|
|
FrameSize = (Offset16 + DecrementOffset);
|
|
goto StackAllocation;
|
|
|
|
} else if (Rb == SP_REG) {
|
|
|
|
//
|
|
// The destination (Ra) register is SP and the base (Rb)
|
|
// register is the decrement register, so a two
|
|
// instruction, large size (>32768 bytes) stack frame
|
|
// allocation was performed. Add the new displacement
|
|
// value to the previous displacement value. The negated
|
|
// displacement value is the stack frame size.
|
|
//
|
|
// The prologue instruction sequence is:
|
|
//
|
|
// ==> ldah Rx, -N(sp) // prepare new SP (upper)
|
|
// lda sp, sN(Rx) // allocate stack frame
|
|
//
|
|
|
|
FrameSize = -(Offset16 + (LONG)DecrementOffset);
|
|
goto StackAllocation;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ARITH_OP :
|
|
|
|
if ((Function == ADDQ_FUNC) &&
|
|
(Instruction.OpReg.RbvType != RBV_REGISTER_FORMAT)) {
|
|
|
|
//
|
|
// Add Quadword (immediate) instruction.
|
|
//
|
|
// If the first source register is zero, and the second
|
|
// operand is a literal, and the destination register is
|
|
// the decrement register, then the instruction exists
|
|
// to load an unsigned immediate value less than 256 into
|
|
// the decrement register. The immediate value is the stack
|
|
// frame size.
|
|
//
|
|
// The prologue instruction sequence is:
|
|
//
|
|
// ==> addq zero, N, Rx // set frame size
|
|
// ...
|
|
// subq sp, Rx, sp // allocate stack frame
|
|
//
|
|
|
|
if ((Ra == ZERO_REG) && (Rc == DecrementRegister)) {
|
|
FrameSize = Literal8;
|
|
goto StackAllocation;
|
|
}
|
|
|
|
} else if ((Function == SUBQ_FUNC) &&
|
|
(Instruction.OpReg.RbvType == RBV_REGISTER_FORMAT)) {
|
|
|
|
//
|
|
// Subtract Quadword (register) instruction.
|
|
//
|
|
// If both source operands are registers and the first
|
|
// source (minuend) register and the destination
|
|
// (difference) register are both SP, then a register value
|
|
// stack allocation was performed and the second source
|
|
// (subtrahend) register value will be added to SP when its
|
|
// value is known. Until that time save the register number of
|
|
// this decrement register.
|
|
//
|
|
// The prologue instruction sequence is:
|
|
//
|
|
// ldiq Rx, N // set frame size
|
|
// ...
|
|
// ==> subq sp, Rx, sp // allocate stack frame
|
|
//
|
|
|
|
if ((Ra == SP_REG) && (Rc == SP_REG)) {
|
|
DecrementRegister = Rb;
|
|
DecrementOffset = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case BIT_OP :
|
|
|
|
//
|
|
// If the second operand is a register the bit set instruction
|
|
// may be a register move instruction, otherwise if the second
|
|
// operand is a literal, the bit set instruction may be a load
|
|
// immediate value instruction.
|
|
//
|
|
|
|
if ((Function == BIS_FUNC) && (Rc != ZERO_REG)) {
|
|
if (Instruction.OpReg.RbvType == RBV_REGISTER_FORMAT) {
|
|
|
|
//
|
|
// Bit Set (register move) instruction.
|
|
//
|
|
// If both source registers are the same register, or
|
|
// one of the source registers is zero, then this is a
|
|
// register move operation. Restore the value of the
|
|
// source register by copying the current destination
|
|
// register value back to the source register.
|
|
//
|
|
// The prologue instruction sequence is:
|
|
//
|
|
// ==> bis Rx, Rx, Ry // copy register Rx
|
|
// or
|
|
//
|
|
// ==> bis Rx, zero, Ry // copy register Rx
|
|
// or
|
|
//
|
|
// ==> bis zero, Rx, Ry // copy register Rx
|
|
//
|
|
|
|
if (Ra == ZERO_REG) {
|
|
|
|
//
|
|
// Map the third case above to the first case.
|
|
//
|
|
|
|
Ra = Rb;
|
|
|
|
} else if (Rb == ZERO_REG) {
|
|
|
|
//
|
|
// Map the second case above to the first case.
|
|
//
|
|
|
|
Rb = Ra;
|
|
}
|
|
|
|
if ((Ra == Rb) && (Ra != ZERO_REG)) {
|
|
IntegerRegister[Ra] = IntegerRegister[Rc];
|
|
|
|
|
|
//
|
|
// If the destination register is RA and this is the
|
|
// first time that RA is being restored, then set the
|
|
// address of where control left the previous frame.
|
|
// Otherwise, if this is the second time RA is being
|
|
// restored, then the first one was an interrupt or
|
|
// exception address and the return PC should not
|
|
// have been biased by 4.
|
|
//
|
|
|
|
if (Ra == RA_REG) {
|
|
if (RestoredRa == FALSE) {
|
|
NextPc = Context->IntRa - 4;
|
|
RestoredRa = TRUE;
|
|
|
|
} else {
|
|
NextPc += 4;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the source register is SP and this is the first
|
|
// time SP is set, then this is a frame pointer set
|
|
// instruction. Reset the frame pointers to this new
|
|
// value of SP.
|
|
//
|
|
|
|
if ((Ra == SP_REG) && (RestoredSp == FALSE)) {
|
|
RestoredSp = TRUE;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Bit Set (load immediate) instruction.
|
|
//
|
|
// If the first source register is zero, and the second
|
|
// operand is a literal, and the destination register is
|
|
// the decrement register, then this instruction exists
|
|
// to load an unsigned immediate value less than 256 into
|
|
// the decrement register. The decrement register value is
|
|
// the stack frame size.
|
|
//
|
|
// The prologue instruction sequence is:
|
|
//
|
|
// ==> bis zero, N, Rx // set frame size
|
|
// ...
|
|
// subq sp, Rx, sp // allocate stack frame
|
|
//
|
|
|
|
if ((Ra == ZERO_REG) && (Rc == DecrementRegister)) {
|
|
FrameSize = Literal8;
|
|
StackAllocation:
|
|
//
|
|
// Add the frame size to SP to reverse the stack frame
|
|
// allocation, leave the real frame pointer as is, set
|
|
// the virtual frame pointer with the updated SP value,
|
|
// and clear the decrement register.
|
|
//
|
|
|
|
Context->IntSp += FrameSize;
|
|
DecrementRegister = ZERO_REG;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case STT_OP :
|
|
|
|
//
|
|
// Store T-Floating (quadword integer) instruction.
|
|
//
|
|
// If the base register is SP, then reload the source register
|
|
// value from the value stored on the stack.
|
|
//
|
|
// The prologue instruction sequence is:
|
|
//
|
|
// ==> stt Fx, N(sp) // save floating register Fx
|
|
//
|
|
|
|
if ((Rb == SP_REG) && (Ra != FZERO_REG)) {
|
|
|
|
//
|
|
// Reload the register by retrieving the value previously
|
|
// stored on the stack.
|
|
//
|
|
|
|
Address = (Offset16 + Context->IntSp);
|
|
if (!ReadMemory(hProcess, Address, &FloatingRegister[Ra], 8L, &cb)) {
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// If a context pointer record is specified, then record
|
|
// the address where the destination register contents are
|
|
// stored.
|
|
//
|
|
|
|
//if (ContextPointers != (PKNONVOLATILE_CONTEXT_POINTERS) NULL) {
|
|
// ContextPointers->FloatingContext[Ra] = (PULONGLONG)Address;
|
|
//}
|
|
}
|
|
break;
|
|
|
|
|
|
case STS_OP :
|
|
|
|
//
|
|
// Store T-Floating (dword integer) instruction.
|
|
//
|
|
// If the base register is SP, then reload the source register
|
|
// value from the value stored on the stack.
|
|
//
|
|
// The prologue instruction sequence is:
|
|
//
|
|
// ==> stt Fx, N(sp) // save floating register Fx
|
|
//
|
|
|
|
if ((Rb == SP_REG) && (Ra != FZERO_REG)) {
|
|
|
|
//
|
|
// Reload the register by retrieving the value previously
|
|
// stored on the stack.
|
|
//
|
|
|
|
float f;
|
|
|
|
Address = (Offset16 + Context->IntSp);
|
|
if (!ReadMemory(hProcess, Address, &f, sizeof(float), &cb)) {
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// value was stored as a float. Do a conversion to a
|
|
// double, since registers are Always read as doubles
|
|
//
|
|
FloatingRegister[Ra] = (ULONGLONG)(double)f;
|
|
|
|
//
|
|
// If a context pointer record is specified, then record
|
|
// the address where the destination register contents are
|
|
// stored.
|
|
//
|
|
|
|
//if (ContextPointers != (PKNONVOLATILE_CONTEXT_POINTERS) NULL) {
|
|
// ContextPointers->FloatingContext[Ra] = (PULONGLONG)Address;
|
|
//}
|
|
}
|
|
break;
|
|
|
|
case FPOP_OP :
|
|
|
|
//
|
|
// N.B. The floating operate function field is not the same as
|
|
// the integer operate nor the jump function fields.
|
|
//
|
|
|
|
if (Instruction.FpOp.Function == CPYS_FUNC) {
|
|
|
|
//
|
|
// Copy Sign (floating-point move) instruction.
|
|
//
|
|
// If both source registers are the same register, then this is
|
|
// a floating-point register move operation. Restore the value
|
|
// of the source register by copying the current destination
|
|
// register value to the source register.
|
|
//
|
|
// The prologue instruction sequence is:
|
|
//
|
|
// ==> cpys Fx, Fx, Fy // copy floating register Fx
|
|
//
|
|
|
|
if ((Ra == Rb) && (Ra != FZERO_REG)) {
|
|
FloatingRegister[Ra] = FloatingRegister[Rc];
|
|
}
|
|
}
|
|
|
|
default :
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (StackAdjust) {
|
|
// Check for exlicit stack adjust amount
|
|
|
|
Context->IntSp += StackAdjust;
|
|
}
|
|
|
|
if (FixedReturn != 0) {
|
|
NextPc = FixedReturn;
|
|
}
|
|
|
|
return NextPc;
|
|
}
|
|
|
|
BOOL
|
|
WalkAlphaGetStackFrame(
|
|
HANDLE hProcess,
|
|
PULONG64 ReturnAddress,
|
|
PULONG64 FramePointer,
|
|
PALPHA_NT5_CONTEXT Context,
|
|
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
|
|
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
|
|
PFUNCTION_TABLE_ACCESS_ROUTINE64 GetFunctionEntry,
|
|
PKDHELP64 KdHelp,
|
|
BOOL Use64
|
|
)
|
|
{
|
|
FeCacheEntry* CacheEntry;
|
|
FeCacheEntry UnwindFunctionEntry;
|
|
ULONG64 NextPc = Context->IntRa;
|
|
BOOL rval = TRUE;
|
|
ULONG cb;
|
|
ULONG StackAdjust;
|
|
ULONG64 FixedReturn;
|
|
|
|
|
|
if (*ReturnAddress == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
__try {
|
|
FunctionEntryCache* Cache =
|
|
GetFeCache(Use64 ?
|
|
IMAGE_FILE_MACHINE_ALPHA64 : IMAGE_FILE_MACHINE_ALPHA,
|
|
TRUE);
|
|
if (Cache != NULL) {
|
|
CacheEntry = Cache->Find(hProcess, *ReturnAddress, ReadMemory,
|
|
GetModuleBase, GetFunctionEntry);
|
|
} else {
|
|
CacheEntry = NULL;
|
|
}
|
|
|
|
if (CacheEntry != NULL) {
|
|
|
|
// Construct a function entry suitable for unwinding from ControlPc
|
|
|
|
UnwindFunctionEntry.Address = 0;
|
|
UnwindFunctionEntry.Process = 0;
|
|
|
|
GetUnwindFunctionEntry( hProcess, *ReturnAddress, ReadMemory, GetModuleBase, GetFunctionEntry, Use64, CacheEntry,
|
|
&UnwindFunctionEntry.Data.Axp64, &StackAdjust, &FixedReturn );
|
|
FE_SET_DESC(&UnwindFunctionEntry, "from UnwindFunctionEntry");
|
|
FE_ShowRuntimeFunctionAxp64((&UnwindFunctionEntry,
|
|
"VirtualUnwind: unwind function entry"));
|
|
#if DBG
|
|
if (tlsvar(DebugFunctionEntries)) {
|
|
dbPrint(" FixedReturn = %16.8I64x\n", FixedReturn );
|
|
dbPrint(" StackAdjust = %16x\n", StackAdjust );
|
|
}
|
|
#endif
|
|
|
|
NextPc = VirtualUnwind( hProcess, *ReturnAddress, &UnwindFunctionEntry.Data.Axp64, StackAdjust, FixedReturn, Context, ReadMemory);
|
|
#if DBG
|
|
if (tlsvar(DebugFunctionEntries)) {
|
|
dbPrint("NextPc = %.8I64x\n", NextPc );
|
|
}
|
|
#endif
|
|
if (!NextPc) {
|
|
rval = FALSE;
|
|
}
|
|
|
|
//
|
|
// The Ra value coming out of mainCRTStartup is set by some RTL
|
|
// routines to be "1"; return out of mainCRTStartup is actually
|
|
// done through Jump/Unwind, so this serves to cause an error if
|
|
// someone actually does a return. That's why we check here for
|
|
// NextPc == 1 - this happens when in the frame for CRTStartup.
|
|
//
|
|
// We test for (0-4) and (1-4) because on ALPHA, the value returned by
|
|
// VirtualUnwind is the value to be passed to the next call to
|
|
// VirtualUnwind, which is NOT the same as the Ra - it's sometimes
|
|
// decremented by four - this gives the faulting instruction -
|
|
// in particular, we want the fault instruction so we can get the
|
|
// correct scope in the case of an exception.
|
|
//
|
|
if ((NextPc == 1) || (NextPc == 4) || (NextPc == (0-4)) || (NextPc == (1-4)) ) {
|
|
NextPc = 0;
|
|
}
|
|
if ( !NextPc || (NextPc == *ReturnAddress && *FramePointer == Context->IntSp) ) {
|
|
rval = FALSE;
|
|
}
|
|
|
|
*ReturnAddress = NextPc;
|
|
*FramePointer = Context->IntSp;
|
|
|
|
} else {
|
|
|
|
if ( (NextPc == *ReturnAddress && *FramePointer == Context->IntSp) ||
|
|
(NextPc == 1) || (NextPc == 0) || (NextPc == (-4)) ) {
|
|
rval = FALSE;
|
|
}
|
|
|
|
*ReturnAddress = Context->IntRa;
|
|
*FramePointer = Context->IntSp;
|
|
|
|
}
|
|
|
|
} __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
|
|
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
|
|
rval = FALSE;
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
|
|
BOOL
|
|
WalkAlphaInit(
|
|
HANDLE hProcess,
|
|
LPSTACKFRAME64 StackFrame,
|
|
PALPHA_NT5_CONTEXT Context,
|
|
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
|
|
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
|
|
PFUNCTION_TABLE_ACCESS_ROUTINE64 GetFunctionEntry,
|
|
BOOL Use64
|
|
)
|
|
{
|
|
ALPHA_NT5_CONTEXT ContextSave;
|
|
ULONG64 PcOffset;
|
|
ULONG64 FrameOffset;
|
|
DWORD cb;
|
|
ALPHA_KEXCEPTION_FRAME ExceptionFrame;
|
|
PALPHA_KEXCEPTION_FRAME pef = &ExceptionFrame;
|
|
DWORD Result;
|
|
|
|
if (StackFrame->AddrFrame.Offset) {
|
|
#if 0
|
|
if (ReadMemory( hProcess,
|
|
StackFrame->AddrFrame.Offset,
|
|
&ExceptionFrame,
|
|
sizeof(ALPHA_KEXCEPTION_FRAME),
|
|
&cb )) {
|
|
//
|
|
// successfully read an exception frame from the stack
|
|
//
|
|
Context->IntSp = StackFrame->AddrFrame.Offset;
|
|
Context->Fir = pef->SwapReturn;
|
|
Context->IntRa = pef->SwapReturn;
|
|
Context->IntS0 = pef->IntS0;
|
|
Context->IntS1 = pef->IntS1;
|
|
Context->IntS2 = pef->IntS2;
|
|
Context->IntS3 = pef->IntS3;
|
|
Context->IntS4 = pef->IntS4;
|
|
Context->IntS5 = pef->IntS5;
|
|
Context->Psr = pef->Psr;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
ZeroMemory( StackFrame, FIELD_OFFSET( STACKFRAME64, KdHelp.ThCallbackBStore) );
|
|
|
|
StackFrame->Virtual = TRUE;
|
|
|
|
StackFrame->AddrPC.Offset = Context->Fir;
|
|
StackFrame->AddrPC.Mode = AddrModeFlat;
|
|
|
|
StackFrame->AddrFrame.Offset = Context->IntSp;
|
|
StackFrame->AddrFrame.Mode = AddrModeFlat;
|
|
|
|
ContextSave = *Context;
|
|
PcOffset = StackFrame->AddrPC.Offset;
|
|
FrameOffset = StackFrame->AddrFrame.Offset;
|
|
|
|
if (!WalkAlphaGetStackFrame( hProcess,
|
|
&PcOffset,
|
|
&FrameOffset,
|
|
&ContextSave,
|
|
ReadMemory,
|
|
GetModuleBase,
|
|
GetFunctionEntry,
|
|
&StackFrame->KdHelp,
|
|
Use64) ) {
|
|
|
|
StackFrame->AddrReturn.Offset = Context->IntRa;
|
|
|
|
} else {
|
|
|
|
StackFrame->AddrReturn.Offset = PcOffset;
|
|
}
|
|
|
|
StackFrame->AddrReturn.Mode = AddrModeFlat;
|
|
|
|
//
|
|
// get the arguments to the function
|
|
//
|
|
StackFrame->Params[0] = Context->IntA0;
|
|
StackFrame->Params[1] = Context->IntA1;
|
|
StackFrame->Params[2] = Context->IntA2;
|
|
StackFrame->Params[3] = Context->IntA3;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
WalkAlphaNext(
|
|
HANDLE hProcess,
|
|
LPSTACKFRAME64 StackFrame,
|
|
PALPHA_NT5_CONTEXT Context,
|
|
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
|
|
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
|
|
PFUNCTION_TABLE_ACCESS_ROUTINE64 GetFunctionEntry,
|
|
BOOL Use64
|
|
)
|
|
{
|
|
DWORD cb;
|
|
ALPHA_NT5_CONTEXT ContextSave;
|
|
BOOL rval = TRUE;
|
|
ULONG64 Address;
|
|
FunctionEntryCache* Cache;
|
|
FeCacheEntry* CacheEntry;
|
|
ULONG64 SystemRangeStart;
|
|
DWORD dw;
|
|
ULONG64 qw;
|
|
|
|
|
|
if (!WalkAlphaGetStackFrame( hProcess,
|
|
&StackFrame->AddrPC.Offset,
|
|
&StackFrame->AddrFrame.Offset,
|
|
Context,
|
|
ReadMemory,
|
|
GetModuleBase,
|
|
GetFunctionEntry,
|
|
&StackFrame->KdHelp,
|
|
Use64) ) {
|
|
|
|
rval = FALSE;
|
|
|
|
//
|
|
// If the frame could not be unwound or is terminal, see if
|
|
// there is a callback frame:
|
|
//
|
|
|
|
if (g.AppVersion.Revision >= 4 && CALLBACK_STACK(StackFrame)) {
|
|
|
|
if (g.AppVersion.Revision >= 6) {
|
|
SystemRangeStart = SYSTEM_RANGE_START(StackFrame);
|
|
} else {
|
|
//
|
|
// This might not really work right with old debuggers, but it keeps
|
|
// us from looking off the end of the structure anyway.
|
|
//
|
|
SystemRangeStart = 0x80000000;
|
|
}
|
|
|
|
if (CALLBACK_STACK(StackFrame) >= SystemRangeStart) {
|
|
|
|
//
|
|
// it is the pointer to the stack frame that we want,
|
|
// or -1.
|
|
|
|
Address = CALLBACK_STACK(StackFrame);
|
|
|
|
} else {
|
|
|
|
//
|
|
// if it is a positive integer, it is the offset to
|
|
// the address in the thread.
|
|
// Look up the pointer:
|
|
//
|
|
|
|
if (Use64) {
|
|
rval = ReadMemory(hProcess,
|
|
(CALLBACK_THREAD(StackFrame) +
|
|
CALLBACK_STACK(StackFrame)),
|
|
&Address,
|
|
sizeof(ULONG64),
|
|
&cb);
|
|
} else {
|
|
rval = ReadMemory(hProcess,
|
|
(CALLBACK_THREAD(StackFrame) +
|
|
CALLBACK_STACK(StackFrame)),
|
|
&dw,
|
|
sizeof(DWORD),
|
|
&cb);
|
|
Address = (ULONG64)(LONG64)(LONG)dw;
|
|
}
|
|
|
|
if (!rval || Address == 0) {
|
|
Address = (ULONG64)-1;
|
|
CALLBACK_STACK(StackFrame) = (DWORD)-1;
|
|
}
|
|
|
|
}
|
|
|
|
if ( (Address == (ULONG64)-1) ||
|
|
(Cache = GetFeCache(Use64 ?
|
|
IMAGE_FILE_MACHINE_ALPHA64 :
|
|
IMAGE_FILE_MACHINE_ALPHA,
|
|
TRUE)) == NULL ||
|
|
(CacheEntry = Cache->Find(hProcess, CALLBACK_FUNC(StackFrame),
|
|
ReadMemory, GetModuleBase,
|
|
GetFunctionEntry)) == NULL ) {
|
|
|
|
rval = FALSE;
|
|
|
|
} else {
|
|
|
|
if (Use64) {
|
|
ReadMemory(hProcess,
|
|
(Address + CALLBACK_NEXT(StackFrame)),
|
|
&CALLBACK_STACK(StackFrame),
|
|
sizeof(ULONG64),
|
|
&cb);
|
|
StackFrame->AddrPC.Offset = ALPHA_RF_PROLOG_END_ADDRESS(&CacheEntry->Data.Axp64);
|
|
} else {
|
|
ReadMemory(hProcess,
|
|
(Address + CALLBACK_NEXT(StackFrame)),
|
|
&dw,
|
|
sizeof(DWORD),
|
|
&cb);
|
|
CALLBACK_STACK(StackFrame) = dw;
|
|
StackFrame->AddrPC.Offset = ALPHA_RF_PROLOG_END_ADDRESS((PIMAGE_ALPHA_RUNTIME_FUNCTION_ENTRY)&CacheEntry->Data.Axp64);
|
|
}
|
|
|
|
StackFrame->AddrFrame.Offset = Address;
|
|
Context->IntSp = Address;
|
|
|
|
rval = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// get the return address
|
|
//
|
|
ContextSave = *Context;
|
|
StackFrame->AddrReturn.Offset = StackFrame->AddrPC.Offset;
|
|
qw = 0;
|
|
|
|
if (!WalkAlphaGetStackFrame( hProcess,
|
|
&StackFrame->AddrReturn.Offset,
|
|
&qw,
|
|
&ContextSave,
|
|
ReadMemory,
|
|
GetModuleBase,
|
|
GetFunctionEntry,
|
|
&StackFrame->KdHelp,
|
|
Use64) ) {
|
|
|
|
StackFrame->AddrReturn.Offset = 0;
|
|
|
|
}
|
|
|
|
//
|
|
// get the arguments to the function
|
|
//
|
|
StackFrame->Params[0] = ContextSave.IntA0;
|
|
StackFrame->Params[1] = ContextSave.IntA1;
|
|
StackFrame->Params[2] = ContextSave.IntA2;
|
|
StackFrame->Params[3] = ContextSave.IntA3;
|
|
|
|
return rval;
|
|
}
|
|
|
|
|
|
VOID
|
|
GetUnwindFunctionEntry(
|
|
HANDLE hProcess,
|
|
ULONG64 ControlPc,
|
|
PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
|
|
PGET_MODULE_BASE_ROUTINE64 GetModuleBase,
|
|
PFUNCTION_TABLE_ACCESS_ROUTINE64 GetFunctionEntry,
|
|
BOOL Use64,
|
|
FeCacheEntry* CacheEntry,
|
|
PIMAGE_ALPHA64_RUNTIME_FUNCTION_ENTRY UnwindFunctionEntry,
|
|
PULONG StackAdjust,
|
|
PULONG64 FixedReturn
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns a function entry (RUNTIME_FUNCTION) suitable
|
|
for unwinding from ControlPc. It encapsulates the handling of primary
|
|
and secondary function entries so that this processing is not duplicated
|
|
in VirtualUnwind and other similar functions.
|
|
|
|
Arguments:
|
|
|
|
ControlPc - Supplies the address where control left the specified
|
|
function.
|
|
|
|
FunctionEntry - Supplies the address of the function table entry for the
|
|
specified function.
|
|
|
|
UnwindFunctionEntry - Supplies the address of a function table entry which
|
|
will be setup with appropriate fields for unwinding from ControlPc
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG EntryType = 0;
|
|
PIMAGE_ALPHA64_RUNTIME_FUNCTION_ENTRY SecondaryFunctionEntry = NULL;
|
|
ULONG64 AlternateProlog;
|
|
FeCacheEntry LocalCache;
|
|
PIMAGE_ALPHA64_RUNTIME_FUNCTION_ENTRY FunctionEntry;
|
|
FunctionEntryCache* Cache;
|
|
FeCacheEntry* SecCache;
|
|
|
|
*FixedReturn = 0;
|
|
*StackAdjust = 0;
|
|
|
|
#if DBG
|
|
if (ControlPc & 0x3) {
|
|
dbPrint("GetUnwindFunctionEntry: invalid PC for unwinding (low bits set): %16.8I64x\n", ControlPc);
|
|
}
|
|
#endif
|
|
|
|
Cache = GetFeCache(Use64 ?
|
|
IMAGE_FILE_MACHINE_ALPHA64 : IMAGE_FILE_MACHINE_ALPHA,
|
|
TRUE);
|
|
|
|
// CacheEntry should never be null, but if it is create one that
|
|
// looks like a leaf entry for ControlPc
|
|
|
|
if (Cache == NULL || CacheEntry == NULL) {
|
|
#if DBG
|
|
dbPrint("\nGetUnwindFunctionEntry: Null function table entry for unwinding\n");
|
|
#endif
|
|
UnwindFunctionEntry->BeginAddress = ControlPc;
|
|
UnwindFunctionEntry->EndAddress = ControlPc+4;
|
|
UnwindFunctionEntry->ExceptionHandler = 0;
|
|
UnwindFunctionEntry->HandlerData = 0;
|
|
UnwindFunctionEntry->PrologEndAddress = ControlPc;
|
|
return;
|
|
}
|
|
|
|
// Work off a local copy of the function entry passed in
|
|
// as later function entry lookups may cause it to be
|
|
// evicted from the cache, invalidating the pointer.
|
|
LocalCache = *CacheEntry;
|
|
CacheEntry = &LocalCache;
|
|
FunctionEntry = &CacheEntry->Data.Axp64;
|
|
|
|
//
|
|
// Because of the secondary-to-primary function entry indirection applied by
|
|
// Find() ControlPc may not be within the range described
|
|
// by the supplied function entry. Call FindDirect()
|
|
// to recover the actual (secondary) function entry. If we don't get a
|
|
// valid associated function entry then process the unwind with the one
|
|
// supplied, trusting that the caller has supplied the given entry intentionally.
|
|
//
|
|
// A secondary function entry is a RUNTIME_FUNCTION entry where
|
|
// PrologEndAddress is not in the range of BeginAddress to EndAddress.
|
|
// There are three types of secondary function entries. They are
|
|
// distinquished by the Entry Type field (2 bits):
|
|
//
|
|
// ALPHA_RF_NOT_CONTIGUOUS - discontiguous code
|
|
// ALPHA_RF_ALT_ENT_PROLOG - alternate entry point prologue
|
|
// ALPHA_RF_NULL_CONTEXT - null-context code
|
|
//
|
|
|
|
if ((ControlPc < ALPHA_RF_BEGIN_ADDRESS(FunctionEntry)) ||
|
|
(ControlPc >= ALPHA_RF_END_ADDRESS(FunctionEntry))) {
|
|
|
|
// ControlPC is not in the range of the supplied function entry.
|
|
// Get the actual function entry which is expected to be the
|
|
// associated secondary function entry or a fixed return primary function.
|
|
#if DBG
|
|
if (tlsvar(DebugFunctionEntries)) {
|
|
dbPrint("\nGetUnwindFunctionEntry:LookupDirectFunctionEntry(ControlPc=%.8I64x,Use64=%d)\n", ControlPc, Use64 );
|
|
}
|
|
#endif
|
|
|
|
SecCache = Cache->
|
|
FindDirect( hProcess, ControlPc, ReadMemory, GetModuleBase,
|
|
GetFunctionEntry );
|
|
if (SecCache != NULL) {
|
|
SecondaryFunctionEntry = &SecCache->Data.Axp64;
|
|
} else {
|
|
SecondaryFunctionEntry = NULL;
|
|
}
|
|
|
|
if (SecondaryFunctionEntry) {
|
|
|
|
FE_ShowRuntimeFunctionAxp64((SecCache, "GetUnwindFunctionEntry: LookupDirectFunctionEntry"));
|
|
|
|
// If this is a null-context tail region then unwind with a null-context-like descriptor
|
|
|
|
if ((ControlPc >= ALPHA_RF_END_ADDRESS(SecondaryFunctionEntry)-(ALPHA_RF_NULL_CONTEXT_COUNT(SecondaryFunctionEntry)*4)) &&
|
|
(ControlPc < ALPHA_RF_END_ADDRESS(SecondaryFunctionEntry))) {
|
|
|
|
// Use the secondary function entry with PrologEndAddress = BeginAddress.
|
|
// This ensures that the prologue is not reverse executed.
|
|
|
|
UnwindFunctionEntry->BeginAddress = ALPHA_RF_BEGIN_ADDRESS(SecondaryFunctionEntry);
|
|
UnwindFunctionEntry->EndAddress = ALPHA_RF_END_ADDRESS(SecondaryFunctionEntry);
|
|
UnwindFunctionEntry->ExceptionHandler = 0;
|
|
UnwindFunctionEntry->HandlerData = 0;
|
|
UnwindFunctionEntry->PrologEndAddress = ALPHA_RF_BEGIN_ADDRESS(SecondaryFunctionEntry);
|
|
return;
|
|
}
|
|
|
|
if ((SecondaryFunctionEntry->PrologEndAddress < ALPHA_RF_BEGIN_ADDRESS(SecondaryFunctionEntry)) ||
|
|
(SecondaryFunctionEntry->PrologEndAddress > ALPHA_RF_END_ADDRESS(SecondaryFunctionEntry))) {
|
|
|
|
// Got a secondary function entry as expected. But if indirection doesn't point
|
|
// to FunctionEntry then ignore it and use the caller supplied FunctionEntry.
|
|
|
|
if (ALPHA_RF_PROLOG_END_ADDRESS(SecondaryFunctionEntry) != CacheEntry->Address) {
|
|
FE_ShowRuntimeFunctionAxp64((SecCache,
|
|
"GetUnwindFunctionEntry: unexpected secondary function entry from LookupDirectFunctionEntry"));
|
|
SecondaryFunctionEntry = NULL;
|
|
}
|
|
} else if (ALPHA_RF_IS_FIXED_RETURN(SecondaryFunctionEntry)) {
|
|
// Got a fixed return entry. Switch to using the fixed return entry as the primary.
|
|
|
|
FunctionEntry = SecondaryFunctionEntry;
|
|
CacheEntry = SecCache;
|
|
SecondaryFunctionEntry = NULL;
|
|
|
|
} else {
|
|
|
|
// Got a primary function entry. Ignore it and use caller supplied FunctionEntry.
|
|
FE_ShowRuntimeFunctionAxp64((SecCache,
|
|
"GetUnwindFunctionEntry: unexpected primary function entry from LookupDirectFunctionEntry"));
|
|
SecondaryFunctionEntry = NULL;
|
|
}
|
|
#if DBG
|
|
} else {
|
|
ShowRuntimeFunctionAxp64(SecCache, "GetUnwindFunctionEntry: LookupDirectFunctionEntry returned NULL");
|
|
#endif
|
|
}
|
|
} else {
|
|
|
|
// ControlPC is in the range of the supplied function entry.
|
|
|
|
// If this is a null-context tail region then unwind with a null-context-like descriptor
|
|
|
|
if ((ControlPc >= ALPHA_RF_END_ADDRESS(FunctionEntry)-(ALPHA_RF_NULL_CONTEXT_COUNT(FunctionEntry)*4)) &&
|
|
(ControlPc < ALPHA_RF_END_ADDRESS(FunctionEntry))) {
|
|
|
|
// Use the secondary function entry with PrologEndAddress = BeginAddress.
|
|
// This ensures that the prologue is not reverse executed.
|
|
|
|
UnwindFunctionEntry->BeginAddress = ALPHA_RF_BEGIN_ADDRESS(FunctionEntry);
|
|
UnwindFunctionEntry->EndAddress = ALPHA_RF_END_ADDRESS(FunctionEntry);
|
|
UnwindFunctionEntry->ExceptionHandler = 0;
|
|
UnwindFunctionEntry->HandlerData = 0;
|
|
UnwindFunctionEntry->PrologEndAddress = ALPHA_RF_BEGIN_ADDRESS(FunctionEntry);
|
|
return;
|
|
}
|
|
|
|
// Check if it is a secondary function entry. This shouldn't happen because
|
|
// LookupFunctionEntry is always supposed to return a primary function entry.
|
|
// But if we get passed a secondary, then switch to it's primary. However note
|
|
// that we've gone through this pass
|
|
|
|
if ((FunctionEntry->PrologEndAddress < ALPHA_RF_BEGIN_ADDRESS(FunctionEntry)) ||
|
|
(FunctionEntry->PrologEndAddress > ALPHA_RF_END_ADDRESS(FunctionEntry))) {
|
|
SecondaryFunctionEntry = FunctionEntry;
|
|
SecCache = Cache->ReadImage
|
|
( hProcess,
|
|
ALPHA_RF_PROLOG_END_ADDRESS(SecondaryFunctionEntry),
|
|
ReadMemory, GetModuleBase );
|
|
if (SecCache != NULL) {
|
|
SecondaryFunctionEntry = &SecCache->Data.Axp64;
|
|
} else {
|
|
SecondaryFunctionEntry = NULL;
|
|
}
|
|
FE_ShowRuntimeFunctionAxp64((SecCache,
|
|
"GetUnwindFunctionEntry: received secondary function entry"));
|
|
}
|
|
}
|
|
|
|
// FunctionEntry is now the primary function entry and if SecondaryFunctionEntry is
|
|
// not NULL then it is the secondary function entry that contains the ControlPC. Setup a
|
|
// copy of the FunctionEntry suitable for unwinding. By default use the supplied FunctionEntry.
|
|
|
|
if (SecondaryFunctionEntry) {
|
|
|
|
// Extract the secondary function entry type.
|
|
|
|
EntryType = ALPHA_RF_ENTRY_TYPE(SecondaryFunctionEntry);
|
|
|
|
if (EntryType == ALPHA_RF_NOT_CONTIGUOUS) {
|
|
// The exception happened in the body of the procedure but in a non-contiguous
|
|
// section of code. Regardless of what entry point was used, it is normally valid
|
|
// to unwind using the primary entry point prologue. The only exception is when an
|
|
// alternate prologue is specified However, there may be an
|
|
// alternate prologue end addresss specified in which case unwind using this
|
|
// block as though it were the primary.
|
|
|
|
AlternateProlog = ALPHA_RF_ALT_PROLOG64(SecondaryFunctionEntry);
|
|
|
|
if ((AlternateProlog >= ALPHA_RF_BEGIN_ADDRESS(SecondaryFunctionEntry)) &&
|
|
(AlternateProlog < ALPHA_RF_END_ADDRESS(SecondaryFunctionEntry))) {
|
|
|
|
// If the control PC is in the alternate prologue, use the secondary.
|
|
// The control Pc is not in procedure context.
|
|
|
|
if ((ControlPc >= ALPHA_RF_BEGIN_ADDRESS(SecondaryFunctionEntry)) &&
|
|
(ControlPc < ALPHA_RF_END_ADDRESS(SecondaryFunctionEntry))) {
|
|
|
|
UnwindFunctionEntry->BeginAddress = ALPHA_RF_BEGIN_ADDRESS(SecondaryFunctionEntry);
|
|
UnwindFunctionEntry->EndAddress = ALPHA_RF_END_ADDRESS(SecondaryFunctionEntry);
|
|
UnwindFunctionEntry->ExceptionHandler = 0;
|
|
UnwindFunctionEntry->HandlerData = 0;
|
|
UnwindFunctionEntry->PrologEndAddress = AlternateProlog;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Fall out of the if statement to pick up the primary function entry below.
|
|
// This code is in-procedure-context and subject to the primary's prologue
|
|
// and exception handlers.
|
|
|
|
} else if (EntryType == ALPHA_RF_ALT_ENT_PROLOG) {
|
|
// Exception occured in an alternate entry point prologue.
|
|
// Use the secondary function entry with a fixed-up PrologEndAddress.
|
|
|
|
UnwindFunctionEntry->BeginAddress = ALPHA_RF_BEGIN_ADDRESS(SecondaryFunctionEntry);
|
|
UnwindFunctionEntry->EndAddress = ALPHA_RF_END_ADDRESS(SecondaryFunctionEntry);
|
|
UnwindFunctionEntry->ExceptionHandler = 0;
|
|
UnwindFunctionEntry->HandlerData = 0;
|
|
UnwindFunctionEntry->PrologEndAddress = ALPHA_RF_END_ADDRESS(UnwindFunctionEntry);
|
|
|
|
// Check for an alternate prologue.
|
|
|
|
AlternateProlog = ALPHA_RF_ALT_PROLOG64(SecondaryFunctionEntry);
|
|
if (AlternateProlog >= UnwindFunctionEntry->BeginAddress &&
|
|
AlternateProlog < UnwindFunctionEntry->EndAddress ) {
|
|
// The prologue is only part of the procedure
|
|
UnwindFunctionEntry->PrologEndAddress = AlternateProlog;
|
|
}
|
|
|
|
return;
|
|
|
|
} else if (EntryType == ALPHA_RF_NULL_CONTEXT) {
|
|
|
|
// Exception occured in null-context code associated with a primary function.
|
|
// Use the secondary function entry with a PrologEndAddress = BeginAddress.
|
|
// There is no prologue for null-context code.
|
|
|
|
*StackAdjust = ALPHA_RF_STACK_ADJUST(SecondaryFunctionEntry);
|
|
UnwindFunctionEntry->BeginAddress = ALPHA_RF_BEGIN_ADDRESS(SecondaryFunctionEntry);
|
|
UnwindFunctionEntry->EndAddress = ALPHA_RF_END_ADDRESS(SecondaryFunctionEntry);
|
|
UnwindFunctionEntry->ExceptionHandler = 0;
|
|
UnwindFunctionEntry->HandlerData = 0;
|
|
UnwindFunctionEntry->PrologEndAddress = ALPHA_RF_BEGIN_ADDRESS(SecondaryFunctionEntry);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// FunctionEntry is only null if there was an error fetching it from a passed in
|
|
// secondary function entry.
|
|
|
|
if (FunctionEntry == NULL) {
|
|
#if DBG
|
|
dbPrint("\nGetUnwindFunctionEntry: Error in FetchFunctionEntry\n");
|
|
#endif
|
|
UnwindFunctionEntry->BeginAddress = ControlPc;
|
|
UnwindFunctionEntry->EndAddress = ControlPc+4;
|
|
UnwindFunctionEntry->ExceptionHandler = 0;
|
|
UnwindFunctionEntry->HandlerData = 0;
|
|
UnwindFunctionEntry->PrologEndAddress = ControlPc;
|
|
return;
|
|
}
|
|
|
|
#if DBG
|
|
if (ALPHA_RF_BEGIN_ADDRESS(FunctionEntry) >= ALPHA_RF_END_ADDRESS(FunctionEntry)) {
|
|
ShowRuntimeFunctionAxp64(CacheEntry, "GetUnwindFunctionEntry: Warning - BeginAddress < EndAddress.");
|
|
} else if (FunctionEntry->PrologEndAddress < ALPHA_RF_BEGIN_ADDRESS(FunctionEntry)) {
|
|
ShowRuntimeFunctionAxp64(CacheEntry, "GetUnwindFunctionEntry: Warning - PrologEndAddress < BeginAddress.");
|
|
} else if (FunctionEntry->PrologEndAddress > ALPHA_RF_END_ADDRESS(FunctionEntry)) {
|
|
ShowRuntimeFunctionAxp64(CacheEntry, "GetUnwindFunctionEntry: Warning - PrologEndAddress > EndAddress.");
|
|
}
|
|
#endif
|
|
|
|
// Use the primary function entry
|
|
|
|
*UnwindFunctionEntry = *FunctionEntry;
|
|
UnwindFunctionEntry->EndAddress = ALPHA_RF_END_ADDRESS(UnwindFunctionEntry); // Remove null-context count
|
|
|
|
// If the primary has a fixed return address, pull that out now.
|
|
|
|
if (ALPHA_RF_IS_FIXED_RETURN(FunctionEntry)) {
|
|
*FixedReturn = ALPHA_RF_FIXED_RETURN64(FunctionEntry);
|
|
UnwindFunctionEntry->ExceptionHandler = 0;
|
|
UnwindFunctionEntry->HandlerData = 0;
|
|
}
|
|
}
|