[PATCH 04/13] [DbgHelp]: added preliminary CFA management
Eric Pouech
eric.pouech at orange.fr
Sat Mar 27 03:08:11 CDT 2010
A+
---
dlls/dbghelp/cpu_x86_64.c | 19 +
dlls/dbghelp/dbghelp_private.h | 3
dlls/dbghelp/dwarf.c | 697 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 716 insertions(+), 3 deletions(-)
diff --git a/dlls/dbghelp/cpu_x86_64.c b/dlls/dbghelp/cpu_x86_64.c
index 8d35d92..c664fd9 100644
--- a/dlls/dbghelp/cpu_x86_64.c
+++ b/dlls/dbghelp/cpu_x86_64.c
@@ -477,6 +477,8 @@ static BOOL interpret_function_table_entry(struct cpu_stack_walk* csw, LPSTACKFR
static BOOL x86_64_stack_walk(struct cpu_stack_walk* csw, LPSTACKFRAME64 frame, CONTEXT* context)
{
DWORD64 base;
+ DWORD_PTR cfa;
+ unsigned deltapc = 0;
/* sanity check */
if (curr_mode >= stm_done) return FALSE;
@@ -511,18 +513,33 @@ static BOOL x86_64_stack_walk(struct cpu_stack_walk* csw, LPSTACKFRAME64 frame,
{
if (frame->AddrReturn.Offset == 0) goto done_err;
frame->AddrPC = frame->AddrReturn;
+ deltapc = 1;
}
if (frame->AddrPC.Offset && (base = sw_module_base(csw, frame->AddrPC.Offset)))
frame->FuncTableEntry = sw_table_access(csw, frame->AddrPC.Offset);
else
frame->FuncTableEntry = NULL;
+ frame->AddrStack.Mode = frame->AddrFrame.Mode = frame->AddrReturn.Mode = AddrModeFlat;
if (frame->FuncTableEntry)
{
if (!interpret_function_table_entry(csw, frame, context, frame->FuncTableEntry, base))
goto done_err;
}
- /* FIXME: should check "native" debug format for native modules */
+ else if (dwarf2_virtual_unwind(csw, frame->AddrPC.Offset - deltapc, context, &cfa))
+ {
+ frame->AddrStack.Offset = context->Rsp = cfa;
+ frame->AddrReturn.Offset = context->Rip;
+ TRACE("next function rip=%016lx\n", context->Rip);
+ TRACE(" rax=%016lx rbx=%016lx rcx=%016lx rdx=%016lx\n",
+ context->Rax, context->Rbx, context->Rcx, context->Rdx);
+ TRACE(" rsi=%016lx rdi=%016lx rbp=%016lx rsp=%016lx\n",
+ context->Rsi, context->Rdi, context->Rbp, context->Rsp);
+ TRACE(" r8=%016lx r9=%016lx r10=%016lx r11=%016lx\n",
+ context->R8, context->R9, context->R10, context->R11);
+ TRACE(" r12=%016lx r13=%016lx r14=%016lx r15=%016lx\n",
+ context->R12, context->R13, context->R14, context->R15);
+ }
else if (!default_unwind(csw, frame, context)) goto done_err;
memset(&frame->Params, 0, sizeof(frame->Params));
diff --git a/dlls/dbghelp/dbghelp_private.h b/dlls/dbghelp/dbghelp_private.h
index 728e464..a341b03 100644
--- a/dlls/dbghelp/dbghelp_private.h
+++ b/dlls/dbghelp/dbghelp_private.h
@@ -120,6 +120,7 @@ extern unsigned dbghelp_options;
#define SYMOPT_WINE_WITH_NATIVE_MODULES 0x40000000
enum location_kind {loc_error, /* reg is the error code */
+ loc_unavailable, /* location is not available */
loc_absolute, /* offset is the location */
loc_register, /* reg is the location */
loc_regrel, /* [reg+offset] is the location */
@@ -601,6 +602,8 @@ extern BOOL stabs_parse(struct module* module, unsigned long load_offset
extern BOOL dwarf2_parse(struct module* module, unsigned long load_offset,
const struct elf_thunk_area* thunks,
struct image_file_map* fmap);
+extern BOOL dwarf2_virtual_unwind(struct cpu_stack_walk* csw, DWORD_PTR ip,
+ CONTEXT* context, ULONG_PTR* cfa);
/* stack.c */
extern BOOL sw_read_mem(struct cpu_stack_walk* csw, DWORD64 addr, void* ptr, DWORD sz);
diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c
index a1410b2..94563af 100644
--- a/dlls/dbghelp/dwarf.c
+++ b/dlls/dbghelp/dwarf.c
@@ -2,7 +2,8 @@
* File dwarf.c - read dwarf2 information from the ELF modules
*
* Copyright (C) 2005, Raphael Junqueira
- * Copyright (C) 2006, Eric Pouech
+ * Copyright (C) 2006-2010, Eric Pouech
+ * Copyright (C) 2010, Alexandre Julliard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -2253,6 +2254,696 @@ static enum location_error loc_compute_frame(struct process* pcs,
return loc_err_internal;
}
+enum reg_rule
+{
+ RULE_UNSET, /* not set at all */
+ RULE_UNDEFINED, /* undefined value */
+ RULE_SAME, /* same value as previous frame */
+ RULE_CFA_OFFSET, /* stored at cfa offset */
+ RULE_OTHER_REG, /* stored in other register */
+ RULE_EXPRESSION, /* address specified by expression */
+ RULE_VAL_EXPRESSION /* value specified by expression */
+};
+
+/* make it large enough for all CPUs */
+#define NB_FRAME_REGS 64
+
+struct frame_info
+{
+ ULONG_PTR ip;
+ ULONG_PTR code_align;
+ LONG_PTR data_align;
+ ULONG_PTR cfa_offset;
+ enum reg_rule cfa_rule;
+ unsigned char cfa_reg;
+ unsigned char retaddr_reg;
+ unsigned char fde_encoding;
+ unsigned char lsda_encoding;
+ unsigned char signal_frame;
+ unsigned char aug_z_format;
+ enum reg_rule rules[NB_FRAME_REGS];
+ ULONG_PTR regs[NB_FRAME_REGS];
+};
+
+static ULONG_PTR dwarf2_parse_augmentation_ptr(dwarf2_traverse_context_t* ctx, unsigned char encoding)
+{
+ ULONG_PTR base;
+
+ if (encoding == DW_EH_PE_omit) return 0;
+
+ switch (encoding & 0xf0)
+ {
+ case DW_EH_PE_abs:
+ base = 0;
+ break;
+ case DW_EH_PE_pcrel:
+ base = (ULONG_PTR)ctx->data;
+ break;
+ default:
+ FIXME("unsupported encoding %02x\n", encoding);
+ return 0;
+ }
+
+ switch (encoding & 0x0f)
+ {
+ case DW_EH_PE_native:
+ return base + dwarf2_parse_addr(ctx);
+ case DW_EH_PE_leb128:
+ return base + dwarf2_leb128_as_unsigned(ctx);
+ case DW_EH_PE_data2:
+ return base + dwarf2_parse_u2(ctx);
+ case DW_EH_PE_data4:
+ return base + dwarf2_parse_u4(ctx);
+ case DW_EH_PE_data8:
+ return base + dwarf2_parse_u8(ctx);
+ case DW_EH_PE_signed|DW_EH_PE_leb128:
+ return base + dwarf2_leb128_as_signed(ctx);
+ case DW_EH_PE_signed|DW_EH_PE_data2:
+ return base + (signed short)dwarf2_parse_u2(ctx);
+ case DW_EH_PE_signed|DW_EH_PE_data4:
+ return base + (signed int)dwarf2_parse_u4(ctx);
+ case DW_EH_PE_signed|DW_EH_PE_data8:
+ return base + (LONG64)dwarf2_parse_u8(ctx);
+ default:
+ FIXME("unsupported encoding %02x\n", encoding);
+ return 0;
+ }
+}
+
+static BOOL parse_cie_details(dwarf2_traverse_context_t* ctx, struct frame_info* info)
+{
+ unsigned char version;
+ const char* augmentation;
+ const unsigned char* end;
+ ULONG_PTR len;
+
+ memset(info, 0, sizeof(*info));
+ info->lsda_encoding = DW_EH_PE_omit;
+ info->aug_z_format = 0;
+
+ /* parse the CIE first */
+ version = dwarf2_parse_byte(ctx);
+ if (version != 1)
+ {
+ FIXME("unknown CIE version %u at %p\n", version, ctx->data - 1);
+ return FALSE;
+ }
+ augmentation = (const char*)ctx->data;
+ ctx->data += strlen(augmentation) + 1;
+
+ info->code_align = dwarf2_leb128_as_unsigned(ctx);
+ info->data_align = dwarf2_leb128_as_signed(ctx);
+ info->retaddr_reg = dwarf2_parse_byte(ctx);
+ info->cfa_rule = RULE_CFA_OFFSET;
+
+ end = NULL;
+ TRACE("\tparsing augmentation %s\n", augmentation);
+ if (*augmentation) do
+ {
+ switch (*augmentation)
+ {
+ case 'z':
+ len = dwarf2_leb128_as_unsigned(ctx);
+ end = ctx->data + len;
+ info->aug_z_format = 1;
+ continue;
+ case 'L':
+ info->lsda_encoding = dwarf2_parse_byte(ctx);
+ continue;
+ case 'P':
+ {
+ unsigned char encoding = dwarf2_parse_byte(ctx);
+ dwarf2_parse_augmentation_ptr(ctx, encoding); /* handler */
+ continue;
+ }
+ case 'R':
+ info->fde_encoding = dwarf2_parse_byte(ctx);
+ continue;
+ case 'S':
+ info->signal_frame = 1;
+ continue;
+ }
+ FIXME("unknown augmentation '%c'\n", *augmentation);
+ if (!end) return FALSE;
+ break;
+ } while (*++augmentation);
+ if (end) ctx->data = end;
+ return TRUE;
+}
+
+static BOOL dwarf2_get_cie(unsigned long addr, struct module* module, DWORD_PTR delta,
+ dwarf2_traverse_context_t* fde_ctx, dwarf2_traverse_context_t* cie_ctx,
+ struct frame_info* info)
+{
+ const unsigned char* ptr_blk;
+ const unsigned char* cie_ptr;
+ const unsigned char* last_cie_ptr = (const unsigned char*)~0;
+ unsigned len, id;
+ unsigned long start, range;
+
+ for (; fde_ctx->data + 2 * 4 < fde_ctx->end_data; fde_ctx->data = ptr_blk)
+ {
+ /* find the FDE for address addr (skip CIE) */
+ len = dwarf2_parse_u4(fde_ctx);
+ if (len == 0xffffffff) FIXME("Unsupported yet 64-bit CIEs\n");
+ ptr_blk = fde_ctx->data + len;
+ id = dwarf2_parse_u4(fde_ctx);
+ if (id == 0) /* FIXME DW_CIE_ID */
+ {
+ last_cie_ptr = fde_ctx->data - 8;
+ /* we need some bits out of the CIE in order to parse all contents */
+ if (!parse_cie_details(fde_ctx, info)) return FALSE;
+ cie_ctx->data = fde_ctx->data;
+ cie_ctx->end_data = ptr_blk;
+ cie_ctx->word_size = fde_ctx->word_size;
+ continue;
+ }
+ cie_ptr = fde_ctx->data - id - 4;
+ if (cie_ptr != last_cie_ptr)
+ {
+ last_cie_ptr = cie_ptr;
+ cie_ctx->data = cie_ptr;
+ cie_ctx->word_size = fde_ctx->word_size;
+ cie_ctx->end_data = cie_ptr + 4;
+ cie_ctx->end_data = cie_ptr + 4 + dwarf2_parse_u4(cie_ctx);
+ if (dwarf2_parse_u4(cie_ctx) != 0) /* FIXME DW_CIE_ID */
+ {
+ FIXME("wrong CIE pointer\n");
+ return FALSE;
+ }
+ if (!parse_cie_details(cie_ctx, info)) return FALSE;
+ }
+ start = delta + dwarf2_parse_augmentation_ptr(fde_ctx, info->fde_encoding);
+ range = dwarf2_parse_augmentation_ptr(fde_ctx, info->fde_encoding & 0x0F);
+
+ if (addr >= start && addr < start + range)
+ {
+ /* reset the FDE context */
+ fde_ctx->end_data = ptr_blk;
+
+ info->ip = start;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static int valid_reg(ULONG_PTR reg)
+{
+ if (reg >= NB_FRAME_REGS) FIXME("unsupported reg %lx\n", reg);
+ return (reg < NB_FRAME_REGS);
+}
+
+static void execute_cfa_instructions(dwarf2_traverse_context_t* ctx,
+ ULONG_PTR last_ip, struct frame_info *info)
+{
+ while (ctx->data < ctx->end_data && info->ip < last_ip + info->signal_frame)
+ {
+ enum dwarf_call_frame_info op = dwarf2_parse_byte(ctx);
+
+ if (op & 0xc0)
+ {
+ switch (op & 0xc0)
+ {
+ case DW_CFA_advance_loc:
+ {
+ ULONG_PTR offset = (op & 0x3f) * info->code_align;
+ TRACE("%lx: DW_CFA_advance_loc %lu\n", info->ip, offset);
+ info->ip += offset;
+ break;
+ }
+ case DW_CFA_offset:
+ {
+ ULONG_PTR reg = op & 0x3f;
+ LONG_PTR offset = dwarf2_leb128_as_unsigned(ctx) * info->data_align;
+ if (!valid_reg(reg)) break;
+ TRACE("%lx: DW_CFA_offset %s, %ld\n",
+ info->ip,
+ dbghelp_current_cpu->fetch_regname(dbghelp_current_cpu->map_dwarf_register(reg)),
+ offset);
+ info->regs[reg] = offset;
+ info->rules[reg] = RULE_CFA_OFFSET;
+ break;
+ }
+ case DW_CFA_restore:
+ {
+ ULONG_PTR reg = op & 0x3f;
+ if (!valid_reg(reg)) break;
+ TRACE("%lx: DW_CFA_restore %s\n",
+ info->ip,
+ dbghelp_current_cpu->fetch_regname(dbghelp_current_cpu->map_dwarf_register(reg)));
+ info->rules[reg] = RULE_UNSET;
+ break;
+ }
+ }
+ }
+ else switch (op)
+ {
+ case DW_CFA_nop:
+ break;
+ case DW_CFA_set_loc:
+ {
+ ULONG_PTR loc = dwarf2_parse_augmentation_ptr(ctx, info->fde_encoding);
+ TRACE("%lx: DW_CFA_set_loc %lx\n", info->ip, loc);
+ info->ip = loc;
+ break;
+ }
+ case DW_CFA_advance_loc1:
+ {
+ ULONG_PTR offset = dwarf2_parse_byte(ctx) * info->code_align;
+ TRACE("%lx: DW_CFA_advance_loc1 %lu\n", info->ip, offset);
+ info->ip += offset;
+ break;
+ }
+ case DW_CFA_advance_loc2:
+ {
+ ULONG_PTR offset = dwarf2_parse_u2(ctx) * info->code_align;
+ TRACE("%lx: DW_CFA_advance_loc2 %lu\n", info->ip, offset);
+ info->ip += offset;
+ break;
+ }
+ case DW_CFA_advance_loc4:
+ {
+ ULONG_PTR offset = dwarf2_parse_u4(ctx) * info->code_align;
+ TRACE("%lx: DW_CFA_advance_loc4 %lu\n", info->ip, offset);
+ info->ip += offset;
+ break;
+ }
+ case DW_CFA_offset_extended:
+ case DW_CFA_offset_extended_sf:
+ {
+ ULONG_PTR reg = dwarf2_leb128_as_unsigned(ctx);
+ LONG_PTR offset = (op == DW_CFA_offset_extended) ? dwarf2_leb128_as_unsigned(ctx) * info->data_align
+ : dwarf2_leb128_as_signed(ctx) * info->data_align;
+ if (!valid_reg(reg)) break;
+ TRACE("%lx: DW_CFA_offset_extended %s, %ld\n",
+ info->ip,
+ dbghelp_current_cpu->fetch_regname(dbghelp_current_cpu->map_dwarf_register(reg)),
+ offset);
+ info->regs[reg] = offset;
+ info->rules[reg] = RULE_CFA_OFFSET;
+ break;
+ }
+ case DW_CFA_restore_extended:
+ {
+ ULONG_PTR reg = dwarf2_leb128_as_unsigned(ctx);
+ if (!valid_reg(reg)) break;
+ TRACE("%lx: DW_CFA_restore_extended %s\n",
+ info->ip,
+ dbghelp_current_cpu->fetch_regname(dbghelp_current_cpu->map_dwarf_register(reg)));
+ info->rules[reg] = RULE_UNSET;
+ break;
+ }
+ case DW_CFA_undefined:
+ {
+ ULONG_PTR reg = dwarf2_leb128_as_unsigned(ctx);
+ if (!valid_reg(reg)) break;
+ TRACE("%lx: DW_CFA_undefined %s\n",
+ info->ip,
+ dbghelp_current_cpu->fetch_regname(dbghelp_current_cpu->map_dwarf_register(reg)));
+ info->rules[reg] = RULE_UNDEFINED;
+ break;
+ }
+ case DW_CFA_same_value:
+ {
+ ULONG_PTR reg = dwarf2_leb128_as_unsigned(ctx);
+ if (!valid_reg(reg)) break;
+ TRACE("%lx: DW_CFA_same_value %s\n",
+ info->ip,
+ dbghelp_current_cpu->fetch_regname(dbghelp_current_cpu->map_dwarf_register(reg)));
+ info->regs[reg] = reg;
+ info->rules[reg] = RULE_SAME;
+ break;
+ }
+ case DW_CFA_register:
+ {
+ ULONG_PTR reg = dwarf2_leb128_as_unsigned(ctx);
+ ULONG_PTR reg2 = dwarf2_leb128_as_unsigned(ctx);
+ if (!valid_reg(reg) || !valid_reg(reg2)) break;
+ TRACE("%lx: DW_CFA_register %s == %s\n",
+ info->ip,
+ dbghelp_current_cpu->fetch_regname(dbghelp_current_cpu->map_dwarf_register(reg)),
+ dbghelp_current_cpu->fetch_regname(dbghelp_current_cpu->map_dwarf_register(reg2)));
+ info->regs[reg] = reg2;
+ info->rules[reg] = RULE_OTHER_REG;
+ break;
+ }
+ case DW_CFA_remember_state:
+ FIXME("%lx: DW_CFA_remember_state not implemented\n", info->ip);
+ break;
+ case DW_CFA_restore_state:
+ FIXME("%lx: DW_CFA_restore_state not implemented\n", info->ip);
+ break;
+ case DW_CFA_def_cfa:
+ case DW_CFA_def_cfa_sf:
+ {
+ ULONG_PTR reg = dwarf2_leb128_as_unsigned(ctx);
+ ULONG_PTR offset = (op == DW_CFA_def_cfa) ? dwarf2_leb128_as_unsigned(ctx)
+ : dwarf2_leb128_as_signed(ctx) * info->data_align;
+ if (!valid_reg(reg)) break;
+ TRACE("%lx: DW_CFA_def_cfa %s, %lu\n",
+ info->ip,
+ dbghelp_current_cpu->fetch_regname(dbghelp_current_cpu->map_dwarf_register(reg)),
+ offset);
+ info->cfa_reg = reg;
+ info->cfa_offset = offset;
+ info->cfa_rule = RULE_CFA_OFFSET;
+ break;
+ }
+ case DW_CFA_def_cfa_register:
+ {
+ ULONG_PTR reg = dwarf2_leb128_as_unsigned(ctx);
+ if (!valid_reg(reg)) break;
+ TRACE("%lx: DW_CFA_def_cfa_register %s\n",
+ info->ip,
+ dbghelp_current_cpu->fetch_regname(dbghelp_current_cpu->map_dwarf_register(reg)));
+ info->cfa_reg = reg;
+ info->cfa_rule = RULE_CFA_OFFSET;
+ break;
+ }
+ case DW_CFA_def_cfa_offset:
+ case DW_CFA_def_cfa_offset_sf:
+ {
+ ULONG_PTR offset = (op == DW_CFA_def_cfa_offset) ? dwarf2_leb128_as_unsigned(ctx)
+ : dwarf2_leb128_as_signed(ctx) * info->data_align;
+ TRACE("%lx: DW_CFA_def_cfa_offset %lu\n", info->ip, offset);
+ info->cfa_offset = offset;
+ info->cfa_rule = RULE_CFA_OFFSET;
+ break;
+ }
+ case DW_CFA_def_cfa_expression:
+ {
+ ULONG_PTR expr = (ULONG_PTR)ctx->data;
+ ULONG_PTR len = dwarf2_leb128_as_unsigned(ctx);
+ TRACE("%lx: DW_CFA_def_cfa_expression %lx-%lx\n", info->ip, expr, expr+len);
+ info->cfa_offset = expr;
+ info->cfa_rule = RULE_VAL_EXPRESSION;
+ ctx->data += len;
+ break;
+ }
+ case DW_CFA_expression:
+ case DW_CFA_val_expression:
+ {
+ ULONG_PTR reg = dwarf2_leb128_as_unsigned(ctx);
+ ULONG_PTR expr = (ULONG_PTR)ctx->data;
+ ULONG_PTR len = dwarf2_leb128_as_unsigned(ctx);
+ if (!valid_reg(reg)) break;
+ TRACE("%lx: DW_CFA_%sexpression %s %lx-%lx\n",
+ info->ip, (op == DW_CFA_expression) ? "" : "val_",
+ dbghelp_current_cpu->fetch_regname(dbghelp_current_cpu->map_dwarf_register(reg)),
+ expr, expr + len);
+ info->regs[reg] = expr;
+ info->rules[reg] = (op == DW_CFA_expression) ? RULE_EXPRESSION : RULE_VAL_EXPRESSION;
+ ctx->data += len;
+ break;
+ }
+ default:
+ FIXME("%lx: unknown CFA opcode %02x\n", info->ip, op);
+ break;
+ }
+ }
+}
+
+/* retrieve a context register from its dwarf number */
+static ULONG_PTR get_context_reg(CONTEXT *context, ULONG_PTR dw_reg)
+{
+ unsigned regno = dbghelp_current_cpu->map_dwarf_register(dw_reg), sz;
+ ULONG_PTR* ptr = dbghelp_current_cpu->fetch_context_reg(context, regno, &sz);
+
+ if (sz != sizeof(ULONG_PTR))
+ {
+ FIXME("reading register %lu/%u of wrong size %u\n", dw_reg, regno, sz);
+ return 0;
+ }
+ return *ptr;
+}
+
+/* set a context register from its dwarf number */
+static void set_context_reg(struct cpu_stack_walk* csw, CONTEXT *context, ULONG_PTR dw_reg,
+ ULONG_PTR val, BOOL isdebuggee)
+{
+ unsigned regno = dbghelp_current_cpu->map_dwarf_register(dw_reg), sz;
+ ULONG_PTR* ptr = dbghelp_current_cpu->fetch_context_reg(context, regno, &sz);
+
+ if (isdebuggee)
+ {
+ char tmp[16];
+
+ if (sz > sizeof(tmp))
+ {
+ FIXME("register %lu/%u size is too wide: %u\n", dw_reg, regno, sz);
+ return;
+ }
+ if (!sw_read_mem(csw, val, tmp, sz))
+ {
+ WARN("Couldn't read memory at %p\n", (void*)val);
+ return;
+ }
+ memcpy(ptr, tmp, sz);
+ }
+ else
+ {
+ if (sz != sizeof(ULONG_PTR))
+ {
+ FIXME("assigning to register %lu/%u of wrong size %u\n", dw_reg, regno, sz);
+ return;
+ }
+ *ptr = val;
+ }
+}
+
+/* copy a register from one context to another using dwarf number */
+static void copy_context_reg(CONTEXT *dstcontext, ULONG_PTR dwregdst, CONTEXT* srccontext, ULONG_PTR dwregsrc)
+{
+ unsigned regdstno = dbghelp_current_cpu->map_dwarf_register(dwregdst), szdst;
+ unsigned regsrcno = dbghelp_current_cpu->map_dwarf_register(dwregsrc), szsrc;
+ ULONG_PTR* ptrdst = dbghelp_current_cpu->fetch_context_reg(dstcontext, regdstno, &szdst);
+ ULONG_PTR* ptrsrc = dbghelp_current_cpu->fetch_context_reg(srccontext, regsrcno, &szsrc);
+
+ if (szdst != szsrc)
+ {
+ FIXME("Cannot copy register %lu/%u => %lu/%u because of size mismatch (%u => %u)\n",
+ dwregsrc, regsrcno, dwregdst, regdstno, szsrc, szdst);
+ return;
+ }
+ memcpy(ptrdst, ptrsrc, szdst);
+}
+
+static ULONG_PTR eval_expression(struct cpu_stack_walk* csw, const unsigned char* zp, CONTEXT *context)
+{
+ dwarf2_traverse_context_t ctx;
+ ULONG_PTR reg, sz, tmp, stack[64];
+ int sp = -1;
+ ULONG_PTR len;
+
+ ctx.data = zp;
+ ctx.end_data = zp + 4;
+ len = dwarf2_leb128_as_unsigned(&ctx);
+ ctx.end_data = ctx.data + len;
+ ctx.word_size = 8; /* FIXME: wordsize!! */
+
+ while (ctx.data < ctx.end_data)
+ {
+ unsigned char opcode = dwarf2_parse_byte(&ctx);
+
+ if (opcode >= DW_OP_lit0 && opcode <= DW_OP_lit31)
+ stack[++sp] = opcode - DW_OP_lit0;
+ else if (opcode >= DW_OP_reg0 && opcode <= DW_OP_reg31)
+ stack[++sp] = get_context_reg(context, opcode - DW_OP_reg0);
+ else if (opcode >= DW_OP_breg0 && opcode <= DW_OP_breg31)
+ stack[++sp] = get_context_reg(context, opcode - DW_OP_breg0) + dwarf2_leb128_as_signed(&ctx);
+ else switch (opcode)
+ {
+ case DW_OP_nop: break;
+ case DW_OP_addr: stack[++sp] = dwarf2_parse_addr(&ctx); break;
+ case DW_OP_const1u: stack[++sp] = dwarf2_parse_byte(&ctx); break;
+ case DW_OP_const1s: stack[++sp] = (signed char)dwarf2_parse_byte(&ctx); break;
+ case DW_OP_const2u: stack[++sp] = dwarf2_parse_u2(&ctx); break;
+ case DW_OP_const2s: stack[++sp] = (short)dwarf2_parse_u2(&ctx); break;
+ case DW_OP_const4u: stack[++sp] = dwarf2_parse_u4(&ctx); break;
+ case DW_OP_const4s: stack[++sp] = (signed int)dwarf2_parse_u4(&ctx); break;
+ case DW_OP_const8u: stack[++sp] = dwarf2_parse_u8(&ctx); break;
+ case DW_OP_const8s: stack[++sp] = (LONG_PTR)dwarf2_parse_u8(&ctx); break;
+ case DW_OP_constu: stack[++sp] = dwarf2_leb128_as_unsigned(&ctx); break;
+ case DW_OP_consts: stack[++sp] = dwarf2_leb128_as_signed(&ctx); break;
+ case DW_OP_deref:
+ if (!sw_read_mem(csw, stack[sp], &tmp, sizeof(tmp)))
+ {
+ ERR("Couldn't read memory at %lx\n", stack[sp]);
+ tmp = 0;
+ }
+ stack[sp] = tmp;
+ break;
+ case DW_OP_dup: stack[sp + 1] = stack[sp]; sp++; break;
+ case DW_OP_drop: sp--; break;
+ case DW_OP_over: stack[sp + 1] = stack[sp - 1]; sp++; break;
+ case DW_OP_pick: stack[sp + 1] = stack[sp - dwarf2_parse_byte(&ctx)]; sp++; break;
+ case DW_OP_swap: tmp = stack[sp]; stack[sp] = stack[sp-1]; stack[sp-1] = tmp; break;
+ case DW_OP_rot: tmp = stack[sp]; stack[sp] = stack[sp-1]; stack[sp-1] = stack[sp-2]; stack[sp-2] = tmp; break;
+ case DW_OP_abs: stack[sp] = labs(stack[sp]); break;
+ case DW_OP_neg: stack[sp] = -stack[sp]; break;
+ case DW_OP_not: stack[sp] = ~stack[sp]; break;
+ case DW_OP_and: stack[sp-1] &= stack[sp]; sp--; break;
+ case DW_OP_or: stack[sp-1] |= stack[sp]; sp--; break;
+ case DW_OP_minus: stack[sp-1] -= stack[sp]; sp--; break;
+ case DW_OP_mul: stack[sp-1] *= stack[sp]; sp--; break;
+ case DW_OP_plus: stack[sp-1] += stack[sp]; sp--; break;
+ case DW_OP_xor: stack[sp-1] ^= stack[sp]; sp--; break;
+ case DW_OP_shl: stack[sp-1] <<= stack[sp]; sp--; break;
+ case DW_OP_shr: stack[sp-1] >>= stack[sp]; sp--; break;
+ case DW_OP_plus_uconst: stack[sp] += dwarf2_leb128_as_unsigned(&ctx); break;
+ case DW_OP_shra: stack[sp-1] = (LONG_PTR)stack[sp-1] / (1 << stack[sp]); sp--; break;
+ case DW_OP_div: stack[sp-1] = (LONG_PTR)stack[sp-1] / (LONG_PTR)stack[sp]; sp--; break;
+ case DW_OP_mod: stack[sp-1] = (LONG_PTR)stack[sp-1] % (LONG_PTR)stack[sp]; sp--; break;
+ case DW_OP_ge: stack[sp-1] = ((LONG_PTR)stack[sp-1] >= (LONG_PTR)stack[sp]); sp--; break;
+ case DW_OP_gt: stack[sp-1] = ((LONG_PTR)stack[sp-1] > (LONG_PTR)stack[sp]); sp--; break;
+ case DW_OP_le: stack[sp-1] = ((LONG_PTR)stack[sp-1] <= (LONG_PTR)stack[sp]); sp--; break;
+ case DW_OP_lt: stack[sp-1] = ((LONG_PTR)stack[sp-1] < (LONG_PTR)stack[sp]); sp--; break;
+ case DW_OP_eq: stack[sp-1] = (stack[sp-1] == stack[sp]); sp--; break;
+ case DW_OP_ne: stack[sp-1] = (stack[sp-1] != stack[sp]); sp--; break;
+ case DW_OP_skip: tmp = (short)dwarf2_parse_u2(&ctx); ctx.data += tmp; break;
+ case DW_OP_bra: tmp = (short)dwarf2_parse_u2(&ctx); if (!stack[sp--]) ctx.data += tmp; break;
+ case DW_OP_GNU_encoded_addr:
+ tmp = dwarf2_parse_byte(&ctx);
+ stack[++sp] = dwarf2_parse_augmentation_ptr(&ctx, tmp);
+ break;
+ case DW_OP_regx:
+ stack[++sp] = get_context_reg(context, dwarf2_leb128_as_unsigned(&ctx));
+ break;
+ case DW_OP_bregx:
+ reg = dwarf2_leb128_as_unsigned(&ctx);
+ tmp = dwarf2_leb128_as_signed(&ctx);
+ stack[++sp] = get_context_reg(context, reg) + tmp;
+ break;
+ case DW_OP_deref_size:
+ sz = dwarf2_parse_byte(&ctx);
+ if (!sw_read_mem(csw, stack[sp], &tmp, sz))
+ {
+ ERR("Couldn't read memory at %lx\n", stack[sp]);
+ tmp = 0;
+ }
+ /* do integral promotion */
+ switch (sz)
+ {
+ case 1: stack[sp] = *(unsigned char*)&tmp; break;
+ case 2: stack[sp] = *(unsigned short*)&tmp; break;
+ case 4: stack[sp] = *(unsigned int*)&tmp; break;
+ case 8: stack[sp] = *(ULONG_PTR*)&tmp; break; /* FIXME: won't work on 32bit platform */
+ default: FIXME("Unknown size for deref 0x%lx\n", sz);
+ }
+ break;
+ default:
+ FIXME("unhandled opcode %02x\n", opcode);
+ }
+ }
+ return stack[sp];
+}
+
+static void apply_frame_info(struct cpu_stack_walk* csw, CONTEXT *context, struct frame_info *info, ULONG_PTR* cfa)
+{
+ unsigned int i;
+ ULONG_PTR value;
+ CONTEXT new_context = *context;
+
+ switch (info->cfa_rule)
+ {
+ case RULE_EXPRESSION:
+ *cfa = *(ULONG_PTR*)eval_expression(csw, (const unsigned char*)info->cfa_offset, context);
+ break;
+ case RULE_VAL_EXPRESSION:
+ *cfa = eval_expression(csw, (const unsigned char*)info->cfa_offset, context);
+ break;
+ default:
+ *cfa = get_context_reg(context, info->cfa_reg) + info->cfa_offset;
+ break;
+ }
+ if (!*cfa) return;
+
+ for (i = 0; i < NB_FRAME_REGS; i++)
+ {
+ switch (info->rules[i])
+ {
+ case RULE_UNSET:
+ case RULE_UNDEFINED:
+ case RULE_SAME:
+ break;
+ case RULE_CFA_OFFSET:
+ set_context_reg(csw, &new_context, i, *cfa + info->regs[i], TRUE);
+ break;
+ case RULE_OTHER_REG:
+ copy_context_reg(&new_context, i, context, info->regs[i]);
+ break;
+ case RULE_EXPRESSION:
+ value = eval_expression(csw, (const unsigned char*)info->regs[i], context);
+ set_context_reg(csw, &new_context, i, value, TRUE);
+ break;
+ case RULE_VAL_EXPRESSION:
+ value = eval_expression(csw, (const unsigned char*)info->regs[i], context);
+ set_context_reg(csw, &new_context, i, value, FALSE);
+ break;
+ }
+ }
+ *context = new_context;
+}
+
+/***********************************************************************
+ * dwarf2_virtual_unwind
+ *
+ */
+BOOL dwarf2_virtual_unwind(struct cpu_stack_walk* csw, ULONG_PTR ip, CONTEXT* context, ULONG_PTR* cfa)
+{
+ struct module_pair pair;
+ struct frame_info info;
+ dwarf2_traverse_context_t cie_ctx, fde_ctx;
+ struct module_format* modfmt;
+ const unsigned char* end;
+ DWORD_PTR delta;
+
+ if (!(pair.pcs = process_find_by_handle(csw->hProcess)) ||
+ !(pair.requested = module_find_by_addr(pair.pcs, ip, DMT_UNKNOWN)) ||
+ !module_get_debug(&pair))
+ return FALSE;
+ modfmt = pair.effective->format_info[DFI_DWARF];
+ if (!modfmt) return FALSE;
+ memset(&info, 0, sizeof(info));
+ fde_ctx.data = modfmt->u.dwarf2_info->eh_frame.address;
+ fde_ctx.end_data = fde_ctx.data + modfmt->u.dwarf2_info->eh_frame.size;
+ fde_ctx.word_size = modfmt->u.dwarf2_info->word_size;
+ /* let offsets relative to the eh_frame sections be correctly computed, as we'll map
+ * in this process the IMAGE section at a different address as the one expected by
+ * the image
+ */
+ delta = pair.effective->module.BaseOfImage + modfmt->u.dwarf2_info->eh_frame.rva -
+ (DWORD_PTR)modfmt->u.dwarf2_info->eh_frame.address;
+ if (!dwarf2_get_cie(ip, pair.effective, delta, &fde_ctx, &cie_ctx, &info))
+ {
+ TRACE("Couldn't find information for %lx\n", ip);
+ return FALSE;
+ }
+
+ TRACE("function %lx/%lx code_align %lu data_align %ld retaddr %s\n",
+ ip, info.ip, info.code_align, info.data_align,
+ dbghelp_current_cpu->fetch_regname(dbghelp_current_cpu->map_dwarf_register(info.retaddr_reg)));
+
+ /* if at very beginning of function, return and use default unwinder */
+ if (ip == info.ip) return FALSE;
+ execute_cfa_instructions(&cie_ctx, ip, &info);
+
+ if (info.aug_z_format) /* get length of augmentation data */
+ {
+ ULONG_PTR len = dwarf2_leb128_as_unsigned(&fde_ctx);
+ end = fde_ctx.data + len;
+ }
+ else end = NULL;
+ dwarf2_parse_augmentation_ptr(&fde_ctx, info.lsda_encoding); /* handler_data */
+ if (end) fde_ctx.data = end;
+
+ execute_cfa_instructions(&fde_ctx, ip, &info);
+ apply_frame_info(csw, context, &info, cfa);
+
+ return TRUE;
+}
+
static void dwarf2_location_compute(struct process* pcs,
const struct module_format* modfmt,
const struct symt_function* func,
@@ -2348,7 +3039,6 @@ BOOL dwarf2_parse(struct module* module, unsigned long load_offset,
dwarf2_traverse_context_t mod_ctx;
struct image_section_map debug_sect, debug_str_sect, debug_abbrev_sect,
debug_line_sect;
-
BOOL ret = TRUE;
struct module_format* dwarf2_modfmt;
@@ -2398,6 +3088,9 @@ BOOL dwarf2_parse(struct module* module, unsigned long load_offset,
dwarf2_modfmt->u.dwarf2_info->word_size = 0; /* will be correctly set later on */
dwarf2_modfmt->module->format_info[DFI_DWARF] = dwarf2_modfmt;
+ /* As we'll need later some sections' content, we won't unmap these
+ * sections upon existing this function
+ */
dwarf2_init_section(&dwarf2_modfmt->u.dwarf2_info->debug_loc, fmap, ".debug_loc", NULL);
dwarf2_init_section(&dwarf2_modfmt->u.dwarf2_info->debug_frame, fmap, ".debug_frame", NULL);
dwarf2_init_section(&dwarf2_modfmt->u.dwarf2_info->eh_frame, fmap, ".eh_frame", NULL);
More information about the wine-patches
mailing list