disas.c (8675B)
1 #include <kit/core.h> 2 #include <kit/disasm.h> 3 #include <stddef.h> 4 #include <stdint.h> 5 #include <string.h> 6 7 #include "driver.h" 8 #include "env.h" 9 10 /* `kit disas` — disassemble raw machine-code bytes for a target arch. Unlike 11 * `objdump -d`, the input is a headerless byte buffer (a file, stdin, or an 12 * inline hex string), not a parsed object — handy for inspecting a snippet of 13 * codegen or an emitted instruction. The arch comes from -target (host 14 * default); --base sets the address shown for the first byte. */ 15 16 #define DISAS_TOOL "disas" 17 18 typedef struct DisasOpts { 19 KitTargetSpec target; 20 uint64_t base; /* --base: vaddr of the first byte */ 21 const char* hex; /* -x: inline hex string, or NULL */ 22 const char* in; /* positional file, "-" for stdin, or NULL */ 23 } DisasOpts; 24 25 void driver_help_disas(void) { 26 driver_printf( 27 "%.*s", 28 KIT_SLICE_ARG(KIT_SLICE_LIT( 29 "kit disas — disassemble raw machine-code bytes\n" 30 "\n" 31 "USAGE\n" 32 " kit disas [-target TRIPLE] [--base ADDR] -x \"HEX...\"\n" 33 " kit disas [-target TRIPLE] [--base ADDR] [FILE|-]\n" 34 "\n" 35 "DESCRIPTION\n" 36 " Decodes a headerless buffer of machine code. Bytes come from an\n" 37 " inline hex string (-x), a FILE, or stdin (no FILE, or `-`).\n" 38 " Hex may contain spaces. The buffer is treated as code for the\n" 39 " -target architecture (the host arch by default).\n" 40 "\n" 41 "OPTIONS\n" 42 " -target TRIPLE architecture to decode for (e.g. aarch64,\n" 43 " x86_64, riscv64). See `kit cc --help`.\n" 44 " -x \"HEX...\" disassemble these hex bytes (spaces allowed)\n" 45 " --base ADDR address of the first byte (default 0)\n" 46 " -h, --help show this help\n" 47 "\n" 48 "EXIT CODES\n" 49 " 0 success 1 error 2 bad usage\n"))); 50 } 51 52 static int disas_is_hex(int c) { 53 return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || 54 (c >= 'A' && c <= 'F'); 55 } 56 static int disas_hexval(int c) { 57 if (c >= '0' && c <= '9') return c - '0'; 58 if (c >= 'a' && c <= 'f') return c - 'a' + 10; 59 return c - 'A' + 10; 60 } 61 62 /* Decode a whitespace-tolerant hex string into freshly-allocated bytes. 63 * Returns 0 on success (caller frees via driver_free(env, *out, *outlen)). */ 64 static int disas_parse_hex(DriverEnv* env, const char* s, uint8_t** out, 65 size_t* outlen) { 66 size_t ndig = 0, n, bi = 0; 67 const char* p; 68 uint8_t* buf; 69 int hi = -1; 70 for (p = s; *p; ++p) { 71 if (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') continue; 72 if (!disas_is_hex((unsigned char)*p)) { 73 driver_errf(DISAS_TOOL, "invalid hex digit: '%c'", *p); 74 return 1; 75 } 76 ++ndig; 77 } 78 if (ndig == 0) { 79 driver_errf(DISAS_TOOL, "no hex bytes given"); 80 return 1; 81 } 82 if (ndig & 1) { 83 driver_errf(DISAS_TOOL, "hex string has an odd number of digits"); 84 return 1; 85 } 86 n = ndig / 2; 87 buf = (uint8_t*)driver_alloc(env, n); 88 if (!buf) { 89 driver_errf(DISAS_TOOL, "out of memory"); 90 return 1; 91 } 92 for (p = s; *p; ++p) { 93 int v; 94 if (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') continue; 95 v = disas_hexval((unsigned char)*p); 96 if (hi < 0) 97 hi = v; 98 else { 99 buf[bi++] = (uint8_t)((hi << 4) | v); 100 hi = -1; 101 } 102 } 103 *out = buf; 104 *outlen = n; 105 return 0; 106 } 107 108 static void disas_run(const KitDisasmContext* dctx, const uint8_t* data, 109 size_t len, uint64_t base) { 110 KitDisasmIter* it = NULL; 111 KitInsn insn; 112 if (kit_disasm_iter_new(dctx, data, len, base, NULL, &it) != KIT_OK) { 113 driver_errf(DISAS_TOOL, "no disassembler for the selected target"); 114 return; 115 } 116 while (kit_disasm_iter_next(it, &insn) == KIT_ITER_ITEM) { 117 uint32_t b; 118 driver_printf("%8llx:\t", (unsigned long long)insn.vaddr); 119 for (b = 0; b < insn.nbytes; ++b) driver_printf("%02x ", insn.bytes[b]); 120 for (b = insn.nbytes; b < 8; ++b) driver_printf(" "); 121 driver_printf("\t%.*s", KIT_SLICE_ARG(insn.mnemonic)); 122 if (insn.operands.len) driver_printf(" %.*s", KIT_SLICE_ARG(insn.operands)); 123 if (insn.annotation.len) 124 driver_printf(" # %.*s", KIT_SLICE_ARG(insn.annotation)); 125 driver_printf("\n"); 126 } 127 kit_disasm_iter_free(it); 128 } 129 130 int driver_disas(int argc, char** argv) { 131 DriverEnv env; 132 KitContext ctx; 133 KitDisasmContext dctx; 134 DisasOpts o; 135 DriverTargetFeatures tf = {0}; 136 KitTarget* target = NULL; 137 const uint8_t* data = NULL; 138 size_t len = 0; 139 DriverLoad ld = {0}; 140 uint8_t* sbuf = NULL; 141 size_t sbuf_len = 0; 142 uint8_t* hexbuf = NULL; 143 size_t hexbuf_len = 0; 144 int loaded = 0, npos = 0, rc = 2; 145 int i; 146 147 if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) { 148 driver_help_disas(); 149 return argc < 2 ? 2 : 0; 150 } 151 152 memset(&o, 0, sizeof o); 153 o.target = driver_host_target(); 154 driver_env_init(&env); 155 if (driver_target_features_init(&tf, &env, argc) != 0) { 156 driver_errf(DISAS_TOOL, "out of memory"); 157 driver_target_features_fini(&tf, &env); 158 driver_env_fini(&env); 159 return 1; 160 } 161 162 for (i = 1; i < argc; ++i) { 163 const char* a = argv[i]; 164 if (driver_streq(a, "-target")) { 165 if (i + 1 >= argc) { 166 driver_errf(DISAS_TOOL, "-target requires an argument"); 167 goto done; 168 } 169 if (driver_target_from_triple(argv[++i], &o.target) != 0) { 170 driver_errf(DISAS_TOOL, "unrecognized target: %s", argv[i]); 171 goto done; 172 } 173 continue; 174 } 175 { 176 int tr = driver_target_features_try_consume(&tf, &env, DISAS_TOOL, argc, 177 argv, &i); 178 if (tr < 0) goto done; 179 if (tr > 0) continue; 180 } 181 if (driver_streq(a, "-x")) { 182 if (i + 1 >= argc) { 183 driver_errf(DISAS_TOOL, "-x requires a hex string"); 184 goto done; 185 } 186 o.hex = argv[++i]; 187 continue; 188 } 189 if (driver_streq(a, "--base")) { 190 if (i + 1 >= argc) { 191 driver_errf(DISAS_TOOL, "--base requires an address"); 192 goto done; 193 } 194 { 195 const char* v = argv[++i]; 196 uint64_t val = 0; 197 int base = 10; 198 if (v[0] == '0' && (v[1] == 'x' || v[1] == 'X')) { 199 base = 16; 200 v += 2; 201 } 202 if (!*v) { 203 driver_errf(DISAS_TOOL, "invalid --base address"); 204 goto done; 205 } 206 for (; *v; ++v) { 207 int d; 208 if (*v >= '0' && *v <= '9') 209 d = *v - '0'; 210 else if (base == 16 && *v >= 'a' && *v <= 'f') 211 d = *v - 'a' + 10; 212 else if (base == 16 && *v >= 'A' && *v <= 'F') 213 d = *v - 'A' + 10; 214 else { 215 driver_errf(DISAS_TOOL, "invalid --base address"); 216 goto done; 217 } 218 val = val * (uint64_t)base + (uint64_t)d; 219 } 220 o.base = val; 221 } 222 continue; 223 } 224 if (driver_streq(a, "-")) { 225 if (npos == 0) o.in = "-"; 226 ++npos; 227 continue; 228 } 229 if (a[0] == '-' && a[1] != '\0') { 230 driver_errf(DISAS_TOOL, "unknown option: %s", a); 231 goto done; 232 } 233 if (npos == 0) 234 o.in = a; 235 else { 236 driver_errf(DISAS_TOOL, "too many operands: %s", a); 237 goto done; 238 } 239 ++npos; 240 } 241 242 /* Resolve the byte source: -x wins; else file/stdin. */ 243 if (o.hex) { 244 if (o.in) { 245 driver_errf(DISAS_TOOL, "give either -x or a file, not both"); 246 goto done; 247 } 248 if (disas_parse_hex(&env, o.hex, &hexbuf, &hexbuf_len) != 0) { 249 rc = 1; 250 goto done; 251 } 252 data = hexbuf; 253 len = hexbuf_len; 254 } else if (o.in && !driver_streq(o.in, "-")) { 255 KitSlice in; 256 if (driver_load_bytes(&env.file_io, DISAS_TOOL, o.in, &ld, &in) != 0) { 257 rc = 1; 258 goto done; 259 } 260 loaded = 1; 261 data = in.data; 262 len = in.len; 263 } else { 264 if (!driver_read_stdin(&env, &sbuf, &sbuf_len)) { 265 driver_errf(DISAS_TOOL, "failed to read stdin"); 266 rc = 1; 267 goto done; 268 } 269 data = sbuf; 270 len = sbuf_len; 271 } 272 273 ctx = driver_env_to_context(&env); 274 if (driver_target_new(&ctx, o.target, &tf, DISAS_TOOL, &target) != KIT_OK) { 275 driver_errf(DISAS_TOOL, "failed to initialize target"); 276 rc = 1; 277 goto done; 278 } 279 memset(&dctx, 0, sizeof dctx); 280 dctx.target = target; 281 dctx.context = ctx; 282 disas_run(&dctx, data, len, o.base); 283 rc = 0; 284 285 done: 286 kit_target_free(target); 287 if (hexbuf) driver_free(&env, hexbuf, hexbuf_len); 288 if (sbuf) driver_free(&env, sbuf, sbuf_len); 289 if (loaded) driver_release_bytes(&env.file_io, &ld); 290 driver_target_features_fini(&tf, &env); 291 driver_env_fini(&env); 292 return rc; 293 }