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