blob: 7ce7913e1ac1ae2011d2c6899508288632e47f9b [file] [edit]
/*
* Stack walking
*
* Copyright 1995 Alexandre Julliard
* Copyright 1996 Eric Youngdale
* Copyright 1999 Ove Kåven
* Copyright 2004 Eric Pouech
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "dbghelp_private.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
static DWORD64 WINAPI addr_to_linear(HANDLE hProcess, HANDLE hThread, ADDRESS64* addr)
{
LDT_ENTRY le;
switch (addr->Mode)
{
case AddrMode1616:
if (GetThreadSelectorEntry(hThread, addr->Segment, &le))
return (le.HighWord.Bits.BaseHi << 24) +
(le.HighWord.Bits.BaseMid << 16) + le.BaseLow + LOWORD(addr->Offset);
break;
case AddrMode1632:
if (GetThreadSelectorEntry(hThread, addr->Segment, &le))
return (le.HighWord.Bits.BaseHi << 24) +
(le.HighWord.Bits.BaseMid << 16) + le.BaseLow + addr->Offset;
break;
case AddrModeReal:
return (DWORD)(LOWORD(addr->Segment) << 4) + addr->Offset;
case AddrModeFlat:
return addr->Offset;
default:
FIXME("Unsupported (yet) mode (%x)\n", addr->Mode);
return 0;
}
FIXME("Failed to linearize address %04x:%I64x (mode %x)\n",
addr->Segment, addr->Offset, addr->Mode);
return 0;
}
#ifndef _WIN64
static BOOL CALLBACK read_mem(HANDLE hProcess, DWORD addr, void* buffer,
DWORD size, LPDWORD nread)
{
SIZE_T r;
if (!ReadProcessMemory(hProcess, (void*)(DWORD_PTR)addr, buffer, size, &r)) return FALSE;
if (nread) *nread = r;
return TRUE;
}
#endif
static BOOL CALLBACK read_mem64(HANDLE hProcess, DWORD64 addr, void* buffer,
DWORD size, LPDWORD nread)
{
SIZE_T r;
if (!ReadProcessMemory(hProcess, (void*)(DWORD_PTR)addr, buffer, size, &r)) return FALSE;
if (nread) *nread = r;
return TRUE;
}
/*
* Access ARM64EC/CHPE metadata that describes the type of code (ARM64,
* ARM64EC, x64) for each range in dbghelp's mapped copy of the module.
* This follows the same load-config CHPEMetadataPointer lookup used by
* tools/winedump/pe.c:get_hybrid_metadata(), but uses dbghelp's module mapping
* helpers to read from the PE module.
*/
BOOL read_module_chpe_metadata(struct module *module, IMAGE_ARM64EC_METADATA *metadata)
{
const IMAGE_LOAD_CONFIG_DIRECTORY64 *cfg;
const BYTE *metadata_ptr;
DWORD size, rva;
DWORD64 image_base;
if (!(cfg = (const IMAGE_LOAD_CONFIG_DIRECTORY64 *)pe_map_directory(module,
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, &size)))
return FALSE;
/* Bail out if the 'load-config directory' doesn't contain CHPE metadata. */
size = min(size, cfg->Size);
if (size <= offsetof(IMAGE_LOAD_CONFIG_DIRECTORY64, CHPEMetadataPointer) || !cfg->CHPEMetadataPointer)
{
pe_unmap_directory(module, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, (const char *)cfg);
return FALSE;
}
image_base = module->module.BaseOfImage - module->reloc_delta;
/* Bail out if CHPEMetadataPointer points outside the module's image. */
if (cfg->CHPEMetadataPointer < image_base || cfg->CHPEMetadataPointer >= image_base + module->module.ImageSize)
{
pe_unmap_directory(module, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, (const char *)cfg);
return FALSE;
}
/* Convert from virtual address to relative virtual address. */
rva = cfg->CHPEMetadataPointer - image_base;
pe_unmap_directory(module, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, (const char *)cfg);
/* Obtain a pointer to the metadata and read from it. */
if (!(metadata_ptr = pe_lock_region_from_rva(module, rva, sizeof(*metadata), NULL))) return FALSE;
*metadata = *(const IMAGE_ARM64EC_METADATA *)metadata_ptr;
pe_unlock_region(module, metadata_ptr);
return TRUE;
}
#ifdef __aarch64__
/* Offset of STACKFRAME64's 'Reserved' field used to store the effective
machine between calls */
#define STACKWALK_EFFECTIVE_MACHINE_RESERVED 2
/*
* Return the walker machine for a PC inside an ARM64EC/ARM64X CodeMap range.
* ARM64EC ranges are ARM64 instructions with ARM64 unwind records, so they
* use the ARM64 walker; x64 ranges use the AMD64 walker.
*/
static DWORD chpe_range_effective_machine(struct module *module, DWORD64 pc)
{
const IMAGE_CHPE_RANGE_ENTRY *map;
IMAGE_ARM64EC_METADATA metadata;
DWORD machine = 0;
ULONG i;
/* Bail out if the module has no CodeMap to classify mixed-code ranges. */
if (!read_module_chpe_metadata(module, &metadata) || !metadata.CodeMap || !metadata.CodeMapCount)
return 0;
/* Map the CodeMap array described by the ARM64EC metadata. */
if (!(map = (const IMAGE_CHPE_RANGE_ENTRY *)pe_lock_region_from_rva(module, metadata.CodeMap,
metadata.CodeMapCount * sizeof(*map), NULL)))
return 0;
/*
* Interpret the CodeMap range table in the same way as
* dlls/ntdll/unix/virtual.c:update_arm64ec_ranges(). dbghelp uses its PE
* module mapping helpers instead of ntdll's live process mapping.
*/
for (i = 0; i < metadata.CodeMapCount; i++)
{
DWORD64 start, end;
DWORD type = map[i].StartOffset & 0x3;
/* Convert the CodeMap RVA range to loaded module addresses. */
start = module->module.BaseOfImage + (map[i].StartOffset & ~3u);
end = start + map[i].Length;
if (pc >= start && pc < end)
{
switch (type)
{
case 0: /* ARM64 */
case 1: /* ARM64EC */
machine = IMAGE_FILE_MACHINE_ARM64;
break;
case 2: /* x64 */
machine = IMAGE_FILE_MACHINE_AMD64;
break;
}
break;
}
}
pe_unlock_region(module, (const BYTE *)map);
return machine;
}
/* Return the stack walker machine for a PC, refining hybrid modules with the CHPE CodeMap. */
DWORD stack_effective_machine(HANDLE process, DWORD64 pc)
{
struct module *module;
DWORD machine;
if (!pc || !(module = module_find_by_addr(process_find_by_handle(process), pc))) return 0;
switch (module->module.MachineType)
{
case IMAGE_FILE_MACHINE_AMD64:
/*
* Some hybrid modules have an AMD64-facing machine type but still carry
* CHPE ranges. Prefer the range classification when present.
*/
if ((machine = chpe_range_effective_machine(module, pc))) return machine;
return IMAGE_FILE_MACHINE_AMD64;
case IMAGE_FILE_MACHINE_ARM64:
return IMAGE_FILE_MACHINE_ARM64;
case IMAGE_FILE_MACHINE_ARM64EC:
case IMAGE_FILE_MACHINE_ARM64X:
/*
* ARM64EC/ARM64X modules are hybrid. Use the CodeMap to distinguish
* ARM64/ARM64EC ranges from x64 ranges; fall back to the x64-compatible
* view if the PC is not classified.
*/
if ((machine = chpe_range_effective_machine(module, pc))) return machine;
return IMAGE_FILE_MACHINE_AMD64;
default:
return 0;
}
}
static BOOL is_supported_effective_machine(DWORD64 machine)
{
return machine == IMAGE_FILE_MACHINE_AMD64 || machine == IMAGE_FILE_MACHINE_ARM64;
}
/*
* ARM64EC code uses ARM64 instructions and unwind records, but exposes an
* x64-compatible register context. This follows the ARM64EC path in
* dlls/ntdll/unwind.c:RtlVirtualUnwind2().
*
* These helpers only reshape the state needed to continue stack walking across
* ARM64EC/x64 transition frames; they do not reconstruct a complete
* architectural context. In particular, SIMD/FPU/debug state and precise flags
* are not modeled here.
*/
/*
* Convert ARM64 unwind state back to an AMD64-shaped context. This is the
* subset of dlls/ntdll/unwind.h:context_arm_to_x64() needed by StackWalkEx().
*/
static void arm64ec_unwind_to_amd64_context(AMD64_CONTEXT *amd64, const CONTEXT *arm64)
{
memset(amd64, 0, sizeof(*amd64));
amd64->ContextFlags = CONTEXT_AMD64_FULL;
/* The AMD64 walker expects plausible segment/flag values, but they are not unwind state. */
amd64->SegCs = 0x33;
amd64->SegDs = 0x2b;
amd64->SegEs = 0x2b;
amd64->SegFs = 0x53;
amd64->SegGs = 0x2b;
amd64->SegSs = 0x2b;
amd64->EFlags = 0x202;
amd64->Rax = arm64->X8;
amd64->Rcx = arm64->X0;
amd64->Rdx = arm64->X1;
amd64->Rbx = arm64->X27;
amd64->Rsp = arm64->Sp;
amd64->Rbp = arm64->Fp;
amd64->Rsi = arm64->X25;
amd64->Rdi = arm64->X26;
amd64->R8 = arm64->X2;
amd64->R9 = arm64->X3;
amd64->R10 = arm64->X4;
amd64->R11 = arm64->X5;
amd64->R12 = arm64->X19;
amd64->R13 = arm64->X20;
amd64->R14 = arm64->X21;
amd64->R15 = arm64->X22;
amd64->Rip = arm64->Pc;
}
/*
* Convert an AMD64-shaped context to the ARM64 state needed by the ARM64
* walker. This is the subset of dlls/ntdll/unwind.h:context_x64_to_arm()
* needed by StackWalkEx().
*/
static void amd64_unwind_to_arm64ec_context(CONTEXT *arm64, const AMD64_CONTEXT *amd64)
{
memset(arm64, 0, sizeof(*arm64));
arm64->ContextFlags = CONTEXT_FULL;
arm64->X8 = amd64->Rax;
arm64->X0 = amd64->Rcx;
arm64->X1 = amd64->Rdx;
arm64->X27 = amd64->Rbx;
arm64->Sp = amd64->Rsp;
arm64->Fp = amd64->Rbp;
arm64->X25 = amd64->Rsi;
arm64->X26 = amd64->Rdi;
arm64->X2 = amd64->R8;
arm64->X3 = amd64->R9;
arm64->X4 = amd64->R10;
arm64->X5 = amd64->R11;
arm64->X19 = amd64->R12;
arm64->X20 = amd64->R13;
arm64->X21 = amd64->R14;
arm64->X22 = amd64->R15;
arm64->Pc = amd64->Rip;
}
/*
* After converting the register context, rebuild the generic STACKFRAME64
* fields expected by the selected CPU walker.
*/
static void set_arm64_frame(STACKFRAME64 *frame, const CONTEXT *ctx)
{
frame->AddrFrame.Mode = frame->AddrPC.Mode = frame->AddrStack.Mode = frame->AddrReturn.Mode = AddrModeFlat;
frame->AddrFrame.Segment = frame->AddrPC.Segment = frame->AddrStack.Segment = frame->AddrReturn.Segment = 0;
frame->AddrFrame.Offset = ctx->Fp;
frame->AddrPC.Offset = ctx->Pc;
frame->AddrStack.Offset = ctx->Sp;
/* ARM64 carries the return address in LR. Keep AddrReturn nonzero if LR is unavailable. */
frame->AddrReturn.Offset = ctx->Lr ? ctx->Lr : ctx->Pc;
}
static void set_amd64_frame(STACKFRAME64 *frame, const AMD64_CONTEXT *ctx)
{
frame->AddrFrame.Mode = frame->AddrPC.Mode = frame->AddrStack.Mode = frame->AddrReturn.Mode = AddrModeFlat;
frame->AddrFrame.Segment = frame->AddrStack.Segment = frame->AddrReturn.Segment = ctx->SegSs;
frame->AddrPC.Segment = ctx->SegCs;
frame->AddrFrame.Offset = ctx->Rbp;
frame->AddrPC.Offset = ctx->Rip;
frame->AddrStack.Offset = ctx->Rsp;
/*
* As AMD64 has no link register, seed AddrReturn with a nonzero value. The
* AMD64 walker will replace it with the caller RIP after trial-unwinding
* the context.
*/
frame->AddrReturn.Offset = ctx->Rip;
}
/*
* Switch the active CPU walker and translate the context when the current
* frame belongs to another machine. Return FALSE only if the requested switch
* cannot be performed; changed records whether a switch was actually made.
*/
static BOOL stack_update_effective_machine(struct cpu_stack_walk *csw, DWORD *machine, struct cpu **cpu,
STACKFRAME64 *frame, union ctx *context, DWORD next_machine,
BOOL *changed)
{
struct cpu *next_cpu;
if (changed) *changed = FALSE;
if (!is_supported_effective_machine(next_machine)) return TRUE;
if (next_machine == *machine)
{
frame->Reserved[STACKWALK_EFFECTIVE_MACHINE_RESERVED] = *machine;
return TRUE;
}
if (!is_supported_effective_machine(*machine)) return TRUE;
if (!(next_cpu = cpu_find(next_machine))) return FALSE;
if (*machine == IMAGE_FILE_MACHINE_AMD64)
{
AMD64_CONTEXT amd64 = context->amd64;
amd64_unwind_to_arm64ec_context(&context->ctx, &amd64);
context->ctx.Lr = frame->AddrReturn.Offset ? frame->AddrReturn.Offset : context->ctx.Pc;
set_arm64_frame(frame, &context->ctx);
}
else
{
CONTEXT arm64 = context->ctx;
arm64ec_unwind_to_amd64_context(&context->amd64, &arm64);
set_amd64_frame(frame, &context->amd64);
}
memset(frame->Reserved, 0, sizeof(frame->Reserved));
frame->Reserved[STACKWALK_EFFECTIVE_MACHINE_RESERVED] = next_machine;
*machine = next_machine;
*cpu = next_cpu;
csw->cpu = next_cpu;
if (changed) *changed = TRUE;
return TRUE;
}
#endif
#ifndef _WIN64
static inline void addr_32to64(const ADDRESS* addr32, ADDRESS64* addr64)
{
addr64->Offset = (ULONG64)addr32->Offset;
addr64->Segment = addr32->Segment;
addr64->Mode = addr32->Mode;
}
#endif
static inline void addr_64to32(const ADDRESS64* addr64, ADDRESS* addr32)
{
addr32->Offset = (ULONG)addr64->Offset;
addr32->Segment = addr64->Segment;
addr32->Mode = addr64->Mode;
}
BOOL sw_read_mem(struct cpu_stack_walk* csw, DWORD64 addr, void* ptr, DWORD sz)
{
DWORD bytes_read = 0;
if (csw->is32)
return csw->u.s32.f_read_mem(csw->hProcess, addr, ptr, sz, &bytes_read);
else
return csw->u.s64.f_read_mem(csw->hProcess, addr, ptr, sz, &bytes_read);
}
DWORD64 sw_xlat_addr(struct cpu_stack_walk* csw, ADDRESS64* addr)
{
if (addr->Mode == AddrModeFlat) return addr->Offset;
if (csw->is32)
{
ADDRESS addr32;
addr_64to32(addr, &addr32);
return csw->u.s32.f_xlat_adr(csw->hProcess, csw->hThread, &addr32);
}
else if (csw->u.s64.f_xlat_adr)
return csw->u.s64.f_xlat_adr(csw->hProcess, csw->hThread, addr);
return addr_to_linear(csw->hProcess, csw->hThread, addr);
}
void* sw_table_access(struct cpu_stack_walk* csw, DWORD64 addr)
{
if (csw->is32)
return csw->u.s32.f_tabl_acs(csw->hProcess, addr);
else
return csw->u.s64.f_tabl_acs(csw->hProcess, addr);
}
DWORD64 sw_module_base(struct cpu_stack_walk* csw, DWORD64 addr)
{
if (csw->is32)
return csw->u.s32.f_modl_bas(csw->hProcess, addr);
else
return csw->u.s64.f_modl_bas(csw->hProcess, addr);
}
#ifndef _WIN64
/***********************************************************************
* StackWalk (DBGHELP.@)
*/
BOOL WINAPI StackWalk(DWORD MachineType, HANDLE hProcess, HANDLE hThread,
LPSTACKFRAME frame32, PVOID ctx,
PREAD_PROCESS_MEMORY_ROUTINE f_read_mem,
PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine,
PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine,
PTRANSLATE_ADDRESS_ROUTINE f_xlat_adr)
{
struct cpu_stack_walk csw;
STACKFRAME64 frame64;
BOOL ret;
struct cpu* cpu;
TRACE("(%ld, %p, %p, %p, %p, %p, %p, %p, %p)\n",
MachineType, hProcess, hThread, frame32, ctx,
f_read_mem, FunctionTableAccessRoutine,
GetModuleBaseRoutine, f_xlat_adr);
if (!(cpu = cpu_find(MachineType)))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
addr_32to64(&frame32->AddrPC, &frame64.AddrPC);
addr_32to64(&frame32->AddrReturn, &frame64.AddrReturn);
addr_32to64(&frame32->AddrFrame, &frame64.AddrFrame);
addr_32to64(&frame32->AddrStack, &frame64.AddrStack);
addr_32to64(&frame32->AddrBStore, &frame64.AddrBStore);
frame64.FuncTableEntry = frame32->FuncTableEntry; /* FIXME */
frame64.Far = frame32->Far;
frame64.Virtual = frame32->Virtual;
frame64.Reserved[0] = frame32->Reserved[0];
frame64.Reserved[1] = frame32->Reserved[1];
frame64.Reserved[2] = frame32->Reserved[2];
/* we don't handle KdHelp */
csw.hProcess = hProcess;
csw.hThread = hThread;
csw.is32 = TRUE;
csw.cpu = cpu;
/* sigh... MS isn't even consistent in the func prototypes */
csw.u.s32.f_read_mem = (f_read_mem) ? f_read_mem : read_mem;
csw.u.s32.f_xlat_adr = f_xlat_adr;
csw.u.s32.f_tabl_acs = (FunctionTableAccessRoutine) ? FunctionTableAccessRoutine : SymFunctionTableAccess;
csw.u.s32.f_modl_bas = (GetModuleBaseRoutine) ? GetModuleBaseRoutine : SymGetModuleBase;
if ((ret = cpu->stack_walk(&csw, &frame64, ctx)))
{
addr_64to32(&frame64.AddrPC, &frame32->AddrPC);
addr_64to32(&frame64.AddrReturn, &frame32->AddrReturn);
addr_64to32(&frame64.AddrFrame, &frame32->AddrFrame);
addr_64to32(&frame64.AddrStack, &frame32->AddrStack);
addr_64to32(&frame64.AddrBStore, &frame32->AddrBStore);
frame32->FuncTableEntry = frame64.FuncTableEntry; /* FIXME */
frame32->Params[0] = frame64.Params[0];
frame32->Params[1] = frame64.Params[1];
frame32->Params[2] = frame64.Params[2];
frame32->Params[3] = frame64.Params[3];
frame32->Far = frame64.Far;
frame32->Virtual = frame64.Virtual;
frame32->Reserved[0] = frame64.Reserved[0];
frame32->Reserved[1] = frame64.Reserved[1];
frame32->Reserved[2] = frame64.Reserved[2];
}
return ret;
}
#endif
/***********************************************************************
* StackWalk64 (DBGHELP.@)
*/
BOOL WINAPI StackWalk64(DWORD MachineType, HANDLE hProcess, HANDLE hThread,
LPSTACKFRAME64 frame, PVOID ctx,
PREAD_PROCESS_MEMORY_ROUTINE64 f_read_mem,
PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
PTRANSLATE_ADDRESS_ROUTINE64 f_xlat_adr)
{
struct cpu_stack_walk csw;
struct cpu* cpu;
TRACE("(%ld, %p, %p, %p, %p, %p, %p, %p, %p)\n",
MachineType, hProcess, hThread, frame, ctx,
f_read_mem, FunctionTableAccessRoutine,
GetModuleBaseRoutine, f_xlat_adr);
if (!(cpu = cpu_find(MachineType)))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
csw.hProcess = hProcess;
csw.hThread = hThread;
csw.is32 = FALSE;
csw.cpu = cpu;
/* sigh... MS isn't even consistent in the func prototypes */
csw.u.s64.f_read_mem = (f_read_mem) ? f_read_mem : read_mem64;
csw.u.s64.f_xlat_adr = (f_xlat_adr) ? f_xlat_adr : addr_to_linear;
csw.u.s64.f_tabl_acs = (FunctionTableAccessRoutine) ? FunctionTableAccessRoutine : SymFunctionTableAccess64;
csw.u.s64.f_modl_bas = (GetModuleBaseRoutine) ? GetModuleBaseRoutine : SymGetModuleBase64;
if (!cpu->stack_walk(&csw, frame, ctx)) return FALSE;
/* we don't handle KdHelp */
return TRUE;
}
/* all the fields of STACKFRAME64 are present in STACKFRAME_EX at same offset
* So casting down a STACKFRAME_EX into a STACKFRAME64 is valid!
*/
C_ASSERT(sizeof(STACKFRAME64) == FIELD_OFFSET(STACKFRAME_EX, StackFrameSize));
C_ASSERT(FIELD_OFFSET(STACKFRAME64, AddrPC) == FIELD_OFFSET(STACKFRAME_EX, AddrPC));
C_ASSERT(FIELD_OFFSET(STACKFRAME64, AddrReturn) == FIELD_OFFSET(STACKFRAME_EX, AddrReturn));
C_ASSERT(FIELD_OFFSET(STACKFRAME64, AddrFrame) == FIELD_OFFSET(STACKFRAME_EX, AddrFrame));
C_ASSERT(FIELD_OFFSET(STACKFRAME64, AddrStack) == FIELD_OFFSET(STACKFRAME_EX, AddrStack));
C_ASSERT(FIELD_OFFSET(STACKFRAME64, AddrBStore) == FIELD_OFFSET(STACKFRAME_EX, AddrBStore));
C_ASSERT(FIELD_OFFSET(STACKFRAME64, FuncTableEntry) == FIELD_OFFSET(STACKFRAME_EX, FuncTableEntry));
C_ASSERT(FIELD_OFFSET(STACKFRAME64, Params) == FIELD_OFFSET(STACKFRAME_EX, Params));
C_ASSERT(FIELD_OFFSET(STACKFRAME64, Far) == FIELD_OFFSET(STACKFRAME_EX, Far));
C_ASSERT(FIELD_OFFSET(STACKFRAME64, Virtual) == FIELD_OFFSET(STACKFRAME_EX, Virtual));
C_ASSERT(FIELD_OFFSET(STACKFRAME64, Reserved) == FIELD_OFFSET(STACKFRAME_EX, Reserved));
C_ASSERT(FIELD_OFFSET(STACKFRAME64, KdHelp) == FIELD_OFFSET(STACKFRAME_EX, KdHelp));
/***********************************************************************
* StackWalkEx (DBGHELP.@)
*/
BOOL WINAPI StackWalkEx(DWORD MachineType, HANDLE hProcess, HANDLE hThread,
LPSTACKFRAME_EX frame, PVOID ctx,
PREAD_PROCESS_MEMORY_ROUTINE64 f_read_mem,
PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
PTRANSLATE_ADDRESS_ROUTINE64 f_xlat_adr,
DWORD flags)
{
struct cpu_stack_walk csw;
struct cpu* cpu;
DWORD64 addr;
DWORD machine = MachineType;
TRACE("(%ld, %p, %p, %p, %p, %p, %p, %p, %p, 0x%lx)\n",
MachineType, hProcess, hThread, frame, ctx,
f_read_mem, FunctionTableAccessRoutine,
GetModuleBaseRoutine, f_xlat_adr, flags);
if (frame->StackFrameSize != sizeof(*frame))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
#ifdef __aarch64__
if (is_supported_effective_machine(MachineType) &&
is_supported_effective_machine(frame->Reserved[STACKWALK_EFFECTIVE_MACHINE_RESERVED]))
machine = frame->Reserved[STACKWALK_EFFECTIVE_MACHINE_RESERVED];
#endif
if (!(cpu = cpu_find(machine)))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (flags != 0)
{
FIXME("Unsupported yet flags 0x%lx\n", flags);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
csw.hProcess = hProcess;
csw.hThread = hThread;
csw.is32 = FALSE;
csw.cpu = cpu;
/* sigh... MS isn't even consistent in the func prototypes */
csw.u.s64.f_read_mem = (f_read_mem) ? f_read_mem : read_mem64;
csw.u.s64.f_xlat_adr = (f_xlat_adr) ? f_xlat_adr : addr_to_linear;
csw.u.s64.f_tabl_acs = (FunctionTableAccessRoutine) ? FunctionTableAccessRoutine : SymFunctionTableAccess64;
csw.u.s64.f_modl_bas = (GetModuleBaseRoutine) ? GetModuleBaseRoutine : SymGetModuleBase64;
#ifdef __aarch64__
if (!stack_update_effective_machine(&csw, &machine, &cpu, (STACKFRAME64*)frame, ctx,
stack_effective_machine(csw.hProcess, frame->AddrPC.Offset), NULL))
return FALSE;
#endif
addr = sw_xlat_addr(&csw, &frame->AddrPC);
if (IFC_MODE(frame->InlineFrameContext) == IFC_MODE_INLINE)
{
DWORD depth = SymAddrIncludeInlineTrace(hProcess, addr);
if (IFC_DEPTH(frame->InlineFrameContext) + 1 < depth) /* move to next inlined function? */
{
TRACE("found inline ctx: depth=%lu current=%lu++\n",
depth, frame->InlineFrameContext);
frame->InlineFrameContext++; /* just increase index, FIXME detect overflow */
}
else
{
frame->InlineFrameContext = IFC_MODE_REGULAR; /* move to next top level function */
}
}
else
{
if (!cpu->stack_walk(&csw, (STACKFRAME64*)frame, ctx)) return FALSE;
#ifdef __aarch64__
{
BOOL changed;
/*
* The produced frame may belong to a different effective machine
* from the frame just consumed. Reclassify it before returning to
* the caller. This runs after every public StackWalkEx() step, so
* later steps can switch back again.
*/
if (!stack_update_effective_machine(&csw, &machine, &cpu, (STACKFRAME64*)frame, ctx,
stack_effective_machine(csw.hProcess, frame->AddrPC.Offset), &changed))
return FALSE;
if (changed)
{
/*
* Switching walkers clears Reserved[0], which is the selected
* walker's private state. The first call to a walker only
* initializes it from the current frame, so do that here.
* Otherwise the next public StackWalkEx() call would return
* the same frame again.
*/
if (!cpu->stack_walk(&csw, (STACKFRAME64*)frame, ctx)) return FALSE;
}
}
#endif
if (frame->InlineFrameContext != INLINE_FRAME_CONTEXT_IGNORE)
{
addr = sw_xlat_addr(&csw, &frame->AddrPC);
frame->InlineFrameContext = SymAddrIncludeInlineTrace(hProcess, addr) == 0 ? IFC_MODE_REGULAR : IFC_MODE_INLINE;
TRACE("setting IFC mode to %lx\n", frame->InlineFrameContext);
}
}
/* we don't handle KdHelp */
return TRUE;
}
#ifndef _WIN64
/******************************************************************
* SymRegisterFunctionEntryCallback (DBGHELP.@)
*
*
*/
BOOL WINAPI SymRegisterFunctionEntryCallback(HANDLE hProc,
PSYMBOL_FUNCENTRY_CALLBACK cb, PVOID user)
{
FIXME("(%p %p %p): stub!\n", hProc, cb, user);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
#endif
/******************************************************************
* SymRegisterFunctionEntryCallback64 (DBGHELP.@)
*
*
*/
BOOL WINAPI SymRegisterFunctionEntryCallback64(HANDLE hProc,
PSYMBOL_FUNCENTRY_CALLBACK64 cb,
ULONG64 user)
{
FIXME("(%p %p %I64x): stub!\n", hProc, cb, user);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}