// finds the address of the call function (essentially where to jump to). static ut64 get_cf_offset(RAnal *anal, const ut8 *data, int len) { ut32 fcn_id; if (!read_u32_leb128 (&data[1], &data[len - 1], &fcn_id)) { return UT64_MAX; } r_cons_push (); // 0xfff.. are bad addresses for wasm // cgvwzq: 0xfff... can be external imported JS funcs char *s = anal->coreb.cmdstrf (anal->coreb.core, "is~FUNC[2:%u]", fcn_id); r_cons_pop (); if (s) { ut64 n = r_num_get (NULL, s); free (s); return n; } return UT64_MAX; }
static void set_br_jump(RAnalOp *op, const ut8 *data, int len) { ut32 val; read_u32_leb128 (data + 1, data + len, &val); ut32 pos = wasm_stack_ptr - (val + 1); if (pos < wasm_stack_ptr) { ut64 jump = wasm_stack[pos].loop; if (jump != UT64_MAX) { // if it is a loop, branch to beginning. op->jump = jump; } else { // if it is a block, branch to the end. jump = wasm_stack[pos].end; if (jump != UT64_MAX) { // always pointing to an 'end' op->jump = jump + 1; } } } }
// analyzes the wasm opcode. static int wasm_op(RAnal *anal, RAnalOp *op, ut64 addr, const ut8 *data, int len) { WasmOp wop = {{0}}; RAnalHint *hint = NULL; memset (op, '\0', sizeof (RAnalOp)); int ret = wasm_dis (&wop, data, len); op->jump = UT64_MAX; op->fail = UT64_MAX; op->ptr = op->val = UT64_MAX; op->size = ret; op->addr = addr; op->sign = true; op->type = R_ANAL_OP_TYPE_UNK; switch (wop.type) { case WASM_TYPE_OP_CORE: op->id = wop.op.core; break; case WASM_TYPE_OP_ATOMIC: op->id = (0xfe << 8) | wop.op.atomic; break; } if (!wop.txt || !strncmp (wop.txt, "invalid", 7)) { op->type = R_ANAL_OP_TYPE_ILL; free (wop.txt); return -1; } if (addr_old == addr && (wop.type != WASM_TYPE_OP_CORE || wop.op.core != WASM_OP_END)) { goto anal_end; } switch (wop.type) { case WASM_TYPE_OP_CORE: switch (wop.op.core) { /* Calls here are using index instead of address */ case WASM_OP_LOOP: op->type = R_ANAL_OP_TYPE_NOP; if (!(hint = r_anal_hint_get (anal, addr))) { scope_hint--; r_anal_hint_set_opcode (anal, scope_hint, "loop"); r_anal_hint_set_jump (anal, scope_hint, addr); } break; case WASM_OP_BLOCK: op->type = R_ANAL_OP_TYPE_NOP; if (!(hint = r_anal_hint_get (anal, addr))) { scope_hint--; r_anal_hint_set_opcode (anal, scope_hint, "block"); r_anal_hint_set_jump (anal, scope_hint, addr); } break; case WASM_OP_IF: if (!(hint = r_anal_hint_get (anal, addr))) { scope_hint--; r_anal_hint_set_opcode (anal, scope_hint, "if"); r_anal_hint_set_jump (anal, scope_hint, addr); if (advance_till_scope_end (anal, op, addr + op->size, R_ANAL_OP_TYPE_CJMP, 0, true)) { op->fail = addr + op->size; } } else { op->type = R_ANAL_OP_TYPE_CJMP; op->jump = hint->jump; op->fail = addr + op->size; } break; case WASM_OP_ELSE: // get if and set hint. if (!(hint = r_anal_hint_get (anal, addr))) { advance_till_scope_end (anal, op, addr + op->size, R_ANAL_OP_TYPE_JMP, 0, true); } else { op->type = R_ANAL_OP_TYPE_JMP; op->jump = hint->jump; } break; case WASM_OP_BR: { RAnalHint *hint2 = NULL; ut32 val; read_u32_leb128 (data + 1, data + len, &val); if ((hint2 = r_anal_hint_get (anal, addr)) && hint2->jump != UT64_MAX) { op->type = R_ANAL_OP_TYPE_JMP; op->jump = hint2->jump; } else if ((hint = r_anal_hint_get (anal, scope_hint))) { if (hint->opcode && !strncmp ("loop", hint->opcode, 4)) { op->type = R_ANAL_OP_TYPE_JMP; op->jump = hint->jump; r_anal_hint_set_jump (anal, addr, op->jump); } else { if (advance_till_scope_end (anal, op, addr + op->size, R_ANAL_OP_TYPE_JMP, val, false)) { r_anal_hint_set_jump (anal, addr, op->jump); } } } else { if (advance_till_scope_end (anal, op, addr + op->size, R_ANAL_OP_TYPE_JMP, val, false)) { eprintf ("[wasm] cannot find jump type for br (using block type)\n"); r_anal_hint_set_jump (anal, addr, op->jump); } else { eprintf ("[wasm] cannot find jump for br\n"); } } r_anal_hint_free (hint2); } break; case WASM_OP_BRIF: { RAnalHint *hint2 = NULL; ut32 val; read_u32_leb128 (data + 1, data + len, &val); if ((hint2 = r_anal_hint_get (anal, addr)) && hint2->jump != UT64_MAX) { op->type = R_ANAL_OP_TYPE_CJMP; op->jump = hint2->jump; op->fail = addr + op->size; } else if ((hint = r_anal_hint_get (anal, scope_hint))) { if (hint->opcode && !strncmp ("loop", hint->opcode, 4)) { op->fail = addr + op->size; op->jump = hint->jump; r_anal_hint_set_jump (anal, addr, op->jump); } else { if (advance_till_scope_end (anal, op, addr + op->size, R_ANAL_OP_TYPE_CJMP, val, false)) { op->fail = addr + op->size; r_anal_hint_set_jump (anal, addr, op->jump); } } } else { if (advance_till_scope_end (anal, op, addr + op->size, R_ANAL_OP_TYPE_CJMP, val, false)) { eprintf ("[wasm] cannot find jump type for br_if (using block type)\n"); op->fail = addr + op->size; r_anal_hint_set_jump (anal, addr, op->jump); } else { eprintf ("[wasm] cannot find jump for br_if\n"); } } r_anal_hint_free (hint2); } break; case WASM_OP_END: { op->type = R_ANAL_OP_TYPE_NOP; if (scope_hint < UT64_MAX) { hint = r_anal_hint_get (anal, scope_hint); if (hint && !strncmp ("loop", hint->opcode, 4)) { r_anal_hint_set_jump (anal, addr, op->jump); r_anal_hint_set_jump (anal, op->jump, addr); } else if (hint && !strncmp ("block", hint->opcode, 5)) { // if/else/block r_anal_hint_set_jump (anal, hint->jump, addr); r_anal_hint_set_jump (anal, addr, UT64_MAX); } if (hint) { r_anal_hint_set_opcode (anal, scope_hint, "invalid"); r_anal_hint_set_jump (anal, scope_hint, UT64_MAX); r_anal_hint_del (anal, scope_hint, 1); scope_hint++; } else { // all wasm routines ends with an end. op->eob = true; op->type = R_ANAL_OP_TYPE_RET; scope_hint = UT64_MAX; } } else { if (!(hint = r_anal_hint_get (anal, addr))) { // all wasm routines ends with an end. op->eob = true; op->type = R_ANAL_OP_TYPE_RET; } } } break; case WASM_OP_I32REMS: case WASM_OP_I32REMU: op->type = R_ANAL_OP_TYPE_MOD; break; case WASM_OP_GETLOCAL: 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: op->type = R_ANAL_OP_TYPE_LOAD; break; case WASM_OP_SETLOCAL: case WASM_OP_TEELOCAL: op->type = R_ANAL_OP_TYPE_STORE; break; 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: op->type = R_ANAL_OP_TYPE_CMP; break; case WASM_OP_I64OR: case WASM_OP_I32OR: op->type = R_ANAL_OP_TYPE_OR; break; case WASM_OP_I64XOR: case WASM_OP_I32XOR: op->type = R_ANAL_OP_TYPE_XOR; break; case WASM_OP_I32CONST: case WASM_OP_I64CONST: case WASM_OP_F32CONST: case WASM_OP_F64CONST: op->type = R_ANAL_OP_TYPE_MOV; { ut8 arg = data[1]; r_strbuf_setf (&op->esil, "4,sp,-=,%d,sp,=[4]", arg); } break; case WASM_OP_I64ADD: case WASM_OP_I32ADD: case WASM_OP_F32ADD: case WASM_OP_F64ADD: op->type = R_ANAL_OP_TYPE_ADD; break; case WASM_OP_I64SUB: case WASM_OP_I32SUB: case WASM_OP_F32SUB: case WASM_OP_F64SUB: op->type = R_ANAL_OP_TYPE_SUB; break; case WASM_OP_NOP: op->type = R_ANAL_OP_TYPE_NOP; r_strbuf_setf (&op->esil, ""); break; case WASM_OP_CALL: case WASM_OP_CALLINDIRECT: op->type = R_ANAL_OP_TYPE_CALL; op->jump = get_cf_offset (anal, data, len); op->fail = addr + op->size; if (op->jump != UT64_MAX) { op->ptr = op->jump; } r_strbuf_setf (&op->esil, "4,sp,-=,0x%"PFMT64x",sp,=[4],0x%"PFMT64x",pc,=", op->fail, op->jump); break; case WASM_OP_RETURN: // should be ret, but if there the analisys is stopped. op->type = R_ANAL_OP_TYPE_CRET; default: break; } break; case WASM_TYPE_OP_ATOMIC: switch (wop.op.atomic) { case WASM_OP_I32ATOMICLOAD: case WASM_OP_I64ATOMICLOAD: case WASM_OP_I32ATOMICLOAD8U: case WASM_OP_I32ATOMICLOAD16U: case WASM_OP_I64ATOMICLOAD8U: case WASM_OP_I64ATOMICLOAD16U: case WASM_OP_I64ATOMICLOAD32U: op->type = R_ANAL_OP_TYPE_LOAD; break; case WASM_OP_I32ATOMICSTORE: case WASM_OP_I64ATOMICSTORE: case WASM_OP_I32ATOMICSTORE8: case WASM_OP_I32ATOMICSTORE16: case WASM_OP_I64ATOMICSTORE8: case WASM_OP_I64ATOMICSTORE16: case WASM_OP_I64ATOMICSTORE32: op->type = R_ANAL_OP_TYPE_STORE; break; case WASM_OP_I32ATOMICRMWADD: case WASM_OP_I64ATOMICRMWADD: case WASM_OP_I32ATOMICRMW8UADD: case WASM_OP_I32ATOMICRMW16UADD: case WASM_OP_I64ATOMICRMW8UADD: case WASM_OP_I64ATOMICRMW16UADD: case WASM_OP_I64ATOMICRMW32UADD: op->type = R_ANAL_OP_TYPE_ADD; break; case WASM_OP_I32ATOMICRMW8USUB: case WASM_OP_I32ATOMICRMW16USUB: case WASM_OP_I32ATOMICRMWSUB: case WASM_OP_I64ATOMICRMW8USUB: case WASM_OP_I64ATOMICRMW16USUB: case WASM_OP_I64ATOMICRMW32USUB: case WASM_OP_I64ATOMICRMWSUB: op->type = R_ANAL_OP_TYPE_SUB; break; case WASM_OP_I32ATOMICRMWAND: case WASM_OP_I64ATOMICRMWAND: case WASM_OP_I32ATOMICRMW8UAND: case WASM_OP_I32ATOMICRMW16UAND: case WASM_OP_I64ATOMICRMW8UAND: case WASM_OP_I64ATOMICRMW16UAND: case WASM_OP_I64ATOMICRMW32UAND: op->type = R_ANAL_OP_TYPE_AND; break; case WASM_OP_I32ATOMICRMWOR: case WASM_OP_I64ATOMICRMWOR: case WASM_OP_I32ATOMICRMW8UOR: case WASM_OP_I32ATOMICRMW16UOR: case WASM_OP_I64ATOMICRMW8UOR: case WASM_OP_I64ATOMICRMW16UOR: case WASM_OP_I64ATOMICRMW32UOR: op->type = R_ANAL_OP_TYPE_OR; break; case WASM_OP_I32ATOMICRMWXOR: case WASM_OP_I64ATOMICRMWXOR: case WASM_OP_I32ATOMICRMW8UXOR: case WASM_OP_I32ATOMICRMW16UXOR: case WASM_OP_I64ATOMICRMW8UXOR: case WASM_OP_I64ATOMICRMW16UXOR: case WASM_OP_I64ATOMICRMW32UXOR: op->type = R_ANAL_OP_TYPE_XOR; break; case WASM_OP_I32ATOMICRMWXCHG: case WASM_OP_I64ATOMICRMWXCHG: case WASM_OP_I32ATOMICRMW8UXCHG: case WASM_OP_I32ATOMICRMW16UXCHG: case WASM_OP_I64ATOMICRMW8UXCHG: case WASM_OP_I64ATOMICRMW16UXCHG: case WASM_OP_I64ATOMICRMW32UXCHG: op->type = R_ANAL_OP_TYPE_XCHG; break; default: break; } default: break; } anal_end: addr_old = addr; free (wop.txt); r_anal_hint_free (hint); return op->size; }
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; }