kit

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

disasm.c (4628B)


      1 /* AArch64 disassembler implementation.
      2  *
      3  * Decodes one 4-byte instruction word per call into a KitInsn whose
      4  * string fields point into iterator-owned StrBufs. The decoder shares
      5  * the aa64_isa.{h,c} descriptor table with the encoder: aa64_disasm_find
      6  * matches the word; aa64_print_operands renders operand text via the
      7  * format's unpack + per-format pretty-printer. Mnemonic rewriting (the
      8  * one bit the printer can't own, because b.cond rolls cond into the
      9  * "operand" text) happens here. */
     10 
     11 #include "arch/aa64/disasm.h"
     12 
     13 #include <string.h>
     14 
     15 #include "arch/aa64/isa.h"
     16 #include "core/heap.h"
     17 #include "core/strbuf.h"
     18 
     19 /* Enough for any aarch64 mnemonic-with-suffix ("b.cond" → "b.le", etc.). */
     20 #define AA64_DASM_MNEM_CAP 16u
     21 /* Operand text. The widest cases (LDP X, X, [SP, #-imm]!) fit easily. */
     22 #define AA64_DASM_OPS_CAP 96u
     23 /* Annotation overlay (symbol + addend). */
     24 #define AA64_DASM_ANN_CAP 96u
     25 
     26 typedef struct AA64Disasm {
     27   ArchDisasm base;
     28   Compiler* c;
     29   Heap* heap;
     30   char mnem_buf[AA64_DASM_MNEM_CAP];
     31   char ops_buf[AA64_DASM_OPS_CAP];
     32   char ann_buf[AA64_DASM_ANN_CAP];
     33   StrBuf mnem;
     34   StrBuf ops;
     35   StrBuf ann;
     36 } AA64Disasm;
     37 
     38 static void aa64_write_mnemonic(AA64Disasm* d, const AA64InsnDesc* desc,
     39                                 u32 word) {
     40   strbuf_reset(&d->mnem);
     41   if (desc->fmt == AA64_FMT_BR_COND) {
     42     /* Synthesize "b.<cond>" so the operands buffer can hold just the
     43      * target. Matches GNU as / objdump conventions. */
     44     u32 cond = word & 0xfu;
     45     strbuf_puts(&d->mnem, "b.");
     46     strbuf_puts(&d->mnem, aa64_cond_name(cond));
     47     return;
     48   }
     49   if (desc->fmt == AA64_FMT_BITFIELD) {
     50     /* SBFM/UBFM disassemble to their preferred aliases when the
     51      * fields match; aa64_print_operands renders the matching operands. */
     52     u32 shift, lsb, width;
     53     const char* alias = aa64_bitfield_shift_alias(word, &shift);
     54     if (!alias) alias = aa64_bitfield_extend_alias(word);
     55     if (!alias) alias = aa64_bitfield_extract_alias(word, &lsb, &width);
     56     (void)lsb;
     57     (void)width;
     58     if (alias) {
     59       strbuf_puts(&d->mnem, alias);
     60       return;
     61     }
     62   }
     63   strbuf_put_slice(&d->mnem, desc->mnemonic);
     64 }
     65 
     66 static void aa64_write_operands(AA64Disasm* d, const AA64InsnDesc* desc,
     67                                 u32 word, u64 vaddr) {
     68   strbuf_reset(&d->ops);
     69   if (desc->fmt == AA64_FMT_BR_COND) {
     70     /* aa64_print_operands prints "<cond> <target>"; we already lifted
     71      * the cond into the mnemonic, so skip the dispatcher and inline
     72      * just the target. */
     73     AA64BrCond f = aa64_brcond_unpack(word);
     74     i64 ofs = (i64)((u64)f.imm19 & 0x7ffffu);
     75     /* sign-extend 19 bits */
     76     if (ofs & 0x40000) ofs |= ~(i64)0x7ffff;
     77     ofs *= 4;
     78     if (vaddr) {
     79       strbuf_put_hex_u64(&d->ops, vaddr + (u64)ofs);
     80     } else {
     81       strbuf_puts(&d->ops, "#");
     82       strbuf_put_i64(&d->ops, ofs);
     83     }
     84     return;
     85   }
     86   aa64_print_operands(&d->ops, desc, word, vaddr);
     87 }
     88 
     89 static u32 aa64_read_u32_le(const u8* b) {
     90   return (u32)b[0] | ((u32)b[1] << 8) | ((u32)b[2] << 16) | ((u32)b[3] << 24);
     91 }
     92 
     93 static void aa64_write_unknown(AA64Disasm* d, u32 word) {
     94   strbuf_reset(&d->mnem);
     95   strbuf_puts(&d->mnem, ".inst");
     96   strbuf_reset(&d->ops);
     97   strbuf_put_hex_u64(&d->ops, (u64)word);
     98 }
     99 
    100 static u32 aa64_decode(ArchDisasm* base, const u8* bytes, size_t len, u64 vaddr,
    101                        KitInsn* out) {
    102   AA64Disasm* d = (AA64Disasm*)base;
    103   if (len < 4u) return 0;
    104   u32 word = aa64_read_u32_le(bytes);
    105   const AA64InsnDesc* desc = aa64_disasm_find(word);
    106   if (desc) {
    107     aa64_write_mnemonic(d, desc, word);
    108     aa64_write_operands(d, desc, word, vaddr);
    109   } else {
    110     aa64_write_unknown(d, word);
    111   }
    112   /* Annotation overlay is owned by the public iterator (kit_disasm_iter_*).
    113    * The arch-level decoder leaves it empty. */
    114   strbuf_reset(&d->ann);
    115   out->vaddr = vaddr;
    116   out->bytes = bytes;
    117   out->nbytes = 4;
    118   out->mnemonic = strbuf_slice(&d->mnem);
    119   out->operands = strbuf_slice(&d->ops);
    120   out->annotation = strbuf_slice(&d->ann);
    121   return 4;
    122 }
    123 
    124 static void aa64_destroy(ArchDisasm* base) {
    125   AA64Disasm* d = (AA64Disasm*)base;
    126   d->heap->free(d->heap, d, sizeof(*d));
    127 }
    128 
    129 ArchDisasm* aa64_disasm_new(Compiler* c) {
    130   Heap* h = (Heap*)c->ctx->heap;
    131   AA64Disasm* d = (AA64Disasm*)h->alloc(h, sizeof(*d), _Alignof(AA64Disasm));
    132   if (!d) return NULL;
    133   memset(d, 0, sizeof(*d));
    134   d->c = c;
    135   d->heap = h;
    136   d->base.decode = aa64_decode;
    137   d->base.destroy = aa64_destroy;
    138   strbuf_init(&d->mnem, d->mnem_buf, sizeof d->mnem_buf);
    139   strbuf_init(&d->ops, d->ops_buf, sizeof d->ops_buf);
    140   strbuf_init(&d->ann, d->ann_buf, sizeof d->ann_buf);
    141   return &d->base;
    142 }