Skip to content
Snippets Groups Projects
dwarf.c 110 KiB
Newer Older
/*
 * File dwarf.c - read dwarf2 information from the ELF modules
 *
 * Copyright (C) 2005, Raphael Junqueira
 * 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
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "config.h"

#include <sys/types.h>
#include <fcntl.h>
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <stdio.h>
#ifndef PATH_MAX
#define PATH_MAX MAX_PATH
#endif
#include <assert.h>
#include <stdarg.h>

#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "ole2.h"
#include "oleauto.h"

#include "dbghelp_private.h"
#include "image_private.h"

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(dbghelp_dwarf);

/* FIXME:
 * - Functions:
 *      o unspecified parameters
 *      o inlined functions
 *      o Debug{Start|End}Point
 *      o CFA
 *      o proper types loading (nesting)
static void dump(const void* ptr, unsigned len)
{
    int         i, j;
    BYTE        msg[128];
    static const char hexof[] = "0123456789abcdef";
    const       BYTE* x = ptr;

    for (i = 0; i < len; i += 16)
        sprintf(msg, "%08x: ", i);
        memset(msg + 10, ' ', 3 * 16 + 1 + 16);
        for (j = 0; j < min(16, len - i); j++)
        {
            msg[10 + 3 * j + 0] = hexof[x[i + j] >> 4];
            msg[10 + 3 * j + 1] = hexof[x[i + j] & 15];
            msg[10 + 3 * j + 2] = ' ';
            msg[10 + 3 * 16 + 1 + j] = (x[i + j] >= 0x20 && x[i + j] < 0x7f) ?
                x[i + j] : '.';
        }
        msg[10 + 3 * 16] = ' ';
        msg[10 + 3 * 16 + 1 + 16] = '\0';
        TRACE("%s\n", msg);
/**
 *
 * Main Specs:
 *  http://www.eagercon.com/dwarf/dwarf3std.htm
 *  http://www.eagercon.com/dwarf/dwarf-2.0.0.pdf
 *
 * dwarf2.h: http://www.hakpetzna.com/b/binutils/dwarf2_8h-source.html
 *
 * example of projects who do dwarf2 parsing:
 *  http://www.x86-64.org/cgi-bin/cvsweb.cgi/binutils.dead/binutils/readelf.c?rev=1.1.1.2
 *  http://elis.ugent.be/diota/log/ltrace_elf.c
 */
Raphael Junqueira's avatar
Raphael Junqueira committed
  unsigned long attribute;
  unsigned long form;
  struct dwarf2_abbrev_entry_attr_s* next;
} dwarf2_abbrev_entry_attr_t;

typedef struct dwarf2_abbrev_entry_s
{
    unsigned long entry_code;
    unsigned long tag;
    unsigned char have_child;
    unsigned num_attr;
    dwarf2_abbrev_entry_attr_t* attrs;
Raphael Junqueira's avatar
Raphael Junqueira committed
} dwarf2_abbrev_entry_t;

Eric Pouech's avatar
Eric Pouech committed
struct dwarf2_block
{
    unsigned                    size;
    const unsigned char*        ptr;
};

};

typedef struct dwarf2_debug_info_s
{
    const dwarf2_abbrev_entry_t*abbrev;
    struct symt*                symt;
    struct vector               children;
} dwarf2_debug_info_t;

typedef struct dwarf2_section_s
{
    const unsigned char*        address;
    unsigned                    size;
} dwarf2_section_t;

enum dwarf2_sections {section_debug, section_string, section_abbrev, section_line, section_max};

typedef struct dwarf2_traverse_context_s
{
    const unsigned char*        data;
    const unsigned char*        end_data;
    unsigned char               word_size;
} dwarf2_traverse_context_t;

/* symt_cache indexes */
#define sc_void 0
#define sc_int1 1
#define sc_int2 2
#define sc_int4 3
#define sc_num  4

typedef struct dwarf2_parse_context_s
{
    const dwarf2_section_t*     sections;
    unsigned                    section;
    struct pool                 pool;
    struct module*              module;
Eric Pouech's avatar
Eric Pouech committed
    const struct elf_thunk_area*thunks;
    struct sparse_array         abbrev_table;
    struct sparse_array         debug_info_table;
    struct symt*                symt_cache[sc_num]; /* void, int1, int2, int4 */
} dwarf2_parse_context_t;

/* stored in the dbghelp's module internal structure for later reuse */
struct dwarf2_module_info_s
{
    dwarf2_section_t            debug_loc;
    dwarf2_section_t            debug_frame;
    dwarf2_section_t            eh_frame;
#define loc_dwarf2_location_list        (loc_user + 0)
#define loc_dwarf2_block                (loc_user + 1)

/* forward declarations */
Eric Pouech's avatar
Eric Pouech committed
static struct symt* dwarf2_parse_enumeration_type(dwarf2_parse_context_t* ctx, dwarf2_debug_info_t* entry);
static unsigned char dwarf2_get_byte(const unsigned char* ptr)
{
    return *ptr;
}

static unsigned char dwarf2_parse_byte(dwarf2_traverse_context_t* ctx)
    unsigned char uvalue = dwarf2_get_byte(ctx->data);
    ctx->data += 1;
    return uvalue;
static unsigned short dwarf2_get_u2(const unsigned char* ptr)
{
static unsigned short dwarf2_parse_u2(dwarf2_traverse_context_t* ctx)
    unsigned short uvalue = dwarf2_get_u2(ctx->data);
    ctx->data += 2;
    return uvalue;
static unsigned long dwarf2_get_u4(const unsigned char* ptr)
{
static unsigned long dwarf2_parse_u4(dwarf2_traverse_context_t* ctx)
    unsigned long uvalue = dwarf2_get_u4(ctx->data);
    ctx->data += 4;
    return uvalue;
static DWORD64 dwarf2_get_u8(const unsigned char* ptr)
{
    return *(const UINT64*)ptr;
}

static DWORD64 dwarf2_parse_u8(dwarf2_traverse_context_t* ctx)
{
    DWORD64 uvalue = dwarf2_get_u8(ctx->data);
    ctx->data += 8;
    return uvalue;
}

static unsigned long dwarf2_get_leb128_as_unsigned(const unsigned char* ptr, const unsigned char** end)
    unsigned long ret = 0;
    unsigned char byte;
    unsigned shift = 0;
        ret |= (byte & 0x7f) << shift;
        shift += 7;
    } while (byte & 0x80);

static unsigned long dwarf2_leb128_as_unsigned(dwarf2_traverse_context_t* ctx)
{
    unsigned long ret;

    assert(ctx);

    ret = dwarf2_get_leb128_as_unsigned(ctx->data, &ctx->data);

    return ret;
}

static long dwarf2_get_leb128_as_signed(const unsigned char* ptr, const unsigned char** end)
    long ret = 0;
    unsigned char byte;
    unsigned shift = 0;
    const unsigned size = sizeof(int) * 8;

        ret |= (byte & 0x7f) << shift;
        shift += 7;
    } while (byte & 0x80);
    /* as spec: sign bit of byte is 2nd high order bit (80x40)
     *  -> 0x80 is used as flag.
     */
    if ((shift < size) && (byte & 0x40))
    {
        ret |= - (1 << shift);
    }
    return ret;
static long dwarf2_leb128_as_signed(dwarf2_traverse_context_t* ctx)
{
    long ret = 0;

    assert(ctx);

    ret = dwarf2_get_leb128_as_signed(ctx->data, &ctx->data);
    return ret;
}

static unsigned dwarf2_leb128_length(const dwarf2_traverse_context_t* ctx)
{
    unsigned    ret;
    for (ret = 0; ctx->data[ret] & 0x80; ret++);
    return ret + 1;
}

/******************************************************************
 *		dwarf2_get_addr
 *
 * Returns an address.
 * We assume that in all cases word size from Dwarf matches the size of
 * addresses in platform where the exec is compiled.
 */
static unsigned long dwarf2_get_addr(const unsigned char* ptr, unsigned word_size)
{
    unsigned long ret;

    case 8:
        ret = dwarf2_get_u8(ptr);
	break;
        FIXME("Unsupported Word Size %u\n", word_size);
static unsigned long dwarf2_parse_addr(dwarf2_traverse_context_t* ctx)
{
    unsigned long ret = dwarf2_get_addr(ctx->data, ctx->word_size);
    ctx->data += ctx->word_size;
    return ret;
}

static const char* dwarf2_debug_traverse_ctx(const dwarf2_traverse_context_t* ctx) 
{
    return wine_dbg_sprintf("ctx(%p)", ctx->data); 
static const char* dwarf2_debug_ctx(const dwarf2_parse_context_t* ctx)
Raphael Junqueira's avatar
Raphael Junqueira committed
{
    return wine_dbg_sprintf("ctx(%p,%s)",
                            ctx, debugstr_w(ctx->module->module.ModuleName));
Raphael Junqueira's avatar
Raphael Junqueira committed
}
static const char* dwarf2_debug_di(const dwarf2_debug_info_t* di)
    return wine_dbg_sprintf("debug_info(abbrev:%p,symt:%p)",
                            di->abbrev, di->symt);
Raphael Junqueira's avatar
Raphael Junqueira committed
}
static dwarf2_abbrev_entry_t*
dwarf2_abbrev_table_find_entry(const struct sparse_array* abbrev_table,
                               unsigned long entry_code)
    assert( NULL != abbrev_table );
    return sparse_array_find(abbrev_table, entry_code);
static void dwarf2_parse_abbrev_set(dwarf2_traverse_context_t* abbrev_ctx, 
                                    struct sparse_array* abbrev_table,
                                    struct pool* pool)
    unsigned long entry_code;
    dwarf2_abbrev_entry_t* abbrev_entry;
    dwarf2_abbrev_entry_attr_t* new = NULL;
    dwarf2_abbrev_entry_attr_t* last = NULL;
    unsigned long attribute;
    unsigned long form;
    assert( NULL != abbrev_ctx );
    TRACE("%s, end at %p\n",
          dwarf2_debug_traverse_ctx(abbrev_ctx), abbrev_ctx->end_data); 

    sparse_array_init(abbrev_table, sizeof(dwarf2_abbrev_entry_t), 32);
    while (abbrev_ctx->data < abbrev_ctx->end_data)
    {
        TRACE("now at %s\n", dwarf2_debug_traverse_ctx(abbrev_ctx)); 
        entry_code = dwarf2_leb128_as_unsigned(abbrev_ctx);
        TRACE("found entry_code %lu\n", entry_code);
        if (!entry_code)
        {
            TRACE("NULL entry code at %s\n", dwarf2_debug_traverse_ctx(abbrev_ctx)); 
            break;
        }
        abbrev_entry = sparse_array_add(abbrev_table, entry_code, pool);
        assert( NULL != abbrev_entry );
        abbrev_entry->entry_code = entry_code;
        abbrev_entry->tag        = dwarf2_leb128_as_unsigned(abbrev_ctx);
        abbrev_entry->have_child = dwarf2_parse_byte(abbrev_ctx);
        abbrev_entry->attrs      = NULL;
        abbrev_entry->num_attr   = 0;
        TRACE("table:(%p,#%u) entry_code(%lu) tag(0x%lx) have_child(%u) -> %p\n",
              abbrev_table, sparse_array_length(abbrev_table),
              entry_code, abbrev_entry->tag, abbrev_entry->have_child, abbrev_entry);
        last = NULL;
        while (1)
        {
            attribute = dwarf2_leb128_as_unsigned(abbrev_ctx);
            form = dwarf2_leb128_as_unsigned(abbrev_ctx);
            if (!attribute) break;

            new = pool_alloc(pool, sizeof(dwarf2_abbrev_entry_attr_t));
            assert(new);

            new->attribute = attribute;
            new->form      = form;
            new->next      = NULL;
            if (abbrev_entry->attrs)    last->next = new;
            else                        abbrev_entry->attrs = new;
            last = new;
            abbrev_entry->num_attr++;
        }
    TRACE("found %u entries\n", sparse_array_length(abbrev_table));
static void dwarf2_swallow_attribute(dwarf2_traverse_context_t* ctx,
                                     const dwarf2_abbrev_entry_attr_t* abbrev_attr)
    TRACE("(attr:0x%lx,form:0x%lx)\n", abbrev_attr->attribute, abbrev_attr->form);

    case DW_FORM_ref_addr:
    case DW_FORM_addr:   step = ctx->word_size; break;
    case DW_FORM_flag:
    case DW_FORM_data1:
    case DW_FORM_data2:
    case DW_FORM_data4:
    case DW_FORM_ref4:
    case DW_FORM_strp:   step = 4; break;
    case DW_FORM_data8:
    case DW_FORM_ref8:   step = 8; break;
    case DW_FORM_sdata:
    case DW_FORM_ref_udata:
    case DW_FORM_udata:  step = dwarf2_leb128_length(ctx); break;
    case DW_FORM_string: step = strlen((const char*)ctx->data) + 1; break;
    case DW_FORM_block:  step = dwarf2_leb128_as_unsigned(ctx); break;
    case DW_FORM_block1: step = dwarf2_parse_byte(ctx); break;
    case DW_FORM_block2: step = dwarf2_parse_u2(ctx); break;
    case DW_FORM_block4: step = dwarf2_parse_u4(ctx); break;
    default:
        FIXME("Unhandled attribute form %lx\n", abbrev_attr->form);
static void dwarf2_fill_attr(const dwarf2_parse_context_t* ctx,
                             const dwarf2_abbrev_entry_attr_t* abbrev_attr,
                             const unsigned char* data,
                             struct attribute* attr)
        attr->u.uvalue = dwarf2_get_addr(data,
                                         ctx->module->format_info[DFI_DWARF]->u.dwarf2_info->word_size);
    case DW_FORM_flag:
        attr->u.uvalue = dwarf2_get_byte(data);
        TRACE("flag<0x%lx>\n", attr->u.uvalue);
        break;
    case DW_FORM_data1:
        attr->u.uvalue = dwarf2_get_byte(data);
        TRACE("data1<%lu>\n", attr->u.uvalue);
        break;
    case DW_FORM_data2:
        attr->u.uvalue = dwarf2_get_u2(data);
        TRACE("data2<%lu>\n", attr->u.uvalue);
        break;
    case DW_FORM_data4:
        attr->u.uvalue = dwarf2_get_u4(data);
        TRACE("data4<%lu>\n", attr->u.uvalue);
        break;
        attr->u.lluvalue = dwarf2_get_u8(data);
        TRACE("data8<%s>\n", wine_dbgstr_longlong(attr->u.uvalue));
    case DW_FORM_ref1:
        attr->u.uvalue = ctx->ref_offset + dwarf2_get_byte(data);
        TRACE("ref1<0x%lx>\n", attr->u.uvalue);
        break;
    case DW_FORM_ref2:
        attr->u.uvalue = ctx->ref_offset + dwarf2_get_u2(data);
        TRACE("ref2<0x%lx>\n", attr->u.uvalue);
        break;
    case DW_FORM_ref4:
        attr->u.uvalue = ctx->ref_offset + dwarf2_get_u4(data);
        TRACE("ref4<0x%lx>\n", attr->u.uvalue);
        break;
    case DW_FORM_ref8:
        FIXME("Unhandled 64 bit support\n");
        break;
    case DW_FORM_sdata:
        attr->u.svalue = dwarf2_get_leb128_as_signed(data, NULL);
        break;
    case DW_FORM_ref_udata:
        attr->u.uvalue = dwarf2_get_leb128_as_unsigned(data, NULL);
        break;
    case DW_FORM_udata:
        attr->u.uvalue = dwarf2_get_leb128_as_unsigned(data, NULL);
        break;
    case DW_FORM_string:
        attr->u.string = (const char *)data;
        TRACE("string<%s>\n", attr->u.string);
        break;
    case DW_FORM_strp:
    {
        unsigned long offset = dwarf2_get_u4(data);
        attr->u.string = (const char*)ctx->sections[section_string].address + offset;
    }
    TRACE("strp<%s>\n", attr->u.string);
    break;
        
    case DW_FORM_block:
        attr->u.block.size = dwarf2_get_leb128_as_unsigned(data, &attr->u.block.ptr);
        break;
    case DW_FORM_block1:
        attr->u.block.size = dwarf2_get_byte(data);
        attr->u.block.ptr  = data + 1;
        break;
    case DW_FORM_block2:
        attr->u.block.size = dwarf2_get_u2(data);
        attr->u.block.ptr  = data + 2;
        break;
    case DW_FORM_block4:
        attr->u.block.size = dwarf2_get_u4(data);
        attr->u.block.ptr  = data + 4;
        break;
    default:
        FIXME("Unhandled attribute form %lx\n", abbrev_attr->form);
        break;
    }
}

static BOOL dwarf2_find_attribute(const dwarf2_parse_context_t* ctx,
                                  const dwarf2_debug_info_t* di,
                                  unsigned at, struct attribute* attr)
{
    unsigned                    i, ai = 0;
    dwarf2_abbrev_entry_attr_t* abbrev_attr;
    dwarf2_abbrev_entry_attr_t* abstract_abbrev_attr;

    while (di)
    {
        abstract_abbrev_attr = NULL;
        for (i = 0, abbrev_attr = di->abbrev->attrs; abbrev_attr; i++, abbrev_attr = abbrev_attr->next)
        {
            if (abbrev_attr->attribute == at)
            {
                dwarf2_fill_attr(ctx, abbrev_attr, di->data[i], attr);
                return TRUE;
            }
            if (abbrev_attr->attribute == DW_AT_abstract_origin &&
                at != DW_AT_sibling)
            {
                abstract_abbrev_attr = abbrev_attr;
                ai = i;
        /* do we have an abstract origin debug entry to look into ? */
        if (!abstract_abbrev_attr) break;
        dwarf2_fill_attr(ctx, abstract_abbrev_attr, di->data[ai], attr);
        if (!(di = sparse_array_find(&ctx->debug_info_table, attr->u.uvalue)))
            FIXME("Should have found the debug info entry\n");
    }
    return FALSE;
}

Eric Pouech's avatar
Eric Pouech committed
static void dwarf2_load_one_entry(dwarf2_parse_context_t*, dwarf2_debug_info_t*,
                                  struct symt_compiland*);

#define Wine_DW_no_register     0x7FFFFFFF
static unsigned dwarf2_map_register(int regno)
        FIXME("What the heck map reg 0x%x\n",regno);
    return dbghelp_current_cpu->map_dwarf_register(regno);
static enum location_error
compute_location(dwarf2_traverse_context_t* ctx, struct location* loc,
                 HANDLE hproc, const struct location* frame)
    DWORD_PTR tmp, stack[64];
Eric Pouech's avatar
Eric Pouech committed
    unsigned stk;
    unsigned char op;
    BOOL piece_found = FALSE;

    stack[stk = 0] = 0;

    loc->kind = loc_absolute;
    loc->reg = Wine_DW_no_register;

    while (ctx->data < ctx->end_data)
    {
        op = dwarf2_parse_byte(ctx);

        if (op >= DW_OP_lit0 && op <= DW_OP_lit31)
            stack[++stk] = op - DW_OP_lit0;
        else if (op >= DW_OP_reg0 && op <= DW_OP_reg31)
            /* dbghelp APIs don't know how to cope with this anyway
             * (for example 'long long' stored in two registers)
             * FIXME: We should tell winedbg how to deal with it (sigh)
             */
            if (!piece_found)
            {
                if (loc->reg != Wine_DW_no_register)
                    FIXME("Only supporting one reg (%d -> %d)\n",
                          loc->reg, dwarf2_map_register(op - DW_OP_reg0));
                loc->reg = dwarf2_map_register(op - DW_OP_reg0);
            }
            loc->kind = loc_register;
        }
        else if (op >= DW_OP_breg0 && op <= DW_OP_breg31)
        {
            /* dbghelp APIs don't know how to cope with this anyway
             * (for example 'long long' stored in two registers)
             * FIXME: We should tell winedbg how to deal with it (sigh)
             */
            {
                if (loc->reg != Wine_DW_no_register)
                    FIXME("Only supporting one breg (%d -> %d)\n",
                          loc->reg, dwarf2_map_register(op - DW_OP_breg0));
                loc->reg = dwarf2_map_register(op - DW_OP_breg0);
            stack[++stk] = dwarf2_leb128_as_signed(ctx);
            loc->kind = loc_regrel;
        }
        else switch (op)
        {
        case DW_OP_nop:         break;
        case DW_OP_addr:        stack[++stk] = dwarf2_parse_addr(ctx); break;
        case DW_OP_const1u:     stack[++stk] = dwarf2_parse_byte(ctx); break;
        case DW_OP_const1s:     stack[++stk] = dwarf2_parse_byte(ctx); break;
        case DW_OP_const2u:     stack[++stk] = dwarf2_parse_u2(ctx); break;
        case DW_OP_const2s:     stack[++stk] = dwarf2_parse_u2(ctx); break;
        case DW_OP_const4u:     stack[++stk] = dwarf2_parse_u4(ctx); break;
        case DW_OP_const4s:     stack[++stk] = dwarf2_parse_u4(ctx); break;
        case DW_OP_const8u:     stack[++stk] = dwarf2_parse_u8(ctx); break;
        case DW_OP_const8s:     stack[++stk] = dwarf2_parse_u8(ctx); break;
        case DW_OP_constu:      stack[++stk] = dwarf2_leb128_as_unsigned(ctx); break;
        case DW_OP_consts:      stack[++stk] = dwarf2_leb128_as_signed(ctx); break;
        case DW_OP_dup:         stack[stk + 1] = stack[stk]; stk++; break;
        case DW_OP_drop:        stk--; break;
        case DW_OP_over:        stack[stk + 1] = stack[stk - 1]; stk++; break;
        case DW_OP_pick:        stack[stk + 1] = stack[stk - dwarf2_parse_byte(ctx)]; stk++; break;
        case DW_OP_swap:        tmp = stack[stk]; stack[stk] = stack[stk-1]; stack[stk-1] = tmp; break;
        case DW_OP_rot:         tmp = stack[stk]; stack[stk] = stack[stk-1]; stack[stk-1] = stack[stk-2]; stack[stk-2] = tmp; break;
        case DW_OP_abs:         stack[stk] = labs(stack[stk]); break;
        case DW_OP_neg:         stack[stk] = -stack[stk]; break;
        case DW_OP_not:         stack[stk] = ~stack[stk]; break;
        case DW_OP_and:         stack[stk-1] &= stack[stk]; stk--; break;
        case DW_OP_or:          stack[stk-1] |= stack[stk]; stk--; break;
        case DW_OP_minus:       stack[stk-1] -= stack[stk]; stk--; break;
        case DW_OP_mul:         stack[stk-1] *= stack[stk]; stk--; break;
        case DW_OP_plus:        stack[stk-1] += stack[stk]; stk--; break;
        case DW_OP_xor:         stack[stk-1] ^= stack[stk]; stk--; break;
        case DW_OP_shl:         stack[stk-1] <<= stack[stk]; stk--; break;
        case DW_OP_shr:         stack[stk-1] >>= stack[stk]; stk--; break;
        case DW_OP_plus_uconst: stack[stk] += dwarf2_leb128_as_unsigned(ctx); break;
        case DW_OP_shra:        stack[stk-1] = stack[stk-1] / (1 << stack[stk]); stk--; break;
        case DW_OP_div:         stack[stk-1] = stack[stk-1] / stack[stk]; stk--; break;
        case DW_OP_mod:         stack[stk-1] = stack[stk-1] % stack[stk]; stk--; break;
        case DW_OP_ge:          stack[stk-1] = (stack[stk-1] >= stack[stk]); stk--; break;
        case DW_OP_gt:          stack[stk-1] = (stack[stk-1] >  stack[stk]); stk--; break;
        case DW_OP_le:          stack[stk-1] = (stack[stk-1] <= stack[stk]); stk--; break;
        case DW_OP_lt:          stack[stk-1] = (stack[stk-1] <  stack[stk]); stk--; break;
        case DW_OP_eq:          stack[stk-1] = (stack[stk-1] == stack[stk]); stk--; break;
        case DW_OP_ne:          stack[stk-1] = (stack[stk-1] != stack[stk]); stk--; break;
        case DW_OP_skip:        tmp = dwarf2_parse_u2(ctx); ctx->data += tmp; break;
        case DW_OP_bra:         tmp = dwarf2_parse_u2(ctx); if (!stack[stk--]) ctx->data += tmp; break;
        case DW_OP_regx:
            tmp = dwarf2_leb128_as_unsigned(ctx);
            if (!piece_found)
            {
                if (loc->reg != Wine_DW_no_register)
                    FIXME("Only supporting one reg\n");
                loc->reg = dwarf2_map_register(tmp);
            }
            loc->kind = loc_register;
            break;
        case DW_OP_bregx:
            tmp = dwarf2_leb128_as_unsigned(ctx);
            if (loc->reg != Wine_DW_no_register)
                FIXME("Only supporting one regx\n");
            loc->reg = dwarf2_map_register(tmp);
            stack[++stk] = dwarf2_leb128_as_signed(ctx);
            loc->kind = loc_regrel;
        case DW_OP_fbreg:
            if (loc->reg != Wine_DW_no_register)
                FIXME("Only supporting one reg (%d -> -2)\n", loc->reg);
            if (frame && frame->kind == loc_register)
            {
                loc->kind = loc_regrel;
                loc->reg = frame->reg;
                stack[++stk] = dwarf2_leb128_as_signed(ctx);
            }
            else if (frame && frame->kind == loc_regrel)
            {
                loc->kind = loc_regrel;
                loc->reg = frame->reg;
                stack[++stk] = dwarf2_leb128_as_signed(ctx) + frame->offset;
            }
            else
            {
                /* FIXME: this could be later optimized by not recomputing
                 * this very location expression
                 */
                loc->kind = loc_dwarf2_block;
                stack[++stk] = dwarf2_leb128_as_signed(ctx);
            }
            break;
        case DW_OP_piece:
            {
                unsigned sz = dwarf2_leb128_as_unsigned(ctx);
                WARN("Not handling OP_piece (size=%d)\n", sz);
        case DW_OP_deref:
            if (!stk)
            {
                FIXME("Unexpected empty stack\n");
                return loc_err_internal;
            }
            if (loc->reg != Wine_DW_no_register)
            {
                WARN("Too complex expression for deref\n");
                return loc_err_too_complex;
            }
            if (hproc)
            {
                DWORD_PTR addr = stack[stk--];
                DWORD_PTR deref;

                if (!ReadProcessMemory(hproc, (void*)addr, &deref, sizeof(deref), NULL))
                {
                    WARN("Couldn't read memory at %lx\n", addr);
                    return loc_err_cant_read;
                }
                stack[++stk] = deref;
            }
            else
            {
               loc->kind = loc_dwarf2_block;
            }
            break;
        case DW_OP_deref_size:
            if (!stk)
            {
                FIXME("Unexpected empty stack\n");
                return loc_err_internal;
            }
            if (loc->reg != Wine_DW_no_register)
            {
                WARN("Too complex expression for deref\n");
                return loc_err_too_complex;
            }
            if (hproc)
            {
                DWORD_PTR addr = stack[stk--];
                BYTE derefsize = dwarf2_parse_byte(ctx);
                DWORD64 deref;

                if (!ReadProcessMemory(hproc, (void*)addr, &deref, derefsize, NULL))
                {
                    WARN("Couldn't read memory at %lx\n", addr);
                       return loc_err_cant_read;
                }

                switch (derefsize)
                {
                   case 1: stack[++stk] = *(unsigned char*)&deref; break;
                   case 2: stack[++stk] = *(unsigned short*)&deref; break;
                   case 4: stack[++stk] = *(DWORD*)&deref; break;
                   case 8: if (ctx->word_size >= derefsize) stack[++stk] = deref; break;
                }
            }
            else
            {
               loc->kind = loc_dwarf2_block;
            }
            break;
            if (op < DW_OP_lo_user) /* as DW_OP_hi_user is 0xFF, we don't need to test against it */
                FIXME("Unhandled attr op: %x\n", op);
            /* FIXME else unhandled extension */
}

static BOOL dwarf2_compute_location_attr(dwarf2_parse_context_t* ctx,
                                         const dwarf2_debug_info_t* di,
                                         unsigned long dw,
                                         struct location* loc,
                                         const struct location* frame)
{
    struct attribute xloc;

    if (!dwarf2_find_attribute(ctx, di, dw, &xloc)) return FALSE;
    switch (xloc.form)
    {
    case DW_FORM_data1: case DW_FORM_data2:
    case DW_FORM_udata: case DW_FORM_sdata:
        loc->kind = loc_absolute;
        loc->reg = 0;
        loc->offset = xloc.u.uvalue;
        return TRUE;
    case DW_FORM_data4: case DW_FORM_data8:
        loc->kind = loc_dwarf2_location_list;
        loc->reg = Wine_DW_no_register;
        loc->offset = xloc.u.uvalue;
    case DW_FORM_block:
    case DW_FORM_block1:
    case DW_FORM_block2:
    case DW_FORM_block4:
        break;
    default: FIXME("Unsupported yet form %lx\n", xloc.form);
        return FALSE;
        dwarf2_traverse_context_t       lctx;
        enum location_error             err;
        lctx.data = xloc.u.block.ptr;
        lctx.end_data = xloc.u.block.ptr + xloc.u.block.size;
        lctx.word_size = ctx->module->format_info[DFI_DWARF]->u.dwarf2_info->word_size;
        err = compute_location(&lctx, loc, NULL, frame);
        }
        else if (loc->kind == loc_dwarf2_block)
        {
            unsigned*   ptr = pool_alloc(&ctx->module->pool,
                                         sizeof(unsigned) + xloc.u.block.size);
            *ptr = xloc.u.block.size;
            memcpy(ptr + 1, xloc.u.block.ptr, xloc.u.block.size);
            loc->offset = (unsigned long)ptr;
        }
Eric Pouech's avatar
Eric Pouech committed
static struct symt* dwarf2_lookup_type(dwarf2_parse_context_t* ctx,
                                       const dwarf2_debug_info_t* di)
    if (dwarf2_find_attribute(ctx, di, DW_AT_type, &attr))
Eric Pouech's avatar
Eric Pouech committed
    {
        dwarf2_debug_info_t*    type;
        
        type = sparse_array_find(&ctx->debug_info_table, attr.u.uvalue);
        if (!type) FIXME("Unable to find back reference to type %lx\n", attr.u.uvalue);
Eric Pouech's avatar
Eric Pouech committed
        if (!type->symt)
        {
            /* load the debug info entity */
            dwarf2_load_one_entry(ctx, type, NULL);
        }
        return type->symt;
    }
    return NULL;
/******************************************************************
 *		dwarf2_read_one_debug_info
 *
 * Loads into memory one debug info entry, and recursively its children (if any)
 */
static BOOL dwarf2_read_one_debug_info(dwarf2_parse_context_t* ctx,
                                       dwarf2_traverse_context_t* traverse,
                                       dwarf2_debug_info_t** pdi)
{
    const dwarf2_abbrev_entry_t*abbrev;
    unsigned long               entry_code;
    unsigned long               offset;
    dwarf2_debug_info_t*        di;
    dwarf2_debug_info_t*        child;
    dwarf2_debug_info_t**       where;
    dwarf2_abbrev_entry_attr_t* attr;
    unsigned                    i;
    offset = traverse->data - ctx->sections[ctx->section].address;
    entry_code = dwarf2_leb128_as_unsigned(traverse);
    TRACE("found entry_code %lu at 0x%lx\n", entry_code, offset);
    if (!entry_code)
    {
        *pdi = NULL;
        return TRUE;
    }
    abbrev = dwarf2_abbrev_table_find_entry(&ctx->abbrev_table, entry_code);
    if (!abbrev)
    {
	WARN("Cannot find abbrev entry for %lu at 0x%lx\n", entry_code, offset);
	return FALSE;
    }
    di = sparse_array_add(&ctx->debug_info_table, offset, &ctx->pool);
    if (!di) return FALSE;
    di->abbrev = abbrev;
    di->symt   = NULL;

    if (abbrev->num_attr)
    {
        di->data = pool_alloc(&ctx->pool, abbrev->num_attr * sizeof(const char*));
        for (i = 0, attr = abbrev->attrs; attr; i++, attr = attr->next)
        {
            di->data[i] = traverse->data;
            dwarf2_swallow_attribute(traverse, attr);
    if (abbrev->have_child)
    {
        vector_init(&di->children, sizeof(dwarf2_debug_info_t*), 16);
        while (traverse->data < traverse->end_data)
            if (!dwarf2_read_one_debug_info(ctx, traverse, &child)) return FALSE;
            if (!child) break;
            where = vector_add(&di->children, &ctx->pool);
            if (!where) return FALSE;
            *where = child;