[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