kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

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