kit

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

dwarf_query.c (13763B)


      1 /* dwarf_query.c — public kit_dwarf_* query entry points.
      2  *
      3  * Implements the consumer half of doc/DWARF.md:
      4  *   subprogram_at / func_at, var_at, vars_at_*, param_iter_*, loc_read.
      5  */
      6 
      7 #include <kit/arch.h>
      8 #include <kit/dwarf.h>
      9 #include <stddef.h>
     10 #include <stdint.h>
     11 #include <string.h>
     12 
     13 #include "core/core.h"
     14 #include "core/heap.h"
     15 #include "debug/dwarf_internal.h"
     16 
     17 static void fill_subprogram(KitDebugInfo* d, DwSubprog* sp,
     18                             KitDwarfSubprogram* out) {
     19   memset(out, 0, sizeof(*out));
     20   out->name = sp->name ? kit_slice_cstr(sp->name) : KIT_SLICE_NULL;
     21   out->low_pc = sp->low_pc;
     22   out->high_pc = sp->high_pc;
     23   out->decl_file =
     24       sp->decl_file ? kit_slice_cstr(sp->decl_file) : KIT_SLICE_NULL;
     25   out->decl_line = sp->decl_line;
     26   out->return_type = sp->type_die_offset
     27                          ? dw_type_from_die(d, sp->cu_idx, sp->type_die_offset)
     28                          : dw_void_type(d);
     29   out->inlined = sp->inlined;
     30 }
     31 
     32 KitStatus kit_dwarf_subprogram_at(KitDebugInfo* d, uint64_t pc,
     33                                   KitDwarfSubprogram* out) {
     34   DwSubprog* sp;
     35   if (!d || !out) return KIT_INVALID;
     36   sp = dw_find_subprog(d, pc);
     37   if (!sp) return KIT_NOT_FOUND;
     38   fill_subprogram(d, sp, out);
     39   return KIT_OK;
     40 }
     41 
     42 static DwSubprog* dw_find_subprog_named(KitDebugInfo* d, const char* name) {
     43   u32 i;
     44   if (!d || !name) return NULL;
     45   dw_build_subs(d);
     46   for (i = 0; i < d->nsubs; ++i) {
     47     DwSubprog* sp = &d->subs[i];
     48     if (sp->inlined) continue;
     49     if (!sp->name || !dw_streq(sp->name, name)) continue;
     50     if (sp->high_pc > sp->low_pc) return sp;
     51   }
     52   for (i = 0; i < d->nsubs; ++i) {
     53     DwSubprog* sp = &d->subs[i];
     54     if (sp->inlined) continue;
     55     if (sp->name && dw_streq(sp->name, name)) return sp;
     56   }
     57   return NULL;
     58 }
     59 
     60 KitStatus kit_dwarf_subprogram_named(KitDebugInfo* d, KitSlice name,
     61                                      KitDwarfSubprogram* out) {
     62   DwSubprog* sp;
     63   if (!d || !name.s || !out) return KIT_INVALID;
     64   sp = dw_find_subprog_named(d, name.s);
     65   if (!sp) return KIT_NOT_FOUND;
     66   fill_subprogram(d, sp, out);
     67   return KIT_OK;
     68 }
     69 
     70 KitStatus kit_dwarf_func_at(KitDebugInfo* d, uint64_t pc, KitSlice* name_out,
     71                             uint64_t* low_out, uint64_t* high_out) {
     72   KitDwarfSubprogram sp;
     73   KitStatus st = kit_dwarf_subprogram_at(d, pc, &sp);
     74   if (st != KIT_OK) return st;
     75   if (name_out) *name_out = sp.name;
     76   if (low_out) *low_out = sp.low_pc;
     77   if (high_out) *high_out = sp.high_pc;
     78   return KIT_OK;
     79 }
     80 
     81 /* ---- variable resolution -------------------------------------------- */
     82 
     83 static void fill_varloc(KitDebugInfo* d, u32 cu_idx, const DwLocal* v, u64 pc,
     84                         KitDwarfVarLoc* out) {
     85   const u8* lbytes = v->loc;
     86   u32 llen = v->loc_len;
     87   memset(out, 0, sizeof(*out));
     88   out->kind = KIT_DLOC_EXPR;
     89   out->byte_size = 0;
     90   out->type = NULL;
     91   if (v->type_die_offset) {
     92     out->type = dw_type_from_die(d, cu_idx, v->type_die_offset);
     93     if (out->type) out->byte_size = out->type->byte_size;
     94   }
     95   /* If the variable was emitted with a loclistx, resolve it now. The
     96    * resolved bytes get the same single-op fast-path treatment below. */
     97   if (v->has_loclist && cu_idx < d->ncus) {
     98     const u8* lb = NULL;
     99     u32 ll = 0;
    100     if (dw_loclist_resolve(d, &d->cus[cu_idx], v->loclist_index, pc, &lb,
    101                            &ll)) {
    102       lbytes = lb;
    103       llen = ll;
    104     } else {
    105       /* No active entry for this PC — variable is currently unavailable. */
    106       out->kind = KIT_DLOC_EXPR;
    107       out->v.expr.bytes = NULL;
    108       out->v.expr.len = 0;
    109       return;
    110     }
    111   }
    112   /* Inspect the loc bytes — if it's a single op of a recognized form,
    113    * we expose the structured kind so callers can fast-path. Otherwise
    114    * we surface the raw bytes as EXPR. */
    115   if (lbytes && llen > 0) {
    116     const u8* e = lbytes;
    117     if (llen == 1 && e[0] >= DW_OP_reg0 && e[0] <= DW_OP_reg0 + 31) {
    118       out->kind = KIT_DLOC_REG;
    119       out->v.reg = e[0] - DW_OP_reg0;
    120       return;
    121     }
    122     if (e[0] == DW_OP_regx) {
    123       u32 off = 1;
    124       u64 r = dw_uleb(e, llen, &off);
    125       if (off == llen) {
    126         out->kind = KIT_DLOC_REG;
    127         out->v.reg = (u32)r;
    128         return;
    129       }
    130     }
    131     if (e[0] == DW_OP_fbreg) {
    132       u32 off = 1;
    133       i64 ofs = dw_sleb(e, llen, &off);
    134       if (off == llen) {
    135         out->kind = KIT_DLOC_FRAME_OFS;
    136         out->v.frame_ofs = (i32)ofs;
    137         return;
    138       }
    139     }
    140     if (e[0] == DW_OP_addr && llen == 9) {
    141       u32 off = 1;
    142       out->kind = KIT_DLOC_GLOBAL;
    143       out->v.global = dw_u64(e, llen, &off);
    144       return;
    145     }
    146     /* Fallback: opaque expression bytes. */
    147     out->kind = KIT_DLOC_EXPR;
    148     out->v.expr.bytes = lbytes;
    149     out->v.expr.len = llen;
    150     return;
    151   }
    152   /* No location at all — leave kind=EXPR with NULL/0. */
    153   out->kind = KIT_DLOC_EXPR;
    154   out->v.expr.bytes = NULL;
    155   out->v.expr.len = 0;
    156 }
    157 
    158 KitStatus kit_dwarf_var_at(KitDebugInfo* d, uint64_t pc, KitSlice name,
    159                            KitDwarfVarLoc* out) {
    160   /* Status codes:
    161    *   KIT_OK         — found; *out filled.
    162    *   KIT_INVALID    — bad args.
    163    *   KIT_NOT_FOUND  — pc inside a subprog but no var named `name`, or
    164    *                      pc outside any subprogram and not a global.
    165    */
    166   DwSubprog* sp;
    167   u32 i;
    168   if (!d || !name.s || !out) return KIT_INVALID;
    169   memset(out, 0, sizeof(*out));
    170   sp = dw_find_subprog(d, pc);
    171   if (sp) {
    172     dw_build_locals(d, sp);
    173     /* Deepest scope first: walk locals from end (innermost blocks added
    174      * after enclosing). */
    175     for (i = sp->nlocals; i > 0; --i) {
    176       DwLocal* v = &sp->locals[i - 1];
    177       if (!v->name || !dw_streq(v->name, name.s)) continue;
    178       if (v->has_scope && (pc < v->scope_lo || pc >= v->scope_hi)) continue;
    179       fill_varloc(d, sp->cu_idx, v, pc, out);
    180       return KIT_OK;
    181     }
    182     /* Then params. */
    183     for (i = 0; i < sp->nparams; ++i) {
    184       DwLocal* v = &sp->params[i];
    185       if (!v->name || !dw_streq(v->name, name.s)) continue;
    186       fill_varloc(d, sp->cu_idx, v, pc, out);
    187       return KIT_OK;
    188     }
    189   }
    190   /* Globals. */
    191   dw_build_globals(d);
    192   for (i = 0; i < d->nglobals; ++i) {
    193     DwLocal* v = &d->globals[i];
    194     if (!v->name || !dw_streq(v->name, name.s)) continue;
    195     fill_varloc(d, 0, v, pc, out);
    196     return KIT_OK;
    197   }
    198   return KIT_NOT_FOUND;
    199 }
    200 
    201 KitStatus kit_dwarf_loc_read(KitDebugInfo* d, const KitDwarfVarLoc* loc,
    202                              const KitUnwindFrame* frame,
    203                              KitDwarfReadMemFn read_mem, void* read_user,
    204                              void* dst, size_t cap, size_t* read_out) {
    205   size_t want;
    206   if (read_out) *read_out = 0;
    207   if (!d || !loc || !frame || !dst) return KIT_INVALID;
    208   want = loc->byte_size ? loc->byte_size : cap;
    209   if (want > cap) want = cap;
    210   switch (loc->kind) {
    211     case KIT_DLOC_REG: {
    212       uint64_t v = (loc->v.reg < 32) ? frame->regs[loc->v.reg] : 0;
    213       size_t n = want > sizeof(v) ? sizeof(v) : want;
    214       memcpy(dst, &v, n);
    215       if (read_out) *read_out = n;
    216       return KIT_OK;
    217     }
    218     case KIT_DLOC_FRAME_OFS: {
    219       uint64_t addr = frame->cfa + (uint64_t)(int64_t)loc->v.frame_ofs;
    220       KitStatus st;
    221       if (!read_mem) return KIT_INVALID;
    222       st = read_mem(read_user, addr, dst, want);
    223       if (st != KIT_OK) return st;
    224       if (read_out) *read_out = want;
    225       return KIT_OK;
    226     }
    227     case KIT_DLOC_GLOBAL: {
    228       uint64_t addr = loc->v.global;
    229       KitStatus st;
    230       if (!read_mem) return KIT_INVALID;
    231       st = read_mem(read_user, addr, dst, want);
    232       if (st != KIT_OK) return st;
    233       if (read_out) *read_out = want;
    234       return KIT_OK;
    235     }
    236     case KIT_DLOC_EXPR: {
    237       /* Evaluate. We don't have direct access to the variable's
    238        * subprogram's frame_base here — caller-supplied frame must already
    239        * carry the right CFA. The expression itself may be DW_OP_call_frame_cfa
    240        * + DW_OP_consts + DW_OP_plus, etc. */
    241       DwExprResult r;
    242       if (loc->v.expr.bytes == NULL || loc->v.expr.len == 0)
    243         return KIT_NOT_FOUND;
    244       if (dw_eval_expr(d, loc->v.expr.bytes, (u32)loc->v.expr.len, NULL, 0,
    245                        frame, &r) != 0)
    246         return KIT_UNSUPPORTED;
    247       if (r.kind == 0) {
    248         KitStatus st;
    249         if (!read_mem) return KIT_INVALID;
    250         st = read_mem(read_user, r.value, dst, want);
    251         if (st != KIT_OK) return st;
    252         if (read_out) *read_out = want;
    253         return KIT_OK;
    254       } else if (r.kind == 1) {
    255         size_t n = want > sizeof(r.value) ? sizeof(r.value) : want;
    256         memcpy(dst, &r.value, n);
    257         if (read_out) *read_out = n;
    258         return KIT_OK;
    259       } else if (r.kind == 2) {
    260         u64 v = (r.value < 32) ? frame->regs[r.value] : 0;
    261         size_t n = want > sizeof(v) ? sizeof(v) : want;
    262         memcpy(dst, &v, n);
    263         if (read_out) *read_out = n;
    264         return KIT_OK;
    265       }
    266       return KIT_UNSUPPORTED;
    267     }
    268   }
    269   return KIT_UNSUPPORTED;
    270 }
    271 
    272 /* ---- vars_at_* iterator --------------------------------------------- */
    273 
    274 struct KitDwarfVarIter {
    275   KitDebugInfo* d;
    276   DwSubprog* sp;
    277   u64 pc;
    278   u32 mask;
    279   u32 phase; /* 0 = locals, 1 = params, 2 = globals, 3 = done */
    280   u32 idx;
    281 };
    282 
    283 KitStatus kit_dwarf_vars_at_new(KitDebugInfo* d, uint64_t pc, uint32_t mask,
    284                                 KitDwarfVarIter** out) {
    285   KitDwarfVarIter* it;
    286   if (!out) return KIT_INVALID;
    287   *out = NULL;
    288   if (!d) return KIT_INVALID;
    289   it = (KitDwarfVarIter*)d->h->alloc(d->h, sizeof(*it),
    290                                      _Alignof(KitDwarfVarIter));
    291   if (!it) return KIT_NOMEM;
    292   it->d = d;
    293   it->pc = pc;
    294   it->mask = mask;
    295   it->sp = dw_find_subprog(d, pc);
    296   if (it->sp) dw_build_locals(d, it->sp);
    297   it->phase = 0;
    298   it->idx = it->sp ? it->sp->nlocals : 0;
    299   *out = it;
    300   return KIT_OK;
    301 }
    302 
    303 KitIterResult kit_dwarf_vars_at_next(KitDwarfVarIter* it, KitDwarfVar* out) {
    304   if (!it || !out) return KIT_ITER_ERROR;
    305   for (;;) {
    306     switch (it->phase) {
    307       case 0: {
    308         if (!(it->mask & (1u << KIT_DVR_LOCAL))) {
    309           it->phase = 1;
    310           it->idx = 0;
    311           break;
    312         }
    313         if (it->idx == 0) {
    314           it->phase = 1;
    315           it->idx = 0;
    316           break;
    317         }
    318         {
    319           DwLocal* v = &it->sp->locals[--it->idx];
    320           if (v->has_scope && (it->pc < v->scope_lo || it->pc >= v->scope_hi))
    321             break;
    322           out->name = v->name ? kit_slice_cstr(v->name) : KIT_SLICE_NULL;
    323           out->role = KIT_DVR_LOCAL;
    324           fill_varloc(it->d, it->sp->cu_idx, v, it->pc, &out->loc);
    325           return KIT_ITER_ITEM;
    326         }
    327       }
    328       case 1: {
    329         if (!it->sp || !(it->mask & (1u << KIT_DVR_ARG))) {
    330           it->phase = 2;
    331           it->idx = 0;
    332           break;
    333         }
    334         if (it->idx >= it->sp->nparams) {
    335           it->phase = 2;
    336           it->idx = 0;
    337           break;
    338         }
    339         {
    340           DwLocal* v = &it->sp->params[it->idx++];
    341           out->name = v->name ? kit_slice_cstr(v->name) : KIT_SLICE_NULL;
    342           out->role = KIT_DVR_ARG;
    343           fill_varloc(it->d, it->sp->cu_idx, v, it->pc, &out->loc);
    344           return KIT_ITER_ITEM;
    345         }
    346       }
    347       case 2: {
    348         if (!(it->mask & (1u << KIT_DVR_GLOBAL))) {
    349           it->phase = 3;
    350           break;
    351         }
    352         dw_build_globals(it->d);
    353         if (it->idx >= it->d->nglobals) {
    354           it->phase = 3;
    355           break;
    356         }
    357         {
    358           DwLocal* v = &it->d->globals[it->idx++];
    359           out->name = v->name ? kit_slice_cstr(v->name) : KIT_SLICE_NULL;
    360           out->role = KIT_DVR_GLOBAL;
    361           fill_varloc(it->d, 0, v, it->pc, &out->loc);
    362           return KIT_ITER_ITEM;
    363         }
    364       }
    365       default:
    366         return KIT_ITER_END;
    367     }
    368   }
    369 }
    370 
    371 void kit_dwarf_vars_at_free(KitDwarfVarIter* it) {
    372   if (!it) return;
    373   it->d->h->free(it->d->h, it, sizeof(*it));
    374 }
    375 
    376 /* ---- param_iter_* --------------------------------------------------- */
    377 
    378 struct KitDwarfParamIter {
    379   KitDebugInfo* d;
    380   DwSubprog* sp;
    381   u64 pc;
    382   u32 idx;
    383 };
    384 
    385 KitStatus kit_dwarf_param_iter_new(KitDebugInfo* d, uint64_t pc,
    386                                    KitDwarfParamIter** out) {
    387   KitDwarfParamIter* it;
    388   DwSubprog* sp;
    389   if (!out) return KIT_INVALID;
    390   *out = NULL;
    391   if (!d) return KIT_INVALID;
    392   sp = dw_find_subprog(d, pc);
    393   if (!sp) return KIT_NOT_FOUND;
    394   dw_build_locals(d, sp);
    395   it = (KitDwarfParamIter*)d->h->alloc(d->h, sizeof(*it),
    396                                        _Alignof(KitDwarfParamIter));
    397   if (!it) return KIT_NOMEM;
    398   it->d = d;
    399   it->sp = sp;
    400   it->pc = pc;
    401   it->idx = 0;
    402   *out = it;
    403   return KIT_OK;
    404 }
    405 
    406 KitStatus kit_dwarf_param_iter_new_named(KitDebugInfo* d, KitSlice name,
    407                                          KitDwarfParamIter** out) {
    408   KitDwarfParamIter* it;
    409   DwSubprog* sp;
    410   if (!out) return KIT_INVALID;
    411   *out = NULL;
    412   if (!d || !name.s) return KIT_INVALID;
    413   sp = dw_find_subprog_named(d, name.s);
    414   if (!sp) return KIT_NOT_FOUND;
    415   dw_build_locals(d, sp);
    416   it = (KitDwarfParamIter*)d->h->alloc(d->h, sizeof(*it),
    417                                        _Alignof(KitDwarfParamIter));
    418   if (!it) return KIT_NOMEM;
    419   it->d = d;
    420   it->sp = sp;
    421   it->pc = sp->low_pc;
    422   it->idx = 0;
    423   *out = it;
    424   return KIT_OK;
    425 }
    426 
    427 KitIterResult kit_dwarf_param_iter_next(KitDwarfParamIter* it,
    428                                         KitDwarfVar* out) {
    429   if (!it || !out) return KIT_ITER_ERROR;
    430   if (it->idx >= it->sp->nparams) return KIT_ITER_END;
    431   {
    432     DwLocal* v = &it->sp->params[it->idx++];
    433     out->name = v->name ? kit_slice_cstr(v->name) : KIT_SLICE_NULL;
    434     out->role = KIT_DVR_ARG;
    435     fill_varloc(it->d, it->sp->cu_idx, v, it->pc, &out->loc);
    436   }
    437   return KIT_ITER_ITEM;
    438 }
    439 
    440 void kit_dwarf_param_iter_free(KitDwarfParamIter* it) {
    441   if (!it) return;
    442   it->d->h->free(it->d->h, it, sizeof(*it));
    443 }