/*
 * dwarf_parse.c - DWARF4/5 调试信息解析模块
 *
 * 从目标可执行文件解析 .debug_info/.debug_abbrev/.debug_str 等节，
 * 提取 struct 成员布局、typedef/数组/指针类型链与全局变量类型，
 * 并提供运行期类型内省接口（resolve_typedef_chain_rt / find_struct_*）。
 * 全局 DWARF 工作列表（g_so_list/g_vr_list/g_td_list/g_ts_list/
 * g_arr_list）归本模块所有；ELF 符号表/路径解析见 symbol_parse.c。
 */

#include "lnxall_trace.h"

/* ========================================================================
 * DWARF type-resolution working lists (global BSS buffers)
 * ======================================================================== */
struct_offset_t   g_so_list[MAX_STRUCT_OFFSETS];
var_ref_t         g_vr_list[MAX_VAR_REFS];
typedef_ref_t     g_td_list[MAX_TYPEDEF_REFS];
type_size_entry_t g_ts_list[MAX_BASE_TYPE_SIZES];
array_ref_t       g_arr_list[MAX_ARRAY_REFS];
int g_so_count;    /* runtime count of g_so_list (set by dwarf_parse_types) */
int g_vr_count;    /* runtime count of g_vr_list (set by dwarf_parse_types) */
int g_arr_count;   /* runtime count of g_arr_list (set by dwarf_parse_types) */
int g_td_count;    /* runtime count of g_td_list  (set by dwarf_parse_types) */
int g_ts_count;    /* runtime count of g_ts_list  (set by dwarf_parse_types) */

/* ========================================================================
 * DWARF4 Debug Info Parser  (extract struct member layouts)
 * ======================================================================== */

/* ---- DWARF constants ---- */
#define DW_TAG_structure_type       0x13
#define DW_TAG_member               0x0d
#define DW_TAG_compile_unit         0x11
#define DW_TAG_variable             0x34
#define DW_TAG_typedef              0x16
#define DW_TAG_const_type           0x26
#define DW_TAG_volatile_type        0x35
#define DW_TAG_pointer_type         0x0f
#define DW_TAG_subroutine_type      0x15
#define DW_TAG_array_type           0x01
#define DW_TAG_subrange_type        0x21

#define DW_AT_name                  0x03
#define DW_AT_byte_size             0x0b
#define DW_AT_data_member_location  0x38
#define DW_AT_type                  0x49
#define DW_AT_lower_bound           0x22
#define DW_AT_upper_bound           0x2f
#define DW_AT_count                 0x37
#define DW_AT_sibling               0x01
#define DW_AT_str_offsets_base      0x72  /* DWARF5: base for .debug_str_offsets */

#define DW_FORM_addr                0x01  /* address, size = CU addr_sz */
#define DW_FORM_block2              0x03  /* 2-byte length + block data */
#define DW_FORM_block4              0x04  /* 4-byte length + block data */
#define DW_FORM_data1               0x0b
#define DW_FORM_data2               0x05
#define DW_FORM_data4               0x06
#define DW_FORM_data8               0x07
#define DW_FORM_udata               0x0f
#define DW_FORM_strp                0x0e
#define DW_FORM_string              0x08
#define DW_FORM_flag_present        0x19
#define DW_FORM_exprloc             0x18
#define DW_FORM_ref4                0x13
#define DW_FORM_ref_addr            0x10
#define DW_FORM_ref1                0x11  /* 1-byte CU-relative ref  */
#define DW_FORM_ref2                0x12  /* 2-byte CU-relative ref  */
#define DW_FORM_ref_udata           0x15
#define DW_FORM_ref8                0x14
#define DW_FORM_sec_offset          0x17
/* DWARF4 additional forms */
#define DW_FORM_block               0x09  /* ULEB length + block data */
#define DW_FORM_block1              0x0a  /* 1-byte length + block    */
#define DW_FORM_flag                0x0c  /* 1-byte flag              */
#define DW_FORM_sdata               0x0d  /* SLEB128 signed constant  */
#define DW_FORM_ref_sig8            0x20  /* 8-byte type signature    */
/* DWARF5 index forms */
#define DW_FORM_strx                0x1a  /* ULEB128 string index */
#define DW_FORM_addrx               0x1b  /* ULEB128 addr  index  */
#define DW_FORM_strp_sup            0x1d  /* supplemental strp   */
#define DW_FORM_line_strp           0x1f  /* .debug_line_str     */
#define DW_FORM_implicit_const      0x21  /* value in abbrev     */
#define DW_FORM_strx1               0x25  /* 1-byte string index */
#define DW_FORM_strx2               0x26  /* 2-byte string index */
#define DW_FORM_strx3               0x27  /* 3-byte string index */
#define DW_FORM_strx4               0x28  /* 4-byte string index */
#define DW_FORM_addrx1              0x29  /* 1-byte addr  index  */
#define DW_FORM_addrx2              0x2a  /* 2-byte addr  index  */
#define DW_FORM_addrx3              0x2b  /* 3-byte addr  index  */
#define DW_FORM_addrx4              0x2c  /* 4-byte addr  index  */

#define DW_CHILDREN_no              0x00
#define DW_CHILDREN_yes             0x01

/* ---- Unaligned safe readers (ARM64-compatible) ---- */
static uint16_t read_unaligned16(const uint8_t *p)
{
    uint16_t v;
    memcpy(&v, p, sizeof(v));
    return v;
}
static uint32_t read_unaligned32(const uint8_t *p)
{
    uint32_t v;
    memcpy(&v, p, sizeof(v));
    return v;
}
static uint64_t read_unaligned64(const uint8_t *p)
{
    uint64_t v;
    memcpy(&v, p, sizeof(v));
    return v;
}

/* ---- LEB128 helpers ---- */
static const uint8_t *read_uleb128(const uint8_t *p, uint64_t *out)
{
    uint64_t result = 0;
    int shift = 0;
    while (1) {
        uint8_t byte = *p++;
        result |= ((uint64_t)(byte & 0x7f)) << shift;
        if (!(byte & 0x80)) break;
        shift += 7;
    }
    *out = result;
    return p;
}
static const uint8_t *read_sleb128(const uint8_t *p, int64_t *out)
{
    int64_t result = 0;
    int shift = 0;
    uint8_t byte;
    while (1) {
        byte = *p++;
        result |= ((int64_t)(byte & 0x7f)) << shift;
        shift += 7;
        if (!(byte & 0x80)) break;
    }
    if (byte & 0x40) result |= -(1LL << shift);
    *out = result;
    return p;
}
/* ---- Dwarf abbrev table (per CU) ---- */
#define DWARF_MAX_ATTRS 12
typedef struct {
    uint64_t      code;           /* abbreviation code (non-sequential!) */
    uint64_t      tag;            /* ULEB128 解码值，可能超过 32 位 */
    int           has_children;
    unsigned long attr_name[DWARF_MAX_ATTRS];
    unsigned long attr_form[DWARF_MAX_ATTRS];
    uint64_t      attr_form_val[DWARF_MAX_ATTRS]; /* DW_FORM_implicit_const value */
    int           attr_count;
} dwarf_abbrev_t;

#define DWARF_MAX_ABBREVS 512
typedef struct {
    dwarf_abbrev_t entries[DWARF_MAX_ABBREVS];
    int            count;
} dwarf_abbrev_table_t;

/* qsort helper: order abbrev entries by code for binary search */
static int cmp_abbrev_code(const void *a, const void *b)
{
    const dwarf_abbrev_t *ea = (const dwarf_abbrev_t *)a;
    const dwarf_abbrev_t *eb = (const dwarf_abbrev_t *)b;
    if (ea->code < eb->code) return -1;
    if (ea->code > eb->code) return 1;
    return 0;
}

static int dwarf_parse_abbrev_table(const uint8_t *data, size_t size,
                                    dwarf_abbrev_table_t *table)
{
    const uint8_t *p = data;
    const uint8_t *end = data + size;
    table->count = 0;

    while (p < end) {
        uint64_t code;
        p = read_uleb128(p, &code);
        if (code == 0) break;                     /* end of abbrev table */

        dwarf_abbrev_t *ab = &table->entries[table->count];
        memset(ab, 0, sizeof(*ab));
        ab->code = code;                          /* store code for lookup */
        p = read_uleb128(p, &ab->tag);
        ab->has_children = *p++;

        while (p < end) {
            uint64_t name, form;
            p = read_uleb128(p, &name);
            p = read_uleb128(p, &form);
            if (name == 0 && form == 0) break;    /* end of entry */
            if (ab->attr_count < DWARF_MAX_ATTRS) {
                ab->attr_name[ab->attr_count] = name;
                ab->attr_form[ab->attr_count] = form;
                /* DW_FORM_implicit_const: value embedded in abbrev entry */
                if (form == DW_FORM_implicit_const) {
                    int64_t sval;
                    p = read_sleb128(p, &sval);
                    ab->attr_form_val[ab->attr_count] = (uint64_t)sval;
                } else {
                    ab->attr_form_val[ab->attr_count] = 0;
                }
                ab->attr_count++;
            }
        }
        table->count++;
        if (table->count >= DWARF_MAX_ABBREVS) {
            break;   /* remaining codes dropped */
        }
    }
    /* Sort by code so dwarf_find_abbrev can binary-search instead of
     * doing a linear scan for every DIE (O(dies × abbrevs) → O(dies × log). */
    if (table->count > 1)
        qsort(table->entries, (size_t)table->count, sizeof(table->entries[0]),
              cmp_abbrev_code);
    return 0;
}

static const dwarf_abbrev_t *dwarf_find_abbrev(const dwarf_abbrev_table_t *table,
                                                uint64_t code)
{
    /* Abbreviation codes are NOT necessarily sequential, so an index-based
     * lookup is impossible; the table is code-sorted by
     * dwarf_parse_abbrev_table() → binary search here. */
    int lo = 0, hi = table->count;
    while (lo < hi) {
        int mid = (lo + hi) / 2;
        if (table->entries[mid].code < code)
            lo = mid + 1;
        else
            hi = mid;
    }
    if (lo < table->count && table->entries[lo].code == code)
        return &table->entries[lo];
    return NULL;
}

/* Read a DWARF attribute value; advance ptr.  For string attributes,
 * *out_str is set (non-NULL) and the string is copied into *strbuf
 * (if both are provided).  Otherwise the value lands in *out_val.
 *
 * str_offsets / str_offsets_sz / str_offsets_base: DWARF5 string index
 * table support (.debug_str_offsets).  May be NULL/0 if not available.
 * addr_sz: CU address size for DW_FORM_addr. */
#define DWARF_STRBUF_SZ 256
static const uint8_t *dwarf_read_attr(const uint8_t *p, unsigned long form,
                                       uint64_t *out_val, char *strbuf,
                                       const uint8_t *dbg_str, size_t dbg_str_sz,
                                       const uint8_t *dbg_str_offsets,
                                       size_t dbg_str_offsets_sz,
                                       unsigned long str_offsets_base,
                                       int addr_sz)
{
    *out_val = 0;
    if (strbuf) strbuf[0] = '\0';

    switch (form) {
    case DW_FORM_data1:   *out_val = p[0];             p += 1;  break;
    case DW_FORM_data2:   *out_val = read_unaligned16(p); p += 2;  break;
    case DW_FORM_data4:   *out_val = read_unaligned32(p); p += 4;  break;
    case DW_FORM_data8:   *out_val = read_unaligned64(p); p += 8;  break;
    case DW_FORM_sdata: {
        int64_t sval;
        p = read_sleb128(p, &sval);
        *out_val = (uint64_t)sval;
        break;
    }
    case DW_FORM_flag:    *out_val = p[0];              p += 1;  break;
    case DW_FORM_block: {
        uint64_t blen;
        p = read_uleb128(p, &blen);
        *out_val = (uint64_t)(uintptr_t)p;
        p += blen;
        break;
    }
    case DW_FORM_block1: {
        uint8_t blen = p[0];  p += 1;
        *out_val = (uint64_t)(uintptr_t)p;
        p += blen;
        break;
    }
    case DW_FORM_block2: {
        uint16_t blen = read_unaligned16(p);  p += 2;
        *out_val = (uint64_t)(uintptr_t)p;
        p += blen;
        break;
    }
    case DW_FORM_block4: {
        uint32_t blen = read_unaligned32(p);  p += 4;
        *out_val = (uint64_t)(uintptr_t)p;
        p += blen;
        break;
    }
    case DW_FORM_addr:
        switch (addr_sz) {
        case 4: *out_val = read_unaligned32(p); p += 4; break;
        case 8: *out_val = read_unaligned64(p); p += 8; break;
        default: p += addr_sz; break;        /* skip unknown addr sz */
        }
        break;
    case DW_FORM_ref1:    *out_val = p[0];                    p += 1;  break;
    case DW_FORM_ref2:    *out_val = read_unaligned16(p);    p += 2;  break;
    case DW_FORM_udata:   p = read_uleb128(p, out_val);         break;
    case DW_FORM_ref4:
    case DW_FORM_ref_addr:
    case DW_FORM_sec_offset:
        *out_val = read_unaligned32(p);  p += 4;  break;
    case DW_FORM_ref_udata:
        p = read_uleb128(p, out_val);  break;
    case DW_FORM_ref8:
        *out_val = read_unaligned64(p);  p += 8;  break;
    case DW_FORM_flag_present:
        *out_val = 1;  break;
    case DW_FORM_string:
        if (strbuf) {
            size_t n = strlen((const char *)p);
            if (n >= DWARF_STRBUF_SZ) n = DWARF_STRBUF_SZ - 1;
            memcpy(strbuf, p, n); strbuf[n] = '\0';
        }
        p += strlen((const char *)p) + 1;
        break;
    case DW_FORM_strp: {
        uint32_t off = read_unaligned32(p);  p += 4;
        if (dbg_str && off < dbg_str_sz && strbuf) {
            const char *s = (const char *)(dbg_str + off);
            size_t n = strlen(s);
            if (n >= DWARF_STRBUF_SZ) n = DWARF_STRBUF_SZ - 1;
            memcpy(strbuf, s, n); strbuf[n] = '\0';
        }
        break;
    }
    case DW_FORM_exprloc: {
        uint64_t blen;
        p = read_uleb128(p, &blen);
        *out_val = (uint64_t)(uintptr_t)p;   /* save ptr for later parsing */
        p += blen;
        break;
    }

    /* ── DWARF5 string index forms ── */
    case DW_FORM_strx: {
        /* ULEB128 index into .debug_str_offsets */
        uint64_t idx;
        p = read_uleb128(p, &idx);
        *out_val = idx;
        if (dbg_str_offsets && dbg_str_offsets_sz > 0 && strbuf) {
            unsigned long entry_off = str_offsets_base + (unsigned long)(idx * 4);
            if (entry_off + 4 <= dbg_str_offsets_sz) {
                uint32_t stroff = read_unaligned32(dbg_str_offsets + entry_off);
                if (dbg_str && stroff < dbg_str_sz) {
                    const char *s = (const char *)(dbg_str + stroff);
                    size_t n = strlen(s);
                    if (n >= DWARF_STRBUF_SZ) n = DWARF_STRBUF_SZ - 1;
                    memcpy(strbuf, s, n); strbuf[n] = '\0';
                }
            }
        }
        break;
    }
    case DW_FORM_strx1: {
        uint32_t idx = p[0];  p += 1;  *out_val = idx;
        if (dbg_str_offsets && dbg_str_offsets_sz > 0 && strbuf) {
            unsigned long entry_off = str_offsets_base + (unsigned long)(idx * 4);
            if (entry_off + 4 <= dbg_str_offsets_sz) {
                uint32_t stroff = read_unaligned32(dbg_str_offsets + entry_off);
                if (dbg_str && stroff < dbg_str_sz) {
                    const char *s = (const char *)(dbg_str + stroff);
                    size_t n = strlen(s);
                    if (n >= DWARF_STRBUF_SZ) n = DWARF_STRBUF_SZ - 1;
                    memcpy(strbuf, s, n); strbuf[n] = '\0';
                }
            }
        }
        break;
    }
    case DW_FORM_strx2: {
        uint32_t idx = read_unaligned16(p);  p += 2;  *out_val = idx;
        if (dbg_str_offsets && dbg_str_offsets_sz > 0 && strbuf) {
            unsigned long entry_off = str_offsets_base + (unsigned long)(idx * 4);
            if (entry_off + 4 <= dbg_str_offsets_sz) {
                uint32_t stroff = read_unaligned32(dbg_str_offsets + entry_off);
                if (dbg_str && stroff < dbg_str_sz) {
                    const char *s = (const char *)(dbg_str + stroff);
                    size_t n = strlen(s);
                    if (n >= DWARF_STRBUF_SZ) n = DWARF_STRBUF_SZ - 1;
                    memcpy(strbuf, s, n); strbuf[n] = '\0';
                }
            }
        }
        break;
    }
    case DW_FORM_strx3: {
        uint32_t idx = (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16);
        p += 3;  *out_val = idx;
        if (dbg_str_offsets && dbg_str_offsets_sz > 0 && strbuf) {
            unsigned long entry_off = str_offsets_base + (unsigned long)(idx * 4);
            if (entry_off + 4 <= dbg_str_offsets_sz) {
                uint32_t stroff = read_unaligned32(dbg_str_offsets + entry_off);
                if (dbg_str && stroff < dbg_str_sz) {
                    const char *s = (const char *)(dbg_str + stroff);
                    size_t n = strlen(s);
                    if (n >= DWARF_STRBUF_SZ) n = DWARF_STRBUF_SZ - 1;
                    memcpy(strbuf, s, n); strbuf[n] = '\0';
                }
            }
        }
        break;
    }
    case DW_FORM_strx4: {
        uint32_t idx = read_unaligned32(p);  p += 4;  *out_val = idx;
        if (dbg_str_offsets && dbg_str_offsets_sz > 0 && strbuf) {
            unsigned long entry_off = str_offsets_base + (unsigned long)(idx * 4);
            if (entry_off + 4 <= dbg_str_offsets_sz) {
                uint32_t stroff = read_unaligned32(dbg_str_offsets + entry_off);
                if (dbg_str && stroff < dbg_str_sz) {
                    const char *s = (const char *)(dbg_str + stroff);
                    size_t n = strlen(s);
                    if (n >= DWARF_STRBUF_SZ) n = DWARF_STRBUF_SZ - 1;
                    memcpy(strbuf, s, n); strbuf[n] = '\0';
                }
            }
        }
        break;
    }

    /* ── DWARF5 forms that carry no DIE-body data ── */
    case DW_FORM_implicit_const:
        *out_val = 0;  /* real value is in abbrev table; best-effort=0 */
        /* p does NOT advance */
        break;

    /* ── DWARF4/5 offset/signature forms: advance p correctly ── */
    case DW_FORM_strp_sup:
    case DW_FORM_line_strp:
        *out_val = read_unaligned32(p);  p += 4;  break;
    case DW_FORM_ref_sig8:   /* DWARF4 type signature */
        *out_val = read_unaligned64(p);  p += 8;  break;

    /* ── DWARF5 address index forms: advance p correctly ── */
    case DW_FORM_addrx:
        p = read_uleb128(p, out_val);  break;
    case DW_FORM_addrx1:  *out_val = p[0];                   p += 1;  break;
    case DW_FORM_addrx2:  *out_val = read_unaligned16(p);   p += 2;  break;
    case DW_FORM_addrx3:
        *out_val = (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16);
        p += 3;  break;
    case DW_FORM_addrx4:  *out_val = read_unaligned32(p);    p += 4;  break;

    default:
        /* Unknown form – can't safely advance → stop here to avoid infinite
         * loop.  The caller will see a zero p-advance and handle it. */
        break;
    }
    return p;
}

/* Parse DW_AT_data_member_location expression.  Handles the trivial
 * DW_OP_plus_uconst (0x23) + ULEB and also a plain constant form. */
static int dwarf_parse_member_offset(unsigned long form, uint64_t raw_val,
                                      const uint8_t *dbg_str, size_t dbg_str_sz,
                                      unsigned long *out_offset)
{
    (void)dbg_str; (void)dbg_str_sz;

    switch (form) {
    case DW_FORM_data1: case DW_FORM_data2:
    case DW_FORM_data4: case DW_FORM_udata:
    case DW_FORM_data8:
        *out_offset = (unsigned long)raw_val;
        return 0;
    case DW_FORM_exprloc:
    case DW_FORM_block:
    case DW_FORM_block1:
    case DW_FORM_block2:
    case DW_FORM_block4: {
        const uint8_t *expr = (const uint8_t *)(uintptr_t)raw_val;
        if (!expr) return -1;
        /* Expect DW_OP_plus_uconst (0x23) */
        if (*expr == 0x23) {
            uint64_t off;
            read_uleb128(expr + 1, &off);
            *out_offset = (unsigned long)off;
            return 0;
        }
        return -1;  /* complex expression – not supported */
    }
    default:
        return -1;
    }
}

/* Strip leading "struct " / "union " / "enum " prefix from type name */
static void dwarf_strip_type_prefix(char *name)
{
    if (!name[0]) return;
    static const char *prefixes[] = {"struct ", "union ", "enum ", NULL};
    for (int i = 0; prefixes[i]; i++) {
        size_t plen = strlen(prefixes[i]);
        if (strncmp(name, prefixes[i], plen) == 0) {
            size_t nlen = strlen(name + plen);
            memmove(name, name + plen, nlen + 1);
            return;
        }
    }
}

/* 结构体表按需扩容（方案 B）：容量不足时倍增，避免固定上限截断。
 * realloc 可能移动数组，旧指针随之失效，故调用方一律用 struct_count
 * 索引访问（current_struct_idx），扩容后索引仍有效。
 * 返回 0 成功；-1 表示内存不足或已达硬上限 MAX_STRUCTS。 */
static int dwarf_structs_grow(void)
{
    int old_cap = g_dbg.structs_max;
    int new_cap = old_cap * 2;
    if (new_cap > MAX_STRUCTS)
        new_cap = MAX_STRUCTS;
    if (new_cap <= old_cap)
        return -1;

    struct_info_t *new_arr = (struct_info_t *)realloc(
        g_dbg.structs, (size_t)new_cap * sizeof(struct_info_t));
    if (new_arr == NULL)
        return -1;
    memset(new_arr + old_cap, 0,
           (size_t)(new_cap - old_cap) * sizeof(struct_info_t));
    g_dbg.structs = new_arr;
    g_dbg.structs_max = new_cap;
    return 0;
}

/* --- Struct collection context --- */
typedef struct {
    int             current_struct_idx;  /* index into g_dbg.structs[], -1 = none */
    /* tracking arrays shared across CUs */
    struct_offset_t *so_list;
    int             *so_count;
    var_ref_t       *vr_list;
    int             *vr_count;
    typedef_ref_t   *td_list;
    int             *td_count;
    type_size_entry_t *ts_list;
    int               *ts_count;
    array_ref_t       *arr_list;
    int               *arr_count;
    int                cur_array_idx;  /* index of enclosing array in arr_list, -1 if none */
    /* CU-relative ref → section-absolute conversion base */
    unsigned long   ref_base;            /* CU header offset in .debug_info */
    int             addr_sz;             /* address size from CU header */
    /* DWARF5 .debug_str_offsets support */
    const uint8_t  *dbg_str_offsets;     /* loaded .debug_str_offsets section */
    size_t          dbg_str_offsets_sz;
    unsigned long   str_offsets_base;    /* contribution offset in section */
    /* DIE tag histogram (diagnostic tracing) */
    unsigned long   die_tag_cnt[256];    /* count per tag (reused across CUs) */
    unsigned long   die_total;           /* total DIEs in this walk */
    unsigned long   die_abbrev_fail;     /* abbrev lookup failures */
    unsigned long   die_form_skip;       /* attributes with unknown forms */
    /* variable tracking (across all DIE levels in this CU) */
    unsigned long   total_vars;          /* all DW_TAG_variable DIEs seen */
    unsigned long   total_vars_typed;    /* vars collected into vr_list */
    unsigned long   total_vars_dropped;  /* vars with type but list full */
    /* overflow diagnostics (维测) — set once at first drop */
    unsigned int    overflow_mask;       /* which lists overflowed */
    unsigned long   first_drop_off;      /* die_off of first dropped object */
    char            first_drop_name[MAX_NAME_LEN]; /* its name, if available */
    /* 维测: first failure site (abbrev missing / unknown form) */
    unsigned long   first_abbrev_fail_off;  /* 首个 abbrev 缺失的 DIE 偏移 */
    uint64_t        first_abbrev_fail_code; /* 该 DIE 缺失的 abbrev code */
    unsigned long   first_form_skip_off;    /* 首个未知 form 的 DIE 偏移 */
    unsigned long   first_form_skip_form;   /* 未知的 form 值 */
} dwarf_walk_ctx;

/* overflow diagnostic bit flags */
#define OVF_STRUCTS_TABLE (1u << 0)   /* g_dbg.structs[] full */
#define OVF_STRUCT_OFFSET (1u << 1)   /* so_list full */
#define OVF_TYPEDEF       (1u << 2)   /* td_list full */
#define OVF_VARREF        (1u << 3)   /* vr_list full */
#define OVF_BASE_SIZE     (1u << 4)   /* ts_list full */
#define OVF_ARRAY         (1u << 5)   /* arr_list full */

/* Recursive DIE walker – collects struct definitions,
 * variable type references, and typedef chains.
 * 'info_data' is the start of .debug_info for absolute-offset calculation. */
static const uint8_t *dwarf_walk_dies(const uint8_t *p, const uint8_t *end,
                                       const dwarf_abbrev_table_t *abbrevs,
                                       const uint8_t *dbg_str, size_t dbg_str_sz,
                                       dwarf_walk_ctx *ctx,
                                       const uint8_t *info_data)
{
    while (p < end) {
        /* record absolute offset of this DIE */
        unsigned long die_offset = (unsigned long)(p - info_data);

        uint64_t ab_code;
        p = read_uleb128(p, &ab_code);
        if (ab_code == 0) return p;                    /* null entry → end siblings */

        const dwarf_abbrev_t *ab = dwarf_find_abbrev(abbrevs, ab_code);
        if (!ab) {
            if (ctx->die_abbrev_fail == 0) {
                ctx->first_abbrev_fail_off  = die_offset;
                ctx->first_abbrev_fail_code = ab_code;
            }
            ctx->die_abbrev_fail++;
            return end;                                /* can't parse this DIE */
        }

        /* DIE tag histogram (for diagnostics) */
        ctx->die_total++;
        if (ab->tag < 256) ctx->die_tag_cnt[ab->tag]++;

        char     attr_name_str[DWARF_STRBUF_SZ] = {0};
        uint64_t attr_byte_size = 0;
        uint64_t attr_type_ref  = 0;
        int      has_type_ref   = 0;
        uint64_t raw_mloc = 0;
        int      has_mloc = 0;
        unsigned long mloc_form = 0;
        uint64_t attr_lower_bound = 0, attr_upper_bound = 0, attr_count = 0;
        int      has_lower = 0, has_upper = 0, has_count = 0;
        /* local str_offsets_base that can be updated mid-DIE by
         * DW_AT_str_offsets_base or by children's context. */
        unsigned long local_str_base = ctx->str_offsets_base;

        /* --- read all attributes --- */
        const uint8_t *prev_p;
        for (int a = 0; a < ab->attr_count; a++) {
            uint64_t val;
            char tmp[DWARF_STRBUF_SZ] = {0};
            prev_p = p;
            /* DW_FORM_implicit_const: value comes from abbrev table, no DIE data */
            if ((unsigned long)ab->attr_form[a] == DW_FORM_implicit_const) {
                val = ab->attr_form_val[a];
            } else {
                p = dwarf_read_attr(p, (unsigned long)ab->attr_form[a],
                                    &val, tmp, dbg_str, dbg_str_sz,
                                    ctx->dbg_str_offsets, ctx->dbg_str_offsets_sz,
                                    local_str_base, ctx->addr_sz);
                /* detect unknown forms that didn't advance p */
                if (p == prev_p && ab->attr_form[a] != DW_FORM_flag_present) {
                    if (ctx->die_form_skip == 0) {
                        ctx->first_form_skip_off  = die_offset;
                        ctx->first_form_skip_form = (unsigned long)ab->attr_form[a];
                    }
                    ctx->die_form_skip++;
                    return end;  /* can't continue safely */
                }
            }
            prev_p = p;

            switch (ab->attr_name[a]) {
            case DW_AT_name:
                if (tmp[0]) safe_strcpy(attr_name_str, tmp, DWARF_STRBUF_SZ);
                break;
            case DW_AT_type:
                attr_type_ref = val;
                /* ref1/ref2/ref4/ref8/ref_udata are CU-relative offsets in ALL
                 * DWARF versions (2/3/4/5) → always convert to section-absolute.
                 * DW_FORM_ref_addr is already section-absolute and is
                 * deliberately NOT in this list.  (Old bug: gated on version>=4,
                 * so DWARF2/3 type refs were left CU-relative and never matched
                 * the absolute offsets stored in td/ts/so lookup lists.) */
                if (ab->attr_form[a] == DW_FORM_ref1 ||
                    ab->attr_form[a] == DW_FORM_ref2 ||
                    ab->attr_form[a] == DW_FORM_ref4 ||
                    ab->attr_form[a] == DW_FORM_ref8 ||
                    ab->attr_form[a] == DW_FORM_ref_udata) {
                    attr_type_ref += ctx->ref_base;
                }
                has_type_ref  = 1;
                break;
            case DW_AT_byte_size:
                attr_byte_size = val;
                break;
            case DW_AT_data_member_location:
                raw_mloc  = val;
                mloc_form = (unsigned long)ab->attr_form[a];
                has_mloc  = 1;
                break;
            case DW_AT_lower_bound:
                attr_lower_bound = val; has_lower = 1;
                break;
            case DW_AT_upper_bound:
                attr_upper_bound = val; has_upper = 1;
                break;
            case DW_AT_count:
                attr_count = val; has_count = 1;
                break;
            case DW_AT_str_offsets_base:
                /* DWARF5: CU DIE carries its own contribution offset */
                local_str_base = (unsigned long)val;
                ctx->str_offsets_base = local_str_base;
                break;
            default: break;
            }
        }

        /* record byte_size for ANY DIE that has one → base-type size lookup */
        if (attr_byte_size > 0 &&
            ctx->ts_list && *ctx->ts_count < MAX_BASE_TYPE_SIZES) {
            ctx->ts_list[*ctx->ts_count].die_offset = die_offset;
            ctx->ts_list[*ctx->ts_count].byte_size  = attr_byte_size;
            (*ctx->ts_count)++;
        } else if (attr_byte_size > 0 &&
                   *ctx->ts_count >= MAX_BASE_TYPE_SIZES &&
                   !(ctx->overflow_mask & OVF_BASE_SIZE)) {
            ctx->overflow_mask |= OVF_BASE_SIZE;
            ctx->first_drop_off = die_offset;
        }

        /* --- process DIE --- */
        switch (ab->tag) {
        case DW_TAG_array_type:
            /* Record the array type so members whose DW_AT_type points here
             * can resolve their TOTAL size (element size × count).  The
             * element count is filled in by the DW_TAG_subrange_type child
             * DIE below (from DW_AT_upper_bound / DW_AT_count). */
            ctx->cur_array_idx = -1;
            if (has_type_ref && ctx->arr_list &&
                *ctx->arr_count < MAX_ARRAY_REFS) {
                array_ref_t *ar = &ctx->arr_list[*ctx->arr_count];
                ar->arr_offset = die_offset;
                ar->elem_ref   = attr_type_ref;
                ar->elem_count = 1;   /* multiplied by each subrange below */
                ctx->cur_array_idx = *ctx->arr_count;
                (*ctx->arr_count)++;
            } else if (has_type_ref &&
                       *ctx->arr_count >= MAX_ARRAY_REFS &&
                       !(ctx->overflow_mask & OVF_ARRAY)) {
                ctx->overflow_mask |= OVF_ARRAY;
                ctx->first_drop_off = die_offset;
            }
            break;

        case DW_TAG_subrange_type:
            /* child of DW_TAG_array_type: contributes the element count */
            if (ctx->cur_array_idx >= 0) {
                unsigned long cnt = 0;
                if (has_count) {
                    cnt = (unsigned long)attr_count;
                } else if (has_upper) {
                    unsigned long lb = has_lower ? (unsigned long)attr_lower_bound : 0;
                    cnt = (attr_upper_bound >= lb)
                              ? (unsigned long)(attr_upper_bound - lb + 1) : 0;
                }
                array_ref_t *ar = &ctx->arr_list[ctx->cur_array_idx];
                if (cnt > 1) ar->elem_count *= cnt;
                else if (cnt == 0) ar->elem_count = 0;  /* flexible/unknown */
            }
            break;

        case DW_TAG_structure_type:
            /* Accept BOTH named and anonymous structs.  Anonymous structs
             * (typedef struct { ... } name_t;) carry an empty DW_AT_name,
             * yet variables reference them via DW_AT_type → typedef → this
             * DIE.  Skipping them (old behaviour) left every typedef'd
             * anonymous-struct variable unresolved, e.g. dev_info_all_t:
             *   typedef struct { ... } dev_info_all_t;
             * Its struct DIE has no DW_AT_name, so it never entered
             * g_dbg.structs[] / so_list and the var→struct match failed. */
            /* 容量不足时先按需扩容；扩容失败（OOM/达硬上限）才记录溢出
             * 并放弃该定义，避免固定上限（8192）导致结构体截断 */
            if (g_dbg.struct_count >= g_dbg.structs_max &&
                dwarf_structs_grow() != 0) {
                if (!(ctx->overflow_mask & OVF_STRUCTS_TABLE)) {
                    /* 维测: structs table full — record first dropped definition */
                    ctx->overflow_mask |= OVF_STRUCTS_TABLE;
                    ctx->first_drop_off = die_offset;
                    safe_strcpy(ctx->first_drop_name,
                                attr_name_str[0] ? attr_name_str : "<anonymous>",
                                MAX_NAME_LEN);
                }
                break;
            }
            {
                const char *disp_name = attr_name_str;
                if (attr_name_str[0])
                    dwarf_strip_type_prefix(attr_name_str);

                struct_info_t *st = &g_dbg.structs[g_dbg.struct_count];
                memset(st, 0, sizeof(*st));
                /* 按需分配：初始仅 STRUCT_FIELDS_INIT 个槽位，成员写入时倍增，
                 * 上限 MAX_STRUCT_FIELDS。多数结构体字段 < 8，可省约 90% 内存 */
                st->fields = calloc(STRUCT_FIELDS_INIT, sizeof(struct_field_t));
                st->fields_cap = STRUCT_FIELDS_INIT;
                if (!st->fields) {
                    /* 字段内存不足：放弃该结构体定义（避免空指针解引用） */
                    ctx->overflow_mask |= OVF_STRUCTS_TABLE;
                    ctx->first_drop_off = die_offset;
                    safe_strcpy(ctx->first_drop_name,
                                attr_name_str[0] ? attr_name_str : "<anonymous>",
                                MAX_NAME_LEN);
                    break;
                }
                if (attr_name_str[0]) {
                    safe_strcpy(st->name, attr_name_str, MAX_NAME_LEN);
                    disp_name = st->name;
                } else {
                    /* leave name empty; display code prints <anonymous> */
                    disp_name = "<anonymous>";
                }
                st->size = (unsigned long)attr_byte_size;
                st->die_offset = die_offset;
                st->field_count = 0;
                /* 扩容可能移动数组，故记录索引而非指针 */
                ctx->current_struct_idx = g_dbg.struct_count;

                /* record offset for later var→struct matching */
                if (ctx->so_list && *ctx->so_count < MAX_STRUCT_OFFSETS) {
                    struct_offset_t *so = &ctx->so_list[*ctx->so_count];
                    so->die_offset = die_offset;
                    so->struct_idx = g_dbg.struct_count;
                    (*ctx->so_count)++;
                } else if (*ctx->so_count == MAX_STRUCT_OFFSETS) {
                    (*ctx->so_count)++;  /* only warn once */
                    if (!(ctx->overflow_mask & OVF_STRUCT_OFFSET)) {
                        ctx->overflow_mask |= OVF_STRUCT_OFFSET;
                        ctx->first_drop_off = die_offset;
                        safe_strcpy(ctx->first_drop_name, disp_name, MAX_NAME_LEN);
                    }
                }
                g_dbg.struct_count++;
            }
            break;

        case DW_TAG_member:
            if (ctx->current_struct_idx >= 0 && attr_name_str[0]) {
                struct_info_t *cs = &g_dbg.structs[ctx->current_struct_idx];
                if (cs->field_count >= cs->fields_cap) {
                    /* 容量不足：按需倍增（上限 MAX_STRUCT_FIELDS）；realloc
                     * 失败则保持原容量，静默跳过该成员，不影响既有数据 */
                    int new_cap = (cs->fields_cap > 0)
                                      ? (cs->fields_cap * 2) : STRUCT_FIELDS_INIT;
                    if (new_cap > MAX_STRUCT_FIELDS)
                        new_cap = MAX_STRUCT_FIELDS;
                    if (cs->field_count < MAX_STRUCT_FIELDS) {
                        struct_field_t *nf = (struct_field_t *)realloc(
                            cs->fields,
                            (size_t)new_cap * sizeof(struct_field_t));
                        if (nf) {
                            cs->fields = nf;
                            cs->fields_cap = new_cap;
                        }
                    }
                }
                if (cs->field_count < cs->fields_cap) {
                    struct_field_t *sf = &cs->fields[cs->field_count];
                    unsigned long moff = 0;
                    if (has_mloc && dwarf_parse_member_offset(
                            mloc_form, raw_mloc, dbg_str, dbg_str_sz, &moff) == 0) {
                        safe_strcpy(sf->name, attr_name_str, MAX_FIELD_NAME_LEN);
                        sf->offset   = moff;
                        sf->size     = (unsigned long)attr_byte_size;
                        sf->type_ref = has_type_ref ? attr_type_ref : 0;
                        cs->field_count++;
                    }
                }
            }
            break;

        case DW_TAG_variable:
            ctx->total_vars++;
            /* record variable name + type reference for later matching */
            if (attr_name_str[0] && has_type_ref &&
                ctx->vr_list && *ctx->vr_count < MAX_VAR_REFS) {
                var_ref_t *vr = &ctx->vr_list[*ctx->vr_count];
                safe_strcpy(vr->name, attr_name_str, MAX_NAME_LEN);
                vr->type_ref = attr_type_ref;
                (*ctx->vr_count)++;
                ctx->total_vars_typed++;
            } else if (attr_name_str[0] && has_type_ref) {
                /* has type but list is full */
                ctx->total_vars_dropped++;
                if (!(ctx->overflow_mask & OVF_VARREF)) {
                    ctx->overflow_mask |= OVF_VARREF;
                    ctx->first_drop_off = die_offset;
                    safe_strcpy(ctx->first_drop_name, attr_name_str, MAX_NAME_LEN);
                }
            }
            break;

        case DW_TAG_typedef:
            /* record typedef offset → target_ref chain */
            if (has_type_ref &&
                ctx->td_list && *ctx->td_count < MAX_TYPEDEF_REFS) {
                typedef_ref_t *td = &ctx->td_list[*ctx->td_count];
                td->td_offset  = die_offset;
                td->target_ref = attr_type_ref;
                (*ctx->td_count)++;
            } else if (has_type_ref &&
                       *ctx->td_count >= MAX_TYPEDEF_REFS &&
                       !(ctx->overflow_mask & OVF_TYPEDEF)) {
                ctx->overflow_mask |= OVF_TYPEDEF;
                ctx->first_drop_off = die_offset;
            }
            break;

        case DW_TAG_const_type:
        case DW_TAG_volatile_type:
        case DW_TAG_pointer_type:
            /* wrapper types: record as typedef-like chain entries */
            if (has_type_ref &&
                ctx->td_list && *ctx->td_count < MAX_TYPEDEF_REFS) {
                typedef_ref_t *td = &ctx->td_list[*ctx->td_count];
                td->td_offset  = die_offset;
                td->target_ref = attr_type_ref;
                (*ctx->td_count)++;
            } else if (has_type_ref &&
                       *ctx->td_count >= MAX_TYPEDEF_REFS &&
                       !(ctx->overflow_mask & OVF_TYPEDEF)) {
                ctx->overflow_mask |= OVF_TYPEDEF;
                ctx->first_drop_off = die_offset;
            }
            /* pointer types: size = architecture address size */
            if (ab->tag == DW_TAG_pointer_type &&
                ctx->ts_list && *ctx->ts_count < MAX_BASE_TYPE_SIZES) {
                ctx->ts_list[*ctx->ts_count].die_offset = die_offset;
                ctx->ts_list[*ctx->ts_count].byte_size  = ctx->addr_sz;
                (*ctx->ts_count)++;
            } else if (ab->tag == DW_TAG_pointer_type &&
                       *ctx->ts_count >= MAX_BASE_TYPE_SIZES &&
                       !(ctx->overflow_mask & OVF_BASE_SIZE)) {
                ctx->overflow_mask |= OVF_BASE_SIZE;
                ctx->first_drop_off = die_offset;
            }
            break;

        default: break;
        }

        /* --- recurse into children --- */
        if (ab->has_children) {
            dwarf_walk_ctx child_ctx = *ctx;
            p = dwarf_walk_dies(p, end, abbrevs,
                                dbg_str, dbg_str_sz, &child_ctx, info_data);
            /* ── merge child-only counters back into parent ── */
            ctx->die_total       = child_ctx.die_total;
            ctx->die_abbrev_fail = child_ctx.die_abbrev_fail;
            ctx->die_form_skip   = child_ctx.die_form_skip;
            memcpy(ctx->die_tag_cnt, child_ctx.die_tag_cnt,
                   sizeof(ctx->die_tag_cnt));
            ctx->total_vars       = child_ctx.total_vars;
            ctx->total_vars_typed  = child_ctx.total_vars_typed;
            ctx->total_vars_dropped = child_ctx.total_vars_dropped;
            ctx->overflow_mask    = child_ctx.overflow_mask;
            if (child_ctx.first_drop_off != 0 && ctx->first_drop_off == 0) {
                ctx->first_drop_off = child_ctx.first_drop_off;
                safe_strcpy(ctx->first_drop_name,
                            child_ctx.first_drop_name, MAX_NAME_LEN);
            }
        }
    }
    return p;
}

/* ──────────────────────────────────────────────────────────────────────
 * Type resolution helpers  (sorted + binary-search for O(log N) lookups)
 * ────────────────────────────────────────────────────────────────────── */

/* qsort comparators — sort by offset ascending */
static int cmp_struct_offset(const void *a, const void *b) {
    const struct_offset_t *x = (const struct_offset_t *)a;
    const struct_offset_t *y = (const struct_offset_t *)b;
    return (x->die_offset > y->die_offset) - (x->die_offset < y->die_offset);
}
static int cmp_typedef_ref(const void *a, const void *b) {
    const typedef_ref_t *x = (const typedef_ref_t *)a;
    const typedef_ref_t *y = (const typedef_ref_t *)b;
    return (x->td_offset > y->td_offset) - (x->td_offset < y->td_offset);
}
static int cmp_type_size_entry(const void *a, const void *b) {
    const type_size_entry_t *x = (const type_size_entry_t *)a;
    const type_size_entry_t *y = (const type_size_entry_t *)b;
    return (x->die_offset > y->die_offset) - (x->die_offset < y->die_offset);
}

/* binary-search typed helpers; return pointer or NULL */
static const struct_offset_t *
    bsearch_so(unsigned long key, const struct_offset_t *a, int n) {
    int lo = 0, hi = n - 1;
    while (lo <= hi) {
        int mid = lo + (hi - lo) / 2;
        if (a[mid].die_offset < key) lo = mid + 1;
        else if (a[mid].die_offset > key) hi = mid - 1;
        else return &a[mid];
    }
    return NULL;
}
static const typedef_ref_t *
    bsearch_td(unsigned long key, const typedef_ref_t *a, int n) {
    int lo = 0, hi = n - 1;
    while (lo <= hi) {
        int mid = lo + (hi - lo) / 2;
        if (a[mid].td_offset < key) lo = mid + 1;
        else if (a[mid].td_offset > key) hi = mid - 1;
        else return &a[mid];
    }
    return NULL;
}
static const type_size_entry_t *
    bsearch_ts(unsigned long key, const type_size_entry_t *a, int n) {
    int lo = 0, hi = n - 1;
    while (lo <= hi) {
        int mid = lo + (hi - lo) / 2;
        if (a[mid].die_offset < key) lo = mid + 1;
        else if (a[mid].die_offset > key) hi = mid - 1;
        else return &a[mid];
    }
    return NULL;
}
static int cmp_array_ref(const void *a, const void *b) {
    const array_ref_t *x = (const array_ref_t *)a;
    const array_ref_t *y = (const array_ref_t *)b;
    return (x->arr_offset > y->arr_offset) - (x->arr_offset < y->arr_offset);
}
static const array_ref_t *
    bsearch_arr(unsigned long key, const array_ref_t *a, int n) {
    int lo = 0, hi = n - 1;
    while (lo <= hi) {
        int mid = lo + (hi - lo) / 2;
        if (a[mid].arr_offset < key) lo = mid + 1;
        else if (a[mid].arr_offset > key) hi = mid - 1;
        else return &a[mid];
    }
    return NULL;
}

/* Resolve a DIE type reference through typedef/const/volatile chains.
 * Returns the struct DIE offset if a struct is found, 0 otherwise.
 * Expects so_list and td_list to be pre-sorted by offset. */
static unsigned long dwarf_resolve_to_struct_offset(
    unsigned long start_ref,
    const struct_offset_t *so_list, int so_cnt,
    const typedef_ref_t    *td_list, int td_cnt,
    int max_hops)
{
    unsigned long ref = start_ref;
    for (int hop = 0; hop < max_hops; hop++) {
        if (bsearch_so(ref, so_list, so_cnt))
            return ref;
        const typedef_ref_t *td = bsearch_td(ref, td_list, td_cnt);
        if (!td) break;
        ref = td->target_ref;
    }
    return 0;
}

/* Resolve a DIE type reference through typedef/const/volatile/pointer chains
 * to find the byte_size of the ultimate base type.
 * Expects ts_list and td_list to be pre-sorted by offset. */
static unsigned long dwarf_resolve_to_base_size(
    unsigned long start_ref,
    const type_size_entry_t *ts_list, int ts_cnt,
    const typedef_ref_t     *td_list, int td_cnt,
    int max_hops)
{
    unsigned long ref = start_ref;
    for (int hop = 0; hop < max_hops; hop++) {
        const type_size_entry_t *ts = bsearch_ts(ref, ts_list, ts_cnt);
        if (ts) return ts->byte_size;
        const typedef_ref_t *td = bsearch_td(ref, td_list, td_cnt);
        if (!td) break;
        ref = td->target_ref;
    }
    return 0;
}

/* Resolve a member whose DW_AT_type chain reaches a DW_TAG_array_type.
 * Returns the TOTAL array size in bytes (element_size × element_count),
 * or 0 if the chain never reaches an array type.
 * Optionally reports element count / element size via out params.
 * Element type resolution: struct → struct size; nested array → recurse;
 * otherwise base-type size.  Expects all lists pre-sorted by offset. */
static unsigned long dwarf_resolve_to_array_size(
    unsigned long start_ref,
    const array_ref_t     *arr_list, int arr_cnt,
    const struct_offset_t *so_list, int so_cnt,
    const typedef_ref_t   *td_list, int td_cnt,
    const type_size_entry_t *ts_list, int ts_cnt,
    int max_hops,
    unsigned long *out_count, unsigned long *out_elem_sz,
    unsigned long *out_elem_struct_ref)
{
    if (out_elem_struct_ref) *out_elem_struct_ref = 0;
    unsigned long ref = start_ref;
    for (int hop = 0; hop < max_hops; hop++) {
        const array_ref_t *ar = bsearch_arr(ref, arr_list, arr_cnt);
        if (!ar) {
            const typedef_ref_t *td = bsearch_td(ref, td_list, td_cnt);
            if (!td) return 0;
            ref = td->target_ref;
            continue;
        }
        /* array type found — resolve its element size */
        unsigned long elem_sz = 0;
        unsigned long so_off = dwarf_resolve_to_struct_offset(
            ar->elem_ref, so_list, so_cnt, td_list, td_cnt, max_hops);
        if (so_off) {
            if (out_elem_struct_ref) *out_elem_struct_ref = so_off;
            const struct_offset_t *sop = bsearch_so(so_off, so_list, so_cnt);
            if (sop) elem_sz = g_dbg.structs[sop->struct_idx].size;
        } else {
            /* nested array (multi-dim / clang style)? */
            unsigned long nested_cnt = 0, nested_elem = 0;
            elem_sz = dwarf_resolve_to_array_size(
                ar->elem_ref, arr_list, arr_cnt, so_list, so_cnt,
                td_list, td_cnt, ts_list, ts_cnt, max_hops,
                &nested_cnt, &nested_elem, NULL);
            if (elem_sz == 0)
                elem_sz = dwarf_resolve_to_base_size(
                    ar->elem_ref, ts_list, ts_cnt, td_list, td_cnt, 12);
        }
        if (elem_sz == 0) return 0;
        /* NOTE: ar->elem_count may be 0 for flexible/incomplete arrays
         * ('T x[]' — e.g. 'const char *show_log[]').  DWARF then carries
         * no upper bound.  Do not treat that as failure: the caller keeps
         * the ELF symbol size as the total and the runtime derives the
         * element count as size / element_size. */
        if (out_count)   *out_count   = ar->elem_count;
        if (out_elem_sz) *out_elem_sz = elem_sz;
        return elem_sz * (ar->elem_count > 0 ? ar->elem_count : 1);
    }
    return 0;
}

/* Resolve member sizes that were left as 0 by the DIE walker
 * (DW_TAG_member often omits DW_AT_byte_size, relying on DW_AT_type).
 * Array members now get their TOTAL size (element size × count) via
 * the array lookup list, instead of failing as unknown (old behaviour). */
static void dwarf_resolve_member_sizes(
    const struct_offset_t   *so_list, int so_cnt,
    const typedef_ref_t     *td_list, int td_cnt,
    const type_size_entry_t *ts_list, int ts_cnt,
    const array_ref_t       *arr_list, int arr_cnt)
{
    for (int i = 0; i < g_dbg.struct_count; i++) {
        struct_info_t *st = &g_dbg.structs[i];
        for (int j = 0; j < st->field_count; j++) {
            struct_field_t *sf = &st->fields[j];
            if (sf->type_ref != 0) {
                /* array member: type chain reaches DW_TAG_array_type.
                 * Run unconditionally (not only when size==0) so array
                 * shape info is filled even if the compiler emitted an
                 * explicit DW_AT_byte_size for the member. */
                unsigned long a_cnt = 0, a_elem = 0;
                unsigned long arr_sz = dwarf_resolve_to_array_size(
                    sf->type_ref, arr_list, arr_cnt,
                    so_list, so_cnt, td_list, td_cnt, ts_list, ts_cnt,
                    8, &a_cnt, &a_elem, NULL);
                if (arr_sz > 0) {
                    sf->arr_count     = a_cnt;
                    sf->arr_elem_size = a_elem;
                    if (sf->size == 0) {
                        sf->size = arr_sz;   /* total array size in bytes */
                    }
                    continue;   /* keep explicit byte_size if one was given */
                }
            }
            if (sf->size == 0 && sf->type_ref != 0) {
                /* then struct resolution */
                unsigned long found = dwarf_resolve_to_struct_offset(
                    sf->type_ref, so_list, so_cnt, td_list, td_cnt, 5);
                if (found) {
                    /* look up struct size from match (so_list is sorted) */
                    const struct_offset_t *sop = bsearch_so(found, so_list, so_cnt);
                    if (sop) {
                        sf->size = g_dbg.structs[sop->struct_idx].size;
                    }
                } else {
                    /* fallback: resolve through typedef chain to base type */
                    unsigned long base_sz = dwarf_resolve_to_base_size(
                        sf->type_ref, ts_list, ts_cnt, td_list, td_cnt, 12);
                    if (base_sz > 0) {
                        sf->size = base_sz;
                    }
                }
            }
        }
    }
}

/* qsort helper: order ELF symbol indices by symbol name */
static int cmp_sym_name_idx(const void *a, const void *b)
{
    int ia = *(const int *)a, ib = *(const int *)b;
    return strcmp(g_dbg.sym_table[ia].name, g_dbg.sym_table[ib].name);
}

/* Resolve each DWARF variable's type_ref → fine-grained symbol kind,
 * writing results directly back into g_dbg.sym_table[]. */
static void dwarf_resolve_var_to_struct(
    const var_ref_t        *vr_list, int vr_cnt,
    const struct_offset_t  *so_list, int so_cnt,
    const typedef_ref_t    *td_list, int td_cnt,
    const type_size_entry_t *ts_list, int ts_cnt,
    const array_ref_t      *arr_list, int arr_cnt)
{
    /* Build a name-sorted index of the ELF symbol table once, so the
     * per-variable match below is O(log N) instead of O(N).  Previously
     * this was a vr_cnt × sym_count strcmp scan — the dominant cost at
     * attach time for binaries with large symbol/DWARF data. */
    int *sym_idx = NULL;
    if (g_dbg.sym_count > 0) {
        sym_idx = malloc((size_t)g_dbg.sym_count * sizeof(int));
        if (sym_idx) {
            for (int i = 0; i < g_dbg.sym_count; i++) sym_idx[i] = i;
            qsort(sym_idx, (size_t)g_dbg.sym_count, sizeof(int),
                  cmp_sym_name_idx);
        }
    }

    for (int v = 0; v < vr_cnt; v++) {
        unsigned long found = dwarf_resolve_to_struct_offset(
            vr_list[v].type_ref, so_list, so_cnt, td_list, td_cnt, 5);
        int struct_idx = -1;
        if (found) {
            const struct_offset_t *sop = bsearch_so(found, so_list, so_cnt);
            if (sop) struct_idx = sop->struct_idx;
        }

        /* fallback: resolve to base type size (int/short/char/float/ptr...) */
        unsigned long base_sz = 0;
        if (struct_idx < 0) {
            base_sz = dwarf_resolve_to_base_size(
                vr_list[v].type_ref, ts_list, ts_cnt, td_list, td_cnt, 12);
        }

        /* NEW: the variable itself may be an array type (e.g. a struct
         * array global such as 'struct xxx name[N]').  Its DW_AT_type
         * points straight at a DW_TAG_array_type DIE which is not in the
         * struct/typedef/base lists, so without this branch resolution
         * used to fail with "type resolution FAILED". */
        unsigned long arr_size = 0, arr_count_v = 0;
        unsigned long elem_struct_ref = 0;
        if (struct_idx < 0 && base_sz == 0) {
            arr_size = dwarf_resolve_to_array_size(
                vr_list[v].type_ref, arr_list, arr_cnt,
                so_list, so_cnt, td_list, td_cnt, ts_list, ts_cnt,
                8, &arr_count_v, NULL, &elem_struct_ref);
            if (arr_size > 0 && elem_struct_ref) {
                const struct_offset_t *sop =
                    bsearch_so(elem_struct_ref, so_list, so_cnt);
                if (sop) struct_idx = sop->struct_idx;
            }
        }

        if (struct_idx < 0 && base_sz == 0 && arr_size == 0) {
            continue;
        }

        /* match DWARF variable name against ELF symbol table.
         * O(log N) via the name-sorted index; linear scan only as a
         * fallback if the index allocation failed. */
        symbol_t *match = NULL;
        if (sym_idx) {
            int lo = 0, hi = g_dbg.sym_count;
            while (lo < hi) {
                int mid = (lo + hi) / 2;
                if (strcmp(g_dbg.sym_table[sym_idx[mid]].name,
                           vr_list[v].name) < 0)
                    lo = mid + 1;
                else
                    hi = mid;
            }
            for (int k = lo; k < g_dbg.sym_count &&
                             strcmp(g_dbg.sym_table[sym_idx[k]].name,
                                    vr_list[v].name) == 0; k++) {
                if (g_dbg.sym_table[sym_idx[k]].type == SYM_OBJECT) {
                    match = &g_dbg.sym_table[sym_idx[k]];
                    break;
                }
            }
        } else {
            for (int i = 0; i < g_dbg.sym_count; i++) {
                if (strcmp(g_dbg.sym_table[i].name, vr_list[v].name) == 0 &&
                    g_dbg.sym_table[i].type == SYM_OBJECT) {
                    match = &g_dbg.sym_table[i];
                    break;
                }
            }
        }
        if (match) {
            symbol_t *sym = match;
            sym->has_dwarf = 1;
            sym->dwarf_type_ref = vr_list[v].type_ref;
            if (arr_size > 0) {
                /* array variable (element may be a struct) */
                sym->kind = SYM_KIND_ARRAY;
                sym->struct_idx = struct_idx;   /* element struct, or -1 */
                if (arr_count_v > 0)
                    sym->size = arr_size;       /* total bytes known */
                /* else: flexible/incomplete array ('T x[]') — keep the
                 * ELF symbol size as total; runtime derives the count
                 * as size / element_size */
            } else if (struct_idx >= 0) {
                sym->kind = SYM_KIND_STRUCT;
                sym->struct_idx = struct_idx;
            } else {
                sym->kind = SYM_KIND_INT;
                sym->struct_idx = -1;
                sym->size = base_sz;   /* refine ELF size with DWARF size */
            }
        }
    }
    free(sym_idx);
}

/* Main entry – parse DWARF debug info for struct types */
int dwarf_parse_types(const char *path)
{
    FILE *fp = NULL;
    elf_file_hdr_t hdr;

    /* 统一 ELF 头/section 解析（32/64 位目标均支持，见 symbol_parse.c
     * elf_read_file）；DWARF 数据内容与 ELF 位数无关，section 定位后
     * 按 offset/size 直接加载。 */
    memset(&hdr, 0, sizeof(hdr));
    if (elf_read_file(path, &fp, &hdr) != 0) {
        fprintf(stderr, "[DWARF] error: cannot read ELF file '%s'\n", path);
        return -1;
    }

    /* ── ELF 头诊断：确认位数/section 表解析结果 ── */
    fprintf(stderr, "[DWARF] ELF hdr: %s e_type=0x%x shoff=0x%lx "
                    "shentsize=%u shnum=%u shstrndx=%u\n",
            hdr.is_elf64 ? "ELF64" : "ELF32", hdr.e_type, hdr.e_shoff,
            (unsigned)hdr.e_shentsize, (unsigned)hdr.e_shnum,
            (unsigned)hdr.e_shstrndx);

    /* --- locate needed DWARF sections --- */
    const elf_section_t *info_sh = NULL, *abbrev_sh = NULL, *str_sh = NULL;
    const elf_section_t *str_offsets_sh = NULL;
    for (int i = 0; i < hdr.e_shnum; i++) {
        const elf_section_t *s = &hdr.shdrs[i];
        if (strcmp(s->name, ".debug_info") == 0)          info_sh        = s;
        if (strcmp(s->name, ".debug_abbrev") == 0)        abbrev_sh      = s;
        if (strcmp(s->name, ".debug_str") == 0)           str_sh         = s;
        if (strcmp(s->name, ".debug_str_offsets") == 0)   str_offsets_sh = s;
    }

    int ret = -1;
    if (!info_sh || !abbrev_sh) {
        fprintf(stderr, "[DWARF] error: .debug_info / .debug_abbrev not found in '%s'\n"
                        "         (stripped binary, or compiled without -g?)\n", path);
        /* 维测：dump 全部 section，区分"无调试段"与"段名/解析异常" */
        int sym_found = 0;   /* .symtab 存在性（默认 strip 会删除它） */
        for (int i = 0; i < hdr.e_shnum; i++) {
            if (strcmp(hdr.shdrs[i].name, ".symtab") == 0) sym_found = 1;
        }
        fprintf(stderr, "         section table dump (%u entries, .symtab %s):\n",
                (unsigned)hdr.e_shnum, sym_found ? "present" : "ABSENT(stripped)");
        for (int i = 0; i < hdr.e_shnum; i++) {
            const elf_section_t *s = &hdr.shdrs[i];
            if (strncmp(s->name, ".zdebug_", 8) == 0) {
                fprintf(stderr, "         [%2u] %-28s type=0x%lx off=0x%lx size=0x%lx "
                                "  *** compressed (built with -gz, not supported)\n",
                        i, s->name, s->type, s->offset, s->size);
            } else if (s->name[0] == '\0') {
                fprintf(stderr, "         [%2u] <no name> type=0x%lx off=0x%lx size=0x%lx\n",
                        i, s->type, s->offset, s->size);
            } else {
                fprintf(stderr, "         [%2u] %-28s type=0x%lx off=0x%lx size=0x%lx\n",
                        i, s->name, s->type, s->offset, s->size);
            }
        }
        goto dwarf_done;
    }
    /* DWARF 段就位确认 */
    fprintf(stderr, "[DWARF] sections: .debug_info(off 0x%lx, %luB) "
                    ".debug_abbrev(off 0x%lx, %luB)%s\n",
            info_sh->offset, (unsigned long)info_sh->size,
            abbrev_sh->offset, (unsigned long)abbrev_sh->size,
            (str_sh != NULL) ? " .debug_str ok" : " .debug_str MISSING");

    /* --- load sections into memory --- */
    uint8_t *info_data = malloc((size_t)info_sh->size);
    uint8_t *abbrev_data = malloc((size_t)abbrev_sh->size);
    uint8_t *dbg_str = NULL;
    size_t   dbg_str_sz = 0;
    /* DWARF5 .debug_str_offsets 段缓冲：与 dbg_str 一同在 goto dwarf_free
     * 跳转源之前声明初始化，保证 malloc 失败提前退出时
     * dwarf_free 处的 free(dbg_str_offsets) 始终安全（NULL 或有效指针） */
    uint8_t *dbg_str_offsets = NULL;
    size_t   dbg_str_offsets_sz = 0;

    if (!info_data || !abbrev_data) goto dwarf_free;

    /* 维测：短读检查——文件与 section 头不符时读到的是垃圾，解析必然异常 */
    if (fseeko(fp, info_sh->offset, SEEK_SET) != 0 ||
        fread(info_data, 1, (size_t)info_sh->size, fp) != (size_t)info_sh->size) {
        fprintf(stderr, "[DWARF] warning: short read on .debug_info "
                        "(off 0x%lx, %luB)\n",
                info_sh->offset, (unsigned long)info_sh->size);
    }
    if (fseeko(fp, abbrev_sh->offset, SEEK_SET) != 0 ||
        fread(abbrev_data, 1, (size_t)abbrev_sh->size, fp) != (size_t)abbrev_sh->size) {
        fprintf(stderr, "[DWARF] warning: short read on .debug_abbrev\n");
    }

    if (str_sh) {
        dbg_str    = malloc((size_t)str_sh->size);
        dbg_str_sz = (size_t)str_sh->size;
        if (dbg_str) {
            if (fseeko(fp, str_sh->offset, SEEK_SET) != 0 ||
                fread(dbg_str, 1, dbg_str_sz, fp) != dbg_str_sz) {
                fprintf(stderr, "[DWARF] warning: short read on .debug_str\n");
            }
        } else {
            fprintf(stderr, "[DWARF] warning: malloc(%lu) failed for .debug_str\n",
                    (unsigned long)str_sh->size);
        }
    }

    /* ── load .debug_str_offsets (DWARF5 string index table) ── */
    if (str_offsets_sh) {
        dbg_str_offsets    = malloc((size_t)str_offsets_sh->size);
        dbg_str_offsets_sz = (size_t)str_offsets_sh->size;
        if (dbg_str_offsets) {
            fseeko(fp, str_offsets_sh->offset, SEEK_SET);
            fread(dbg_str_offsets, 1, dbg_str_offsets_sz, fp);
        }
    }

    /* --- walk compilation units with type tracking --- */

    /* 类型表动态化：释放上一次解析的结果再按需分配。
     * 原内嵌 structs[16384]*19KB ≈ 300MB 静态 BSS 导致嵌入式设备
     * 启动即被 OOM killer 杀掉（dmesg 可见 Killed process lnxall_trace） */
    if (g_dbg.structs) {
        for (int i = 0; i < g_dbg.struct_count; i++)
            free(g_dbg.structs[i].fields);
        free(g_dbg.structs);
        g_dbg.structs = NULL;
    }
    g_dbg.struct_count = 0;
    g_dbg.structs_max  = STRUCTS_INIT_CAP;
    g_dbg.structs = calloc((size_t)g_dbg.structs_max, sizeof(struct_info_t));
    if (!g_dbg.structs) {
        fprintf(stderr, "[DWARF] error: calloc(%d struct_info_t) failed\n",
                g_dbg.structs_max);
        goto dwarf_free;
    }
    memset(g_so_list, 0, sizeof(g_so_list));
    memset(g_vr_list, 0, sizeof(g_vr_list));
    memset(g_td_list, 0, sizeof(g_td_list));
    memset(g_ts_list, 0, sizeof(g_ts_list));
    memset(g_arr_list, 0, sizeof(g_arr_list));

    /* set up tracking arrays (global buffers — see g_so_list etc.) */
    struct_offset_t *so_list = g_so_list;
    var_ref_t       *vr_list = g_vr_list;
    typedef_ref_t   *td_list = g_td_list;
    type_size_entry_t *ts_list = g_ts_list;
    array_ref_t     *arr_list = g_arr_list;
    int so_cnt = 0, vr_cnt = 0, td_cnt = 0, ts_cnt = 0, arr_cnt = 0;

    /* 跨 CU 聚合错误诊断：abbrev/form 失败即该 CU 解析不完整 */
    unsigned long cu_aborted            = 0;  /* abbrev/form 失败被丢弃的 CU 数 */
    unsigned long first_abbrev_fail_off = 0;  /* 首个 abbrev 失败现场 */
    uint64_t      first_abbrev_fail_code = 0;
    unsigned long first_form_skip_off   = 0;  /* 首个 form 失败现场 */
    unsigned long first_form_skip_form  = 0;
    /* 跨 CU 聚合的列表溢出诊断（ctx 为每 CU 局部变量，循环外汇总） */
    unsigned int  ovf_mask_agg          = 0;  /* 哪些列表达到上限 */
    unsigned long first_drop_off_agg    = 0;  /* 首个被丢弃对象的 DIE 偏移 */
    char          first_drop_name_agg[MAX_NAME_LEN] = ""; /* 其名称 */

    const uint8_t *cu      = info_data;
    const uint8_t *cu_end  = info_data + info_sh->size;

    /* Cache the last parsed abbrev table: adjacent CUs very often share
     * the same DW_AT_abbrev_offset, so re-parsing it per CU is wasted
     * work that dominated attach time on many-CU binaries. */
    uint32_t cached_abbr_off = UINT32_MAX;
    dwarf_abbrev_table_t cached_abt;

    while (cu + 11 <= cu_end) {
        unsigned long cu_off  = (unsigned long)(cu - info_data);
        uint32_t unit_len     = read_unaligned32(cu);
        if (unit_len == 0) break;
        if (unit_len >= 0xfffffff0) {       /* DWARF64 – not supported */
            break;
        }

        uint16_t version   = read_unaligned16(cu + 4);
        unsigned  hdr_sz   = 11;            /* DWARF3/4 default header size */
        uint32_t  abbr_off = read_unaligned32(cu + 6);
        uint8_t   addr_size;

        /* ── CU header ── */
        if (version >= 5) {
            /* DWARF5 header: 4(length) + 2(ver) + 1(unit_type) + 1(addr) + 4(abbrev_off)
             * For DW_UT_skeleton/dwo: +8(dwo_id). We only support DW_UT_compile(0x01). */
            uint8_t unit_type = cu[6];
            if (unit_type != 0x01) {
                cu += 4 + unit_len;
                continue;
            }
            addr_size = cu[7];
            abbr_off  = read_unaligned32(cu + 8);
            hdr_sz    = 12;
        } else {
            addr_size = cu[10];
        }

        /* version gate */
        if (version < 3) {
            cu += 4 + unit_len;
            continue;
        }

        const uint8_t *die_start = cu + hdr_sz;
        const uint8_t *die_end   = cu + 4 + unit_len;
        if (die_start >= die_end || abbr_off >= abbrev_sh->size) {
            cu = die_end;
            continue;
        }

        /* ── parse abbreviation table (cached across CUs) ── */
        dwarf_abbrev_table_t abt;
        if (abbr_off == cached_abbr_off) {
            abt = cached_abt;
        } else {
            dwarf_parse_abbrev_table(abbrev_data + abbr_off,
                                     (size_t)abbrev_sh->size - abbr_off, &abt);
            cached_abbr_off = abbr_off;
            cached_abt      = abt;
        }

        /* ── walk DIEs with type/variable/typedef tracking ── */
        dwarf_walk_ctx ctx;
        memset(&ctx, 0, sizeof(ctx));
        ctx.so_list       = so_list;       ctx.so_count     = &so_cnt;
        ctx.vr_list       = vr_list;       ctx.vr_count     = &vr_cnt;
        ctx.td_list       = td_list;       ctx.td_count     = &td_cnt;
        ctx.ts_list       = ts_list;       ctx.ts_count     = &ts_cnt;
        ctx.arr_list      = arr_list;      ctx.arr_count    = &arr_cnt;
        ctx.cur_array_idx     = -1;
        ctx.current_struct_idx = -1;
        ctx.ref_base      = cu_off;
        ctx.addr_sz       = addr_size;
        /* DWARF5 string index table: default to offset 8 (skip 8-byte
         * contribution header); individual CU DIE may override via
         * DW_AT_str_offsets_base. */
        ctx.dbg_str_offsets    = dbg_str_offsets;
        ctx.dbg_str_offsets_sz = dbg_str_offsets_sz;
        ctx.str_offsets_base   = (version >= 5 && dbg_str_offsets) ? 8 : 0;

        dwarf_walk_dies(die_start, die_end, &abt,
                        dbg_str, dbg_str_sz, &ctx, info_data);

        /* 跨 CU 聚合错误诊断：abbrev/form 失败即该 CU 解析不完整 */
        if (ctx.die_abbrev_fail > 0) {
            cu_aborted++;
            if (first_abbrev_fail_code == 0) {
                first_abbrev_fail_off  = ctx.first_abbrev_fail_off;
                first_abbrev_fail_code = ctx.first_abbrev_fail_code;
            }
        }
        if (ctx.die_form_skip > 0) {
            cu_aborted++;
            if (first_form_skip_off == 0) {
                first_form_skip_off  = ctx.first_form_skip_off;
                first_form_skip_form = ctx.first_form_skip_form;
            }
        }

        /* 跨 CU 聚合列表溢出诊断：structs 表满 / so / vr / td / ts / arr */
        ovf_mask_agg |= ctx.overflow_mask;
        if (ctx.first_drop_off != 0 && first_drop_off_agg == 0) {
            first_drop_off_agg = ctx.first_drop_off;
            safe_strcpy(first_drop_name_agg, ctx.first_drop_name,
                        sizeof(first_drop_name_agg));
        }

        cu = die_end;
    }

    /* ── sort lists for O(log N) lookup ── */
    if (so_cnt > 1) qsort(so_list, so_cnt, sizeof(so_list[0]), cmp_struct_offset);
    if (td_cnt > 1) qsort(td_list, td_cnt, sizeof(td_list[0]), cmp_typedef_ref);
    if (ts_cnt > 1) qsort(ts_list, ts_cnt, sizeof(ts_list[0]), cmp_type_size_entry);
    if (arr_cnt > 1) qsort(arr_list, arr_cnt, sizeof(arr_list[0]), cmp_array_ref);

    /* ── Phase 2: resolve member sizes from DW_AT_type refs ── */
    dwarf_resolve_member_sizes(so_list, so_cnt, td_list, td_cnt, ts_list, ts_cnt,
                               arr_list, arr_cnt);

    /* ── Phase 3: resolve DWARF variables → symbol kinds (into sym_table) ── */
    dwarf_resolve_var_to_struct(vr_list, vr_cnt, so_list, so_cnt, td_list, td_cnt,
                                ts_list, ts_cnt, arr_list, arr_cnt);

    /* keep runtime lookup counts (g_*_list are global BSS buffers) */
    g_so_count = so_cnt;
    g_vr_count = vr_cnt;
    g_arr_count = arr_cnt;
    g_td_count  = td_cnt;
    g_ts_count  = ts_cnt;

    ret = 0;

    /* ── DWARF 错误/警告诊断（仅错误与资源受限提示） ── */
    if (cu_aborted > 0) {
        fprintf(stderr, "[DWARF] warning: %lu CU(s) aborted mid-parse", cu_aborted);
        if (first_abbrev_fail_code != 0) {
            fprintf(stderr, " - abbrev 0x%llx missing at DIE 0x%lx",
                    (unsigned long long)first_abbrev_fail_code, first_abbrev_fail_off);
        }
        if (first_form_skip_off != 0) {
            fprintf(stderr, " - unknown form 0x%lx at DIE 0x%lx",
                    first_form_skip_form, first_form_skip_off);
        }
        fprintf(stderr, " (some variables/types missing)\n");
    }

    /* 维测：列表溢出诊断——容量达到上限时部分结构体/变量/类型未收录，
     * 涉及这些类型的解析（struct 查询、变量类型解析）将不完整 */
    if (ovf_mask_agg != 0) {
        fprintf(stderr, "[DWARF] overflow: 0x%x", ovf_mask_agg);
        if (first_drop_off_agg != 0) {
            fprintf(stderr, " - first dropped '%s' at DIE 0x%lx",
                    first_drop_name_agg, first_drop_off_agg);
        }
        fprintf(stderr, "\n");
        if (ovf_mask_agg & OVF_STRUCTS_TABLE) {
            fprintf(stderr, "[DWARF] warning: structs table full (%d), "
                            "some struct/union definitions NOT parsed\n",
                    g_dbg.structs_max);
        }
        if (ovf_mask_agg & OVF_STRUCT_OFFSET) {
            fprintf(stderr, "[DWARF] warning: struct offset list full (%d), "
                            "var->struct matching incomplete\n", MAX_STRUCT_OFFSETS);
        }
        if (ovf_mask_agg & OVF_VARREF) {
            fprintf(stderr, "[DWARF] warning: variable ref list full (%d), "
                            "some variables unresolved\n", MAX_VAR_REFS);
        }
        if (ovf_mask_agg & OVF_TYPEDEF) {
            fprintf(stderr, "[DWARF] warning: typedef list full (%d), "
                            "type chains truncated\n", MAX_TYPEDEF_REFS);
        }
        if (ovf_mask_agg & OVF_BASE_SIZE) {
            fprintf(stderr, "[DWARF] warning: base-size list full (%d), "
                            "member sizes unresolved\n", MAX_BASE_TYPE_SIZES);
        }
        if (ovf_mask_agg & OVF_ARRAY) {
            fprintf(stderr, "[DWARF] warning: array ref list full (%d), "
                            "array members unresolved\n", MAX_ARRAY_REFS);
        }
    }

dwarf_free:
    free(info_data); free(abbrev_data); free(dbg_str); free(dbg_str_offsets);
dwarf_done:
    elf_free_file(&hdr);
    if (fp) fclose(fp);
    return ret;
}

/* ========================================================================
 * Runtime Type Introspection (struct / array resolution)
 * ======================================================================== */

struct_info_t *find_struct_by_size(unsigned long size)
{
    for (int i = 0; i < g_dbg.struct_count; i++) {
        if (g_dbg.structs[i].size == size && g_dbg.structs[i].field_count > 0)
            return &g_dbg.structs[i];
    }
    return NULL;
}

/* Find a struct definition by its exact DWARF DIE offset */
struct_info_t *find_struct_by_die_offset(unsigned long off)
{
    for (int i = 0; i < g_dbg.struct_count; i++) {
        if (g_dbg.structs[i].die_offset == off)
            return &g_dbg.structs[i];
    }
    return NULL;
}

/* Follow a DW_AT_type ref through typedef/wrapper chain (runtime, using the
 * global sorted g_td_list).  Returns the terminal DIE offset. */
unsigned long resolve_typedef_chain_rt(unsigned long ref)
{
    for (int hop = 0; hop < 16; hop++) {
        const typedef_ref_t *td = bsearch_td(ref, g_td_list, g_td_count);
        if (!td) break;
        ref = td->target_ref;
    }
    return ref;
}

/* Array member's DW_AT_type → element type DIE offset (via g_arr_list) */
unsigned long resolve_array_elem_ref_rt(unsigned long type_ref)
{
    const array_ref_t *ar = bsearch_arr(type_ref, g_arr_list, g_arr_count);
    return ar ? ar->elem_ref : 0;
}

/* Runtime introspection of an array-typed symbol (kind == SYM_KIND_ARRAY).
 * Recovers element count / element size and, if the elements are structs,
 * the element struct definition.  Returns 0 on success, -1 if the array
 * shape cannot be recovered (symbol lacks DWARF info). */
int resolve_var_array_rt(const symbol_t *sym,
                         unsigned long *out_count,
                         unsigned long *out_elem_size,
                         struct_info_t **out_struct)
{
    if (!sym || sym->kind != SYM_KIND_ARRAY) return -1;
    unsigned long arr_ref = resolve_typedef_chain_rt(sym->dwarf_type_ref);
    const array_ref_t *ar = bsearch_arr(arr_ref, g_arr_list, g_arr_count);
    if (!ar) return -1;

    struct_info_t *esi = NULL;
    unsigned long esize = 0;
    unsigned long elem_ref = resolve_array_elem_ref_rt(arr_ref);
    if (elem_ref) {
        unsigned long term = resolve_typedef_chain_rt(elem_ref);
        esi = find_struct_by_die_offset(term);
        if (esi) {
            esize = esi->size;
        } else {
            /* base / pointer element (e.g. 'const char *show_log[]'):
             * walk the typedef chain looking for a type-size entry;
             * pointer types are registered with the architecture
             * address size. */
            unsigned long ref = elem_ref;
            for (int hop = 0; hop < 16; hop++) {
                const type_size_entry_t *ts =
                    bsearch_ts(ref, g_ts_list, g_ts_count);
                if (ts) { esize = ts->byte_size; break; }
                const typedef_ref_t *td =
                    bsearch_td(ref, g_td_list, g_td_count);
                if (!td) break;
                ref = td->target_ref;
            }
        }
    }
    if (esize == 0 && sym->size > 0) {
        if (ar->elem_count > 0)
            esize = sym->size / ar->elem_count;   /* last-resort fallback */
    }
    if (esize == 0 || sym->size == 0) return -1;

    /* element count: DWARF-derived; flexible arrays derive from ELF size */
    unsigned long count = ar->elem_count;
    if (count == 0 && esize > 0)
        count = sym->size / esize;
    if (count == 0) return -1;

    if (out_count)     *out_count     = count;
    if (out_elem_size) *out_elem_size = esize;
    if (out_struct)    *out_struct    = esi;
    return 0;
}
