disas.c (8989B)
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. Recognized audited\n" 43 " spellings include aarch64, x86_64, and riscv64;\n" 44 " a canonical triple with one of those arches is\n" 45 " also accepted. Default: host architecture.\n" 46 " -x \"HEX...\" disassemble these hex bytes (spaces allowed)\n" 47 " --base ADDR address of the first byte (default 0)\n" 48 " -h, --help show this help\n" 49 "\n" 50 "INPUT AND LIMITATIONS\n" 51 " FILE is raw code, not an object file; use kit objdump -d for\n" 52 " objects. Use -- before a leading-dash file name. A trailing\n" 53 " partial fixed-width instruction is\n" 54 " rendered as `(truncated)` and currently still returns success.\n" 55 "\n" 56 "EXAMPLES\n" 57 " kit disas -target x86_64 -x 'b8 2a000000 c3'\n" 58 " kit disas -target aarch64 --base 0x1000 -x 'c0035fd6'\n" 59 " kit disas -target riscv64 code.bin\n" 60 " bytes=$(kit mc -target aarch64 -p 'ret')\n" 61 " kit disas -target aarch64 -x \"$bytes\"\n" 62 "\n" 63 "EXIT CODES\n" 64 " 0 success 1 error 2 bad usage\n"))); 65 } 66 67 static int disas_is_hex(int c) { 68 return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || 69 (c >= 'A' && c <= 'F'); 70 } 71 72 /* Decode a whitespace-tolerant hex string into freshly-allocated bytes. 73 * Returns 0 on success (caller frees via driver_free(env, *out, *outlen)). */ 74 static int disas_parse_hex(DriverEnv* env, const char* s, uint8_t** out, 75 size_t* outlen) { 76 size_t ndig = 0, n, bi = 0; 77 const char* p; 78 uint8_t* buf; 79 int hi = -1; 80 for (p = s; *p; ++p) { 81 if (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') continue; 82 if (!disas_is_hex((unsigned char)*p)) { 83 driver_errf(DISAS_TOOL, "invalid hex digit: '%c'", *p); 84 return 1; 85 } 86 ++ndig; 87 } 88 if (ndig == 0) { 89 driver_errf(DISAS_TOOL, "no hex bytes given"); 90 return 1; 91 } 92 if (ndig & 1) { 93 driver_errf(DISAS_TOOL, "hex string has an odd number of digits"); 94 return 1; 95 } 96 n = ndig / 2; 97 buf = (uint8_t*)driver_alloc(env, n); 98 if (!buf) { 99 driver_errf(DISAS_TOOL, "out of memory"); 100 return 1; 101 } 102 for (p = s; *p; ++p) { 103 int v; 104 if (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') continue; 105 v = driver_hex_nibble((char)(unsigned char)*p); 106 if (hi < 0) 107 hi = v; 108 else { 109 buf[bi++] = (uint8_t)((hi << 4) | v); 110 hi = -1; 111 } 112 } 113 *out = buf; 114 *outlen = n; 115 return 0; 116 } 117 118 static void disas_run(const KitDisasmContext* dctx, const uint8_t* data, 119 size_t len, uint64_t base) { 120 KitDisasmIter* it = NULL; 121 KitInsn insn; 122 if (kit_disasm_iter_new(dctx, data, len, base, NULL, &it) != KIT_OK) { 123 driver_errf(DISAS_TOOL, "no disassembler for the selected target"); 124 return; 125 } 126 while (kit_disasm_iter_next(it, &insn) == KIT_ITER_ITEM) { 127 uint32_t b; 128 driver_printf("%8llx:\t", (unsigned long long)insn.vaddr); 129 for (b = 0; b < insn.nbytes; ++b) driver_printf("%02x ", insn.bytes[b]); 130 for (b = insn.nbytes; b < 8; ++b) driver_printf(" "); 131 driver_printf("\t%.*s", KIT_SLICE_ARG(insn.mnemonic)); 132 if (insn.operands.len) driver_printf(" %.*s", KIT_SLICE_ARG(insn.operands)); 133 if (insn.annotation.len) 134 driver_printf(" # %.*s", KIT_SLICE_ARG(insn.annotation)); 135 driver_printf("\n"); 136 } 137 kit_disasm_iter_free(it); 138 } 139 140 int driver_disas(int argc, char** argv) { 141 DriverEnv env; 142 KitContext ctx; 143 KitDisasmContext dctx; 144 DisasOpts o; 145 DriverTargetFeatures tf = {0}; 146 KitTarget* target = NULL; 147 const uint8_t* data = NULL; 148 size_t len = 0; 149 DriverLoad ld = {0}; 150 uint8_t* sbuf = NULL; 151 size_t sbuf_len = 0; 152 uint8_t* hexbuf = NULL; 153 size_t hexbuf_len = 0; 154 int loaded = 0, npos = 0, rc = 2, options = 1; 155 int i; 156 157 if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) { 158 driver_help_disas(); 159 return argc < 2 ? 2 : 0; 160 } 161 162 memset(&o, 0, sizeof o); 163 o.target = driver_host_target(); 164 driver_env_init(&env); 165 if (driver_target_features_init(&tf, &env, argc) != 0) { 166 driver_errf(DISAS_TOOL, "out of memory"); 167 driver_target_features_fini(&tf, &env); 168 driver_env_fini(&env); 169 return 1; 170 } 171 172 for (i = 1; i < argc; ++i) { 173 const char* a = argv[i]; 174 if (options && driver_streq(a, "--")) { 175 options = 0; 176 continue; 177 } 178 if (!options) { 179 if (npos == 0) 180 o.in = a; 181 else { 182 driver_errf(DISAS_TOOL, "too many operands: %s", a); 183 goto done; 184 } 185 ++npos; 186 continue; 187 } 188 if (driver_streq(a, "-target")) { 189 if (i + 1 >= argc) { 190 driver_errf(DISAS_TOOL, "-target requires an argument"); 191 goto done; 192 } 193 if (driver_target_from_triple(argv[++i], &o.target) != 0) { 194 driver_err_unknown_target(DISAS_TOOL, argv[i]); 195 goto done; 196 } 197 continue; 198 } 199 { 200 int tr = driver_target_features_try_consume(&tf, &env, DISAS_TOOL, argc, 201 argv, &i); 202 if (tr < 0) goto done; 203 if (tr > 0) continue; 204 } 205 if (driver_streq(a, "-x")) { 206 if (i + 1 >= argc) { 207 driver_errf(DISAS_TOOL, "-x requires a hex string"); 208 goto done; 209 } 210 o.hex = argv[++i]; 211 continue; 212 } 213 if (driver_streq(a, "--base")) { 214 if (i + 1 >= argc) { 215 driver_errf(DISAS_TOOL, "--base requires an address"); 216 goto done; 217 } 218 if (driver_parse_u64(argv[++i], &o.base) != 0) { 219 driver_errf(DISAS_TOOL, "invalid --base address"); 220 goto done; 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 }