kit

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

bp.c (7129B)


      1 /* Breakpoint table for the JIT debugger session.
      2  *
      3  * Keyed by runtime address. Each slot owns the bytes overwritten by the
      4  * per-arch trap patch, a refcount (so step.c can drop temporaries without
      5  * disturbing user bps at the same PC), and a monotonic user-visible id. Reads
      6  * of the patched range substitute the saved bytes so `x` and disasm stay
      7  * honest. */
      8 
      9 #include <string.h>
     10 
     11 #include "dbg/dbg.h"
     12 
     13 static u32 bp_find_slot(DbgBpTable* t, uint64_t addr) {
     14   u32 i;
     15   for (i = 0; i < t->cap; ++i) {
     16     if (t->slots[i].user_id != 0 && t->slots[i].addr == addr) return i + 1;
     17   }
     18   return 0;
     19 }
     20 
     21 static u32 bp_alloc_slot(KitJitSession* s) {
     22   DbgBpTable* t = &s->bps;
     23   u32 i;
     24   for (i = 0; i < t->cap; ++i) {
     25     if (t->slots[i].user_id == 0) return i;
     26   }
     27   /* grow */
     28   {
     29     u32 ncap = t->cap ? t->cap * 2 : 16;
     30     DbgBp* nslots =
     31         (DbgBp*)s->heap->alloc(s->heap, sizeof(DbgBp) * ncap, _Alignof(DbgBp));
     32     if (!nslots) return (u32)-1;
     33     memset(nslots, 0, sizeof(DbgBp) * ncap);
     34     if (t->slots) {
     35       memcpy(nslots, t->slots, sizeof(DbgBp) * t->cap);
     36       s->heap->free(s->heap, t->slots, sizeof(DbgBp) * t->cap);
     37     }
     38     t->slots = nslots;
     39     i = t->cap;
     40     t->cap = ncap;
     41     return i;
     42   }
     43 }
     44 
     45 void dbg_bp_init(KitJitSession* s) {
     46   memset(&s->bps, 0, sizeof(s->bps));
     47   s->bps.next_user_id = 1;
     48   s->bps.next_internal_id = DBG_BP_ID_INTERNAL_BASE;
     49 }
     50 
     51 void dbg_bp_fini(KitJitSession* s) {
     52   DbgBpTable* t = &s->bps;
     53   u32 i;
     54   if (t->slots) {
     55     /* Restore any still-armed patches so the JIT image is left clean. */
     56     for (i = 0; i < t->cap; ++i) {
     57       DbgBp* b = &t->slots[i];
     58       if (b->user_id != 0 && b->enabled && b->saved_len) {
     59         void* write_addr = NULL;
     60         if (s->os->code_write_begin(s->os->user, (void*)(uintptr_t)b->addr,
     61                                     b->saved_len, &write_addr) == KIT_OK &&
     62             write_addr) {
     63           memcpy(write_addr, b->saved, b->saved_len);
     64           s->os->code_write_end(s->os->user, (void*)(uintptr_t)b->addr,
     65                                 b->saved_len);
     66           if (s->os->flush_icache)
     67             s->os->flush_icache(s->os->user, (void*)(uintptr_t)b->addr,
     68                                 b->saved_len);
     69         }
     70       }
     71     }
     72     s->heap->free(s->heap, t->slots, sizeof(DbgBp) * t->cap);
     73     t->slots = NULL;
     74     t->cap = 0;
     75   }
     76 }
     77 
     78 static KitStatus bp_install_patch(KitJitSession* s, DbgBp* b) {
     79   void* write_addr = NULL;
     80   uint8_t patch[ARCH_DBG_MAX_TRAP_BYTES];
     81   u32 patch_len = 0;
     82   KitStatus st;
     83   if (!s->arch_dbg || !s->arch_dbg->breakpoint_patch) return KIT_UNSUPPORTED;
     84   st = s->arch_dbg->breakpoint_patch(patch, (u32)sizeof(patch), &patch_len);
     85   if (st != KIT_OK) return st;
     86   if (patch_len == 0 || patch_len > sizeof(b->saved)) return KIT_ERR;
     87   b->saved_len = patch_len;
     88   st = s->os->code_write_begin(s->os->user, (void*)(uintptr_t)b->addr,
     89                                b->saved_len, &write_addr);
     90   if (st != KIT_OK || !write_addr) {
     91     return st != KIT_OK ? st : KIT_ERR;
     92   }
     93   memcpy(b->saved, write_addr, b->saved_len);
     94   memcpy(write_addr, patch, b->saved_len);
     95   s->os->code_write_end(s->os->user, (void*)(uintptr_t)b->addr, b->saved_len);
     96   if (s->os->flush_icache)
     97     s->os->flush_icache(s->os->user, (void*)(uintptr_t)b->addr, b->saved_len);
     98   b->enabled = 1;
     99   return KIT_OK;
    100 }
    101 
    102 static void bp_remove_patch(KitJitSession* s, DbgBp* b) {
    103   void* write_addr = NULL;
    104   if (!b->enabled || !b->saved_len) return;
    105   if (s->os->code_write_begin(s->os->user, (void*)(uintptr_t)b->addr,
    106                               b->saved_len, &write_addr) != KIT_OK ||
    107       !write_addr) {
    108     return;
    109   }
    110   memcpy(write_addr, b->saved, b->saved_len);
    111   s->os->code_write_end(s->os->user, (void*)(uintptr_t)b->addr, b->saved_len);
    112   if (s->os->flush_icache)
    113     s->os->flush_icache(s->os->user, (void*)(uintptr_t)b->addr, b->saved_len);
    114   b->enabled = 0;
    115 }
    116 
    117 static KitStatus bp_set_common(KitJitSession* s, const KitBreakpointSpec* spec,
    118                                int internal, u32* id_out) {
    119   uint64_t addr = spec->addr;
    120   u32 slot;
    121   DbgBp* b;
    122   /* Internal bps may live in the displaced-step scratch page, which is
    123    * outside the JIT image; only user bps are constrained to image range. */
    124   if (!internal && !kit_jit_image_contains(s->jit, addr)) return KIT_INVALID;
    125 
    126   /* idempotent: existing slot at this address bumps refcount and returns
    127    * its already-issued id. */
    128   slot = bp_find_slot(&s->bps, addr);
    129   if (slot) {
    130     b = &s->bps.slots[slot - 1];
    131     b->refcount++;
    132     if (id_out) *id_out = b->user_id;
    133     return KIT_OK;
    134   }
    135 
    136   {
    137     u32 i = bp_alloc_slot(s);
    138     KitStatus st;
    139     if (i == (u32)-1) return KIT_NOMEM;
    140     b = &s->bps.slots[i];
    141     memset(b, 0, sizeof(*b));
    142     b->addr = addr;
    143     b->refcount = 1;
    144     b->skip_count = spec->skip_count;
    145     b->max_hits = spec->max_hits;
    146     b->condition = spec->condition;
    147     b->condition_user = spec->condition_user;
    148     b->internal = (u8)(internal ? 1 : 0);
    149     if (internal) {
    150       b->user_id = s->bps.next_internal_id++;
    151     } else {
    152       b->user_id = s->bps.next_user_id++;
    153     }
    154     st = bp_install_patch(s, b);
    155     if (st != KIT_OK) {
    156       memset(b, 0, sizeof(*b));
    157       return st;
    158     }
    159     s->bps.count++;
    160     if (id_out) *id_out = b->user_id;
    161   }
    162   return KIT_OK;
    163 }
    164 
    165 KitStatus dbg_bp_set(KitJitSession* s, uint64_t addr, u32* id_out) {
    166   KitBreakpointSpec spec;
    167   memset(&spec, 0, sizeof(spec));
    168   spec.addr = addr;
    169   return bp_set_common(s, &spec, 0, id_out);
    170 }
    171 
    172 KitStatus dbg_bp_set_spec(KitJitSession* s, const KitBreakpointSpec* spec,
    173                           u32* id_out) {
    174   if (!spec) return KIT_INVALID;
    175   return bp_set_common(s, spec, 0, id_out);
    176 }
    177 
    178 KitStatus dbg_bp_set_internal(KitJitSession* s, uint64_t addr, u32* id_out) {
    179   KitBreakpointSpec spec;
    180   memset(&spec, 0, sizeof(spec));
    181   spec.addr = addr;
    182   return bp_set_common(s, &spec, 1, id_out);
    183 }
    184 
    185 KitStatus dbg_bp_clear(KitJitSession* s, u32 id) {
    186   u32 i;
    187   if (id == 0) return KIT_OK;
    188   for (i = 0; i < s->bps.cap; ++i) {
    189     DbgBp* b = &s->bps.slots[i];
    190     if (b->user_id != id) continue;
    191     if (b->refcount > 1) {
    192       b->refcount--;
    193       return KIT_OK;
    194     }
    195     bp_remove_patch(s, b);
    196     memset(b, 0, sizeof(*b));
    197     s->bps.count--;
    198     return KIT_OK;
    199   }
    200   return KIT_OK; /* silent on unknown id, per contract */
    201 }
    202 
    203 u32 dbg_bp_lookup_index(KitJitSession* s, uint64_t addr) {
    204   return bp_find_slot(&s->bps, addr);
    205 }
    206 
    207 DbgBp* dbg_bp_at_index(KitJitSession* s, u32 idx) {
    208   if (idx == 0 || idx > s->bps.cap) return NULL;
    209   return &s->bps.slots[idx - 1];
    210 }
    211 
    212 void dbg_bp_unpatch_read(KitJitSession* s, uint64_t addr, void* buf, size_t n) {
    213   u32 i;
    214   u8* out = (u8*)buf;
    215   uint64_t end = addr + n;
    216   for (i = 0; i < s->bps.cap; ++i) {
    217     DbgBp* b = &s->bps.slots[i];
    218     uint64_t bp_end;
    219     uint64_t lo;
    220     uint64_t hi;
    221     if (b->user_id == 0 || !b->enabled || !b->saved_len) continue;
    222     bp_end = b->addr + b->saved_len;
    223     if (bp_end <= addr || b->addr >= end) continue;
    224     lo = b->addr > addr ? b->addr : addr;
    225     hi = bp_end < end ? bp_end : end;
    226     memcpy(out + (lo - addr), b->saved + (lo - b->addr), (size_t)(hi - lo));
    227   }
    228 }