kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

symbolic.c (28985B)


      1 /* Symbolic inspection layer over a stopped KitDebugSession.
      2  *
      3  * The control substrate (session.c/bp.c/step.c/mem.c) moves bytes and raw
      4  * registers. This TU composes that substrate with the session's attached DWARF
      5  * (s->dwarf) and JIT image (s->jit) into the operations an interactive debugger
      6  * performs: PC symbolization, frame-pointer backtraces, typed variable values
      7  * with navigation, scope enumeration, location resolution, and disassembly at
      8  * the stopped PC.
      9  *
     10  * Two pieces of glue every caller would otherwise repeat live here, once:
     11  *   - address-space translation: DWARF queries key off image vaddrs while the
     12  *     session reads/writes runtime addresses (sym_pc_rt_to_img / _img_to_rt /
     13  *     sym_translate_loc);
     14  *   - the memory provider: kit_dwarf_loc_read wants a KitDwarfReadMemFn, which
     15  *     here just forwards into the session (sym_read_mem).
     16  *
     17  * KitDebugValue byte storage comes from the session's stop-epoch arena
     18  * (s->values), reset by session.c on every call/resume. Backtraces and variable
     19  * iterators are heap-allocated cursors with explicit frees; the KitSlice names
     20  * they expose borrow from the DWARF / JIT image. */
     21 
     22 #include <stdarg.h>
     23 #include <stdio.h>
     24 #include <string.h>
     25 
     26 #include "dbg/dbg.h"
     27 
     28 /* ---- address-space + memory glue ------------------------------------- */
     29 
     30 static uint64_t sym_pc_rt_to_img(KitDebugSession* s, uint64_t rt) {
     31   uint64_t v = kit_jit_runtime_to_image(s->jit, rt);
     32   return v ? v : rt;
     33 }
     34 
     35 static uint64_t sym_pc_img_to_rt(KitDebugSession* s, uint64_t img) {
     36   uint64_t v = kit_jit_image_to_runtime(s->jit, img);
     37   return v ? v : img;
     38 }
     39 
     40 /* Only DLOC_GLOBAL carries an absolute address straight from .debug_info; the
     41  * other kinds derive their effective address from live (runtime) register
     42  * state or are evaluated against the frame. */
     43 static void sym_translate_loc(KitDebugSession* s, KitDwarfVarLoc* loc) {
     44   if (loc && loc->kind == KIT_DLOC_GLOBAL)
     45     loc->v.global = sym_pc_img_to_rt(s, loc->v.global);
     46 }
     47 
     48 static KitStatus sym_read_mem(void* user, uint64_t addr, void* dst, size_t n) {
     49   return kit_dbg_session_read_mem((KitDebugSession*)user, addr, dst, n);
     50 }
     51 
     52 /* ---- little-endian scalar loads -------------------------------------- */
     53 
     54 static uint64_t sym_load_u(const uint8_t* b, size_t n) {
     55   uint64_t v = 0;
     56   size_t i;
     57   for (i = 0; i < n && i < 8; ++i) v |= ((uint64_t)b[i]) << (8 * i);
     58   return v;
     59 }
     60 
     61 static int64_t sym_load_s(const uint8_t* b, size_t n) {
     62   uint64_t v = sym_load_u(b, n);
     63   if (n > 0 && n < 8) {
     64     uint64_t sign = (uint64_t)1 << (8 * n - 1);
     65     if (v & sign) v |= ~((sign << 1) - 1);
     66   }
     67   return (int64_t)v;
     68 }
     69 
     70 /* ---- per-arch frame-pointer walk knobs -------------------------------- */
     71 
     72 static int sym_fp_reg(KitArchKind a) {
     73   switch (a) {
     74     case KIT_ARCH_ARM_64:
     75       return 29;
     76     case KIT_ARCH_X86_64:
     77       return 6;
     78     case KIT_ARCH_RV32:
     79     case KIT_ARCH_RV64:
     80       return 8;
     81     default:
     82       return -1;
     83   }
     84 }
     85 
     86 static int sym_ptr_size(KitArchKind a) {
     87   switch (a) {
     88     case KIT_ARCH_ARM_64:
     89     case KIT_ARCH_X86_64:
     90     case KIT_ARCH_RV64:
     91       return 8;
     92     case KIT_ARCH_RV32:
     93       return 4;
     94     default:
     95       return 0;
     96   }
     97 }
     98 
     99 /* Advance `fr` to its caller via the uniform fp record (fp[0]=caller fp,
    100  * fp[1]=saved return address). Returns 1 on a valid in-image caller, 0 at the
    101  * chain terminator / image boundary / garbage. */
    102 static int sym_unwind_next(KitDebugSession* s, KitUnwindFrame* fr,
    103                            int fpreg, int ptr) {
    104   uint64_t fp, ra = 0, nfp = 0, align;
    105   if (fpreg < 0 || ptr <= 0) return 0;
    106   fp = fr->regs[fpreg];
    107   if (fp == 0) return 0;
    108   align = (uint64_t)ptr - 1u;
    109   if (fp & align) return 0;
    110   if (kit_dbg_session_read_mem(s, fp + (uint64_t)ptr, &ra, (size_t)ptr) != KIT_OK)
    111     return 0;
    112   if (kit_dbg_session_read_mem(s, fp, &nfp, (size_t)ptr) != KIT_OK) return 0;
    113   if (ra == 0) return 0;
    114   if (nfp <= fp) return 0;
    115   if (nfp & align) return 0;
    116   if (kit_jit_runtime_to_image(s->jit, ra) == 0) return 0; /* left the image */
    117   fr->pc = ra;
    118   fr->regs[fpreg] = nfp;
    119   fr->cfa = nfp + 2u * (uint64_t)ptr; /* just above the saved pair */
    120   return 1;
    121 }
    122 
    123 /* ---- single-PC symbolization ----------------------------------------- */
    124 
    125 static void sym_describe(KitJit* jit, KitDebugInfo* dwarf, uint64_t pc,
    126                          KitDebugFrame* f) {
    127   /* Image vaddr for DWARF queries; fall back to the runtime pc when the image
    128    * translation yields 0 (it overloads 0 as "not in image", which also covers
    129    * a symbol sitting exactly at the image base). in_image is reported for the
    130    * caller's benefit (e.g. a crash reporter suppressing foreign frames) but is
    131    * NOT used to gate symbolization: kit_jit_addr_to_sym already returns nothing
    132    * for an address its table does not cover. */
    133   uint64_t v = jit ? kit_jit_runtime_to_image(jit, pc) : 0;
    134   uint64_t img = v ? v : pc;
    135   f->pc = pc;
    136   f->in_image = jit ? (v != 0) : true;
    137 
    138   if (jit) {
    139     KitSlice sym = KIT_SLICE_NULL;
    140     uint64_t off = 0;
    141     if (kit_jit_addr_to_sym(jit, pc, &sym, &off) == KIT_OK && sym.s) {
    142       f->sym = sym;
    143       f->sym_offset = off;
    144     }
    145   }
    146   if (dwarf) {
    147     KitDwarfSubprogram sp;
    148     KitSlice file = KIT_SLICE_NULL;
    149     uint32_t line = 0, col = 0;
    150     if (kit_dwarf_subprogram_at(dwarf, img, &sp) == KIT_OK && sp.name.s) {
    151       f->func = sp.name;
    152       f->inlined = sp.inlined;
    153       f->func_offset = (img >= sp.low_pc) ? (img - sp.low_pc) : 0;
    154     }
    155     if (kit_dwarf_addr_to_line(dwarf, img, &file, &line, &col) == KIT_OK &&
    156         file.s) {
    157       f->file = file;
    158       f->line = line;
    159       f->col = col;
    160     }
    161   }
    162 }
    163 
    164 KitStatus kit_dbg_symbolize_pc(KitJit* jit, KitDebugInfo* dwarf, uint64_t pc,
    165                                KitDebugFrame* out) {
    166   if (!jit || !out) return KIT_INVALID;
    167   memset(out, 0, sizeof(*out));
    168   sym_describe(jit, dwarf, pc, out);
    169   return KIT_OK;
    170 }
    171 
    172 /* ---- backtrace -------------------------------------------------------- */
    173 
    174 struct KitDebugBacktrace {
    175   Heap* heap;
    176   KitDebugFrame* frames;
    177   uint32_t count;
    178 };
    179 
    180 #define SYM_BT_MAX 256u
    181 
    182 KitStatus kit_dbg_backtrace_new(KitDebugSession* s, const KitUnwindFrame* top,
    183                                 KitDebugBacktrace** out) {
    184   KitArchKind arch;
    185   int fpreg, ptr;
    186   KitUnwindFrame walk, start;
    187   KitDebugBacktrace* bt;
    188   uint32_t n, i;
    189 
    190   if (!s || !out) return KIT_INVALID;
    191   *out = NULL;
    192   arch = kit_jit_image_arch(s->jit);
    193   fpreg = sym_fp_reg(arch);
    194   ptr = sym_ptr_size(arch);
    195   start = top ? *top : s->stop.regs;
    196 
    197   /* Pass 1: count the chain (bounded). */
    198   n = 1;
    199   walk = start;
    200   while (n < SYM_BT_MAX && sym_unwind_next(s, &walk, fpreg, ptr)) ++n;
    201 
    202   bt = (KitDebugBacktrace*)s->heap->alloc(s->heap, sizeof(*bt), _Alignof(KitDebugBacktrace));
    203   if (!bt) return KIT_NOMEM;
    204   bt->heap = s->heap;
    205   bt->count = n;
    206   bt->frames = (KitDebugFrame*)s->heap->alloc(
    207       s->heap, (size_t)n * sizeof(KitDebugFrame), _Alignof(KitDebugFrame));
    208   if (!bt->frames) {
    209     s->heap->free(s->heap, bt, sizeof(*bt));
    210     return KIT_NOMEM;
    211   }
    212 
    213   /* Pass 2: fill. */
    214   walk = start;
    215   for (i = 0; i < n; ++i) {
    216     KitDebugFrame* f = &bt->frames[i];
    217     memset(f, 0, sizeof(*f));
    218     f->regs = walk;
    219     f->cfa = walk.cfa;
    220     sym_describe(s->jit, s->dwarf, walk.pc, f);
    221     if (i + 1 < n) sym_unwind_next(s, &walk, fpreg, ptr);
    222   }
    223   *out = bt;
    224   return KIT_OK;
    225 }
    226 
    227 uint32_t kit_dbg_backtrace_count(const KitDebugBacktrace* bt) {
    228   return bt ? bt->count : 0;
    229 }
    230 
    231 KitStatus kit_dbg_backtrace_frame(const KitDebugBacktrace* bt, uint32_t level,
    232                                   KitDebugFrame* out) {
    233   if (!bt || !out || level >= bt->count) return KIT_INVALID;
    234   *out = bt->frames[level];
    235   return KIT_OK;
    236 }
    237 
    238 void kit_dbg_backtrace_free(KitDebugBacktrace* bt) {
    239   if (!bt) return;
    240   bt->heap->free(bt->heap, bt->frames, (size_t)bt->count * sizeof(KitDebugFrame));
    241   bt->heap->free(bt->heap, bt, sizeof(*bt));
    242 }
    243 
    244 /* ---- typed values ----------------------------------------------------- */
    245 
    246 /* Resolve through DW_TAG_typedef chains to the underlying type. */
    247 static const KitDwarfType* sym_strip_typedef(const KitDwarfType* t) {
    248   while (t) {
    249     KitDwarfTypeInfo ti = kit_dwarf_type_info(t);
    250     if (ti.kind != KIT_DT_TYPEDEF || !ti.inner) break;
    251     t = ti.inner;
    252   }
    253   return t;
    254 }
    255 
    256 static void* sym_value_buf(KitDebugSession* s, size_t sz) {
    257   return arena_alloc(&s->values, sz ? sz : 1, 16);
    258 }
    259 
    260 /* Materialize a translated loc against `fr` (runtime regs/cfa) into the
    261  * stop-epoch arena. */
    262 static KitStatus sym_materialize(KitDebugSession* s, const KitDwarfVarLoc* loc,
    263                                  const KitUnwindFrame* fr, KitDebugValue* out) {
    264   size_t cap = loc->byte_size ? loc->byte_size : 8;
    265   uint8_t* buf = (uint8_t*)sym_value_buf(s, cap);
    266   size_t got = 0;
    267   if (!buf) return KIT_NOMEM;
    268   if (kit_dwarf_loc_read(s->dwarf, loc, fr, sym_read_mem, s, buf, cap, &got) !=
    269       KIT_OK)
    270     return KIT_ERR;
    271   out->type = loc->type;
    272   out->bytes = buf;
    273   out->len = got;
    274   out->has_address = false;
    275   out->address = 0;
    276   if (loc->kind == KIT_DLOC_GLOBAL) {
    277     out->has_address = true;
    278     out->address = loc->v.global;
    279   } else if (loc->kind == KIT_DLOC_FRAME_OFS) {
    280     out->has_address = true;
    281     out->address = fr->cfa + (uint64_t)(int64_t)loc->v.frame_ofs;
    282   }
    283   return KIT_OK;
    284 }
    285 
    286 KitStatus kit_dbg_var_read(KitDebugSession* s, const KitUnwindFrame* frame,
    287                            KitSlice name, KitDebugValue* out) {
    288   KitUnwindFrame fr;
    289   if (!s || !out) return KIT_INVALID;
    290   memset(out, 0, sizeof(*out));
    291   fr = frame ? *frame : s->stop.regs;
    292 
    293   if (s->dwarf) {
    294     KitDwarfVarLoc loc;
    295     if (kit_dwarf_var_at(s->dwarf, sym_pc_rt_to_img(s, fr.pc), name, &loc) ==
    296         KIT_OK) {
    297       sym_translate_loc(s, &loc);
    298       return sym_materialize(s, &loc, &fr, out);
    299     }
    300   }
    301 
    302   /* DWARF didn't know it — fall back to a JIT global symbol. The value bytes
    303    * hold the symbol's runtime address; type stays NULL. */
    304   {
    305     void* p = kit_jit_lookup(s->jit, name);
    306     if (p) {
    307       union {
    308         void* p;
    309         uint64_t u;
    310       } cv;
    311       uint8_t* buf = (uint8_t*)sym_value_buf(s, sizeof(uint64_t));
    312       if (!buf) return KIT_NOMEM;
    313       cv.p = p;
    314       memcpy(buf, &cv.u, sizeof(cv.u));
    315       out->type = NULL;
    316       out->bytes = buf;
    317       out->len = sizeof(uint64_t);
    318       out->has_address = false;
    319       out->address = 0;
    320       return KIT_OK;
    321     }
    322   }
    323   return KIT_NOT_FOUND;
    324 }
    325 
    326 KitStatus kit_dbg_value_at(KitDebugSession* s, uint64_t addr,
    327                            const KitDwarfType* type, KitDebugValue* out) {
    328   KitDwarfTypeInfo ti;
    329   size_t sz = 8;
    330   uint8_t* buf;
    331   if (!s || !out) return KIT_INVALID;
    332   memset(out, 0, sizeof(*out));
    333   if (type) {
    334     ti = kit_dwarf_type_info(type);
    335     if (ti.byte_size) sz = ti.byte_size;
    336   }
    337   buf = (uint8_t*)sym_value_buf(s, sz);
    338   if (!buf) return KIT_NOMEM;
    339   if (kit_dbg_session_read_mem(s, addr, buf, sz) != KIT_OK) return KIT_ERR;
    340   out->type = type;
    341   out->bytes = buf;
    342   out->len = sz;
    343   out->has_address = true;
    344   out->address = addr;
    345   return KIT_OK;
    346 }
    347 
    348 KitStatus kit_dbg_value_field(KitDebugSession* s, const KitDebugValue* v,
    349                               KitSlice field, KitDebugValue* out) {
    350   const KitDwarfType* t;
    351   KitDwarfTypeInfo ti;
    352   KitDwarfFieldIter* it = NULL;
    353   KitDwarfField f;
    354   if (!s || !v || !out) return KIT_INVALID;
    355   t = sym_strip_typedef(v->type);
    356   if (!t) return KIT_INVALID;
    357   ti = kit_dwarf_type_info(t);
    358   if (ti.kind != KIT_DT_STRUCT && ti.kind != KIT_DT_UNION) return KIT_INVALID;
    359   if (kit_dwarf_field_iter_new(s->dwarf, t, &it) != KIT_OK) return KIT_ERR;
    360   for (;;) {
    361     KitIterResult r = kit_dwarf_field_iter_next(it, &f);
    362     if (r != KIT_ITER_ITEM) break;
    363     if (f.name.len == field.len && f.name.len &&
    364         memcmp(f.name.s, field.s, field.len) == 0) {
    365       size_t fsz = 0;
    366       kit_dwarf_field_iter_free(it);
    367       if (f.bit_size) return KIT_UNSUPPORTED; /* bitfields: not sub-sliceable */
    368       if (f.type) {
    369         KitDwarfTypeInfo fti = kit_dwarf_type_info(f.type);
    370         fsz = fti.byte_size;
    371       }
    372       if (!fsz || (size_t)f.byte_offset + fsz > v->len) return KIT_ERR;
    373       memset(out, 0, sizeof(*out));
    374       out->type = f.type;
    375       out->bytes = v->bytes + f.byte_offset;
    376       out->len = fsz;
    377       out->has_address = v->has_address;
    378       out->address = v->has_address ? v->address + f.byte_offset : 0;
    379       return KIT_OK;
    380     }
    381   }
    382   kit_dwarf_field_iter_free(it);
    383   return KIT_NOT_FOUND;
    384 }
    385 
    386 KitStatus kit_dbg_value_index(KitDebugSession* s, const KitDebugValue* v,
    387                               uint64_t index, KitDebugValue* out) {
    388   const KitDwarfType* t;
    389   KitDwarfTypeInfo ti, ei;
    390   size_t esz;
    391   if (!s || !v || !out) return KIT_INVALID;
    392   t = sym_strip_typedef(v->type);
    393   if (!t) return KIT_INVALID;
    394   ti = kit_dwarf_type_info(t);
    395   if ((ti.kind != KIT_DT_ARRAY && ti.kind != KIT_DT_PTR) || !ti.inner)
    396     return KIT_INVALID;
    397   ei = kit_dwarf_type_info(ti.inner);
    398   esz = ei.byte_size ? ei.byte_size : 1;
    399   if (ti.kind == KIT_DT_ARRAY) {
    400     size_t off = (size_t)index * esz;
    401     if (off + esz > v->len) return KIT_INVALID;
    402     memset(out, 0, sizeof(*out));
    403     out->type = ti.inner;
    404     out->bytes = v->bytes + off;
    405     out->len = esz;
    406     out->has_address = v->has_address;
    407     out->address = v->has_address ? v->address + off : 0;
    408     return KIT_OK;
    409   }
    410   /* pointer: read element[index] through the session. */
    411   {
    412     uint64_t base = sym_load_u(v->bytes, v->len < 8 ? v->len : 8);
    413     return kit_dbg_value_at(s, base + index * esz, ti.inner, out);
    414   }
    415 }
    416 
    417 KitStatus kit_dbg_value_deref(KitDebugSession* s, const KitDebugValue* v,
    418                               KitDebugValue* out) {
    419   const KitDwarfType* t;
    420   KitDwarfTypeInfo ti;
    421   uint64_t ptr;
    422   if (!s || !v || !out) return KIT_INVALID;
    423   t = sym_strip_typedef(v->type);
    424   if (!t) return KIT_INVALID;
    425   ti = kit_dwarf_type_info(t);
    426   if (ti.kind != KIT_DT_PTR) return KIT_INVALID;
    427   ptr = sym_load_u(v->bytes, v->len < 8 ? v->len : 8);
    428   return kit_dbg_value_at(s, ptr, ti.inner, out);
    429 }
    430 
    431 KitStatus kit_dbg_value_as_u64(const KitDebugValue* v, uint64_t* out) {
    432   if (!v || !out || !v->bytes) return KIT_INVALID;
    433   *out = sym_load_u(v->bytes, v->len);
    434   return KIT_OK;
    435 }
    436 
    437 KitStatus kit_dbg_value_as_i64(const KitDebugValue* v, int64_t* out) {
    438   if (!v || !out || !v->bytes) return KIT_INVALID;
    439   *out = sym_load_s(v->bytes, v->len);
    440   return KIT_OK;
    441 }
    442 
    443 KitStatus kit_dbg_value_as_f64(const KitDebugValue* v, double* out) {
    444   if (!v || !out || !v->bytes) return KIT_INVALID;
    445   if (v->len == 4) {
    446     union {
    447       uint32_t u;
    448       float f;
    449     } cv;
    450     cv.u = (uint32_t)sym_load_u(v->bytes, 4);
    451     *out = (double)cv.f;
    452     return KIT_OK;
    453   }
    454   if (v->len == 8) {
    455     union {
    456       uint64_t u;
    457       double d;
    458     } cv;
    459     cv.u = sym_load_u(v->bytes, 8);
    460     *out = cv.d;
    461     return KIT_OK;
    462   }
    463   return KIT_UNSUPPORTED;
    464 }
    465 
    466 /* ---- value formatting ------------------------------------------------- */
    467 
    468 typedef struct SymFmt {
    469   KitWriter* w;
    470   KitDebugSession* s;
    471   KitStatus st;
    472 } SymFmt;
    473 
    474 static void fw(SymFmt* f, const void* p, size_t n) {
    475   if (f->st == KIT_OK) f->st = kit_writer_write(f->w, p, n);
    476 }
    477 static void fw_cstr(SymFmt* f, const char* s) { fw(f, s, strlen(s)); }
    478 static void fw_slice(SymFmt* f, KitSlice s) {
    479   if (s.s) fw(f, s.s, s.len);
    480 }
    481 static void fw_fmt(SymFmt* f, const char* fmt, ...) {
    482   char b[64];
    483   va_list ap;
    484   int n;
    485   va_start(ap, fmt);
    486   n = vsnprintf(b, sizeof(b), fmt, ap);
    487   va_end(ap);
    488   if (n < 0) return;
    489   if ((size_t)n >= sizeof(b)) n = (int)sizeof(b) - 1;
    490   fw(f, b, (size_t)n);
    491 }
    492 static void fw_dec_u(SymFmt* f, uint64_t v) {
    493   fw_fmt(f, "%llu", (unsigned long long)v);
    494 }
    495 static void fw_dec_i(SymFmt* f, int64_t v) {
    496   fw_fmt(f, "%lld", (long long)v);
    497 }
    498 static void fw_hex(SymFmt* f, uint64_t v) {
    499   fw_fmt(f, "0x%llx", (unsigned long long)v);
    500 }
    501 static void fw_f64(SymFmt* f, double v) { fw_fmt(f, "%g", v); }
    502 static void fw_byte(SymFmt* f, uint8_t b) {
    503   static const char H[] = "0123456789abcdef";
    504   char two[3];
    505   two[0] = ' ';
    506   two[1] = H[b >> 4];
    507   two[2] = H[b & 0xf];
    508   fw(f, two, 3);
    509 }
    510 static void fw_indent(SymFmt* f, int n) {
    511   int i;
    512   for (i = 0; i < n; ++i) fw(f, "  ", 2);
    513 }
    514 
    515 /* Recursively render `buf[0..got)` as a value of `type`. Mirrors the driver's
    516  * historical dbg_print_value byte-for-byte for the non-float kinds (the dbg
    517  * golden tests pin them); floats render via snprintf "%g". */
    518 static void sym_fmt(SymFmt* f, const KitDwarfType* type, const uint8_t* buf,
    519                     size_t got, int depth) {
    520   KitDwarfTypeInfo ti;
    521 
    522   if (!type) {
    523     if (got == 0) {
    524       fw_cstr(f, "<empty>");
    525       return;
    526     }
    527     if (got <= 8) {
    528       uint64_t v = sym_load_u(buf, got);
    529       fw_hex(f, v);
    530       fw_cstr(f, " (");
    531       fw_dec_u(f, v);
    532       fw_cstr(f, ")");
    533       return;
    534     }
    535     {
    536       size_t i;
    537       fw_cstr(f, "{");
    538       for (i = 0; i < got; ++i) fw_byte(f, buf[i]);
    539       fw_cstr(f, " }");
    540       return;
    541     }
    542   }
    543 
    544   ti = kit_dwarf_type_info(type);
    545   switch (ti.kind) {
    546     case KIT_DT_VOID:
    547       fw_cstr(f, "void");
    548       return;
    549     case KIT_DT_SINT:
    550     case KIT_DT_CHAR:
    551       fw_dec_i(f, sym_load_s(buf, got));
    552       return;
    553     case KIT_DT_UINT:
    554     case KIT_DT_BOOL:
    555       fw_dec_u(f, sym_load_u(buf, got));
    556       return;
    557     case KIT_DT_PTR:
    558       fw_hex(f, sym_load_u(buf, got));
    559       return;
    560     case KIT_DT_FLOAT:
    561       if (got == 4) {
    562         union {
    563           uint32_t u;
    564           float f;
    565         } cv;
    566         cv.u = (uint32_t)sym_load_u(buf, 4);
    567         fw_f64(f, (double)cv.f);
    568       } else if (got == 8) {
    569         union {
    570           uint64_t u;
    571           double d;
    572         } cv;
    573         cv.u = sym_load_u(buf, 8);
    574         fw_f64(f, cv.d);
    575       } else {
    576         size_t i;
    577         fw_cstr(f, "<float-");
    578         fw_dec_u(f, got);
    579         for (i = 0; i < got; ++i) fw_byte(f, buf[i]);
    580         fw_cstr(f, ">");
    581       }
    582       return;
    583     case KIT_DT_ENUM: {
    584       int64_t v = sym_load_s(buf, got);
    585       KitDwarfEnumIter* it = NULL;
    586       KitDwarfEnumVal ev;
    587       KitSlice match = KIT_SLICE_NULL;
    588       if (kit_dwarf_enum_iter_new(f->s->dwarf, type, &it) == KIT_OK) {
    589         for (;;) {
    590           KitIterResult r = kit_dwarf_enum_iter_next(it, &ev);
    591           if (r != KIT_ITER_ITEM) break;
    592           if (ev.value == v) {
    593             match = ev.name;
    594             break;
    595           }
    596         }
    597         kit_dwarf_enum_iter_free(it);
    598       }
    599       if (match.s) {
    600         fw_slice(f, match);
    601         fw_cstr(f, " (");
    602         fw_dec_i(f, v);
    603         fw_cstr(f, ")");
    604       } else {
    605         fw_dec_i(f, v);
    606       }
    607       return;
    608     }
    609     case KIT_DT_TYPEDEF:
    610       sym_fmt(f, ti.inner, buf, got, depth);
    611       return;
    612     case KIT_DT_ARRAY: {
    613       uint32_t n = ti.element_count;
    614       size_t esz = 0;
    615       uint32_t i;
    616       if (ti.inner) {
    617         KitDwarfTypeInfo ein = kit_dwarf_type_info(ti.inner);
    618         esz = ein.byte_size;
    619       }
    620       if (esz == 0 || n == 0 || (size_t)n * esz > got) {
    621         size_t k;
    622         fw_cstr(f, "{");
    623         for (k = 0; k < got; ++k) fw_byte(f, buf[k]);
    624         fw_cstr(f, " }");
    625         return;
    626       }
    627       fw_cstr(f, "{\n");
    628       for (i = 0; i < n; ++i) {
    629         fw_indent(f, depth + 1);
    630         fw_cstr(f, "[");
    631         fw_dec_u(f, i);
    632         fw_cstr(f, "] = ");
    633         sym_fmt(f, ti.inner, buf + (size_t)i * esz, esz, depth + 1);
    634         fw_cstr(f, ",\n");
    635       }
    636       fw_indent(f, depth);
    637       fw_cstr(f, "}");
    638       return;
    639     }
    640     case KIT_DT_STRUCT:
    641     case KIT_DT_UNION: {
    642       KitDwarfFieldIter* it = NULL;
    643       KitDwarfField fld;
    644       fw_cstr(f, "{\n");
    645       if (kit_dwarf_field_iter_new(f->s->dwarf, type, &it) == KIT_OK) {
    646         for (;;) {
    647           KitIterResult r = kit_dwarf_field_iter_next(it, &fld);
    648           size_t fsz = 0;
    649           if (r != KIT_ITER_ITEM) break;
    650           fw_indent(f, depth + 1);
    651           fw_cstr(f, ".");
    652           fw_slice(f, fld.name.len ? fld.name : KIT_SLICE_LIT("<anon>"));
    653           fw_cstr(f, " = ");
    654           if (fld.bit_size) {
    655             size_t off = fld.byte_offset;
    656             size_t take = (off + 8 <= got) ? 8 : (off < got ? got - off : 0);
    657             uint64_t raw = take ? sym_load_u(buf + off, take) : 0;
    658             uint64_t mask = (fld.bit_size >= 64)
    659                                 ? (uint64_t)-1
    660                                 : (((uint64_t)1 << fld.bit_size) - 1);
    661             uint64_t v = (raw >> fld.bit_offset) & mask;
    662             fw_dec_u(f, v);
    663           } else {
    664             if (fld.type) {
    665               KitDwarfTypeInfo fti = kit_dwarf_type_info(fld.type);
    666               fsz = fti.byte_size;
    667             }
    668             if (fld.type && fsz > 0 && (size_t)fld.byte_offset + fsz <= got) {
    669               sym_fmt(f, fld.type, buf + fld.byte_offset, fsz, depth + 1);
    670             } else {
    671               fw_cstr(f, "<truncated>");
    672             }
    673           }
    674           fw_cstr(f, ",\n");
    675         }
    676         kit_dwarf_field_iter_free(it);
    677       }
    678       fw_indent(f, depth);
    679       fw_cstr(f, "}");
    680       return;
    681     }
    682     case KIT_DT_FUNC:
    683       fw_cstr(f, "<function@");
    684       fw_hex(f, sym_load_u(buf, got));
    685       fw_cstr(f, ">");
    686       return;
    687   }
    688   fw_cstr(f, "<?>");
    689 }
    690 
    691 KitStatus kit_dbg_value_format(KitDebugSession* s, const KitDebugValue* v,
    692                                KitWriter* out,
    693                                const KitDebugFormatOptions* opt) {
    694   SymFmt f;
    695   if (!s || !v || !out) return KIT_INVALID;
    696   f.w = out;
    697   f.s = s;
    698   f.st = KIT_OK;
    699   sym_fmt(&f, v->type, v->bytes, v->len, opt ? (int)opt->indent : 0);
    700   return f.st;
    701 }
    702 
    703 KitStatus kit_dbg_var_write_u64(KitDebugSession* s, const KitUnwindFrame* frame,
    704                                 KitSlice name, uint64_t value) {
    705   KitUnwindFrame fr;
    706   KitDwarfVarLoc loc;
    707   uint8_t buf[8];
    708   size_t sz, i;
    709   if (!s) return KIT_INVALID;
    710   if (!s->dwarf) return KIT_NOT_FOUND;
    711   fr = frame ? *frame : s->stop.regs;
    712   if (kit_dwarf_var_at(s->dwarf, sym_pc_rt_to_img(s, fr.pc), name, &loc) !=
    713       KIT_OK)
    714     return KIT_NOT_FOUND;
    715   sym_translate_loc(s, &loc);
    716   sz = (loc.byte_size == 0 || loc.byte_size > 8) ? 8 : loc.byte_size;
    717   for (i = 0; i < sz; ++i) buf[i] = (uint8_t)(value >> (8 * i));
    718 
    719   switch (loc.kind) {
    720     case KIT_DLOC_FRAME_OFS:
    721       return kit_dbg_session_write_mem(
    722           s, fr.cfa + (uint64_t)(int64_t)loc.v.frame_ofs, buf, sz);
    723     case KIT_DLOC_GLOBAL:
    724       return kit_dbg_session_write_mem(s, loc.v.global, buf, sz);
    725     case KIT_DLOC_REG: {
    726       KitUnwindFrame w = s->stop.regs;
    727       if (loc.v.reg >= 32) return KIT_INVALID;
    728       w.regs[loc.v.reg] = value;
    729       return kit_dbg_session_set_regs(s, &w);
    730     }
    731     case KIT_DLOC_EXPR:
    732       return KIT_UNSUPPORTED;
    733   }
    734   return KIT_UNSUPPORTED;
    735 }
    736 
    737 /* ---- scope enumeration ------------------------------------------------ */
    738 
    739 struct KitDebugVarIter {
    740   KitDebugSession* s;
    741   KitUnwindFrame frame;
    742   KitDwarfVarIter* inner;
    743 };
    744 
    745 KitStatus kit_dbg_vars_new(KitDebugSession* s, const KitUnwindFrame* frame,
    746                            uint32_t roles, KitDebugVarIter** out) {
    747   KitDebugVarIter* it;
    748   KitDwarfVarIter* inner = NULL;
    749   KitStatus st;
    750   if (!s || !out) return KIT_INVALID;
    751   *out = NULL;
    752   if (!s->dwarf) return KIT_NOT_FOUND;
    753   it = (KitDebugVarIter*)s->heap->alloc(s->heap, sizeof(*it),
    754                                         _Alignof(KitDebugVarIter));
    755   if (!it) return KIT_NOMEM;
    756   it->s = s;
    757   it->frame = frame ? *frame : s->stop.regs;
    758   st = kit_dwarf_vars_at_new(s->dwarf, sym_pc_rt_to_img(s, it->frame.pc), roles,
    759                              &inner);
    760   if (st != KIT_OK) {
    761     s->heap->free(s->heap, it, sizeof(*it));
    762     return st;
    763   }
    764   it->inner = inner;
    765   *out = it;
    766   return KIT_OK;
    767 }
    768 
    769 KitIterResult kit_dbg_vars_next(KitDebugVarIter* it, KitSlice* name_out,
    770                                 KitDebugValue* value_out) {
    771   KitDwarfVar v;
    772   KitIterResult r;
    773   if (!it || !value_out) return KIT_ITER_END;
    774   r = kit_dwarf_vars_at_next(it->inner, &v);
    775   if (r != KIT_ITER_ITEM) return r;
    776   if (name_out) *name_out = v.name;
    777   memset(value_out, 0, sizeof(*value_out));
    778   value_out->type = v.loc.type;
    779   sym_translate_loc(it->s, &v.loc);
    780   /* On a read failure leave bytes NULL: the caller renders "<unreadable>" but
    781    * the enumeration continues. */
    782   (void)sym_materialize(it->s, &v.loc, &it->frame, value_out);
    783   return KIT_ITER_ITEM;
    784 }
    785 
    786 void kit_dbg_vars_free(KitDebugVarIter* it) {
    787   if (!it) return;
    788   kit_dwarf_vars_at_free(it->inner);
    789   it->s->heap->free(it->s->heap, it, sizeof(*it));
    790 }
    791 
    792 /* ---- location resolution + breakpoints -------------------------------- */
    793 
    794 static int sym_is_digit(int c) { return c >= '0' && c <= '9'; }
    795 
    796 /* Parse a decimal or 0x-hex integer from [s, s+n); returns bytes consumed (0 on
    797  * no digits) and writes the value. */
    798 static size_t sym_parse_u64(const char* s, size_t n, uint64_t* out) {
    799   uint64_t v = 0;
    800   size_t i = 0;
    801   int any = 0;
    802   if (n >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
    803     i = 2;
    804     for (; i < n; ++i) {
    805       int c = s[i], d;
    806       if (sym_is_digit(c))
    807         d = c - '0';
    808       else if (c >= 'a' && c <= 'f')
    809         d = c - 'a' + 10;
    810       else if (c >= 'A' && c <= 'F')
    811         d = c - 'A' + 10;
    812       else
    813         break;
    814       v = (v << 4) | (uint64_t)d;
    815       any = 1;
    816     }
    817   } else {
    818     for (; i < n; ++i) {
    819       if (!sym_is_digit(s[i])) break;
    820       v = v * 10u + (uint64_t)(s[i] - '0');
    821       any = 1;
    822     }
    823   }
    824   if (!any) return 0;
    825   *out = v;
    826   return i;
    827 }
    828 
    829 KitStatus kit_dbg_resolve(KitDebugSession* s, KitSlice spec, uint64_t* addr_out,
    830                           KitDwarfLineMatch* cands, uint32_t cap,
    831                           uint32_t* ncand) {
    832   const char* p;
    833   size_t n, i;
    834   if (!s || !addr_out || !spec.s || spec.len == 0) return KIT_INVALID;
    835   if (ncand) *ncand = 0;
    836   p = spec.s;
    837   n = spec.len;
    838 
    839   /* file:line — a ':' immediately followed by a digit. */
    840   for (i = 0; i < n; ++i) {
    841     if (p[i] == ':' && i + 1 < n && sym_is_digit((unsigned char)p[i + 1])) {
    842       KitSlice file = {.s = p, .len = i};
    843       uint64_t line = 0;
    844       uint64_t pc = 0;
    845       KitStatus rc;
    846       size_t used = sym_parse_u64(p + i + 1, n - i - 1, &line);
    847       if (i == 0 || used != n - i - 1) return KIT_INVALID;
    848       if (!s->dwarf) return KIT_NOT_FOUND;
    849       rc = kit_dwarf_line_to_addr(s->dwarf, file, (uint32_t)line, &pc);
    850       if (rc == KIT_AMBIGUOUS) {
    851         if (cands && cap)
    852           kit_dwarf_line_to_addr_all(s->dwarf, file, (uint32_t)line, cands, cap,
    853                                      ncand);
    854         else if (ncand)
    855           kit_dwarf_line_to_addr_all(s->dwarf, file, (uint32_t)line, NULL, 0,
    856                                      ncand);
    857         return KIT_AMBIGUOUS;
    858       }
    859       if (rc != KIT_OK) return rc;
    860       *addr_out = sym_pc_img_to_rt(s, pc);
    861       return KIT_OK;
    862     }
    863   }
    864 
    865   /* 0xADDR or decimal address. */
    866   if ((n >= 1 && sym_is_digit((unsigned char)p[0]))) {
    867     uint64_t v = 0;
    868     size_t used = sym_parse_u64(p, n, &v);
    869     if (!used || used != n) return KIT_INVALID;
    870     *addr_out = v;
    871     return KIT_OK;
    872   }
    873 
    874   /* NAME[+off] */
    875   {
    876     size_t name_n = n;
    877     uint64_t off = 0;
    878     void* resolved;
    879     KitSlice name;
    880     for (i = 0; i < n; ++i) {
    881       if (p[i] == '+') {
    882         name_n = i;
    883         break;
    884       }
    885     }
    886     if (name_n == 0) return KIT_INVALID;
    887     if (name_n < n) {
    888       size_t used = sym_parse_u64(p + name_n + 1, n - name_n - 1, &off);
    889       if (!used || used != n - name_n - 1) return KIT_INVALID;
    890     }
    891     name.s = p;
    892     name.len = name_n;
    893     resolved = kit_jit_lookup(s->jit, name);
    894     if (!resolved) return KIT_NOT_FOUND;
    895     {
    896       union {
    897         void* p;
    898         uint64_t u;
    899       } cv;
    900       cv.p = resolved;
    901       *addr_out = cv.u + off;
    902     }
    903     return KIT_OK;
    904   }
    905 }
    906 
    907 KitStatus kit_dbg_break(KitDebugSession* s, KitSlice spec,
    908                         uint32_t* bp_id_out) {
    909   uint64_t addr = 0;
    910   KitStatus rc = kit_dbg_resolve(s, spec, &addr, NULL, 0, NULL);
    911   if (rc != KIT_OK) return rc;
    912   return kit_dbg_session_breakpoint_set(s, addr, bp_id_out);
    913 }
    914 
    915 /* ---- disassemble at the stopped PC ------------------------------------ */
    916 
    917 KitStatus kit_dbg_disasm_new(KitDebugSession* s, uint64_t addr, uint32_t count,
    918                              KitDisasmIter** out) {
    919   KitDisasmContext* dctx;
    920   uint8_t* buf;
    921   size_t bytec;
    922   if (!s || !out || count == 0) return KIT_INVALID;
    923   *out = NULL;
    924   if (count > SYM_BT_MAX) count = SYM_BT_MAX;
    925   bytec = (size_t)count * 16u; /* generous upper bound per instruction */
    926   buf = (uint8_t*)arena_alloc(&s->values, bytec, 16);
    927   if (!buf) return KIT_NOMEM;
    928   if (kit_dbg_session_read_mem(s, addr, buf, bytec) != KIT_OK) return KIT_ERR;
    929   /* The disasm iterator's compiler keeps a pointer into this KitDisasmContext,
    930    * so it must outlive the iterator: park it in the stop-epoch arena (freed at
    931    * the next resume, after the caller has freed the iterator). */
    932   dctx = arena_znew(&s->values, KitDisasmContext);
    933   if (!dctx) return KIT_NOMEM;
    934   dctx->target = kit_compiler_target(s->c);
    935   dctx->context = *s->c->ctx;
    936   return kit_disasm_iter_new(dctx, buf, bytec, addr, kit_jit_view(s->jit), out);
    937 }