kit

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

disasm.c (9431B)


      1 /* Public disassembler API. */
      2 
      3 #include <kit/disasm.h>
      4 #include <stdint.h>
      5 #include <string.h>
      6 
      7 #include "arch/arch.h"
      8 #include "core/core.h"
      9 #include "core/heap.h"
     10 #include "core/pool.h"
     11 #include "core/slice.h"
     12 #include "core/strbuf.h"
     13 #include "obj/obj.h"
     14 
     15 #define DASM_ANN_CAP 128u
     16 
     17 ObjBuilder* kit_objfile_builder(const KitObjFile* f);
     18 
     19 /* The iterator holds its own private Compiler so the arch decoder has a
     20  * pool to mint strings from without needing a caller-provided KitCompiler. */
     21 struct KitDisasmIter {
     22   Compiler compiler;
     23   const KitContext* ctx;
     24   Heap* heap;
     25   ArchDisasm* arch;
     26   const uint8_t* bytes;
     27   size_t len;
     28   size_t off;
     29   uint64_t vaddr0;
     30   const KitObjFile* annot;
     31   ObjBuilder* annot_ob;
     32   uint32_t annot_section;
     33   char ann_buf[DASM_ANN_CAP];
     34   StrBuf ann;
     35 };
     36 
     37 static Slice dasm_overlay(KitDisasmIter* it, uint64_t vaddr, uint32_t nbytes) {
     38   ObjBuilder* obj = it->annot_ob;
     39   if (!obj) return SLICE_LIT("");
     40   strbuf_reset(&it->ann);
     41 
     42   u64 want = vaddr - it->vaddr0;
     43   u64 end = want + nbytes;
     44   u32 nrel = obj_reloc_total(obj);
     45   for (u32 i = 0; i < nrel; ++i) {
     46     const Reloc* r = obj_reloc_at(obj, i);
     47     if (!r) continue;
     48     if (it->annot_section != KIT_SECTION_NONE &&
     49         r->section_id != (ObjSecId)(it->annot_section + 1))
     50       continue;
     51     if ((u64)r->offset < want || (u64)r->offset >= end) continue;
     52     const ObjSym* sym =
     53         (r->sym != OBJ_SYM_NONE) ? obj_symbol_get(obj, r->sym) : NULL;
     54     if (sym && sym->name) {
     55       Compiler* oc = obj_compiler(obj);
     56       Slice nm = oc ? pool_slice(oc->global, sym->name) : SLICE_NULL;
     57       if (nm.s)
     58         strbuf_putn(&it->ann, nm.s, nm.len);
     59       else
     60         strbuf_puts(&it->ann, "<anon>");
     61     } else {
     62       strbuf_puts(&it->ann, "<anon>");
     63     }
     64     if (r->addend > 0) {
     65       strbuf_puts(&it->ann, "+");
     66       strbuf_put_i64(&it->ann, r->addend);
     67     } else if (r->addend < 0) {
     68       strbuf_put_i64(&it->ann, r->addend);
     69     }
     70     /* Append the reloc kind in brackets so the annotation distinguishes
     71      * HI20 vs LO12 vs CALL forms — useful for rv64 paired AUIPC/ADDI
     72      * sequences and aa64 paired ADRP/ADD pages. */
     73     const char* kn = reloc_kind_name(r->kind);
     74     if (kn && kn[0]) {
     75       strbuf_puts(&it->ann, " [");
     76       strbuf_puts(&it->ann, kn);
     77       strbuf_puts(&it->ann, "]");
     78     }
     79     break;
     80   }
     81   return strbuf_slice(&it->ann);
     82 }
     83 
     84 static uint32_t dasm_find_section(const KitObjFile* f, const uint8_t* bytes,
     85                                   size_t len) {
     86   uint32_t nsec, i;
     87   if (!f || !bytes) return KIT_SECTION_NONE;
     88   nsec = kit_obj_nsections(f);
     89   for (i = 0; i < nsec; ++i) {
     90     const uint8_t* data = NULL;
     91     size_t n = 0;
     92     if (kit_obj_section_data(f, i, &data, &n) != KIT_OK) continue;
     93     if (data == bytes && n == len) return i;
     94   }
     95   return KIT_SECTION_NONE;
     96 }
     97 
     98 KitStatus kit_disasm_iter_new(const KitDisasmContext* dctx,
     99                               const uint8_t* bytes, size_t len, uint64_t vaddr,
    100                               const KitObjFile* annot, KitDisasmIter** out) {
    101   Heap* h;
    102   KitDisasmIter* it;
    103   if (!out) return KIT_INVALID;
    104   if (!dctx) return KIT_INVALID;
    105   if (!bytes && len > 0) return KIT_INVALID;
    106   if (!dctx->context.heap) return KIT_INVALID;
    107   h = dctx->context.heap;
    108   it = (KitDisasmIter*)h->alloc(h, sizeof(*it), _Alignof(KitDisasmIter));
    109   if (!it) return KIT_NOMEM;
    110   memset(it, 0, sizeof(*it));
    111   it->ctx = &dctx->context;
    112   it->heap = h;
    113   if (!dctx->target) {
    114     h->free(h, it, sizeof(*it));
    115     return KIT_INVALID;
    116   }
    117   {
    118     KitStatus st = compiler_init(&it->compiler, dctx->target, &dctx->context);
    119     if (st != KIT_OK) {
    120       h->free(h, it, sizeof(*it));
    121       return st;
    122     }
    123   }
    124   it->arch = arch_disasm_new(&it->compiler);
    125   if (!it->arch) {
    126     compiler_fini(&it->compiler);
    127     h->free(h, it, sizeof(*it));
    128     return KIT_UNSUPPORTED;
    129   }
    130   it->bytes = bytes;
    131   it->len = len;
    132   it->off = 0;
    133   it->vaddr0 = vaddr;
    134   it->annot = annot;
    135   it->annot_ob = annot ? kit_objfile_builder(annot) : NULL;
    136   it->annot_section = dasm_find_section(annot, bytes, len);
    137   strbuf_init(&it->ann, it->ann_buf, sizeof it->ann_buf);
    138   *out = it;
    139   return KIT_OK;
    140 }
    141 
    142 KitIterResult kit_disasm_iter_next(KitDisasmIter* it, KitInsn* out) {
    143   if (!it || !out) return KIT_ITER_ERROR;
    144   if (it->off >= it->len) return KIT_ITER_END;
    145   uint64_t vaddr = it->vaddr0 + (uint64_t)it->off;
    146   u32 n = arch_disasm_decode(it->arch, it->bytes + it->off, it->len - it->off,
    147                              vaddr, out);
    148   if (n == 0) {
    149     if (it->off >= it->len) return KIT_ITER_END;
    150     n = (u32)(it->len - it->off);
    151     if (n > 4) n = 4;
    152     out->vaddr = vaddr;
    153     out->bytes = it->bytes + it->off;
    154     out->nbytes = n;
    155     out->mnemonic = SLICE_LIT("(truncated)");
    156     out->operands = SLICE_LIT("");
    157     out->annotation = SLICE_LIT("");
    158     it->off += n;
    159     return KIT_ITER_ITEM;
    160   }
    161   out->annotation = dasm_overlay(it, vaddr, n);
    162   it->off += n;
    163   return KIT_ITER_ITEM;
    164 }
    165 
    166 void kit_disasm_iter_free(KitDisasmIter* it) {
    167   Heap* h;
    168   if (!it) return;
    169   arch_disasm_free(it->arch);
    170   compiler_fini(&it->compiler);
    171   h = it->heap;
    172   h->free(h, it, sizeof(*it));
    173 }
    174 
    175 static KitStatus w_str(KitWriter* w, const char* s) {
    176   return kit_writer_write(w, s, slice_from_cstr(s).len);
    177 }
    178 
    179 static KitStatus w_slice(KitWriter* w, Slice s) {
    180   return kit_writer_write(w, s.s, s.len);
    181 }
    182 
    183 static KitStatus w_hex(KitWriter* w, u64 v, u32 width) {
    184   static const char H[] = "0123456789abcdef";
    185   char buf[17];
    186   u32 i;
    187   if (width > 16) width = 16;
    188   for (i = 0; i < width; ++i) {
    189     buf[width - 1 - i] = H[(v >> (4 * i)) & 0xfu];
    190   }
    191   buf[width] = '\0';
    192   return kit_writer_write(w, buf, width);
    193 }
    194 
    195 static KitStatus w_hex_padded(KitWriter* w, u64 v, u32 minwidth) {
    196   static const char H[] = "0123456789abcdef";
    197   char buf[24];
    198   u32 i = sizeof(buf);
    199   u32 nch;
    200   KitStatus st;
    201   if (v == 0) {
    202     buf[--i] = '0';
    203   } else {
    204     while (v) {
    205       buf[--i] = H[v & 0xfu];
    206       v >>= 4;
    207     }
    208   }
    209   nch = (u32)(sizeof(buf) - i);
    210   while (nch < minwidth) {
    211     st = kit_writer_write(w, " ", 1);
    212     if (st != KIT_OK) return st;
    213     ++nch;
    214   }
    215   return kit_writer_write(w, buf + i, sizeof(buf) - i);
    216 }
    217 
    218 static Slice dasm_sym_at(const KitObjFile* f, uint32_t section_idx,
    219                          uint64_t offset) {
    220   KitObjSymIter* si = NULL;
    221   Slice found = SLICE_NULL;
    222   KitObjSymInfo s;
    223   if (kit_obj_symiter_new((KitObjFile*)f, &si) != KIT_OK) return SLICE_NULL;
    224   for (;;) {
    225     KitIterResult r = kit_obj_symiter_next(si, &s);
    226     if (r != KIT_ITER_ITEM) break;
    227     if (s.section != section_idx) continue;
    228     if (s.value != offset) continue;
    229     if (!s.name.len) continue;
    230     if (s.name.s[0] == '$') continue;
    231     found = s.name;
    232     if (s.kind == KIT_SK_FUNC) break;
    233   }
    234   kit_obj_symiter_free(si);
    235   return found;
    236 }
    237 
    238 KitStatus kit_disasm_obj(const KitContext* ctx, const KitObjFile* f,
    239                          KitWriter* out) {
    240   uint32_t nsec, i;
    241   KitDisasmContext dctx;
    242   KitTarget* target = NULL;
    243   KitTargetOptions topts;
    244   KitStatus st;
    245   if (!ctx || !f || !out) return KIT_INVALID;
    246   memset(&topts, 0, sizeof topts);
    247   topts.spec = kit_obj_target(f);
    248   st = kit_target_new(ctx, &topts, &target);
    249   if (st != KIT_OK) return st;
    250   dctx.target = target;
    251   dctx.context = *ctx;
    252   nsec = kit_obj_nsections(f);
    253   for (i = 0; i < nsec; ++i) {
    254     KitObjSecInfo s;
    255     const uint8_t* data = NULL;
    256     size_t n = 0;
    257     KitDisasmIter* it = NULL;
    258     Slice head;
    259 
    260     if (kit_obj_section(f, i, &s) != KIT_OK) continue;
    261     if (s.kind != KIT_SEC_TEXT) continue;
    262     if (kit_obj_section_data(f, i, &data, &n) != KIT_OK) continue;
    263     if (!data || !n) continue;
    264 
    265     w_str(out, "Disassembly of section ");
    266     w_slice(out, s.name.len ? s.name : SLICE_LIT(".text"));
    267     w_str(out, ":\n\n");
    268 
    269     st = kit_disasm_iter_new(&dctx, data, n, 0, f, &it);
    270     if (st != KIT_OK) {
    271       kit_target_free(target);
    272       return st;
    273     }
    274     for (;;) {
    275       KitInsn ins;
    276       KitIterResult r = kit_disasm_iter_next(it, &ins);
    277       if (r != KIT_ITER_ITEM) break;
    278       head = dasm_sym_at(f, i, ins.vaddr);
    279       if (head.len) {
    280         w_hex(out, ins.vaddr, 16);
    281         w_str(out, " <");
    282         w_slice(out, head);
    283         w_str(out, ">:\n");
    284       }
    285       w_hex_padded(out, ins.vaddr, 8);
    286       w_str(out, ":\t");
    287       if (ins.nbytes == 4) {
    288         uint32_t w = (uint32_t)ins.bytes[0] | ((uint32_t)ins.bytes[1] << 8) |
    289                      ((uint32_t)ins.bytes[2] << 16) |
    290                      ((uint32_t)ins.bytes[3] << 24);
    291         w_hex(out, w, 8);
    292         w_str(out, " \t");
    293       } else {
    294         uint32_t k;
    295         for (k = 0; k < ins.nbytes; ++k) {
    296           w_hex(out, ins.bytes[k], 2);
    297         }
    298         w_str(out, " \t");
    299       }
    300       w_slice(out, ins.mnemonic);
    301       if (ins.operands.len) {
    302         w_str(out, "\t");
    303         w_slice(out, ins.operands);
    304       }
    305       if (ins.annotation.len) {
    306         w_str(out, " ; ");
    307         w_slice(out, ins.annotation);
    308       }
    309       w_str(out, "\n");
    310     }
    311     kit_disasm_iter_free(it);
    312   }
    313   st = kit_writer_status(out);
    314   kit_target_free(target);
    315   return st;
    316 }
    317 
    318 KitStatus kit_disasm_obj_bytes(const KitContext* ctx, const KitSlice* bytes,
    319                                KitWriter* out) {
    320   KitObjFile* f = NULL;
    321   KitStatus st;
    322   if (!ctx || !bytes || !out) return KIT_INVALID;
    323   st = kit_obj_open(ctx, SLICE_NULL, bytes, &f);
    324   if (st != KIT_OK) return st;
    325   st = kit_disasm_obj(ctx, f, out);
    326   kit_obj_free(f);
    327   return st;
    328 }