dbg.h (7154B)
1 #ifndef KIT_DBG_INTERNAL_H 2 #define KIT_DBG_INTERNAL_H 3 4 /* Internal contracts for src/dbg/. The public KitJitSession entries are 5 * defined in session.c on top of these primitives; bp.c, step.c, mem.c, and 6 * displaced.c own the target-independent session machinery. Per-arch debug 7 * behavior is reached through ArchImpl.dbg. */ 8 9 #include <kit/dbg.h> 10 #include <kit/dwarf.h> 11 #include <kit/jit.h> 12 13 #include "arch/arch.h" 14 #include "core/core.h" 15 16 #define DBG_SCRATCH_PAGE_SIZE 4096u 17 #define DBG_BP_MAX_INSN_LEN ARCH_DBG_MAX_TRAP_BYTES 18 #define DBG_BP_ID_INTERNAL_BASE 0x80000000u 19 #define DBG_DISPLACED_SLOT_BYTES 64u 20 21 /* Bridge into link_jit.c so the session can validate addresses and pick the 22 * arch lifter without dragging LinkImage internals into src/dbg/. */ 23 int kit_jit_image_contains(KitJit*, uint64_t runtime_addr); 24 KitArchKind kit_jit_image_arch(KitJit*); 25 Compiler* kit_jit_compiler(KitJit*); 26 /* The JIT image owns the execmem used to map its code/data; the debugger's 27 * displaced-step scratch is allocated from the same pool. Implemented in 28 * src/link/link_jit.c. */ 29 const KitExecMem* kit_jit_image_execmem(KitJit*); 30 31 /* ---- breakpoint table ------------------------------------------------ */ 32 33 typedef struct DbgBp { 34 uint64_t addr; 35 u8 saved[DBG_BP_MAX_INSN_LEN]; 36 u32 saved_len; 37 u32 refcount; 38 u32 user_id; /* public handle returned to caller; 0 = unused slot */ 39 u8 enabled; 40 u8 internal; /* set when the entry was armed by step.c */ 41 u16 pad; 42 uint64_t hit_count; 43 uint64_t skip_count; 44 uint64_t max_hits; 45 int (*condition)(void*, const KitUnwindFrame*); 46 void* condition_user; 47 } DbgBp; 48 49 typedef struct DbgBpTable { 50 DbgBp* slots; 51 u32 cap; 52 u32 count; 53 u32 next_user_id; /* monotonic, starts at 1 */ 54 u32 next_internal_id; /* monotonic, starts at DBG_BP_ID_INTERNAL_BASE */ 55 } DbgBpTable; 56 57 struct KitJitSession; /* fwd */ 58 59 void dbg_bp_init(struct KitJitSession*); 60 void dbg_bp_fini(struct KitJitSession*); 61 62 /* set/clear with the user-facing handle space. The internal variants are 63 * used by step.c for one-shot temporaries. */ 64 KitStatus dbg_bp_set(struct KitJitSession*, uint64_t addr, u32* id_out); 65 KitStatus dbg_bp_set_spec(struct KitJitSession*, const KitBreakpointSpec*, 66 u32* id_out); 67 KitStatus dbg_bp_set_internal(struct KitJitSession*, uint64_t addr, 68 u32* id_out); 69 KitStatus dbg_bp_clear(struct KitJitSession*, u32 id); 70 71 /* Lookup at a PC. Returns the slot index + 1 (so 0 means "not patched"); 72 * the caller uses dbg_bp_at_index to fetch the entry. */ 73 u32 dbg_bp_lookup_index(struct KitJitSession*, uint64_t addr); 74 DbgBp* dbg_bp_at_index(struct KitJitSession*, u32 idx); 75 76 /* Memory-read fixup: if [addr, addr+n) overlaps any patched bp, write the 77 * original bytes back into `buf` at the right offsets. */ 78 void dbg_bp_unpatch_read(struct KitJitSession*, uint64_t addr, void* buf, 79 size_t n); 80 81 /* ---- memory --------------------------------------------------------- */ 82 KitStatus dbg_mem_read(struct KitJitSession*, uint64_t addr, void* dst, 83 size_t n); 84 KitStatus dbg_mem_write(struct KitJitSession*, uint64_t addr, const void* src, 85 size_t n); 86 87 /* ---- displaced step ------------------------------------------------- */ 88 /* The session owns a single executable scratch region. The per-arch debug 89 * vtable writes a fixed-up copy of the original insn plus a trap sentinel 90 * into it; the worker is then resumed with PC pointing at the scratch entry. 91 * The sentinel is patched into the bp table so the fault classifier recognizes 92 * it and translates the stop back into MODE_DONE. */ 93 typedef struct DbgDisplaced { 94 KitExecMemRegion region; 95 int valid; 96 uint64_t orig_pc; /* original user PC of the insn being stepped */ 97 uint64_t return_pc; /* PC the shim's trap sentinel lives at */ 98 uint64_t fallthrough_pc; 99 uint32_t internal_bp; /* id of the one-shot bp at return_pc */ 100 } DbgDisplaced; 101 102 KitStatus dbg_displaced_init(struct KitJitSession*); 103 void dbg_displaced_fini(struct KitJitSession*); 104 105 /* Prepare an out-of-line single-step at `insn_pc`. Sets *new_pc to the 106 * scratch entry the worker should branch to; arms an internal bp on the 107 * shim's trap sentinel. Returns KIT_OK on success, KIT_UNSUPPORTED if the 108 * insn family is not supported. */ 109 KitStatus dbg_displaced_prepare(struct KitJitSession*, uint64_t insn_pc, 110 uint64_t* new_pc); 111 /* After the shim trap fires, finalize: clear the internal bp, restore the 112 * user-visible PC to the decoded fallthrough PC (or leave a branch target 113 * captured by the shim alone). */ 114 void dbg_displaced_finalize(struct KitJitSession*); 115 KitStatus dbg_arch_decode_insn(struct KitJitSession*, uint64_t pc, 116 ArchDbgInsn* out); 117 118 /* ---- step state machine --------------------------------------------- */ 119 KitStatus dbg_step_resume(struct KitJitSession*, KitResumeMode mode); 120 121 /* ---- session state -------------------------------------------------- */ 122 typedef enum DbgSessionState { 123 DBG_STATE_IDLE = 0, /* no worker call in flight */ 124 DBG_STATE_RUNNING = 1, /* worker has been signaled to run */ 125 DBG_STATE_STOPPED = 2, /* worker is parked, REPL may inspect */ 126 DBG_STATE_EXITED = 3, /* worker entry returned */ 127 } DbgSessionState; 128 129 struct KitJitSession { 130 KitJit* jit; 131 Compiler* c; 132 Heap* heap; 133 const KitDbgOs* os; 134 /* Borrowed from the KitJit. Used to allocate displaced-step scratch 135 * (and only that — the JIT image itself was already mapped at link 136 * time). NULL if the host's JitHost did not supply an execmem. */ 137 const KitExecMem* execmem; 138 const ArchImpl* arch_impl; 139 const ArchDbgOps* arch_dbg; 140 141 /* worker thread + event handshake */ 142 void* worker; 143 void* ev_resume; 144 void* ev_stop; 145 DbgSessionState state; 146 u8 interrupt_pending; 147 u8 worker_alive; 148 u8 worker_should_exit; 149 u8 pad0; 150 151 /* entry args set by _call */ 152 void* entry; 153 KitEntryKind entry_kind; 154 int entry_argc; 155 char** entry_argv; 156 int entry_ret; 157 uint64_t entry_u64_args[8]; 158 uint32_t entry_u64_nargs; 159 uint64_t entry_u64_ret; 160 161 /* current stop slot (filled by the fault handler / worker exit path) */ 162 KitStopInfo stop; 163 KitUnwindFrame regs_scratch; /* used by signal handler */ 164 165 /* pending resume directive (set by REPL before signaling ev_resume) */ 166 KitResumeMode pending_mode; 167 uint64_t pending_pc_override; /* nonzero → write before resume */ 168 u8 pending_has_pc; 169 u8 pending_step_pending; /* MODE_STEP_INSN in progress via displaced */ 170 u8 pad1[2]; 171 172 /* breakpoint table */ 173 DbgBpTable bps; 174 175 /* displaced-step scratch */ 176 DbgDisplaced displaced; 177 178 /* optional DWARF binding (caller-owned; needed for source-level steps) */ 179 KitDebugInfo* dwarf; 180 181 /* set by dbg_step_resume when it has already driven the worker through 182 * its own signal/wait cycles; tells kit_jit_session_resume not to 183 * issue another resume. */ 184 u8 pending_done; 185 u8 pad2[3]; 186 }; 187 188 /* internal helpers shared between session.c and step.c */ 189 KitStatus dbg_session_wait_stop(struct KitJitSession*); 190 KitStatus dbg_session_signal_resume(struct KitJitSession*); 191 192 #endif