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 }