static int disassemble(RAsm *a, RAsmOp *op, const ut8 *buf, int len) { int rep = 1; if (len < 1) goto err; ut8 o = buf[0]; switch (o) { case 0x00: case 0x01: case 0x05: case 0x0b: case 0x0f: case 0x1a: case 0x1b: case 0x45: case 0x46: case 0x47: case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e: case 0x4f: case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57: case 0x58: case 0x59: case 0x5a: case 0x5b: case 0x5c: case 0x5d: case 0x5e: case 0x5f: case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: case 0x68: case 0x69: case 0x6a: case 0x6b: case 0x6c: case 0x6d: case 0x6e: case 0x6f: case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77: case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f: case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87: case 0x88: case 0x89: case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f: case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97: case 0x98: case 0x99: case 0x9a: case 0x9b: case 0x9c: case 0x9d: case 0x9e: case 0x9f: case 0xa0: case 0xa1: case 0xa2: case 0xa3: case 0xa4: case 0xa5: case 0xa6: case 0xa7: case 0xa8: case 0xa9: case 0xaa: case 0xab: case 0xac: case 0xad: case 0xae: case 0xaf: case 0xb0: case 0xb1: case 0xb2: case 0xb3: case 0xb4: case 0xb5: case 0xb6: case 0xb7: case 0xb8: case 0xb9: case 0xba: case 0xbb: case 0xbc: case 0xbd: case 0xbe: case 0xbf: snprintf (op->buf_asm, R_ASM_BUFSIZE, "%s", opcodes[o]); break; case 0x02: case 0x03: case 0x04: { if (len >= 2) { if (buf[1] == 0x40) { snprintf (op->buf_asm, R_ASM_BUFSIZE, "%s", opcodes[o]); } else { // TODO: block_type (value_type) snprintf (op->buf_asm, R_ASM_BUFSIZE, "%s ...", opcodes[o]); } rep = 2; } } break; case 0x0c: case 0x0d: case 0x10: { ut32 val = 0; size_t n = read_u32_leb128 (buf + 1, buf + len, &val); if (n > 0 && n < len) { snprintf (op->buf_asm, R_ASM_BUFSIZE, "%s %d", opcodes[o], val); rep += n; } } break; case 0x0e: { // TODO: br_table ut32 count = 0, table = 0, def = 0; size_t n = read_u32_leb128 (buf + 1, buf + len, &count); if (n > 0 && n < len) { rep += n; int i; for (i = 0; i < count; i++) { n = read_u32_leb128 (buf + n + 1, buf + len, &table); if (!n || len < rep + n) break; rep += n; } n = read_u32_leb128 (buf + n + 1, buf + len, &count); if (n > 0 && n + rep < len) { snprintf (op->buf_asm, R_ASM_BUFSIZE, "%s %d ... %d", opcodes[o], count, def); rep += n; } } } break; case 0x11: { ut32 val = 0, reserved = 0; size_t n = read_u32_leb128 (buf + 1, buf + len, &val); if (n > 0 && n < len) { rep += n; n = read_u32_leb128 (buf + n + 1, buf + len, &reserved); if (n == 1 || n + rep < len) { reserved &= 0x1; snprintf (op->buf_asm, R_ASM_BUFSIZE, "call_indirect %d %d", val, reserved); } } } break; case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: { ut32 val = 0; size_t n = read_u32_leb128 (buf + 1, buf + len, &val); if (n > 0 && n < len) { snprintf (op->buf_asm, R_ASM_BUFSIZE, "%s %d", opcodes[o], val); rep += n; } } break; case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f: case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: { ut32 flag = 0, offset = 0; size_t n = read_u32_leb128 (buf + 1, buf + len, &flag); if (n > 0 && n < len) { rep += n; size_t m = read_u32_leb128 (buf + 1 + n, buf + len, &offset); if (m > 0 && rep + m < len) { snprintf (op->buf_asm, R_ASM_BUFSIZE, "%s %d %d", opcodes[o], flag, offset); rep += m; } } } break; case 0x3f: case 0x40: { ut32 reserved = 0; size_t n = read_u32_leb128 (buf + 1, buf + len, &reserved); if (n == 1 && n < len) { reserved &= 0x1; snprintf (op->buf_asm, R_ASM_BUFSIZE, "%s %d", opcodes[o], reserved); rep += n; } } break; case 0x41: { st32 val = 0; size_t n = read_i32_leb128 (buf + 1, buf + len, &val); if (n > 0 && n < len) { snprintf (op->buf_asm, R_ASM_BUFSIZE, "%s %" PFMT32d, opcodes[o], val); rep += n; } } break; case 0x42: { st64 val = 0; size_t n = read_i64_leb128 (buf + 1, buf + len, &val); if (n > 0 && n < len) { snprintf (op->buf_asm, R_ASM_BUFSIZE, "%s %" PFMT64d, opcodes[o], val); rep += n; } } break; case 0x43: { ut32 val = 0; size_t n = read_u32_leb128 (buf + 1, buf + len, &val); if (n > 0 && n < len) { long double d = (long double)val; snprintf (op->buf_asm, R_ASM_BUFSIZE, "%s %" LDBLFMT, opcodes[o], d); rep += n; } } break; case 0x44: { ut64 val = 0; size_t n = read_u64_leb128 (buf + 1, buf + len, &val); if (n > 0 && n < len) { long double d = (long double)val; snprintf (op->buf_asm, R_ASM_BUFSIZE, "%s %" LDBLFMT, opcodes[o], d); rep += n; } } break; default: break; } err: op->size = rep; return rep; }
int wasm_dis(WasmOp *op, const unsigned char *buf, int buf_len) { op->len = 1; op->op = buf[0]; if (op->op > 0xbf) { return 1; } // add support for extension opcodes (SIMD + atomics) WasmOpDef *opdef = &opcodes[op->op]; switch (op->op) { case WASM_OP_TRAP: case WASM_OP_NOP: case WASM_OP_ELSE: case WASM_OP_RETURN: case WASM_OP_DROP: case WASM_OP_SELECT: case WASM_OP_I32EQZ: case WASM_OP_I32EQ: case WASM_OP_I32NE: case WASM_OP_I32LTS: case WASM_OP_I32LTU: case WASM_OP_I32GTS: case WASM_OP_I32GTU: case WASM_OP_I32LES: case WASM_OP_I32LEU: case WASM_OP_I32GES: case WASM_OP_I32GEU: case WASM_OP_I64EQZ: case WASM_OP_I64EQ: case WASM_OP_I64NE: case WASM_OP_I64LTS: case WASM_OP_I64LTU: case WASM_OP_I64GTS: case WASM_OP_I64GTU: case WASM_OP_I64LES: case WASM_OP_I64LEU: case WASM_OP_I64GES: case WASM_OP_I64GEU: case WASM_OP_F32EQ: case WASM_OP_F32NE: case WASM_OP_F32LT: case WASM_OP_F32GT: case WASM_OP_F32LE: case WASM_OP_F32GE: case WASM_OP_F64EQ: case WASM_OP_F64NE: case WASM_OP_F64LT: case WASM_OP_F64GT: case WASM_OP_F64LE: case WASM_OP_F64GE: case WASM_OP_I32CLZ: case WASM_OP_I32CTZ: case WASM_OP_I32POPCNT: case WASM_OP_I32ADD: case WASM_OP_I32SUB: case WASM_OP_I32MUL: case WASM_OP_I32DIVS: case WASM_OP_I32DIVU: case WASM_OP_I32REMS: case WASM_OP_I32REMU: case WASM_OP_I32AND: case WASM_OP_I32OR: case WASM_OP_I32XOR: case WASM_OP_I32SHL: case WASM_OP_I32SHRS: case WASM_OP_I32SHRU: case WASM_OP_I32ROTL: case WASM_OP_I32ROTR: case WASM_OP_I64CLZ: case WASM_OP_I64CTZ: case WASM_OP_I64POPCNT: case WASM_OP_I64ADD: case WASM_OP_I64SUB: case WASM_OP_I64MUL: case WASM_OP_I64DIVS: case WASM_OP_I64DIVU: case WASM_OP_I64REMS: case WASM_OP_I64REMU: case WASM_OP_I64AND: case WASM_OP_I64OR: case WASM_OP_I64XOR: case WASM_OP_I64SHL: case WASM_OP_I64SHRS: case WASM_OP_I64SHRU: case WASM_OP_I64ROTL: case WASM_OP_I64ROTR: case WASM_OP_F32ABS: case WASM_OP_F32NEG: case WASM_OP_F32CEIL: case WASM_OP_F32FLOOR: case WASM_OP_F32TRUNC: case WASM_OP_F32NEAREST: case WASM_OP_F32SQRT: case WASM_OP_F32ADD: case WASM_OP_F32SUB: case WASM_OP_F32MUL: case WASM_OP_F32DIV: case WASM_OP_F32MIN: case WASM_OP_F32MAX: case WASM_OP_F32COPYSIGN: case WASM_OP_F64ABS: case WASM_OP_F64NEG: case WASM_OP_F64CEIL: case WASM_OP_F64FLOOR: case WASM_OP_F64TRUNC: case WASM_OP_F64NEAREST: case WASM_OP_F64SQRT: case WASM_OP_F64ADD: case WASM_OP_F64SUB: case WASM_OP_F64MUL: case WASM_OP_F64DIV: case WASM_OP_F64MIN: case WASM_OP_F64MAX: case WASM_OP_F64COPYSIGN: case WASM_OP_I32WRAPI64: case WASM_OP_I32TRUNCSF32: case WASM_OP_I32TRUNCUF32: case WASM_OP_I32TRUNCSF64: case WASM_OP_I32TRUNCUF64: case WASM_OP_I64EXTENDSI32: case WASM_OP_I64EXTENDUI32: case WASM_OP_I64TRUNCSF32: case WASM_OP_I64TRUNCUF32: case WASM_OP_I64TRUNCSF64: case WASM_OP_I64TRUNCUF64: case WASM_OP_F32CONVERTSI32: case WASM_OP_F32CONVERTUI32: case WASM_OP_F32CONVERTSI64: case WASM_OP_F32CONVERTUI64: case WASM_OP_F32DEMOTEF64: case WASM_OP_F64CONVERTSI32: case WASM_OP_F64CONVERTUI32: case WASM_OP_F64CONVERTSI64: case WASM_OP_F64CONVERTUI64: case WASM_OP_F64PROMOTEF32: case WASM_OP_I32REINTERPRETF32: case WASM_OP_I64REINTERPRETF64: case WASM_OP_F32REINTERPRETI32: case WASM_OP_F64REINTERPRETI64: case WASM_OP_END: { snprintf (op->txt, R_ASM_BUFSIZE, "%s", opdef->txt); } break; case WASM_OP_BLOCK: case WASM_OP_LOOP: case WASM_OP_IF: { st32 val = 0; size_t n = read_i32_leb128 (buf + 1, buf + buf_len, &val); if (!(n > 0 && n < buf_len)) goto err; switch (0x80 - val) { case R_BIN_WASM_VALUETYPE_EMPTY: snprintf (op->txt, R_ASM_BUFSIZE, "%s", opdef->txt); break; case R_BIN_WASM_VALUETYPE_i32: snprintf (op->txt, R_ASM_BUFSIZE, "%s (result i32)", opdef->txt); break; case R_BIN_WASM_VALUETYPE_i64: snprintf (op->txt, R_ASM_BUFSIZE, "%s (result i64)", opdef->txt); break; case R_BIN_WASM_VALUETYPE_f32: snprintf (op->txt, R_ASM_BUFSIZE, "%s (result f32)", opdef->txt); break; case R_BIN_WASM_VALUETYPE_f64: snprintf (op->txt, R_ASM_BUFSIZE, "%s (result f64)", opdef->txt); break; default: snprintf (op->txt, R_ASM_BUFSIZE, "%s (result ?)", opdef->txt); break; } op->len += n; } break; case WASM_OP_BR: case WASM_OP_BRIF: case WASM_OP_CALL: { ut32 val = 0; size_t n = read_u32_leb128 (buf + 1, buf + buf_len, &val); if (!(n > 0 && n < buf_len)) goto err; snprintf (op->txt, R_ASM_BUFSIZE, "%s %d", opdef->txt, val); op->len += n; } break; case WASM_OP_BRTABLE: { ut32 count = 0, *table = NULL, def = 0; size_t n = read_u32_leb128 (buf + 1, buf + buf_len, &count); if (!(n > 0 && n < buf_len)) { goto err; } if (!(table = calloc (count, sizeof (ut32)))) { goto err; } int i = 0; op->len += n; for (i = 0; i < count; i++) { n = read_u32_leb128 (buf + op->len, buf + buf_len, &table[i]); if (!(op->len + n <= buf_len)) { goto beach; } op->len += n; } n = read_u32_leb128 (buf + op->len, buf + buf_len, &def); if (!(n > 0 && n + op->len < buf_len)) { goto beach; } op->len += n; snprintf (op->txt, R_ASM_BUFSIZE, "%s %d ", opdef->txt, count); for (i = 0; i < count && strlen (op->txt) + 10 < R_ASM_BUFSIZE; i++) { int optxtlen = strlen (op->txt); snprintf (op->txt + optxtlen, R_ASM_BUFSIZE - optxtlen, "%d ", table[i]); } snprintf (op->txt + strlen (op->txt), R_ASM_BUFSIZE, "%d", def); free (table); break; beach: free (table); goto err; } break; case WASM_OP_CALLINDIRECT: { ut32 val = 0, reserved = 0; size_t n = read_u32_leb128 (buf + 1, buf + buf_len, &val); if (!(n > 0 && n < buf_len)) goto err; op->len += n; n = read_u32_leb128 (buf + op->len, buf + buf_len, &reserved); if (!(n == 1 && op->len + n <= buf_len)) goto err; reserved &= 0x1; snprintf (op->txt, R_ASM_BUFSIZE, "%s %d %d", opdef->txt, val, reserved); op->len += n; } break; case WASM_OP_GETLOCAL: case WASM_OP_SETLOCAL: case WASM_OP_TEELOCAL: case WASM_OP_GETGLOBAL: case WASM_OP_SETGLOBAL: { ut32 val = 0; size_t n = read_u32_leb128 (buf + 1, buf + buf_len, &val); if (!(n > 0 && n < buf_len)) goto err; snprintf (op->txt, R_ASM_BUFSIZE, "%s %d", opdef->txt, val); op->len += n; } break; case WASM_OP_I32LOAD: case WASM_OP_I64LOAD: case WASM_OP_F32LOAD: case WASM_OP_F64LOAD: case WASM_OP_I32LOAD8S: case WASM_OP_I32LOAD8U: case WASM_OP_I32LOAD16S: case WASM_OP_I32LOAD16U: case WASM_OP_I64LOAD8S: case WASM_OP_I64LOAD8U: case WASM_OP_I64LOAD16S: case WASM_OP_I64LOAD16U: case WASM_OP_I64LOAD32S: case WASM_OP_I64LOAD32U: case WASM_OP_I32STORE: case WASM_OP_I64STORE: case WASM_OP_F32STORE: case WASM_OP_F64STORE: case WASM_OP_I32STORE8: case WASM_OP_I32STORE16: case WASM_OP_I64STORE8: case WASM_OP_I64STORE16: case WASM_OP_I64STORE32: { ut32 flag = 0, offset = 0; size_t n = read_u32_leb128 (buf + 1, buf + buf_len, &flag); if (!(n > 0 && n < buf_len)) goto err; op->len += n; n = read_u32_leb128 (buf + op->len, buf + buf_len, &offset); if (!(n > 0 && op->len + n <= buf_len)) goto err; snprintf (op->txt, R_ASM_BUFSIZE, "%s %d %d", opdef->txt, flag, offset); op->len += n; } break; case WASM_OP_CURRENTMEMORY: case WASM_OP_GROWMEMORY: { ut32 reserved = 0; size_t n = read_u32_leb128 (buf + 1, buf + buf_len, &reserved); if (!(n == 1 && n < buf_len)) goto err; reserved &= 0x1; snprintf (op->txt, R_ASM_BUFSIZE, "%s %d", opdef->txt, reserved); op->len += n; } break; case WASM_OP_I32CONST: { st32 val = 0; size_t n = read_i32_leb128 (buf + 1, buf + buf_len, &val); if (!(n > 0 && n < buf_len)) goto err; snprintf (op->txt, R_ASM_BUFSIZE, "%s %" PFMT32d, opdef->txt, val); op->len += n; } break; case WASM_OP_I64CONST: { st64 val = 0; size_t n = read_i64_leb128 (buf + 1, buf + buf_len, &val); if (!(n > 0 && n < buf_len)) goto err; snprintf (op->txt, R_ASM_BUFSIZE, "%s %" PFMT64d, opdef->txt, val); op->len += n; } break; case WASM_OP_F32CONST: { ut32 val = 0; size_t n = read_u32_leb128 (buf + 1, buf + buf_len, &val); if (!(n > 0 && n < buf_len)) goto err; long double d = (long double)val; snprintf (op->txt, R_ASM_BUFSIZE, "%s %" LDBLFMT, opdef->txt, d); op->len += n; } break; case WASM_OP_F64CONST: { ut64 val = 0; size_t n = read_u64_leb128 (buf + 1, buf + buf_len, &val); if (!(n > 0 && n < buf_len)) goto err; long double d = (long double)val; snprintf (op->txt, R_ASM_BUFSIZE, "%s %" LDBLFMT, opdef->txt, d); op->len += n; } break; default: goto err; } return op->len; err: op->len = 1; snprintf (op->txt, R_ASM_BUFSIZE, "invalid"); return op->len; }