step.c (9298B)
1 /* Resume-mode state machine. 2 * 3 * STEP_INSN uses the displaced-step primitive directly via pending_pc_override 4 * and the outer session_resume's signal/wait. STEP_LINE / NEXT_LINE / STEP_OUT 5 * drive their own signal/wait cycles in-loop and set pending_done so the outer 6 * resume short-circuits. */ 7 8 #include <string.h> 9 10 #include "core/slice.h" 11 #include "dbg/dbg.h" 12 13 #define DBG_STEP_LINE_INSN_CAP 1024u 14 15 static KitStatus run_step_line_loop(KitDebugSession* s); 16 17 /* DWARF line/CFI tables are authored in image-relative vaddrs (kit's 18 * debug emitter writes them, the JIT view applies relocs against final 19 * image vaddrs). Stop PCs and the values dropped onto the stack by 20 * BL/RET, on the other hand, live in runtime address space. Every 21 * DWARF call from the session translates at the boundary. */ 22 static uint64_t step_rt_to_img(KitDebugSession* s, uint64_t pc) { 23 uint64_t v = kit_jit_runtime_to_image(s->jit, pc); 24 return v ? v : pc; 25 } 26 27 static KitStatus prepare_step_insn(KitDebugSession* s) { 28 uint64_t pc = s->stop.regs.pc; 29 uint64_t scratch_entry = 0; 30 KitStatus st = dbg_displaced_prepare(s, pc, &scratch_entry); 31 if (st != KIT_OK) return st; 32 s->pending_has_pc = 1; 33 s->pending_pc_override = scratch_entry; 34 return KIT_OK; 35 } 36 37 /* Drive a single displaced-step cycle synchronously. Returns KIT_OK on 38 * success; the session is parked again at the post-step PC. */ 39 static KitStatus do_one_displaced(KitDebugSession* s) { 40 KitStatus st = prepare_step_insn(s); 41 if (st != KIT_OK) return st; 42 st = dbg_session_signal_resume(s); 43 if (st != KIT_OK) return st; 44 return dbg_session_wait_stop(s); 45 } 46 47 static int stop_is_internal_completion(const KitDebugSession* s) { 48 return s->stop.kind == KIT_STOP_BREAKPOINT && s->stop.bp_id == 0; 49 } 50 51 /* Arm a one-shot internal bp at `target`, resume the worker, and wait for the 52 * next stop. on_fault clears the internal bp only if it is the bp that traps; 53 * if a user bp / signal / exit intervenes first the one-shot would linger and 54 * later resurface as a spurious bp_id==0 stop, so clear it here on any return 55 * path that is not its own completion. Mirrors the displaced-step sentinel 56 * cleanup. */ 57 static KitStatus run_to_internal_bp(KitDebugSession* s, uint64_t target) { 58 u32 bp_id = 0; 59 KitStatus st = dbg_bp_set_internal(s, target, &bp_id); 60 if (st != KIT_OK) return st; 61 st = dbg_session_signal_resume(s); 62 if (st != KIT_OK) { 63 dbg_bp_clear(s, bp_id); 64 return st; 65 } 66 st = dbg_session_wait_stop(s); 67 if (st != KIT_OK) { 68 dbg_bp_clear(s, bp_id); 69 return st; 70 } 71 if (!stop_is_internal_completion(s)) { 72 dbg_bp_clear(s, bp_id); 73 } 74 return KIT_OK; 75 } 76 77 static KitStatus direct_call_target(KitDebugSession* s, uint64_t* target) { 78 ArchDbgInsn insn; 79 if (!s->arch_dbg || !s->arch_dbg->direct_call_target) return KIT_NOT_FOUND; 80 if (dbg_arch_decode_insn(s, s->stop.regs.pc, &insn) != KIT_OK) 81 return KIT_NOT_FOUND; 82 return s->arch_dbg->direct_call_target(&insn, target); 83 } 84 85 static KitStatus direct_jump_target(KitDebugSession* s, uint64_t* target) { 86 ArchDbgInsn insn; 87 if (!s->arch_dbg || !s->arch_dbg->direct_jump_target) return KIT_NOT_FOUND; 88 if (dbg_arch_decode_insn(s, s->stop.regs.pc, &insn) != KIT_OK) 89 return KIT_NOT_FOUND; 90 return s->arch_dbg->direct_jump_target(&insn, target); 91 } 92 93 static KitStatus dwarf_line_for(KitDebugSession* s, uint64_t pc, KitSlice* file, 94 uint32_t* line) { 95 uint32_t col = 0; 96 *file = KIT_SLICE_NULL; 97 *line = 0; 98 return kit_dwarf_addr_to_line(s->dwarf, step_rt_to_img(s, pc), file, line, 99 &col); 100 } 101 102 static KitStatus dwarf_sub_for(KitDebugSession* s, uint64_t pc, 103 KitDwarfSubprogram* out) { 104 memset(out, 0, sizeof(*out)); 105 return kit_dwarf_subprogram_at(s->dwarf, step_rt_to_img(s, pc), out); 106 } 107 108 static int line_changed(KitSlice base_file, uint32_t base_line, 109 KitSlice new_file, uint32_t new_line) { 110 if (new_line == 0) return 0; 111 if (base_line == 0) return 1; 112 if (new_line != base_line) return 1; 113 if (base_file.s != new_file.s) { 114 if (!base_file.s || !new_file.s) return 1; 115 if (!slice_eq(base_file, new_file)) return 1; 116 } 117 return 0; 118 } 119 120 static KitStatus try_step_into_direct_call(KitDebugSession* s, int* did) { 121 uint64_t target = 0; 122 KitStatus st; 123 *did = 0; 124 st = direct_call_target(s, &target); 125 if (st == KIT_NOT_FOUND || st == KIT_UNSUPPORTED) return KIT_OK; 126 if (st != KIT_OK) return st; 127 *did = 1; 128 st = run_to_internal_bp(s, target); 129 if (st != KIT_OK) return st; 130 if (stop_is_internal_completion(s)) { 131 return run_step_line_loop(s); 132 } 133 return KIT_OK; 134 } 135 136 static KitStatus try_follow_direct_jump(KitDebugSession* s, int* did) { 137 uint64_t target = 0; 138 KitStatus st; 139 *did = 0; 140 st = direct_jump_target(s, &target); 141 if (st == KIT_NOT_FOUND || st == KIT_UNSUPPORTED) return KIT_OK; 142 if (st != KIT_OK) return st; 143 *did = 1; 144 return run_to_internal_bp(s, target); 145 } 146 147 static KitStatus run_step_line_loop(KitDebugSession* s) { 148 KitSlice base_file = KIT_SLICE_NULL; 149 uint32_t base_line = 0; 150 KitDwarfSubprogram base_sub; 151 int have_sub; 152 u32 i; 153 154 (void)dwarf_line_for(s, s->stop.regs.pc, &base_file, &base_line); 155 have_sub = (dwarf_sub_for(s, s->stop.regs.pc, &base_sub) == KIT_OK); 156 157 for (i = 0; i < DBG_STEP_LINE_INSN_CAP; ++i) { 158 KitSlice new_file = KIT_SLICE_NULL; 159 uint32_t new_line = 0; 160 KitDwarfSubprogram new_sub; 161 int have_new_sub; 162 int did_call = 0; 163 int did_jump = 0; 164 KitStatus st; 165 166 st = try_step_into_direct_call(s, &did_call); 167 if (st != KIT_OK) return st; 168 if (did_call) return KIT_OK; 169 170 st = try_follow_direct_jump(s, &did_jump); 171 if (st != KIT_OK) return st; 172 173 if (!did_jump && do_one_displaced(s) != KIT_OK) { 174 /* Lifter declined; surface whatever the current stop is. */ 175 return KIT_OK; 176 } 177 if (!stop_is_internal_completion(s)) { 178 /* User breakpoint, signal, or exit — surface. */ 179 return KIT_OK; 180 } 181 182 (void)dwarf_line_for(s, s->stop.regs.pc, &new_file, &new_line); 183 have_new_sub = (dwarf_sub_for(s, s->stop.regs.pc, &new_sub) == KIT_OK); 184 185 if (have_sub && have_new_sub) { 186 if (new_sub.low_pc != base_sub.low_pc) { 187 /* Left the original subprogram — STEP_LINE follows in. */ 188 return KIT_OK; 189 } 190 } 191 192 if (line_changed(base_file, base_line, new_file, new_line)) { 193 return KIT_OK; 194 } 195 } 196 return KIT_OK; 197 } 198 199 static KitStatus run_step_out(KitDebugSession* s) { 200 KitUnwindFrame frame; 201 KitStatus st; 202 frame = s->stop.regs; 203 frame.pc = step_rt_to_img(s, frame.pc); /* CFI lookup is in image space */ 204 st = kit_dwarf_unwind_step(s->dwarf, &frame); 205 if (st != KIT_OK) { 206 if (!s->arch_dbg || !s->arch_dbg->link_register_return_address || 207 s->arch_dbg->link_register_return_address(&s->stop.regs, &frame.pc) != 208 KIT_OK) { 209 return st; 210 } 211 } 212 /* On success unwind_step or the link-register fallback writes frame.pc from 213 * the saved return-address register / stack slot — already a runtime PC, no 214 * inverse translation needed before the internal bp install. */ 215 if (frame.pc == 0) return KIT_NOT_FOUND; 216 return run_to_internal_bp(s, frame.pc); 217 } 218 219 static KitStatus run_next_line(KitDebugSession* s) { 220 ArchDbgInsn insn; 221 222 if (!s->arch_dbg || !s->arch_dbg->is_call || 223 dbg_arch_decode_insn(s, s->stop.regs.pc, &insn) != KIT_OK) { 224 return run_step_line_loop(s); 225 } 226 if (!s->arch_dbg->is_call(&insn)) { 227 return run_step_line_loop(s); 228 } 229 230 /* Step OVER the call by setting an internal bp at the unwound return PC 231 * and CONTINUE-ing. After the bp fires, fall into the STEP_LINE loop to 232 * keep advancing until the source line actually changes. */ 233 { 234 KitUnwindFrame frame = s->stop.regs; 235 KitStatus st; 236 frame.pc = step_rt_to_img(s, frame.pc); 237 if (kit_dwarf_unwind_step(s->dwarf, &frame) != KIT_OK || frame.pc == 0) { 238 /* Fall back to stepping into the call. */ 239 return run_step_line_loop(s); 240 } 241 /* frame.pc is now a runtime return-address (from the stack). */ 242 st = run_to_internal_bp(s, frame.pc); 243 if (st != KIT_OK) return st; 244 if (stop_is_internal_completion(s)) { 245 return run_step_line_loop(s); 246 } 247 return KIT_OK; 248 } 249 } 250 251 KitStatus dbg_step_resume(KitDebugSession* s, KitResumeMode mode) { 252 switch (mode) { 253 case KIT_RESUME_ABORT: 254 return KIT_OK; 255 case KIT_RESUME_CONTINUE: 256 if (dbg_bp_lookup_index(s, s->stop.regs.pc) != 0) { 257 return prepare_step_insn(s); 258 } 259 return KIT_OK; 260 261 case KIT_RESUME_STEP_INSN: 262 return prepare_step_insn(s); 263 264 case KIT_RESUME_STEP_LINE: { 265 KitStatus st; 266 if (!s->dwarf) return KIT_INVALID; 267 st = run_step_line_loop(s); 268 if (st != KIT_OK) return st; 269 s->pending_done = 1; 270 return KIT_OK; 271 } 272 273 case KIT_RESUME_NEXT_LINE: { 274 KitStatus st; 275 if (!s->dwarf) return KIT_INVALID; 276 st = run_next_line(s); 277 if (st != KIT_OK) return st; 278 s->pending_done = 1; 279 return KIT_OK; 280 } 281 282 case KIT_RESUME_STEP_OUT: { 283 KitStatus st; 284 if (!s->dwarf) return KIT_INVALID; 285 st = run_step_out(s); 286 if (st != KIT_OK) return st; 287 s->pending_done = 1; 288 return KIT_OK; 289 } 290 } 291 return KIT_INVALID; 292 }