kit

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

mc.c (10904B)


      1 #include <kit/compile.h>
      2 #include <kit/core.h>
      3 #include <kit/disasm.h>
      4 #include <kit/object.h>
      5 #include <stddef.h>
      6 #include <stdint.h>
      7 #include <string.h>
      8 
      9 #include "driver.h"
     10 #include "env.h"
     11 
     12 /* `kit mc` — assemble one (or a few) GAS-subset instructions and show the
     13  * machine-code encoding, llvm-mc --show-encoding style. The text is assembled
     14  * through the same back-end as `kit as`, emitted to an in-memory object, and
     15  * the executable section is disassembled to attribute bytes to each
     16  * instruction. Any relocations the assembler emits (e.g. for a branch to an
     17  * undefined symbol) are listed beneath. -p prints just the raw .text hex. */
     18 
     19 #define MC_TOOL "mc"
     20 
     21 typedef struct McOpts {
     22   KitTargetSpec target;
     23   int plain; /* -p: raw .text hex only */
     24 } McOpts;
     25 
     26 void driver_help_mc(void) {
     27   driver_printf(
     28       "%.*s",
     29       KIT_SLICE_ARG(KIT_SLICE_LIT(
     30           "kit mc — assemble an instruction and show its encoding\n"
     31           "\n"
     32           "USAGE\n"
     33           "  kit mc [-target TRIPLE] [-p] \"INSN ...\"\n"
     34           "  kit mc [-target TRIPLE] [-p] -        (read instructions from "
     35           "stdin)\n"
     36           "\n"
     37           "DESCRIPTION\n"
     38           "  Assembles the given instruction text (GAS subset: AT&T on x86,\n"
     39           "  standard mnemonics on aarch64/riscv64) and prints each decoded\n"
     40           "  instruction with its bytes as `# encoding: [0x..,..]`. "
     41           "Relocations\n"
     42           "  emitted for undefined-symbol operands are listed beneath.\n"
     43           "\n"
     44           "OPTIONS\n"
     45           "  -target TRIPLE   architecture to assemble for. Audited spellings:\n"
     46           "                   aarch64, x86_64, riscv64 (host default)\n"
     47           "  -p               print only the raw .text bytes as hex\n"
     48           "  -h, --help       show this help\n"
     49           "\n"
     50           "INPUT AND OUTPUT\n"
     51           "  A quoted operand may contain one or more instructions. A positional\n"
     52           "  - reads instruction text from stdin. Normal output shows decoded\n"
     53           "  instructions and byte arrays; -p emits one continuous hex line.\n"
     54           "  References to undefined symbols retain relocation records and print\n"
     55           "  them below the instruction.\n"
     56           "\n"
     57           "EXAMPLES\n"
     58           "  kit mc -target x86_64 'movq %rax, %rbx'\n"
     59           "  kit mc -target aarch64 -p 'ret'\n"
     60           "  kit mc -target riscv64 -p 'ret'\n"
     61           "  kit mc -target x86_64 'call target_symbol'\n"
     62           "  printf 'nop\\n' | kit mc -target x86_64 -p -\n"
     63           "  bytes=$(kit mc -target riscv64 -p 'ret')\n"
     64           "  kit disas -target riscv64 -x \"$bytes\"\n"
     65           "\n"
     66           "EXIT CODES\n"
     67           "  0   success           1   assemble error       2   bad usage\n")));
     68 }
     69 
     70 /* Locate the executable section's bytes (format-agnostic: .text / __text). */
     71 static int mc_text_section(const KitObjFile* f, const uint8_t** data,
     72                            size_t* len) {
     73   uint32_t n = kit_obj_nsections(f);
     74   uint32_t i;
     75   for (i = 0; i < n; ++i) {
     76     KitObjSecInfo sec;
     77     if (kit_obj_section(f, i, &sec) != KIT_OK) continue;
     78     if (!(sec.flags & KIT_SF_EXEC)) continue;
     79     if (kit_obj_section_data(f, i, data, len) != KIT_OK) continue;
     80     if (*data && *len) return 0;
     81   }
     82   return 1;
     83 }
     84 
     85 static void mc_print_plain(const uint8_t* text, size_t len) {
     86   size_t i;
     87   for (i = 0; i < len; ++i) driver_printf("%02x", text[i]);
     88   driver_printf("\n");
     89 }
     90 
     91 /* Disassemble the assembled .text and print one `mnemonic ops # encoding: [..]`
     92  * line per instruction. */
     93 static void mc_print_encoding(const KitContext* ctx, const KitTarget* target,
     94                               const uint8_t* text, size_t len) {
     95   KitDisasmContext dctx;
     96   KitDisasmIter* it = NULL;
     97   KitInsn insn;
     98   memset(&dctx, 0, sizeof dctx);
     99   dctx.target = target;
    100   dctx.context = *ctx;
    101   if (kit_disasm_iter_new(&dctx, text, len, 0, NULL, &it) != KIT_OK) {
    102     /* No disassembler: fall back to a single raw-hex line. */
    103     mc_print_plain(text, len);
    104     return;
    105   }
    106   while (kit_disasm_iter_next(it, &insn) == KIT_ITER_ITEM) {
    107     uint32_t b;
    108     driver_printf("%.*s", KIT_SLICE_ARG(insn.mnemonic));
    109     if (insn.operands.len) driver_printf(" %.*s", KIT_SLICE_ARG(insn.operands));
    110     driver_printf("\t# encoding: [");
    111     for (b = 0; b < insn.nbytes; ++b)
    112       driver_printf("%s0x%02x", b ? "," : "", insn.bytes[b]);
    113     driver_printf("]\n");
    114   }
    115   kit_disasm_iter_free(it);
    116 }
    117 
    118 /* List any relocations the assembler emitted (undefined-symbol operands). */
    119 static void mc_print_relocs(KitObjFile* f) {
    120   KitObjRelocIter* it = NULL;
    121   KitObjReloc r;
    122   if (kit_obj_reliter_new(f, &it) != KIT_OK) return;
    123   while (kit_obj_reliter_next(it, &r) == KIT_ITER_ITEM) {
    124     driver_printf(
    125         "#  reloc %.*s %.*s",
    126         KIT_SLICE_ARG(r.kind_name.len ? r.kind_name : KIT_SLICE_LIT("?")),
    127         KIT_SLICE_ARG(r.sym_name.len ? r.sym_name : KIT_SLICE_LIT("*ABS*")));
    128     if (r.addend)
    129       driver_printf("%c0x%llx", r.addend < 0 ? '-' : '+',
    130                     (unsigned long long)(r.addend < 0 ? -r.addend : r.addend));
    131     driver_printf(" @ .text+0x%llx\n", (unsigned long long)r.offset);
    132   }
    133   kit_obj_reliter_free(it);
    134 }
    135 
    136 /* Join the instruction operands argv[first..argc) into a newline-terminated
    137  * source buffer. Returns NULL on OOM. */
    138 static char* mc_join_source(DriverEnv* env, int first, int argc, char** argv,
    139                             size_t* out_len) {
    140   size_t total = 0;
    141   int i;
    142   char* buf;
    143   size_t pos = 0;
    144   for (i = first; i < argc; ++i) total += driver_strlen(argv[i]) + 1;
    145   total += 1; /* trailing NUL */
    146   buf = (char*)driver_alloc(env, total);
    147   if (!buf) return NULL;
    148   for (i = first; i < argc; ++i) {
    149     size_t n = driver_strlen(argv[i]);
    150     driver_memcpy(buf + pos, argv[i], n);
    151     pos += n;
    152     buf[pos++] = (i + 1 < argc) ? ' ' : '\n';
    153   }
    154   if (pos == 0) buf[pos++] = '\n';
    155   *out_len = pos;
    156   return buf;
    157 }
    158 
    159 int driver_mc(int argc, char** argv) {
    160   DriverEnv env;
    161   KitContext ctx;
    162   McOpts o;
    163   DriverTargetFeatures tf = {0};
    164   KitTarget* target = NULL;
    165   KitCompiler* compiler = NULL;
    166   KitCompileSession* session = NULL;
    167   KitObjBuilder* ob = NULL;
    168   KitWriter* mem = NULL;
    169   KitObjFile* objf = NULL;
    170   char* src = NULL;
    171   size_t src_len = 0;
    172   uint8_t* stdin_buf = NULL;
    173   size_t stdin_len = 0;
    174   int first_pos = 0; /* argv index of the first instruction token, 0 = none */
    175   int read_stdin = 0;
    176   int i, rc = 2;
    177 
    178   if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) {
    179     driver_help_mc();
    180     return argc < 2 ? 2 : 0;
    181   }
    182 
    183   memset(&o, 0, sizeof o);
    184   o.target = driver_host_target();
    185   driver_env_init(&env);
    186   if (driver_target_features_init(&tf, &env, argc) != 0) {
    187     driver_errf(MC_TOOL, "out of memory");
    188     driver_target_features_fini(&tf, &env);
    189     driver_env_fini(&env);
    190     return 1;
    191   }
    192 
    193   for (i = 1; i < argc; ++i) {
    194     const char* a = argv[i];
    195     if (driver_streq(a, "--")) {
    196       if (i + 1 < argc) first_pos = i + 1;
    197       break;
    198     }
    199     if (driver_streq(a, "-target")) {
    200       if (i + 1 >= argc) {
    201         driver_errf(MC_TOOL, "-target requires an argument");
    202         goto done;
    203       }
    204       if (driver_target_from_triple(argv[++i], &o.target) != 0) {
    205         driver_err_unknown_target(MC_TOOL, argv[i]);
    206         goto done;
    207       }
    208       continue;
    209     }
    210     if (driver_streq(a, "-p")) {
    211       o.plain = 1;
    212       continue;
    213     }
    214     {
    215       int tr = driver_target_features_try_consume(&tf, &env, MC_TOOL, argc,
    216                                                   argv, &i);
    217       if (tr < 0) goto done;
    218       if (tr > 0) continue;
    219     }
    220     if (driver_streq(a, "-")) {
    221       read_stdin = 1;
    222       first_pos = i; /* marks "have input"; stdin overrides token join */
    223       break;
    224     }
    225     if (a[0] == '-' && a[1] != '\0') {
    226       driver_errf(MC_TOOL, "unknown option: %s", a);
    227       goto done;
    228     }
    229     first_pos = i; /* first instruction token */
    230     break;
    231   }
    232 
    233   if (read_stdin) {
    234     if (!driver_read_stdin(&env, &stdin_buf, &stdin_len)) {
    235       driver_errf(MC_TOOL, "failed to read stdin");
    236       rc = 1;
    237       goto done;
    238     }
    239     src = (char*)stdin_buf;
    240     src_len = stdin_len;
    241   } else if (first_pos != 0) {
    242     src = mc_join_source(&env, first_pos, argc, argv, &src_len);
    243     if (!src) {
    244       driver_errf(MC_TOOL, "out of memory");
    245       rc = 1;
    246       goto done;
    247     }
    248   } else {
    249     driver_errf(MC_TOOL, "no instruction given");
    250     goto done;
    251   }
    252 
    253   ctx = driver_env_to_context(&env);
    254   if (driver_target_new(&ctx, o.target, &tf, MC_TOOL, &target) != KIT_OK ||
    255       driver_compiler_new(target, &ctx, &compiler) != KIT_OK) {
    256     driver_errf(MC_TOOL, "failed to initialize compiler");
    257     rc = 1;
    258     goto done;
    259   }
    260 
    261   {
    262     KitCompileSessionOptions sopts;
    263     KitAsmCompileOptions copts;
    264     KitSourceInput sin;
    265     KitStatus st;
    266     const uint8_t* mem_bytes;
    267     size_t mem_len;
    268     KitSlice objslice;
    269     const uint8_t* text = NULL;
    270     size_t text_len = 0;
    271 
    272     memset(&copts, 0, sizeof copts);
    273     memset(&sopts, 0, sizeof sopts);
    274     sopts.lang = KIT_LANG_ASM;
    275     sopts.compile.code = copts.code;
    276     sopts.compile.diagnostics = copts.diagnostics;
    277     sopts.compile.language_options = &copts;
    278     memset(&sin, 0, sizeof sin);
    279     sin.name = KIT_SLICE_LIT("<mc>");
    280     sin.bytes.data = (const uint8_t*)src;
    281     sin.bytes.len = src_len;
    282     sin.lang = KIT_LANG_ASM;
    283 
    284     st = kit_compile_session_new(compiler, &sopts, &session);
    285     if (st == KIT_OK) st = kit_compile_session_compile(session, &sin, &ob);
    286     if (st != KIT_OK) {
    287       /* diagnostics already went to stderr via the diag sink */
    288       rc = 1;
    289       goto done;
    290     }
    291     if (kit_writer_mem(ctx.heap, &mem) != KIT_OK) {
    292       driver_errf(MC_TOOL, "out of memory");
    293       rc = 1;
    294       goto done;
    295     }
    296     if (kit_obj_builder_emit(ob, mem) != KIT_OK) {
    297       driver_errf(MC_TOOL, "failed to emit object");
    298       rc = 1;
    299       goto done;
    300     }
    301     mem_bytes = kit_writer_mem_bytes(mem, &mem_len);
    302     objslice.data = mem_bytes;
    303     objslice.len = mem_len;
    304     if (kit_obj_open(&ctx, KIT_SLICE_LIT("<mc>"), &objslice, &objf) != KIT_OK) {
    305       driver_errf(MC_TOOL, "failed to read assembled object");
    306       rc = 1;
    307       goto done;
    308     }
    309     if (mc_text_section(objf, &text, &text_len) != 0) {
    310       driver_errf(MC_TOOL, "assembled object has no code");
    311       rc = 1;
    312       goto done;
    313     }
    314     if (o.plain)
    315       mc_print_plain(text, text_len);
    316     else
    317       mc_print_encoding(&ctx, target, text, text_len);
    318     mc_print_relocs(objf);
    319     rc = 0;
    320   }
    321 
    322 done:
    323   if (objf) kit_obj_free(objf);
    324   if (mem) kit_writer_close(mem);
    325   if (ob) kit_obj_builder_free(ob);
    326   if (session) kit_compile_session_free(session);
    327   if (compiler) driver_compiler_free(compiler);
    328   kit_target_free(target);
    329   if (stdin_buf) driver_free(&env, stdin_buf, stdin_len);
    330   if (src && !read_stdin) driver_free(&env, src, src_len);
    331   driver_target_features_fini(&tf, &env);
    332   driver_env_fini(&env);
    333   return rc;
    334 }