kit

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

dwarf_query.c (14577B)


      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 KitStatus kit_dwarf_resolve(KitDebugInfo* d, uint64_t addr, int want_func,
     82                             KitDwarfResolve* out) {
     83   KitSlice file;
     84   uint32_t line = 0, col = 0;
     85   if (!d || !out) return KIT_INVALID;
     86   memset(out, 0, sizeof(*out));
     87 
     88   if (kit_dwarf_addr_to_line(d, addr, &file, &line, &col) == KIT_OK) {
     89     out->have_line = 1;
     90     out->file = file;
     91     out->line = line;
     92     out->col = col;
     93   }
     94 
     95   if (want_func) {
     96     KitSlice func;
     97     uint64_t func_lo = 0, func_hi = 0;
     98     if (kit_dwarf_func_at(d, addr, &func, &func_lo, &func_hi) == KIT_OK) {
     99       out->have_func = 1;
    100       out->func = func;
    101     }
    102   }
    103   return KIT_OK;
    104 }
    105 
    106 /* ---- variable resolution -------------------------------------------- */
    107 
    108 static void fill_varloc(KitDebugInfo* d, u32 cu_idx, const DwLocal* v, u64 pc,
    109                         KitDwarfVarLoc* out) {
    110   const u8* lbytes = v->loc;
    111   u32 llen = v->loc_len;
    112   memset(out, 0, sizeof(*out));
    113   out->kind = KIT_DLOC_EXPR;
    114   out->byte_size = 0;
    115   out->type = NULL;
    116   if (v->type_die_offset) {
    117     out->type = dw_type_from_die(d, cu_idx, v->type_die_offset);
    118     if (out->type) out->byte_size = out->type->byte_size;
    119   }
    120   /* If the variable was emitted with a loclistx, resolve it now. The
    121    * resolved bytes get the same single-op fast-path treatment below. */
    122   if (v->has_loclist && cu_idx < d->ncus) {
    123     const u8* lb = NULL;
    124     u32 ll = 0;
    125     if (dw_loclist_resolve(d, &d->cus[cu_idx], v->loclist_index, pc, &lb,
    126                            &ll)) {
    127       lbytes = lb;
    128       llen = ll;
    129     } else {
    130       /* No active entry for this PC — variable is currently unavailable. */
    131       out->kind = KIT_DLOC_EXPR;
    132       out->v.expr.bytes = NULL;
    133       out->v.expr.len = 0;
    134       return;
    135     }
    136   }
    137   /* Inspect the loc bytes — if it's a single op of a recognized form,
    138    * we expose the structured kind so callers can fast-path. Otherwise
    139    * we surface the raw bytes as EXPR. */
    140   if (lbytes && llen > 0) {
    141     const u8* e = lbytes;
    142     if (llen == 1 && e[0] >= DW_OP_reg0 && e[0] <= DW_OP_reg0 + 31) {
    143       out->kind = KIT_DLOC_REG;
    144       out->v.reg = e[0] - DW_OP_reg0;
    145       return;
    146     }
    147     if (e[0] == DW_OP_regx) {
    148       u32 off = 1;
    149       u64 r = dw_uleb(e, llen, &off);
    150       if (off == llen) {
    151         out->kind = KIT_DLOC_REG;
    152         out->v.reg = (u32)r;
    153         return;
    154       }
    155     }
    156     if (e[0] == DW_OP_fbreg) {
    157       u32 off = 1;
    158       i64 ofs = dw_sleb(e, llen, &off);
    159       if (off == llen) {
    160         out->kind = KIT_DLOC_FRAME_OFS;
    161         out->v.frame_ofs = (i32)ofs;
    162         return;
    163       }
    164     }
    165     if (e[0] == DW_OP_addr && cu_idx < d->ncus &&
    166         llen == 1u + d->cus[cu_idx].address_size) {
    167       u32 off = 1;
    168       out->kind = KIT_DLOC_GLOBAL;
    169       out->v.global = d->cus[cu_idx].address_size == 4 ? dw_u32(e, llen, &off)
    170                                                        : dw_u64(e, llen, &off);
    171       return;
    172     }
    173     /* Fallback: opaque expression bytes. */
    174     out->kind = KIT_DLOC_EXPR;
    175     out->v.expr.bytes = lbytes;
    176     out->v.expr.len = llen;
    177     return;
    178   }
    179   /* No location at all — leave kind=EXPR with NULL/0. */
    180   out->kind = KIT_DLOC_EXPR;
    181   out->v.expr.bytes = NULL;
    182   out->v.expr.len = 0;
    183 }
    184 
    185 KitStatus kit_dwarf_var_at(KitDebugInfo* d, uint64_t pc, KitSlice name,
    186                            KitDwarfVarLoc* out) {
    187   /* Status codes:
    188    *   KIT_OK         — found; *out filled.
    189    *   KIT_INVALID    — bad args.
    190    *   KIT_NOT_FOUND  — pc inside a subprog but no var named `name`, or
    191    *                      pc outside any subprogram and not a global.
    192    */
    193   DwSubprog* sp;
    194   u32 i;
    195   if (!d || !name.s || !out) return KIT_INVALID;
    196   memset(out, 0, sizeof(*out));
    197   sp = dw_find_subprog(d, pc);
    198   if (sp) {
    199     dw_build_locals(d, sp);
    200     /* Deepest scope first: walk locals from end (innermost blocks added
    201      * after enclosing). */
    202     for (i = sp->nlocals; i > 0; --i) {
    203       DwLocal* v = &sp->locals[i - 1];
    204       if (!v->name || !dw_streq(v->name, name.s)) continue;
    205       if (v->has_scope && (pc < v->scope_lo || pc >= v->scope_hi)) continue;
    206       fill_varloc(d, sp->cu_idx, v, pc, out);
    207       return KIT_OK;
    208     }
    209     /* Then params. */
    210     for (i = 0; i < sp->nparams; ++i) {
    211       DwLocal* v = &sp->params[i];
    212       if (!v->name || !dw_streq(v->name, name.s)) continue;
    213       fill_varloc(d, sp->cu_idx, v, pc, out);
    214       return KIT_OK;
    215     }
    216   }
    217   /* Globals. */
    218   dw_build_globals(d);
    219   for (i = 0; i < d->nglobals; ++i) {
    220     DwLocal* v = &d->globals[i];
    221     if (!v->name || !dw_streq(v->name, name.s)) continue;
    222     fill_varloc(d, 0, v, pc, out);
    223     return KIT_OK;
    224   }
    225   return KIT_NOT_FOUND;
    226 }
    227 
    228 KitStatus kit_dwarf_loc_read(KitDebugInfo* d, const KitDwarfVarLoc* loc,
    229                              const KitUnwindFrame* frame,
    230                              KitDwarfReadMemFn read_mem, void* read_user,
    231                              void* dst, size_t cap, size_t* read_out) {
    232   size_t want;
    233   if (read_out) *read_out = 0;
    234   if (!d || !loc || !frame || !dst) return KIT_INVALID;
    235   want = loc->byte_size ? loc->byte_size : cap;
    236   if (want > cap) want = cap;
    237   switch (loc->kind) {
    238     case KIT_DLOC_REG: {
    239       uint64_t v = (loc->v.reg < 32) ? frame->regs[loc->v.reg] : 0;
    240       size_t n = want > sizeof(v) ? sizeof(v) : want;
    241       memcpy(dst, &v, n);
    242       if (read_out) *read_out = n;
    243       return KIT_OK;
    244     }
    245     case KIT_DLOC_FRAME_OFS: {
    246       uint64_t addr = frame->cfa + (uint64_t)(int64_t)loc->v.frame_ofs;
    247       KitStatus st;
    248       if (!read_mem) return KIT_INVALID;
    249       st = read_mem(read_user, addr, dst, want);
    250       if (st != KIT_OK) return st;
    251       if (read_out) *read_out = want;
    252       return KIT_OK;
    253     }
    254     case KIT_DLOC_GLOBAL: {
    255       uint64_t addr = loc->v.global;
    256       KitStatus st;
    257       if (!read_mem) return KIT_INVALID;
    258       st = read_mem(read_user, addr, dst, want);
    259       if (st != KIT_OK) return st;
    260       if (read_out) *read_out = want;
    261       return KIT_OK;
    262     }
    263     case KIT_DLOC_EXPR: {
    264       /* Evaluate. We don't have direct access to the variable's
    265        * subprogram's frame_base here — caller-supplied frame must already
    266        * carry the right CFA. The expression itself may be DW_OP_call_frame_cfa
    267        * + DW_OP_consts + DW_OP_plus, etc. */
    268       DwExprResult r;
    269       if (loc->v.expr.bytes == NULL || loc->v.expr.len == 0)
    270         return KIT_NOT_FOUND;
    271       if (dw_eval_expr(d, loc->v.expr.bytes, (u32)loc->v.expr.len, NULL, 0,
    272                        frame, &r) != 0)
    273         return KIT_UNSUPPORTED;
    274       if (r.kind == 0) {
    275         KitStatus st;
    276         if (!read_mem) return KIT_INVALID;
    277         st = read_mem(read_user, r.value, dst, want);
    278         if (st != KIT_OK) return st;
    279         if (read_out) *read_out = want;
    280         return KIT_OK;
    281       } else if (r.kind == 1) {
    282         size_t n = want > sizeof(r.value) ? sizeof(r.value) : want;
    283         memcpy(dst, &r.value, n);
    284         if (read_out) *read_out = n;
    285         return KIT_OK;
    286       } else if (r.kind == 2) {
    287         u64 v = (r.value < 32) ? frame->regs[r.value] : 0;
    288         size_t n = want > sizeof(v) ? sizeof(v) : want;
    289         memcpy(dst, &v, n);
    290         if (read_out) *read_out = n;
    291         return KIT_OK;
    292       }
    293       return KIT_UNSUPPORTED;
    294     }
    295   }
    296   return KIT_UNSUPPORTED;
    297 }
    298 
    299 /* ---- vars_at_* iterator --------------------------------------------- */
    300 
    301 struct KitDwarfVarIter {
    302   KitDebugInfo* d;
    303   DwSubprog* sp;
    304   u64 pc;
    305   u32 mask;
    306   u32 phase; /* 0 = locals, 1 = params, 2 = globals, 3 = done */
    307   u32 idx;
    308 };
    309 
    310 KitStatus kit_dwarf_vars_at_new(KitDebugInfo* d, uint64_t pc, uint32_t mask,
    311                                 KitDwarfVarIter** out) {
    312   KitDwarfVarIter* it;
    313   if (!out) return KIT_INVALID;
    314   *out = NULL;
    315   if (!d) return KIT_INVALID;
    316   it = (KitDwarfVarIter*)d->h->alloc(d->h, sizeof(*it),
    317                                      _Alignof(KitDwarfVarIter));
    318   if (!it) return KIT_NOMEM;
    319   it->d = d;
    320   it->pc = pc;
    321   it->mask = mask;
    322   it->sp = dw_find_subprog(d, pc);
    323   if (it->sp) dw_build_locals(d, it->sp);
    324   it->phase = 0;
    325   it->idx = it->sp ? it->sp->nlocals : 0;
    326   *out = it;
    327   return KIT_OK;
    328 }
    329 
    330 KitIterResult kit_dwarf_vars_at_next(KitDwarfVarIter* it, KitDwarfVar* out) {
    331   if (!it || !out) return KIT_ITER_ERROR;
    332   for (;;) {
    333     switch (it->phase) {
    334       case 0: {
    335         if (!(it->mask & (1u << KIT_DVR_LOCAL))) {
    336           it->phase = 1;
    337           it->idx = 0;
    338           break;
    339         }
    340         if (it->idx == 0) {
    341           it->phase = 1;
    342           it->idx = 0;
    343           break;
    344         }
    345         {
    346           DwLocal* v = &it->sp->locals[--it->idx];
    347           if (v->has_scope && (it->pc < v->scope_lo || it->pc >= v->scope_hi))
    348             break;
    349           out->name = v->name ? kit_slice_cstr(v->name) : KIT_SLICE_NULL;
    350           out->role = KIT_DVR_LOCAL;
    351           fill_varloc(it->d, it->sp->cu_idx, v, it->pc, &out->loc);
    352           return KIT_ITER_ITEM;
    353         }
    354       }
    355       case 1: {
    356         if (!it->sp || !(it->mask & (1u << KIT_DVR_ARG))) {
    357           it->phase = 2;
    358           it->idx = 0;
    359           break;
    360         }
    361         if (it->idx >= it->sp->nparams) {
    362           it->phase = 2;
    363           it->idx = 0;
    364           break;
    365         }
    366         {
    367           DwLocal* v = &it->sp->params[it->idx++];
    368           out->name = v->name ? kit_slice_cstr(v->name) : KIT_SLICE_NULL;
    369           out->role = KIT_DVR_ARG;
    370           fill_varloc(it->d, it->sp->cu_idx, v, it->pc, &out->loc);
    371           return KIT_ITER_ITEM;
    372         }
    373       }
    374       case 2: {
    375         if (!(it->mask & (1u << KIT_DVR_GLOBAL))) {
    376           it->phase = 3;
    377           break;
    378         }
    379         dw_build_globals(it->d);
    380         if (it->idx >= it->d->nglobals) {
    381           it->phase = 3;
    382           break;
    383         }
    384         {
    385           DwLocal* v = &it->d->globals[it->idx++];
    386           out->name = v->name ? kit_slice_cstr(v->name) : KIT_SLICE_NULL;
    387           out->role = KIT_DVR_GLOBAL;
    388           fill_varloc(it->d, 0, v, it->pc, &out->loc);
    389           return KIT_ITER_ITEM;
    390         }
    391       }
    392       default:
    393         return KIT_ITER_END;
    394     }
    395   }
    396 }
    397 
    398 void kit_dwarf_vars_at_free(KitDwarfVarIter* it) {
    399   if (!it) return;
    400   it->d->h->free(it->d->h, it, sizeof(*it));
    401 }
    402 
    403 /* ---- param_iter_* --------------------------------------------------- */
    404 
    405 struct KitDwarfParamIter {
    406   KitDebugInfo* d;
    407   DwSubprog* sp;
    408   u64 pc;
    409   u32 idx;
    410 };
    411 
    412 KitStatus kit_dwarf_param_iter_new(KitDebugInfo* d, uint64_t pc,
    413                                    KitDwarfParamIter** out) {
    414   KitDwarfParamIter* it;
    415   DwSubprog* sp;
    416   if (!out) return KIT_INVALID;
    417   *out = NULL;
    418   if (!d) return KIT_INVALID;
    419   sp = dw_find_subprog(d, pc);
    420   if (!sp) return KIT_NOT_FOUND;
    421   dw_build_locals(d, sp);
    422   it = (KitDwarfParamIter*)d->h->alloc(d->h, sizeof(*it),
    423                                        _Alignof(KitDwarfParamIter));
    424   if (!it) return KIT_NOMEM;
    425   it->d = d;
    426   it->sp = sp;
    427   it->pc = pc;
    428   it->idx = 0;
    429   *out = it;
    430   return KIT_OK;
    431 }
    432 
    433 KitStatus kit_dwarf_param_iter_new_named(KitDebugInfo* d, KitSlice name,
    434                                          KitDwarfParamIter** out) {
    435   KitDwarfParamIter* it;
    436   DwSubprog* sp;
    437   if (!out) return KIT_INVALID;
    438   *out = NULL;
    439   if (!d || !name.s) return KIT_INVALID;
    440   sp = dw_find_subprog_named(d, name.s);
    441   if (!sp) return KIT_NOT_FOUND;
    442   dw_build_locals(d, sp);
    443   it = (KitDwarfParamIter*)d->h->alloc(d->h, sizeof(*it),
    444                                        _Alignof(KitDwarfParamIter));
    445   if (!it) return KIT_NOMEM;
    446   it->d = d;
    447   it->sp = sp;
    448   it->pc = sp->low_pc;
    449   it->idx = 0;
    450   *out = it;
    451   return KIT_OK;
    452 }
    453 
    454 KitIterResult kit_dwarf_param_iter_next(KitDwarfParamIter* it,
    455                                         KitDwarfVar* out) {
    456   if (!it || !out) return KIT_ITER_ERROR;
    457   if (it->idx >= it->sp->nparams) return KIT_ITER_END;
    458   {
    459     DwLocal* v = &it->sp->params[it->idx++];
    460     out->name = v->name ? kit_slice_cstr(v->name) : KIT_SLICE_NULL;
    461     out->role = KIT_DVR_ARG;
    462     fill_varloc(it->d, it->sp->cu_idx, v, it->pc, &out->loc);
    463   }
    464   return KIT_ITER_ITEM;
    465 }
    466 
    467 void kit_dwarf_param_iter_free(KitDwarfParamIter* it) {
    468   if (!it) return;
    469   it->d->h->free(it->d->h, it, sizeof(*it));
    470 }