diff --git a/dlls/dbghelp/cpu_x86_64.c b/dlls/dbghelp/cpu_x86_64.c index 8d35d921617..c664fd91031 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 728e464cd18..a341b03eb87 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 a1410b2401c..94563afcf76 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);