dbghelp: Added preliminary CFA management.

This commit is contained in:
Eric Pouech 2010-03-27 09:08:11 +01:00 committed by Alexandre Julliard
parent 0eef5d6809
commit 036392c5d1
3 changed files with 716 additions and 3 deletions

View file

@ -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));

View file

@ -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);

View file

@ -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);