dbg.c (11917B)
1 /* x86_64 debug support for software breakpoints and displaced stepping. 2 * 3 * The decoder here is intentionally small: it covers the encodings kit's 4 * x64 backend emits plus common branch forms. It measures one instruction, 5 * finds any RIP-relative disp32 operand, and identifies rel8/rel32 control 6 * transfers that need their displacement re-based for the scratch slot. */ 7 8 #include <string.h> 9 10 #include "arch/arch.h" 11 #include "core/bytes.h" 12 13 #define X64_INT3_BYTE 0xCCu 14 #define X64_JMP_REL32_BYTE 0xE9u 15 #define X64_CALL_REL32_BYTE 0xE8u 16 #define X64_JCC_SHORT_BASE 0x70u 17 #define X64_JCC_NEAR_BASE0 0x0Fu 18 #define X64_JCC_NEAR_BASE1 0x80u 19 20 typedef enum X64DbgPcRelKind { 21 X64_DBG_PCREL_NONE, 22 X64_DBG_PCREL_RIP_DISP32, 23 X64_DBG_PCREL_REL8, 24 X64_DBG_PCREL_REL32, 25 } X64DbgPcRelKind; 26 27 typedef struct X64DbgDecode { 28 u32 len; 29 u32 opc_off; 30 u32 disp_off; 31 u8 disp_size; 32 u8 pc_rel_kind; 33 u8 is_call; 34 u8 pad; 35 i64 disp; 36 } X64DbgDecode; 37 38 static int fits_i32(i64 v) { 39 return v >= (i64)INT32_MIN && v <= (i64)INT32_MAX; 40 } 41 static int fits_i8(i64 v) { return v >= -128 && v <= 127; } 42 43 static int is_legacy_prefix(u8 b) { 44 switch (b) { 45 case 0x26: 46 case 0x2e: 47 case 0x36: 48 case 0x3e: 49 case 0x64: 50 case 0x65: 51 case 0x66: 52 case 0x67: 53 case 0xf0: 54 case 0xf2: 55 case 0xf3: 56 return 1; 57 default: 58 return 0; 59 } 60 } 61 62 static u32 x64_dbg_prefix_len(const u8* bytes, u32 len) { 63 u32 off = 0; 64 while (off < len && is_legacy_prefix(bytes[off])) ++off; 65 if (off < len && bytes[off] >= 0x40u && bytes[off] <= 0x4fu) ++off; 66 return off; 67 } 68 69 static int read_i8(const u8* bytes, u32 len, u32 off, i64* out) { 70 if (off >= len) return 0; 71 *out = (i64)(i8)bytes[off]; 72 return 1; 73 } 74 75 static int read_i32(const u8* bytes, u32 len, u32 off, i64* out) { 76 if (off + 4u > len) return 0; 77 *out = (i64)(i32)rd_u32_le(bytes + off); 78 return 1; 79 } 80 81 static int x64_dbg_modrm_len(const u8* bytes, u32 len, u32 modrm_off, 82 u32* total_out, u32* rip_disp_off) { 83 u8 mr; 84 u32 mod; 85 u32 rm; 86 u32 off; 87 if (modrm_off >= len) return 0; 88 mr = bytes[modrm_off]; 89 mod = (mr >> 6) & 3u; 90 rm = mr & 7u; 91 off = modrm_off + 1u; 92 *rip_disp_off = 0; 93 94 if (mod != 3u && rm == 4u) { 95 u8 sib; 96 if (off >= len) return 0; 97 sib = bytes[off++]; 98 if (mod == 0u && (sib & 7u) == 5u) { 99 if (off + 4u > len) return 0; 100 off += 4u; 101 } 102 } else if (mod == 0u && rm == 5u) { 103 if (off + 4u > len) return 0; 104 *rip_disp_off = off; 105 off += 4u; 106 } else if (mod == 1u) { 107 if (off + 1u > len) return 0; 108 off += 1u; 109 } else if (mod == 2u) { 110 if (off + 4u > len) return 0; 111 off += 4u; 112 } 113 114 *total_out = off; 115 return 1; 116 } 117 118 static int onebyte_has_modrm(u8 op) { 119 switch (op) { 120 case 0x01: 121 case 0x09: 122 case 0x21: 123 case 0x29: 124 case 0x31: 125 case 0x39: 126 case 0x85: 127 case 0x87: 128 case 0x88: 129 case 0x89: 130 case 0x8b: 131 case 0x8d: 132 case 0x63: 133 case 0x81: 134 case 0x83: 135 case 0xc1: 136 case 0xd3: 137 case 0xf7: 138 case 0xff: 139 case 0x69: 140 case 0x6b: 141 return 1; 142 default: 143 return 0; 144 } 145 } 146 147 static u32 onebyte_imm_len(u8 op, const u8* bytes, u32 len, u32 modrm_off) { 148 (void)bytes; 149 (void)len; 150 (void)modrm_off; 151 switch (op) { 152 case 0x83: 153 case 0xc1: 154 case 0x6b: 155 return 1; 156 case 0x81: 157 case 0x69: 158 return 4; 159 default: 160 return 0; 161 } 162 } 163 164 static int twobyte_has_modrm(u8 op) { 165 if (op >= 0x40u && op <= 0x4fu) return 1; /* CMOVcc */ 166 if (op >= 0x90u && op <= 0x9fu) return 1; /* SETcc */ 167 switch (op) { 168 case 0x10: 169 case 0x11: 170 case 0x1f: 171 case 0x2a: 172 case 0x2c: 173 case 0x2e: 174 case 0x58: 175 case 0x59: 176 case 0x5a: 177 case 0x5c: 178 case 0x5e: 179 case 0xaf: 180 case 0xb1: 181 case 0xb6: 182 case 0xb7: 183 case 0xb8: 184 case 0xbc: 185 case 0xbd: 186 case 0xbe: 187 case 0xbf: 188 case 0xc1: 189 return 1; 190 default: 191 return 0; 192 } 193 } 194 195 static KitStatus x64_dbg_measure(const u8* bytes, u32 len, u64 pc, 196 X64DbgDecode* out) { 197 u32 off; 198 u8 op; 199 if (!bytes || !out) return KIT_INVALID; 200 memset(out, 0, sizeof(*out)); 201 off = x64_dbg_prefix_len(bytes, len); 202 if (off >= len) return KIT_UNSUPPORTED; 203 out->opc_off = off; 204 op = bytes[off]; 205 206 if (op == X64_CALL_REL32_BYTE || op == X64_JMP_REL32_BYTE) { 207 if (!read_i32(bytes, len, off + 1u, &out->disp)) return KIT_UNSUPPORTED; 208 out->len = off + 5u; 209 out->disp_off = off + 1u; 210 out->disp_size = 4u; 211 out->pc_rel_kind = X64_DBG_PCREL_REL32; 212 out->is_call = (op == X64_CALL_REL32_BYTE); 213 return KIT_OK; 214 } 215 if (op == 0xebu || (op >= 0x70u && op <= 0x7fu) || 216 (op >= 0xe0u && op <= 0xe3u)) { 217 if (!read_i8(bytes, len, off + 1u, &out->disp)) return KIT_UNSUPPORTED; 218 out->len = off + 2u; 219 out->disp_off = off + 1u; 220 out->disp_size = 1u; 221 out->pc_rel_kind = X64_DBG_PCREL_REL8; 222 return KIT_OK; 223 } 224 if (op >= 0x50u && op <= 0x5fu) { 225 out->len = off + 1u; 226 return KIT_OK; 227 } 228 if (op >= 0xb8u && op <= 0xbfu) { 229 u32 imm = 4u; 230 for (u32 i = 0; i < off; ++i) { 231 if (bytes[i] >= 0x48u && bytes[i] <= 0x4fu) imm = 8u; 232 } 233 if (off + 1u + imm > len) return KIT_UNSUPPORTED; 234 out->len = off + 1u + imm; 235 return KIT_OK; 236 } 237 switch (op) { 238 case 0x90: 239 case 0xc3: 240 case 0xc9: 241 case 0x99: 242 out->len = off + 1u; 243 return KIT_OK; 244 default: 245 break; 246 } 247 248 if (op == 0x0fu) { 249 u8 op2; 250 if (off + 1u >= len) return KIT_UNSUPPORTED; 251 op2 = bytes[off + 1u]; 252 if (op2 >= 0x80u && op2 <= 0x8fu) { 253 if (!read_i32(bytes, len, off + 2u, &out->disp)) return KIT_UNSUPPORTED; 254 out->len = off + 6u; 255 out->disp_off = off + 2u; 256 out->disp_size = 4u; 257 out->pc_rel_kind = X64_DBG_PCREL_REL32; 258 return KIT_OK; 259 } 260 if (op2 == 0x0bu || (op2 >= 0xc8u && op2 <= 0xcfu)) { 261 out->len = off + 2u; 262 return KIT_OK; 263 } 264 if (op2 == 0xaeu && off + 2u < len && bytes[off + 2u] == 0xf0u) { 265 out->len = off + 3u; 266 return KIT_OK; 267 } 268 if (twobyte_has_modrm(op2)) { 269 u32 end = 0; 270 u32 rip = 0; 271 if (!x64_dbg_modrm_len(bytes, len, off + 2u, &end, &rip)) 272 return KIT_UNSUPPORTED; 273 out->len = end; 274 if (rip) { 275 if (!read_i32(bytes, len, rip, &out->disp)) return KIT_UNSUPPORTED; 276 out->disp_off = rip; 277 out->disp_size = 4u; 278 out->pc_rel_kind = X64_DBG_PCREL_RIP_DISP32; 279 } 280 return KIT_OK; 281 } 282 return KIT_UNSUPPORTED; 283 } 284 285 if (onebyte_has_modrm(op)) { 286 u32 end = 0; 287 u32 rip = 0; 288 u32 imm; 289 if (!x64_dbg_modrm_len(bytes, len, off + 1u, &end, &rip)) 290 return KIT_UNSUPPORTED; 291 imm = onebyte_imm_len(op, bytes, len, off + 1u); 292 if (end + imm > len) return KIT_UNSUPPORTED; 293 out->len = end + imm; 294 if (rip) { 295 if (!read_i32(bytes, len, rip, &out->disp)) return KIT_UNSUPPORTED; 296 out->disp_off = rip; 297 out->disp_size = 4u; 298 out->pc_rel_kind = X64_DBG_PCREL_RIP_DISP32; 299 } 300 if (op == 0xffu && off + 1u < len) { 301 u8 sub = (bytes[off + 1u] >> 3) & 7u; 302 out->is_call = (sub == 2u); 303 } 304 (void)pc; 305 return KIT_OK; 306 } 307 308 return KIT_UNSUPPORTED; 309 } 310 311 static KitStatus x64_dbg_breakpoint_patch(u8* out, u32 cap, u32* len_out) { 312 if (!out || !len_out) return KIT_INVALID; 313 if (cap < 1u) return KIT_INVALID; 314 out[0] = X64_INT3_BYTE; 315 *len_out = 1u; 316 return KIT_OK; 317 } 318 319 static u64 x64_dbg_breakpoint_addr_from_fault_pc(u64 fault_pc) { 320 return fault_pc ? fault_pc - 1u : 0u; 321 } 322 323 static KitStatus x64_dbg_decode_insn(const u8* bytes, u32 len, u64 pc, 324 ArchDbgInsn* out) { 325 X64DbgDecode d; 326 KitStatus st = x64_dbg_measure(bytes, len, pc, &d); 327 if (st != KIT_OK) return st; 328 if (d.len == 0 || d.len > ARCH_DBG_MAX_INSN_BYTES) return KIT_UNSUPPORTED; 329 memset(out, 0, sizeof(*out)); 330 out->pc = pc; 331 out->len = d.len; 332 memcpy(out->bytes, bytes, d.len); 333 return KIT_OK; 334 } 335 336 static KitStatus x64_dbg_build_displaced_shim( 337 const ArchDbgInsn* insn, void* scratch_write, u64 scratch_runtime, 338 u32 scratch_cap, u32* sentinel_off, u64* fallthrough_pc) { 339 X64DbgDecode d; 340 u8* w = (u8*)scratch_write; 341 KitStatus st; 342 u8 op; 343 if (!insn || !scratch_write || !sentinel_off || !fallthrough_pc) 344 return KIT_INVALID; 345 st = x64_dbg_measure(insn->bytes, insn->len, insn->pc, &d); 346 if (st != KIT_OK) return st; 347 if (d.len != insn->len) return KIT_UNSUPPORTED; 348 if (insn->len + 1u > scratch_cap) return KIT_UNSUPPORTED; 349 350 op = insn->bytes[d.opc_off]; 351 if ((op == 0xe9u || op == 0xebu) && (d.pc_rel_kind == X64_DBG_PCREL_REL32 || 352 d.pc_rel_kind == X64_DBG_PCREL_REL8)) { 353 *fallthrough_pc = (u64)((i64)(insn->pc + insn->len) + d.disp); 354 w[0] = X64_INT3_BYTE; 355 *sentinel_off = 0; 356 return KIT_OK; 357 } 358 359 memcpy(w, insn->bytes, insn->len); 360 *fallthrough_pc = insn->pc + insn->len; 361 362 if (d.pc_rel_kind == X64_DBG_PCREL_RIP_DISP32) { 363 i64 target = (i64)(insn->pc + insn->len) + d.disp; 364 i64 nd = target - (i64)(scratch_runtime + insn->len); 365 if (!fits_i32(nd)) return KIT_UNSUPPORTED; 366 wr_u32_le(w + d.disp_off, (u32)(i32)nd); 367 } else if (d.pc_rel_kind == X64_DBG_PCREL_REL32) { 368 i64 target = (i64)(insn->pc + insn->len) + d.disp; 369 i64 nd = target - (i64)(scratch_runtime + insn->len); 370 if (!fits_i32(nd)) return KIT_UNSUPPORTED; 371 wr_u32_le(w + d.disp_off, (u32)(i32)nd); 372 } else if (d.pc_rel_kind == X64_DBG_PCREL_REL8) { 373 i64 target = (i64)(insn->pc + insn->len) + d.disp; 374 i64 nd = target - (i64)(scratch_runtime + insn->len); 375 if (!fits_i8(nd) && op >= 0x70u && op <= 0x7fu) { 376 if (scratch_cap < 7u) return KIT_UNSUPPORTED; 377 nd = target - (i64)(scratch_runtime + 6u); 378 if (!fits_i32(nd)) return KIT_UNSUPPORTED; 379 w[0] = 0x0fu; 380 w[1] = (u8)(0x80u | (op & 0x0fu)); 381 wr_u32_le(w + 2u, (u32)(i32)nd); 382 w[6] = X64_INT3_BYTE; 383 *sentinel_off = 6u; 384 return KIT_OK; 385 } 386 if (!fits_i8(nd)) return KIT_UNSUPPORTED; 387 w[d.disp_off] = (u8)(i8)nd; 388 } 389 390 w[insn->len] = X64_INT3_BYTE; 391 *sentinel_off = insn->len; 392 return KIT_OK; 393 } 394 395 static int x64_dbg_is_call(const ArchDbgInsn* insn) { 396 X64DbgDecode d; 397 if (!insn) return 0; 398 if (x64_dbg_measure(insn->bytes, insn->len, insn->pc, &d) != KIT_OK) return 0; 399 return d.is_call != 0; 400 } 401 402 static KitStatus x64_dbg_direct_call_target(const ArchDbgInsn* insn, 403 u64* target_out) { 404 X64DbgDecode d; 405 if (!insn || !target_out) return KIT_INVALID; 406 if (x64_dbg_measure(insn->bytes, insn->len, insn->pc, &d) != KIT_OK) 407 return KIT_UNSUPPORTED; 408 if (!d.is_call || d.pc_rel_kind != X64_DBG_PCREL_REL32) return KIT_NOT_FOUND; 409 *target_out = (u64)((i64)(insn->pc + insn->len) + d.disp); 410 return KIT_OK; 411 } 412 413 static KitStatus x64_dbg_direct_jump_target(const ArchDbgInsn* insn, 414 u64* target_out) { 415 X64DbgDecode d; 416 if (!insn || !target_out) return KIT_INVALID; 417 if (x64_dbg_measure(insn->bytes, insn->len, insn->pc, &d) != KIT_OK) 418 return KIT_UNSUPPORTED; 419 if (d.is_call || (d.pc_rel_kind != X64_DBG_PCREL_REL32 && 420 d.pc_rel_kind != X64_DBG_PCREL_REL8)) 421 return KIT_NOT_FOUND; 422 if (insn->bytes[d.opc_off] != X64_JMP_REL32_BYTE && 423 insn->bytes[d.opc_off] != 0xebu) 424 return KIT_NOT_FOUND; 425 *target_out = (u64)((i64)(insn->pc + insn->len) + d.disp); 426 return KIT_OK; 427 } 428 429 const ArchDbgOps x64_dbg_ops = { 430 .min_insn_len = 1u, 431 .max_insn_len = ARCH_DBG_MAX_INSN_BYTES, 432 .breakpoint_patch = x64_dbg_breakpoint_patch, 433 .breakpoint_addr_from_fault_pc = x64_dbg_breakpoint_addr_from_fault_pc, 434 .decode_insn = x64_dbg_decode_insn, 435 .build_displaced_shim = x64_dbg_build_displaced_shim, 436 .is_call = x64_dbg_is_call, 437 .direct_call_target = x64_dbg_direct_call_target, 438 .direct_jump_target = x64_dbg_direct_jump_target, 439 };