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 }