disasm.c (16420B)
1 /* ARM32 (Thumb-2) disassembler — descriptor-table driven. 2 * 3 * Decodes a Thumb-2 instruction by reading the first half-word, picking the 4 * width (the Thumb-2 rule: hw1[15:11] in {0b11101, 0b11110, 0b11111} => 32-bit; 5 * else 16-bit), composing the decode word ((hw1<<16)|hw2 for 32-bit, hw in the 6 * low 16 bits for 16-bit), and linear-scanning arm32_insn_table for the first 7 * matching row. The matched format drives operand printing (arm32_print_ 8 * operands). Conditional branches (B<cond>.W T3, B<cond> T1) and the IT block 9 * carry their condition in the mnemonic suffix. 10 * 11 * IT-state: an `IT{x{y{z}}} cc` opens a block of up to 4 instructions whose 12 * condition is rendered as a mnemonic suffix (e.g. `moveq`). The block formatter 13 * tracks the remaining count + per-slot condition across a decode_block so the 14 * conditional renders show. A standalone decode_one (no preceding IT) renders 15 * the bare mnemonic. 16 * 17 * Unknown words fall back to `.inst <hex>` (32-bit) / `.hword <hex>` (16-bit). */ 18 19 #include "arch/arm32/disasm.h" 20 21 #include <string.h> 22 23 #include "arch/arm32/isa.h" 24 #include "core/heap.h" 25 #include "core/strbuf.h" 26 27 #define ARM32_DASM_MNEM_CAP 24u 28 #define ARM32_DASM_OPS_CAP 96u 29 #define ARM32_DASM_ANN_CAP 32u 30 #define ARM32_ENCODING_UNKNOWN 0xffffffffu 31 32 typedef struct Arm32InsnFormatter { 33 ArchInsnFormatter base; 34 Compiler* c; 35 Heap* heap; 36 /* IT-block state threaded across a decode_block render: `it_count` slots 37 * remain, each conditional on `it_cond[slot]`. Reset by decode_one. */ 38 u32 it_count; 39 u8 it_cond[4]; 40 char mnem_buf[ARM32_DASM_MNEM_CAP]; 41 char ops_buf[ARM32_DASM_OPS_CAP]; 42 char ann_buf[ARM32_DASM_ANN_CAP]; 43 StrBuf mnem; 44 StrBuf ops; 45 StrBuf ann; 46 } Arm32InsnFormatter; 47 48 typedef struct Arm32Disasm { 49 ArchDisasm base; 50 Arm32InsnFormatter fmt; 51 } Arm32Disasm; 52 53 static KitStatus arm32_format_insn(ArchInsnFormatter*, const KitDecodedInsn*, 54 KitInsn*); 55 static void arm32_formatter_destroy(ArchInsnFormatter*); 56 57 static u32 arm_read_u16_le(const u8* b) { return (u32)b[0] | ((u32)b[1] << 8); } 58 59 /* The Thumb-2 32-bit-instruction rule: the first half-word's bits[15:11] are 60 * one of 0b11101 / 0b11110 / 0b11111. */ 61 static int arm_is_32bit(u32 hw1) { 62 u32 top5 = (hw1 >> 11) & 0x1fu; 63 return top5 == 0x1du || top5 == 0x1eu || top5 == 0x1fu; 64 } 65 66 /* ---- operand-decode helpers (KitDecodedInsn.operands) ---- */ 67 static void arm_decop_none(KitDecodedOperand* o) { 68 memset(o, 0, sizeof(*o)); 69 o->kind = KIT_DECOP_NONE; 70 o->index_reg = REG_NONE; 71 } 72 static void arm_decop_reg(KitDecodedOperand* o, u32 reg) { 73 arm_decop_none(o); 74 o->kind = KIT_DECOP_REG; 75 o->width_bits = 32; 76 o->reg = reg; 77 } 78 static void arm_decop_imm(KitDecodedOperand* o, i64 imm) { 79 arm_decop_none(o); 80 o->kind = KIT_DECOP_IMM; 81 o->imm = imm; 82 } 83 static void arm_decop_mem(KitDecodedOperand* o, u32 base, i64 imm) { 84 arm_decop_none(o); 85 o->kind = KIT_DECOP_MEM; 86 o->width_bits = 32; 87 o->reg = base; 88 o->imm = imm; 89 } 90 static void arm_decop_pcrel(KitDecodedOperand* o, u64 pc, i64 disp) { 91 arm_decop_none(o); 92 o->kind = KIT_DECOP_PCREL; 93 o->imm = (i64)(pc + (u64)disp); 94 } 95 96 /* Stable opcode id for an emulator/decode consumer (small set). */ 97 static u32 arm32_semantic_opcode(const Arm32InsnDesc* d) { 98 if (!d) return ARM32_DEC_UNKNOWN; 99 switch ((Arm32Format)d->fmt) { 100 case ARM_FMT_BL: 101 return ARM32_DEC_BL; 102 case ARM_FMT_BX: 103 return slice_eq_cstr(d->mnemonic, "blx") ? ARM32_DEC_BLX : ARM32_DEC_BX; 104 case ARM_FMT_BRANCH_T4: 105 case ARM_FMT_B16: 106 return ARM32_DEC_B; 107 case ARM_FMT_BRANCH_T3: 108 case ARM_FMT_BCC16: 109 case ARM_FMT_CBZ: 110 return ARM32_DEC_BCOND; 111 case ARM_FMT_BKPT: 112 return ARM32_DEC_BKPT; 113 case ARM_FMT_IT: 114 return ARM32_DEC_IT; 115 default: 116 return ARM32_DEC_UNKNOWN; 117 } 118 } 119 120 static u16 arm32_decode_flags(const Arm32InsnDesc* d, u32 word) { 121 u16 flags = 0; 122 if (!d) return 0; 123 switch ((Arm32Format)d->fmt) { 124 case ARM_FMT_BL: 125 flags |= KIT_DECODE_BRANCH | KIT_DECODE_CALL; 126 break; 127 case ARM_FMT_BX: 128 flags |= KIT_DECODE_TERMINATOR | KIT_DECODE_BRANCH; 129 if (slice_eq_cstr(d->mnemonic, "blx")) 130 flags |= KIT_DECODE_CALL; 131 else if (((word >> 3) & 0xfu) == ARM_LR) 132 flags |= KIT_DECODE_RET; /* bx lr */ 133 break; 134 case ARM_FMT_BRANCH_T4: 135 case ARM_FMT_B16: 136 flags |= KIT_DECODE_TERMINATOR | KIT_DECODE_BRANCH; 137 break; 138 case ARM_FMT_BRANCH_T3: 139 case ARM_FMT_BCC16: 140 case ARM_FMT_CBZ: 141 flags |= KIT_DECODE_BRANCH; 142 break; 143 case ARM_FMT_TB: 144 flags |= KIT_DECODE_TERMINATOR | KIT_DECODE_BRANCH | KIT_DECODE_MEMORY; 145 break; 146 case ARM_FMT_BKPT: 147 flags |= KIT_DECODE_TERMINATOR | KIT_DECODE_TRAP; 148 break; 149 case ARM_FMT_LDST_T3: 150 case ARM_FMT_LDST_T4: 151 case ARM_FMT_LDREX: 152 case ARM_FMT_STREX: 153 case ARM_FMT_PUSHPOP: 154 flags |= KIT_DECODE_MEMORY; 155 break; 156 default: 157 break; 158 } 159 return flags; 160 } 161 162 /* Fill KitDecodedInsn.operands from the matched descriptor + word — a coarse 163 * structured view for emu/decode consumers (the textual render goes through 164 * arm32_print_operands separately). */ 165 static void arm32_decode_operands(const Arm32InsnDesc* d, u32 w, u64 pc, 166 KitDecodedInsn* out) { 167 if (!d) return; 168 switch ((Arm32Format)d->fmt) { 169 case ARM_FMT_DP_REG: 170 case ARM_FMT_SHIFT_REG: 171 case ARM_FMT_MUL: 172 case ARM_FMT_DIV: { 173 out->noperands = 3; 174 arm_decop_reg(&out->operands[0], (w >> 8) & 0xfu); 175 arm_decop_reg(&out->operands[1], (w >> 16) & 0xfu); 176 arm_decop_reg(&out->operands[2], w & 0xfu); 177 break; 178 } 179 case ARM_FMT_MOVW: { 180 u32 imm4 = (w >> 16) & 0xfu, i = (w >> 26) & 1u; 181 u32 imm3 = (w >> 12) & 7u, imm8 = w & 0xffu; 182 out->noperands = 2; 183 arm_decop_reg(&out->operands[0], (w >> 8) & 0xfu); 184 arm_decop_imm(&out->operands[1], 185 (i64)((imm4 << 12) | (i << 11) | (imm3 << 8) | imm8)); 186 break; 187 } 188 case ARM_FMT_LDST_T3: { 189 out->noperands = 2; 190 arm_decop_reg(&out->operands[0], (w >> 12) & 0xfu); 191 arm_decop_mem(&out->operands[1], (w >> 16) & 0xfu, (i64)(w & 0xfffu)); 192 break; 193 } 194 case ARM_FMT_BL: 195 case ARM_FMT_BRANCH_T4: { 196 out->noperands = 1; 197 (void)w; /* offset re-decoded by the printer */ 198 arm_decop_pcrel(&out->operands[0], pc, 0); 199 break; 200 } 201 case ARM_FMT_SAT: { 202 /* SSAT/USAT rd, #sat, rm: USAT (word[23]=1) encodes sat directly, SSAT 203 * encodes sat-1. Rm is in hw1[3:0]. */ 204 u32 sat_imm = w & 0x1fu; 205 u32 is_usat = (w >> 23) & 1u; 206 out->noperands = 3; 207 arm_decop_reg(&out->operands[0], (w >> 8) & 0xfu); 208 arm_decop_imm(&out->operands[1], 209 (i64)(is_usat ? sat_imm : sat_imm + 1u)); 210 arm_decop_reg(&out->operands[2], (w >> 16) & 0xfu); 211 break; 212 } 213 case ARM_FMT_QADD: { 214 /* Q{D}ADD/Q{D}SUB rd, rm, rn: Rd=hw2[11:8], Rm=hw2[3:0], Rn=hw1[3:0]. */ 215 out->noperands = 3; 216 arm_decop_reg(&out->operands[0], (w >> 8) & 0xfu); 217 arm_decop_reg(&out->operands[1], w & 0xfu); 218 arm_decop_reg(&out->operands[2], (w >> 16) & 0xfu); 219 break; 220 } 221 default: 222 break; 223 } 224 } 225 226 static KitStatus arm32_decode_one(Compiler* c, const u8* bytes, size_t len, 227 u64 pc, KitDecodedInsn* out) { 228 const Arm32InsnDesc* desc; 229 u32 hw1, word; 230 int is16; 231 (void)c; 232 if (!bytes || !out) return KIT_INVALID; 233 if (len < 2u) return KIT_MALFORMED; 234 memset(out, 0, sizeof(*out)); 235 for (u32 i = 0; i < KIT_DECODE_MAX_OPERANDS; ++i) 236 arm_decop_none(&out->operands[i]); 237 238 hw1 = arm_read_u16_le(bytes); 239 is16 = !arm_is_32bit(hw1); 240 if (is16) { 241 word = hw1; 242 out->nbytes = 2; 243 } else { 244 if (len < 4u) return KIT_MALFORMED; 245 word = (hw1 << 16) | arm_read_u16_le(bytes + 2); 246 out->nbytes = 4; 247 } 248 desc = arm32_disasm_find(word, is16); 249 250 out->pc = pc; 251 out->bytes = bytes; 252 out->encoding_id = 253 desc ? (u32)(desc - arm32_insn_table) : ARM32_ENCODING_UNKNOWN; 254 out->opcode = arm32_semantic_opcode(desc); 255 out->flags = arm32_decode_flags(desc, word); 256 out->arch[0] = word; 257 out->arch[1] = desc ? desc->fmt : 0xffu; 258 arm32_decode_operands(desc, word, pc, out); 259 return KIT_OK; 260 } 261 262 static KitStatus arm32_decode_block(Compiler* c, const u8* bytes, size_t len, 263 u64 pc, KitDecodedInsn* out, u32 cap, 264 u32* n_out) { 265 u32 n = 0; 266 if (n_out) *n_out = 0; 267 if (!bytes || !out || !n_out) return KIT_INVALID; 268 while (n < cap && len > 0) { 269 KitStatus st = arm32_decode_one(c, bytes, len, pc, &out[n]); 270 if (st != KIT_OK) return n ? KIT_OK : st; 271 bytes += out[n].nbytes; 272 len -= out[n].nbytes; 273 pc += out[n].nbytes; 274 ++n; 275 if (out[n - 1u].flags & KIT_DECODE_TERMINATOR) break; 276 } 277 *n_out = n; 278 return KIT_OK; 279 } 280 281 static void arm32_formatter_init(Arm32InsnFormatter* f, Compiler* c, Heap* h) { 282 memset(f, 0, sizeof(*f)); 283 f->c = c; 284 f->heap = h; 285 f->base.format = arm32_format_insn; 286 f->base.destroy = arm32_formatter_destroy; 287 strbuf_init(&f->mnem, f->mnem_buf, sizeof f->mnem_buf); 288 strbuf_init(&f->ops, f->ops_buf, sizeof f->ops_buf); 289 strbuf_init(&f->ann, f->ann_buf, sizeof f->ann_buf); 290 } 291 292 /* Expand an IT mask into the per-slot then/else conditions. firstcond is the 293 * condition of slot 0; subsequent slots use firstcond or its inverse per the 294 * mask's T/E bits. Returns the block length (1..4). */ 295 static u32 arm_it_expand(u32 firstcond, u32 mask, u8 cond_out[4]) { 296 u32 n, k; 297 /* Block length = 4 - (position of the lowest set bit in the 4-bit mask). 298 * mask bit3 is always 1 for a valid IT. */ 299 if (mask & 1u) 300 n = 4; 301 else if (mask & 2u) 302 n = 3; 303 else if (mask & 4u) 304 n = 2; 305 else 306 n = 1; 307 cond_out[0] = (u8)(firstcond & 0xfu); 308 for (k = 1; k < n; ++k) { 309 /* bit (4-k) of mask: when it equals firstcond[0], slot k is the "then" 310 * condition (== firstcond), else the inverse. */ 311 u32 bit = (mask >> (4u - k)) & 1u; 312 u32 then = (firstcond & 1u); 313 cond_out[k] = (u8)((bit == then) ? firstcond : (firstcond ^ 1u)); 314 } 315 for (; k < 4; ++k) cond_out[k] = (u8)(firstcond & 0xfu); 316 return n; 317 } 318 319 /* Build the rendered mnemonic into f->mnem: base mnemonic (keeping the `.w` 320 * width suffix) plus a condition suffix when the instruction is inside an IT 321 * block or is itself a conditional branch (T3 / 16-bit Bcc). The IT mnemonic 322 * itself renders as `it<x><y><z>` with the firstcond as its sole operand. */ 323 static void arm32_render_mnemonic(Arm32InsnFormatter* f, const Arm32InsnDesc* d, 324 u32 word, u32 it_cond, int in_it) { 325 Slice mn = d->mnemonic; 326 strbuf_reset(&f->mnem); 327 if ((Arm32Format)d->fmt == ARM_FMT_BRANCH_T3) { 328 /* b.w -> b<cc>.w */ 329 strbuf_putc(&f->mnem, 'b'); 330 strbuf_puts(&f->mnem, arm32_cond_name(arm32_branch_t3_cond(word))); 331 strbuf_puts(&f->mnem, ".w"); 332 return; 333 } 334 if ((Arm32Format)d->fmt == ARM_FMT_BCC16) { 335 strbuf_putc(&f->mnem, 'b'); 336 strbuf_puts(&f->mnem, arm32_cond_name((word >> 8) & 0xfu)); 337 return; 338 } 339 if ((Arm32Format)d->fmt == ARM_FMT_IT) { 340 u32 firstcond = (word >> 4) & 0xfu; 341 u32 mask = word & 0xfu; 342 u8 cc[4]; 343 u32 nblk = arm_it_expand(firstcond, mask, cc); 344 u32 k; 345 strbuf_puts(&f->mnem, "it"); 346 for (k = 1; k < nblk; ++k) 347 strbuf_putc(&f->mnem, (cc[k] == cc[0]) ? 't' : 'e'); 348 return; 349 } 350 /* Shifted-MOV (ORR rn=1111) aliases to the shift mnemonic (lsl/lsr/asr/ror.w 351 * or rrx); the MVN (ORN) form keeps `mvn.w` and renders the shift as an 352 * operand suffix instead. Substitute the alias as the base mnemonic for the 353 * MOV form, then fall through to the shared IT-cond / `.w` suffix logic 354 * (`rrx` carries no `.w`, so that logic leaves it untouched). */ 355 if ((Arm32Format)d->fmt == ARM_FMT_MOV_REG) { 356 u32 op4 = (word >> 21) & 0xfu; /* hw1[8:5]: 2 = MOV (ORR), 3 = MVN (ORN). */ 357 if (op4 == 2u) { 358 const char* alias = arm32_mov_shift_alias(word, NULL, NULL); 359 if (alias) mn = slice_from_cstr(alias); 360 } 361 } 362 /* Base mnemonic, with optional IT condition suffix inserted before ".w". */ 363 if (in_it) { 364 const char* ccn = arm32_cond_name(it_cond); 365 int is16 = (d->flags & ARM_FMT_W16) != 0; 366 /* In an IT block the 16-bit flag-setting forms drop the implicit trailing 367 * `s` (ARM unified syntax: predication implies it), e.g. movs -> moveq. The 368 * exceptions are mnemonics whose `s` is part of the name, not a flag suffix 369 * (none in the 16-bit DP set this disassembler emits). */ 370 if (is16 && mn.len >= 2 && mn.s[mn.len - 1] == 's') { 371 strbuf_putn(&f->mnem, mn.s, mn.len - 1); 372 strbuf_puts(&f->mnem, ccn); 373 } else if (mn.len >= 2 && mn.s[mn.len - 2] == '.' && mn.s[mn.len - 1] == 'w') { 374 strbuf_putn(&f->mnem, mn.s, mn.len - 2); 375 strbuf_puts(&f->mnem, ccn); 376 strbuf_puts(&f->mnem, ".w"); 377 } else { 378 strbuf_put_slice(&f->mnem, mn); 379 strbuf_puts(&f->mnem, ccn); 380 } 381 return; 382 } 383 strbuf_put_slice(&f->mnem, mn); 384 } 385 386 static KitStatus arm32_format_insn(ArchInsnFormatter* base, 387 const KitDecodedInsn* insn, KitInsn* out) { 388 Arm32InsnFormatter* f = (Arm32InsnFormatter*)base; 389 const Arm32InsnDesc* desc; 390 u32 word; 391 int is16; 392 u32 it_cond = ARM_CC_AL; 393 int in_it = 0; 394 if (!f || !insn || !out) return KIT_INVALID; 395 word = (u32)insn->arch[0]; 396 is16 = insn->nbytes == 2u; 397 desc = arm32_disasm_find(word, is16); 398 399 /* Consume one IT slot if a block is active (and this insn is not the IT). */ 400 if (f->it_count > 0 && (!desc || (Arm32Format)desc->fmt != ARM_FMT_IT)) { 401 it_cond = f->it_cond[0]; 402 in_it = 1; 403 f->it_cond[0] = f->it_cond[1]; 404 f->it_cond[1] = f->it_cond[2]; 405 f->it_cond[2] = f->it_cond[3]; 406 f->it_count--; 407 } 408 409 if (desc) { 410 arm32_render_mnemonic(f, desc, word, it_cond, in_it); 411 strbuf_reset(&f->ops); 412 arm32_print_operands(&f->ops, desc, word, insn->pc); 413 /* The IT instruction prints its firstcond and opens a block whose per-slot 414 * conditions the following instructions consume. */ 415 if ((Arm32Format)desc->fmt == ARM_FMT_IT) { 416 u32 firstcond = (word >> 4) & 0xfu; 417 u32 mask = word & 0xfu; 418 strbuf_puts(&f->ops, arm32_cond_name(firstcond)); 419 f->it_count = arm_it_expand(firstcond, mask, f->it_cond); 420 } 421 } else { 422 strbuf_reset(&f->mnem); 423 strbuf_puts(&f->mnem, is16 ? ".hword" : ".inst"); 424 strbuf_reset(&f->ops); 425 strbuf_put_hex_u64(&f->ops, (u64)word); 426 } 427 428 strbuf_reset(&f->ann); 429 out->vaddr = insn->pc; 430 out->bytes = insn->bytes; 431 out->nbytes = insn->nbytes; 432 out->mnemonic = strbuf_slice(&f->mnem); 433 out->operands = strbuf_slice(&f->ops); 434 out->annotation = strbuf_slice(&f->ann); 435 return KIT_OK; 436 } 437 438 static void arm32_formatter_destroy(ArchInsnFormatter* base) { 439 Arm32InsnFormatter* f = (Arm32InsnFormatter*)base; 440 if (!f) return; 441 f->heap->free(f->heap, f, sizeof(*f)); 442 } 443 444 static ArchInsnFormatter* arm32_formatter_new(Compiler* c) { 445 Heap* h = (Heap*)c->ctx->heap; 446 Arm32InsnFormatter* f = 447 (Arm32InsnFormatter*)h->alloc(h, sizeof(*f), _Alignof(Arm32InsnFormatter)); 448 if (!f) return NULL; 449 arm32_formatter_init(f, c, h); 450 return &f->base; 451 } 452 453 static u32 arm_decode(ArchDisasm* base, const u8* bytes, size_t len, u64 vaddr, 454 KitInsn* out) { 455 Arm32Disasm* d = (Arm32Disasm*)base; 456 KitDecodedInsn insn; 457 KitStatus st = arm32_decode_one(d->fmt.c, bytes, len, vaddr, &insn); 458 if (st != KIT_OK) return 0; 459 st = arm32_format_insn(&d->fmt.base, &insn, out); 460 if (st != KIT_OK) return 0; 461 return insn.nbytes; 462 } 463 464 static void arm32_destroy(ArchDisasm* base) { 465 Arm32Disasm* d = (Arm32Disasm*)base; 466 d->fmt.heap->free(d->fmt.heap, d, sizeof(*d)); 467 } 468 469 ArchDisasm* arm32_disasm_new(Compiler* c) { 470 Heap* h = (Heap*)c->ctx->heap; 471 Arm32Disasm* d = (Arm32Disasm*)h->alloc(h, sizeof(*d), _Alignof(Arm32Disasm)); 472 if (!d) return NULL; 473 memset(d, 0, sizeof(*d)); 474 d->base.decode = arm_decode; 475 d->base.destroy = arm32_destroy; 476 arm32_formatter_init(&d->fmt, c, h); 477 return &d->base; 478 } 479 480 const ArchDecodeOps arm32_decode_ops = { 481 .min_insn_len = 2, 482 .max_insn_len = 4, 483 .decode_one = arm32_decode_one, 484 .decode_block = arm32_decode_block, 485 .formatter_new = arm32_formatter_new, 486 .format = arm32_format_insn, 487 .formatter_destroy = arm32_formatter_destroy, 488 };