kit

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

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 };