session.c (19160B)
1 /* KitJitSession lifecycle, worker handshake, and fault classification. 2 * 3 * The session owns a single worker thread that runs the JIT'd entry. The 4 * REPL thread and worker thread coordinate through two events (resume, 5 * stop) and one shared KitStopInfo slot. Every fault on the worker 6 * (trap / SIGSEGV / SIGBUS / SIGILL / SIGFPE / interrupt_signo) drops into 7 * on_fault here; this TU is also the only place that touches the public 8 * KitJitSession entries. */ 9 10 #include <string.h> 11 12 #include "dbg/dbg.h" 13 14 /* ---- fault classification ------------------------------------------- */ 15 16 static KitStopReason stop_reason_for_step(KitResumeMode mode) { 17 switch (mode) { 18 case KIT_RESUME_STEP_INSN: 19 return KIT_STOP_REASON_STEP_INSN; 20 case KIT_RESUME_STEP_LINE: 21 return KIT_STOP_REASON_STEP_LINE; 22 case KIT_RESUME_NEXT_LINE: 23 return KIT_STOP_REASON_NEXT_LINE; 24 case KIT_RESUME_STEP_OUT: 25 return KIT_STOP_REASON_STEP_OUT; 26 case KIT_RESUME_CONTINUE: 27 case KIT_RESUME_ABORT: 28 break; 29 } 30 return KIT_STOP_REASON_UNKNOWN; 31 } 32 33 static KitStatus on_fault(void* session_v, int signo, KitUnwindFrame* regs) { 34 KitJitSession* s = (KitJitSession*)session_v; 35 uint64_t bp_addr; 36 u32 idx; 37 DbgBp* bp; 38 39 if (!s) return KIT_INVALID; 40 41 /* Snapshot the regs into our stop slot up-front. */ 42 memcpy(&s->stop.regs, regs, sizeof(*regs)); 43 s->stop.signal = signo; 44 s->stop.exit_code = 0; 45 s->stop.bp_id = 0; 46 s->stop.reason = KIT_STOP_REASON_UNKNOWN; 47 48 /* Interrupt — host requested via thread_interrupt. */ 49 if (s->os->interrupt_signo != 0 && signo == s->os->interrupt_signo) { 50 s->stop.kind = KIT_STOP_INTERRUPT; 51 s->stop.reason = KIT_STOP_REASON_INTERRUPT; 52 goto park; 53 } 54 55 /* Breakpoint? The arch layer owns trap-PC normalization (e.g. x86 INT3 56 * reports the PC after the trap byte). Keep non-breakpoint signals at the 57 * raw PC if no patch is found. */ 58 bp_addr = regs->pc; 59 if (s->arch_dbg && s->arch_dbg->breakpoint_addr_from_fault_pc) 60 bp_addr = s->arch_dbg->breakpoint_addr_from_fault_pc(regs->pc); 61 idx = dbg_bp_lookup_index(s, bp_addr); 62 if (idx) { 63 bp = dbg_bp_at_index(s, idx); 64 s->stop.regs.pc = bp_addr; 65 66 /* Displaced-step sentinel: complete the step and either resume 67 * silently (auto_continue path inside dbg_step_resume) or surface 68 * a generic stop for STEP_INSN. */ 69 if (bp && bp->internal && s->displaced.return_pc == bp_addr) { 70 dbg_displaced_finalize(s); 71 /* Sync the on-stack regs with the corrected PC so the OS layer 72 * writes it back into ucontext on return. */ 73 regs->pc = s->stop.regs.pc; 74 if (s->pending_step_pending) { 75 /* CONTINUE-over-bp: do not park; just resume. */ 76 s->pending_step_pending = 0; 77 return KIT_OK; 78 } 79 s->stop.kind = KIT_STOP_BREAKPOINT; 80 s->stop.bp_id = 0; 81 s->stop.reason = stop_reason_for_step(s->pending_mode); 82 goto park; 83 } 84 85 /* Plain internal bp (e.g. fallback one-shot at PC+4): treat like 86 * a step completion. */ 87 if (bp && bp->internal) { 88 /* Clear it so it doesn't linger across resumes. */ 89 dbg_bp_clear(s, bp->user_id); 90 if (s->pending_step_pending) { 91 s->pending_step_pending = 0; 92 return KIT_OK; 93 } 94 s->stop.kind = KIT_STOP_BREAKPOINT; 95 s->stop.bp_id = 0; 96 s->stop.reason = stop_reason_for_step(s->pending_mode); 97 goto park; 98 } 99 100 /* User-visible bp. Apply skip_count / condition / max_hits. */ 101 if (bp) { 102 bp->hit_count++; 103 if (bp->hit_count <= bp->skip_count) { 104 /* Silent skip: re-step over the patch so the original insn 105 * runs and execution continues without notifying the REPL. 106 * Implemented by arming a displaced step then deferring the 107 * "do not park" decision via pending_step_pending. */ 108 s->pending_step_pending = 1; 109 if (dbg_step_resume(s, KIT_RESUME_CONTINUE) != KIT_OK) { 110 /* Couldn't arm a step; surface the stop after all. */ 111 s->pending_step_pending = 0; 112 s->stop.kind = KIT_STOP_BREAKPOINT; 113 s->stop.bp_id = bp->user_id; 114 s->stop.reason = KIT_STOP_REASON_USER_BREAKPOINT; 115 goto park; 116 } 117 /* Apply any PC override the prepare-step path set. */ 118 if (s->pending_has_pc) { 119 regs->pc = s->pending_pc_override; 120 s->pending_has_pc = 0; 121 } 122 return KIT_OK; 123 } 124 if (bp->condition && bp->condition(bp->condition_user, regs) == 0) { 125 /* Condition rejected: same silent-resume path. */ 126 s->pending_step_pending = 1; 127 if (dbg_step_resume(s, KIT_RESUME_CONTINUE) != KIT_OK) { 128 s->pending_step_pending = 0; 129 s->stop.kind = KIT_STOP_BREAKPOINT; 130 s->stop.bp_id = bp->user_id; 131 s->stop.reason = KIT_STOP_REASON_USER_BREAKPOINT; 132 goto park; 133 } 134 if (s->pending_has_pc) { 135 regs->pc = s->pending_pc_override; 136 s->pending_has_pc = 0; 137 } 138 return KIT_OK; 139 } 140 s->stop.kind = KIT_STOP_BREAKPOINT; 141 s->stop.bp_id = bp->user_id; 142 s->stop.reason = KIT_STOP_REASON_USER_BREAKPOINT; 143 if (bp->max_hits != 0 && bp->hit_count >= bp->max_hits + bp->skip_count) { 144 /* Auto-clear after surfacing. Defer to post-park so the bp_id 145 * is still valid when the driver inspects. */ 146 } 147 goto park; 148 } 149 } 150 151 /* Not a patched address — pass through as SIGNAL (covers SEGV, BUS, 152 * ILL, FPE, and any SIGTRAP from a program-emitted trap). */ 153 s->stop.kind = KIT_STOP_SIGNAL; 154 s->stop.reason = (s->os->trap_signo != 0 && signo == s->os->trap_signo) 155 ? KIT_STOP_REASON_TRAP 156 : KIT_STOP_REASON_SIGNAL; 157 158 park: 159 s->state = DBG_STATE_STOPPED; 160 s->os->event_signal(s->os->user, s->ev_stop); 161 s->os->event_wait(s->os->user, s->ev_resume); 162 s->os->event_reset(s->os->user, s->ev_resume); 163 164 if (s->pending_mode == KIT_RESUME_ABORT) { 165 if (s->os->thread_abort) { 166 s->os->thread_abort(s->os->user); 167 } 168 } 169 170 /* Apply pending PC override (set by step_resume) before returning. */ 171 if (s->pending_has_pc) { 172 regs->pc = s->pending_pc_override; 173 s->stop.regs.pc = s->pending_pc_override; 174 s->pending_has_pc = 0; 175 } else { 176 /* Allow REPL set_regs to mutate any field. */ 177 memcpy(regs, &s->stop.regs, sizeof(*regs)); 178 } 179 return KIT_OK; 180 } 181 182 /* ---- worker thread -------------------------------------------------- */ 183 184 static void worker_run_entry(void* arg) { 185 KitJitSession* s = (KitJitSession*)arg; 186 typedef int (*EntryIntArgv)(int, char**); 187 typedef uint64_t (*EntryU64_0)(void); 188 typedef uint64_t (*EntryU64_1)(uint64_t); 189 typedef uint64_t (*EntryU64_2)(uint64_t, uint64_t); 190 typedef uint64_t (*EntryU64_3)(uint64_t, uint64_t, uint64_t); 191 typedef uint64_t (*EntryU64_4)(uint64_t, uint64_t, uint64_t, uint64_t); 192 typedef uint64_t (*EntryU64_5)(uint64_t, uint64_t, uint64_t, uint64_t, 193 uint64_t); 194 typedef uint64_t (*EntryU64_6)(uint64_t, uint64_t, uint64_t, uint64_t, 195 uint64_t, uint64_t); 196 typedef uint64_t (*EntryU64_7)(uint64_t, uint64_t, uint64_t, uint64_t, 197 uint64_t, uint64_t, uint64_t); 198 typedef uint64_t (*EntryU64_8)(uint64_t, uint64_t, uint64_t, uint64_t, 199 uint64_t, uint64_t, uint64_t, uint64_t); 200 int ret = 0; 201 switch (s->entry_kind) { 202 case KIT_ENTRY_INT_ARGV: 203 ret = ((EntryIntArgv)s->entry)(s->entry_argc, s->entry_argv); 204 break; 205 case KIT_ENTRY_U64: 206 switch (s->entry_u64_nargs) { 207 case 0: 208 s->entry_u64_ret = ((EntryU64_0)s->entry)(); 209 break; 210 case 1: 211 s->entry_u64_ret = ((EntryU64_1)s->entry)(s->entry_u64_args[0]); 212 break; 213 case 2: 214 s->entry_u64_ret = ((EntryU64_2)s->entry)(s->entry_u64_args[0], 215 s->entry_u64_args[1]); 216 break; 217 case 3: 218 s->entry_u64_ret = ((EntryU64_3)s->entry)( 219 s->entry_u64_args[0], s->entry_u64_args[1], s->entry_u64_args[2]); 220 break; 221 case 4: 222 s->entry_u64_ret = ((EntryU64_4)s->entry)( 223 s->entry_u64_args[0], s->entry_u64_args[1], s->entry_u64_args[2], 224 s->entry_u64_args[3]); 225 break; 226 case 5: 227 s->entry_u64_ret = ((EntryU64_5)s->entry)( 228 s->entry_u64_args[0], s->entry_u64_args[1], s->entry_u64_args[2], 229 s->entry_u64_args[3], s->entry_u64_args[4]); 230 break; 231 case 6: 232 s->entry_u64_ret = ((EntryU64_6)s->entry)( 233 s->entry_u64_args[0], s->entry_u64_args[1], s->entry_u64_args[2], 234 s->entry_u64_args[3], s->entry_u64_args[4], s->entry_u64_args[5]); 235 break; 236 case 7: 237 s->entry_u64_ret = ((EntryU64_7)s->entry)( 238 s->entry_u64_args[0], s->entry_u64_args[1], s->entry_u64_args[2], 239 s->entry_u64_args[3], s->entry_u64_args[4], s->entry_u64_args[5], 240 s->entry_u64_args[6]); 241 break; 242 case 8: 243 s->entry_u64_ret = ((EntryU64_8)s->entry)( 244 s->entry_u64_args[0], s->entry_u64_args[1], s->entry_u64_args[2], 245 s->entry_u64_args[3], s->entry_u64_args[4], s->entry_u64_args[5], 246 s->entry_u64_args[6], s->entry_u64_args[7]); 247 break; 248 default: 249 s->entry_u64_ret = 0; 250 break; 251 } 252 ret = (int)s->entry_u64_ret; 253 break; 254 } 255 memset(&s->stop, 0, sizeof(s->stop)); 256 s->stop.kind = KIT_STOP_EXIT; 257 s->stop.reason = KIT_STOP_REASON_EXIT; 258 s->stop.exit_code = ret; 259 } 260 261 static void worker_main(void* arg) { 262 KitJitSession* s = (KitJitSession*)arg; 263 for (;;) { 264 s->os->event_wait(s->os->user, s->ev_resume); 265 s->os->event_reset(s->os->user, s->ev_resume); 266 if (s->worker_should_exit) return; 267 if (s->state == DBG_STATE_RUNNING && s->entry) { 268 int aborted = 0; 269 if (s->os->call_with_catch) { 270 aborted = s->os->call_with_catch(s->os->user, worker_run_entry, s); 271 } else { 272 worker_run_entry(s); 273 } 274 if (aborted) { 275 memset(&s->stop, 0, sizeof(s->stop)); 276 s->stop.kind = KIT_STOP_EXIT; 277 s->stop.reason = KIT_STOP_REASON_EXIT; 278 s->stop.exit_code = -1; 279 } 280 s->state = DBG_STATE_EXITED; 281 s->entry = NULL; 282 s->os->event_signal(s->os->user, s->ev_stop); 283 } 284 } 285 } 286 287 /* ---- public entries ------------------------------------------------- */ 288 289 KitStatus kit_jit_session_new(KitJit* jit, const KitDbgHost* host, 290 KitJitSession** out) { 291 KitJitSession* s; 292 Compiler* c; 293 Heap* heap; 294 const KitDbgOs* os; 295 const ArchImpl* arch; 296 KitDbgSignalOps ops; 297 KitStatus st; 298 299 if (out) *out = NULL; 300 if (!jit || !host || !host->os || !out) return KIT_INVALID; 301 c = kit_jit_compiler(jit); 302 if (!c || !c->ctx) return KIT_INVALID; 303 os = host->os; 304 if (!os->thread_start || !os->thread_join || !os->event_new || 305 !os->event_wait || !os->event_signal || !os->event_reset || 306 !os->event_free || !os->signals_install || !os->signals_uninstall || 307 !os->code_write_begin || !os->code_write_end || !os->guarded_copy) { 308 return KIT_INVALID; 309 } 310 arch = arch_lookup(kit_jit_image_arch(jit)); 311 if (!arch || !arch->dbg || !arch->dbg->breakpoint_patch) { 312 return KIT_UNSUPPORTED; 313 } 314 315 heap = c->ctx->heap; 316 s = (KitJitSession*)heap->alloc(heap, sizeof(*s), _Alignof(KitJitSession)); 317 if (!s) return KIT_NOMEM; 318 memset(s, 0, sizeof(*s)); 319 s->jit = jit; 320 s->c = c; 321 s->heap = heap; 322 s->os = os; 323 /* Borrow execmem from the JIT image; displaced-step scratch is the only 324 * consumer. May be NULL if the JIT was constructed without one, in 325 * which case STEP_INSN paths will surface KIT_UNSUPPORTED. */ 326 s->execmem = kit_jit_image_execmem(jit); 327 s->arch_impl = arch; 328 s->arch_dbg = arch->dbg; 329 s->state = DBG_STATE_IDLE; 330 331 st = os->event_new(os->user, &s->ev_resume); 332 if (st != KIT_OK) { 333 heap->free(heap, s, sizeof(*s)); 334 return st; 335 } 336 st = os->event_new(os->user, &s->ev_stop); 337 if (st != KIT_OK) { 338 os->event_free(os->user, s->ev_resume); 339 heap->free(heap, s, sizeof(*s)); 340 return st; 341 } 342 343 dbg_bp_init(s); 344 345 ops.on_fault = on_fault; 346 st = os->signals_install(os->user, &ops, s); 347 if (st != KIT_OK) { 348 os->event_free(os->user, s->ev_resume); 349 os->event_free(os->user, s->ev_stop); 350 heap->free(heap, s, sizeof(*s)); 351 return st; 352 } 353 354 st = os->thread_start(os->user, worker_main, s, &s->worker); 355 if (st != KIT_OK) { 356 os->signals_uninstall(os->user); 357 os->event_free(os->user, s->ev_resume); 358 os->event_free(os->user, s->ev_stop); 359 heap->free(heap, s, sizeof(*s)); 360 return st; 361 } 362 s->worker_alive = 1; 363 *out = s; 364 return KIT_OK; 365 } 366 367 KitStatus kit_jit_session_attach_dwarf(KitJitSession* s, KitDebugInfo* dw) { 368 if (!s) return KIT_INVALID; 369 s->dwarf = dw; 370 return KIT_OK; 371 } 372 373 KitStatus dbg_session_signal_resume(KitJitSession* s) { 374 if (!s) return KIT_INVALID; 375 s->state = DBG_STATE_RUNNING; 376 return s->os->event_signal(s->os->user, s->ev_resume); 377 } 378 379 KitStatus dbg_session_wait_stop(KitJitSession* s) { 380 KitStatus st; 381 if (!s) return KIT_INVALID; 382 st = s->os->event_wait(s->os->user, s->ev_stop); 383 if (st != KIT_OK) return st; 384 return s->os->event_reset(s->os->user, s->ev_stop); 385 } 386 387 void kit_jit_session_free(KitJitSession* s) { 388 if (!s) return; 389 /* If the worker is parked inside the signal handler (STOPPED), there is 390 * no clean way to unwind it without re-running the user's program to 391 * completion: the kernel will restart it at the trap PC and re-trap. 392 * The session is only torn down at process exit, so leak the worker and 393 * let the OS reap it. We skip event/signal/heap teardown for the same 394 * reason — the worker may still touch them before _exit. */ 395 if (s->worker_alive && s->state == DBG_STATE_STOPPED) return; 396 397 s->worker_should_exit = 1; 398 if (s->worker_alive) { 399 s->os->event_signal(s->os->user, s->ev_resume); 400 s->os->thread_join(s->os->user, s->worker); 401 s->worker_alive = 0; 402 } 403 s->os->signals_uninstall(s->os->user); 404 dbg_displaced_fini(s); 405 dbg_bp_fini(s); 406 if (s->ev_resume) s->os->event_free(s->os->user, s->ev_resume); 407 if (s->ev_stop) s->os->event_free(s->os->user, s->ev_stop); 408 s->heap->free(s->heap, s, sizeof(*s)); 409 } 410 411 KitStatus kit_jit_session_call(KitJitSession* s, void* entry, KitEntryKind kind, 412 int argc, char** argv, KitStopInfo* stop_out) { 413 if (!s || !entry) return KIT_INVALID; 414 if (s->state == DBG_STATE_RUNNING || s->state == DBG_STATE_STOPPED) 415 return KIT_INVALID; 416 s->entry = entry; 417 s->entry_kind = kind; 418 s->entry_argc = argc; 419 s->entry_argv = argv; 420 s->state = DBG_STATE_RUNNING; 421 s->pending_mode = KIT_RESUME_CONTINUE; 422 s->pending_has_pc = 0; 423 s->pending_step_pending = 0; 424 s->os->event_reset(s->os->user, s->ev_stop); 425 s->os->event_signal(s->os->user, s->ev_resume); 426 s->os->event_wait(s->os->user, s->ev_stop); 427 s->os->event_reset(s->os->user, s->ev_stop); 428 if (stop_out) *stop_out = s->stop; 429 return KIT_OK; 430 } 431 432 KitStatus kit_jit_session_call_u64(KitJitSession* s, void* entry, 433 const uint64_t* args, uint32_t nargs, 434 uint64_t* ret_out, KitStopInfo* stop_out) { 435 uint32_t i; 436 KitStatus st; 437 if (!s || !entry || nargs > 8u) return KIT_INVALID; 438 if (s->state == DBG_STATE_RUNNING || s->state == DBG_STATE_STOPPED) 439 return KIT_INVALID; 440 for (i = 0; i < nargs; ++i) s->entry_u64_args[i] = args ? args[i] : 0; 441 for (; i < 8u; ++i) s->entry_u64_args[i] = 0; 442 s->entry_u64_nargs = nargs; 443 s->entry_u64_ret = 0; 444 st = kit_jit_session_call(s, entry, KIT_ENTRY_U64, 0, NULL, stop_out); 445 if (st == KIT_OK && ret_out) *ret_out = s->entry_u64_ret; 446 return st; 447 } 448 449 KitStatus kit_jit_session_resume(KitJitSession* s, KitResumeMode mode, 450 KitStopInfo* stop_out) { 451 if (!s) return KIT_INVALID; 452 if (s->state == DBG_STATE_EXITED) return KIT_INVALID; 453 if (s->state != DBG_STATE_STOPPED) return KIT_INVALID; 454 455 s->pending_mode = mode; 456 s->pending_has_pc = 0; 457 s->pending_step_pending = 0; 458 s->pending_done = 0; 459 460 /* For CONTINUE-over-bp we use displaced step to skip the patched insn 461 * and rely on the on_fault handler's pending_step_pending fast-path to 462 * not surface that step's trap to the REPL. */ 463 if (mode == KIT_RESUME_CONTINUE && 464 dbg_bp_lookup_index(s, s->stop.regs.pc) != 0) { 465 s->pending_step_pending = 1; 466 if (dbg_step_resume(s, KIT_RESUME_STEP_INSN) != KIT_OK) { 467 s->pending_step_pending = 0; 468 return KIT_ERR; 469 } 470 } else if (mode != KIT_RESUME_CONTINUE) { 471 KitStatus st = dbg_step_resume(s, mode); 472 if (st != KIT_OK) return st; 473 } 474 475 if (!s->pending_done) { 476 s->state = DBG_STATE_RUNNING; 477 s->os->event_signal(s->os->user, s->ev_resume); 478 s->os->event_wait(s->os->user, s->ev_stop); 479 s->os->event_reset(s->os->user, s->ev_stop); 480 } 481 s->pending_done = 0; 482 if (stop_out) *stop_out = s->stop; 483 return KIT_OK; 484 } 485 486 KitStatus kit_jit_session_interrupt(KitJitSession* s) { 487 if (!s) return KIT_INVALID; 488 if (s->state != DBG_STATE_RUNNING) return KIT_INVALID; 489 if (!s->os->thread_interrupt) return KIT_UNSUPPORTED; 490 return s->os->thread_interrupt(s->os->user, s->worker); 491 } 492 493 KitStatus kit_jit_session_read_mem(KitJitSession* s, uint64_t addr, void* dst, 494 size_t n) { 495 if (!s) return KIT_INVALID; 496 if (s->state != DBG_STATE_STOPPED && s->state != DBG_STATE_EXITED) 497 return KIT_INVALID; 498 return dbg_mem_read(s, addr, dst, n); 499 } 500 501 KitStatus kit_jit_session_write_mem(KitJitSession* s, uint64_t addr, 502 const void* src, size_t n) { 503 if (!s) return KIT_INVALID; 504 if (s->state != DBG_STATE_STOPPED && s->state != DBG_STATE_EXITED) 505 return KIT_INVALID; 506 return dbg_mem_write(s, addr, src, n); 507 } 508 509 KitStatus kit_jit_session_get_regs(KitJitSession* s, KitUnwindFrame* out) { 510 if (!s || !out) return KIT_INVALID; 511 if (s->state != DBG_STATE_STOPPED) return KIT_INVALID; 512 *out = s->stop.regs; 513 return KIT_OK; 514 } 515 516 KitStatus kit_jit_session_set_regs(KitJitSession* s, const KitUnwindFrame* in) { 517 if (!s || !in) return KIT_INVALID; 518 if (s->state != DBG_STATE_STOPPED) return KIT_INVALID; 519 if (!kit_jit_image_contains(s->jit, in->pc)) return KIT_INVALID; 520 s->stop.regs = *in; 521 return KIT_OK; 522 } 523 524 KitStatus kit_jit_session_breakpoint_set(KitJitSession* s, uint64_t addr, 525 uint32_t* bp_id_out) { 526 if (!s) return KIT_INVALID; 527 return dbg_bp_set(s, addr, bp_id_out); 528 } 529 530 KitStatus kit_jit_session_breakpoint_clear(KitJitSession* s, uint32_t bp_id) { 531 if (!s) return KIT_INVALID; 532 return dbg_bp_clear(s, bp_id); 533 } 534 535 KitStatus kit_jit_session_breakpoint_set_spec(KitJitSession* s, 536 const KitBreakpointSpec* spec, 537 uint32_t* bp_id_out) { 538 if (!s || !spec) return KIT_INVALID; 539 return dbg_bp_set_spec(s, spec, bp_id_out); 540 }