michael@0: #include michael@0: #include michael@0: michael@0: #include "ia32_invariant.h" michael@0: #include "ia32_insn.h" michael@0: #include "ia32_settings.h" michael@0: michael@0: extern ia32_table_desc_t *ia32_tables; michael@0: extern ia32_settings_t ia32_settings; michael@0: michael@0: extern size_t ia32_table_lookup( unsigned char *buf, size_t buf_len, michael@0: unsigned int table, ia32_insn_t **raw_insn, michael@0: unsigned int *prefixes ); michael@0: michael@0: michael@0: /* -------------------------------- ModR/M, SIB */ michael@0: /* Convenience flags */ michael@0: #define MODRM_EA 1 /* ModR/M is an effective addr */ michael@0: #define MODRM_reg 2 /* ModR/M is a register */ michael@0: michael@0: /* ModR/M flags */ michael@0: #define MODRM_RM_SIB 0x04 /* R/M == 100 */ michael@0: #define MODRM_RM_NOREG 0x05 /* R/B == 101 */ michael@0: /* if (MODRM.MOD_NODISP && MODRM.RM_NOREG) then just disp32 */ michael@0: #define MODRM_MOD_NODISP 0x00 /* mod == 00 */ michael@0: #define MODRM_MOD_DISP8 0x01 /* mod == 01 */ michael@0: #define MODRM_MOD_DISP32 0x02 /* mod == 10 */ michael@0: #define MODRM_MOD_NOEA 0x03 /* mod == 11 */ michael@0: /* 16-bit modrm flags */ michael@0: #define MOD16_MOD_NODISP 0 michael@0: #define MOD16_MOD_DISP8 1 michael@0: #define MOD16_MOD_DISP16 2 michael@0: #define MOD16_MOD_REG 3 michael@0: michael@0: #define MOD16_RM_BXSI 0 michael@0: #define MOD16_RM_BXDI 1 michael@0: #define MOD16_RM_BPSI 2 michael@0: #define MOD16_RM_BPDI 3 michael@0: #define MOD16_RM_SI 4 michael@0: #define MOD16_RM_DI 5 michael@0: #define MOD16_RM_BP 6 michael@0: #define MOD16_RM_BX 7 michael@0: michael@0: /* SIB flags */ michael@0: #define SIB_INDEX_NONE 0x04 michael@0: #define SIB_BASE_EBP 0x05 michael@0: #define SIB_SCALE_NOBASE 0x00 michael@0: michael@0: /* Convenience struct for modR/M bitfield */ michael@0: struct modRM_byte { michael@0: unsigned int mod : 2; michael@0: unsigned int reg : 3; michael@0: unsigned int rm : 3; michael@0: }; michael@0: michael@0: /* Convenience struct for SIB bitfield */ michael@0: struct SIB_byte { michael@0: unsigned int scale : 2; michael@0: unsigned int index : 3; michael@0: unsigned int base : 3; michael@0: }; michael@0: michael@0: #ifdef WIN32 michael@0: static void byte_decode(unsigned char b, struct modRM_byte *modrm) { michael@0: #else michael@0: static inline void byte_decode(unsigned char b, struct modRM_byte *modrm) { michael@0: #endif michael@0: /* generic bitfield-packing routine */ michael@0: michael@0: modrm->mod = b >> 6; /* top 2 bits */ michael@0: modrm->reg = (b & 56) >> 3; /* middle 3 bits */ michael@0: modrm->rm = b & 7; /* bottom 3 bits */ michael@0: } michael@0: static int ia32_invariant_modrm( unsigned char *in, unsigned char *out, michael@0: unsigned int mode_16, x86_invariant_op_t *op) { michael@0: struct modRM_byte modrm; michael@0: struct SIB_byte sib; michael@0: unsigned char *c, *cin; michael@0: unsigned short *s; michael@0: unsigned int *i; michael@0: int size = 0; /* modrm byte is already counted */ michael@0: michael@0: michael@0: byte_decode(*in, &modrm); /* get bitfields */ michael@0: michael@0: out[0] = in[0]; /* save modrm byte */ michael@0: cin = &in[1]; michael@0: c = &out[1]; michael@0: s = (unsigned short *)&out[1]; michael@0: i = (unsigned int *)&out[1]; michael@0: michael@0: op->type = op_expression; michael@0: op->flags |= op_pointer; michael@0: if ( ! mode_16 && modrm.rm == MODRM_RM_SIB && michael@0: modrm.mod != MODRM_MOD_NOEA ) { michael@0: size ++; michael@0: byte_decode(*cin, (struct modRM_byte *)(void*)&sib); michael@0: michael@0: out[1] = in[1]; /* save sib byte */ michael@0: cin = &in[2]; michael@0: c = &out[2]; michael@0: s = (unsigned short *)&out[2]; michael@0: i = (unsigned int *)&out[2]; michael@0: michael@0: if ( sib.base == SIB_BASE_EBP && ! modrm.mod ) { michael@0: /* disp 32 is variant! */ michael@0: memset( i, X86_WILDCARD_BYTE, 4 ); michael@0: size += 4; michael@0: } michael@0: } michael@0: michael@0: if (! modrm.mod && modrm.rm == 101) { michael@0: if ( mode_16 ) { /* straight RVA in disp */ michael@0: memset( s, X86_WILDCARD_BYTE, 2 ); michael@0: size += 2; michael@0: } else { michael@0: memset( i, X86_WILDCARD_BYTE, 2 ); michael@0: size += 4; michael@0: } michael@0: } else if (modrm.mod && modrm.mod < 3) { michael@0: if (modrm.mod == MODRM_MOD_DISP8) { /* offset in disp */ michael@0: *c = *cin; michael@0: size += 1; michael@0: } else if ( mode_16 ) { michael@0: *s = (* ((unsigned short *) cin)); michael@0: size += 2; michael@0: } else { michael@0: *i = (*((unsigned int *) cin)); michael@0: size += 4; michael@0: } michael@0: } else if ( modrm.mod == 3 ) { michael@0: op->type = op_register; michael@0: op->flags &= ~op_pointer; michael@0: } michael@0: michael@0: return (size); michael@0: } michael@0: michael@0: michael@0: static int ia32_decode_invariant( unsigned char *buf, size_t buf_len, michael@0: ia32_insn_t *t, unsigned char *out, michael@0: unsigned int prefixes, x86_invariant_t *inv) { michael@0: michael@0: unsigned int addr_size, op_size, mode_16; michael@0: unsigned int op_flags[3] = { t->dest_flag, t->src_flag, t->aux_flag }; michael@0: int x, type, bytes = 0, size = 0, modrm = 0; michael@0: michael@0: /* set addressing mode */ michael@0: if (ia32_settings.options & opt_16_bit) { michael@0: op_size = ( prefixes & PREFIX_OP_SIZE ) ? 4 : 2; michael@0: addr_size = ( prefixes & PREFIX_ADDR_SIZE ) ? 4 : 2; michael@0: mode_16 = ( prefixes & PREFIX_ADDR_SIZE ) ? 0 : 1; michael@0: } else { michael@0: op_size = ( prefixes & PREFIX_OP_SIZE ) ? 2 : 4; michael@0: addr_size = ( prefixes & PREFIX_ADDR_SIZE ) ? 2 : 4; michael@0: mode_16 = ( prefixes & PREFIX_ADDR_SIZE ) ? 1 : 0; michael@0: } michael@0: michael@0: for (x = 0; x < 3; x++) { michael@0: inv->operands[x].access = (enum x86_op_access) michael@0: OP_PERM(op_flags[x]); michael@0: inv->operands[x].flags = (enum x86_op_flags) michael@0: (OP_FLAGS(op_flags[x]) >> 12); michael@0: michael@0: switch (op_flags[x] & OPTYPE_MASK) { michael@0: case OPTYPE_c: michael@0: size = (op_size == 4) ? 2 : 1; michael@0: break; michael@0: case OPTYPE_a: case OPTYPE_v: michael@0: size = (op_size == 4) ? 4 : 2; michael@0: break; michael@0: case OPTYPE_p: michael@0: size = (op_size == 4) ? 6 : 4; michael@0: break; michael@0: case OPTYPE_b: michael@0: size = 1; michael@0: break; michael@0: case OPTYPE_w: michael@0: size = 2; michael@0: break; michael@0: case OPTYPE_d: case OPTYPE_fs: case OPTYPE_fd: michael@0: case OPTYPE_fe: case OPTYPE_fb: case OPTYPE_fv: michael@0: case OPTYPE_si: case OPTYPE_fx: michael@0: size = 4; michael@0: break; michael@0: case OPTYPE_s: michael@0: size = 6; michael@0: break; michael@0: case OPTYPE_q: case OPTYPE_pi: michael@0: size = 8; michael@0: break; michael@0: case OPTYPE_dq: case OPTYPE_ps: case OPTYPE_ss: michael@0: case OPTYPE_pd: case OPTYPE_sd: michael@0: size = 16; michael@0: break; michael@0: case OPTYPE_m: michael@0: size = (addr_size == 4) ? 4 : 2; michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: type = op_flags[x] & ADDRMETH_MASK; michael@0: switch (type) { michael@0: case ADDRMETH_E: case ADDRMETH_M: case ADDRMETH_Q: michael@0: case ADDRMETH_R: case ADDRMETH_W: michael@0: modrm = 1; michael@0: bytes += ia32_invariant_modrm( buf, out, michael@0: mode_16, &inv->operands[x]); michael@0: break; michael@0: case ADDRMETH_C: case ADDRMETH_D: case ADDRMETH_G: michael@0: case ADDRMETH_P: case ADDRMETH_S: case ADDRMETH_T: michael@0: case ADDRMETH_V: michael@0: inv->operands[x].type = op_register; michael@0: modrm = 1; michael@0: break; michael@0: case ADDRMETH_A: case ADDRMETH_O: michael@0: /* pad with xF4's */ michael@0: memset( &out[bytes + modrm], X86_WILDCARD_BYTE, michael@0: size ); michael@0: bytes += size; michael@0: inv->operands[x].type = op_offset; michael@0: if ( type == ADDRMETH_O ) { michael@0: inv->operands[x].flags |= op_signed | michael@0: op_pointer; michael@0: } michael@0: break; michael@0: case ADDRMETH_I: case ADDRMETH_J: michael@0: /* grab imm value */ michael@0: if ((op_flags[x] & OPTYPE_MASK) == OPTYPE_v) { michael@0: /* assume this is an address */ michael@0: memset( &out[bytes + modrm], michael@0: X86_WILDCARD_BYTE, size ); michael@0: } else { michael@0: memcpy( &out[bytes + modrm], michael@0: &buf[bytes + modrm], size ); michael@0: } michael@0: michael@0: bytes += size; michael@0: if ( type == ADDRMETH_J ) { michael@0: if ( size == 1 ) { michael@0: inv->operands[x].type = michael@0: op_relative_near; michael@0: } else { michael@0: inv->operands[x].type = michael@0: op_relative_far; michael@0: } michael@0: inv->operands[x].flags |= op_signed; michael@0: } else { michael@0: inv->operands[x].type = op_immediate; michael@0: } michael@0: break; michael@0: case ADDRMETH_F: michael@0: inv->operands[x].type = op_register; michael@0: break; michael@0: case ADDRMETH_X: michael@0: inv->operands[x].flags |= op_signed | michael@0: op_pointer | op_ds_seg | op_string; michael@0: break; michael@0: case ADDRMETH_Y: michael@0: inv->operands[x].flags |= op_signed | michael@0: op_pointer | op_es_seg | op_string; michael@0: break; michael@0: case ADDRMETH_RR: michael@0: inv->operands[x].type = op_register; michael@0: break; michael@0: case ADDRMETH_II: michael@0: inv->operands[x].type = op_immediate; michael@0: break; michael@0: default: michael@0: inv->operands[x].type = op_unused; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return (bytes + modrm); michael@0: } michael@0: michael@0: size_t ia32_disasm_invariant( unsigned char * buf, size_t buf_len, michael@0: x86_invariant_t *inv ) { michael@0: ia32_insn_t *raw_insn = NULL; michael@0: unsigned int prefixes; michael@0: unsigned int type; michael@0: size_t size; michael@0: michael@0: /* Perform recursive table lookup starting with main table (0) */ michael@0: size = ia32_table_lookup( buf, buf_len, 0, &raw_insn, &prefixes ); michael@0: if ( size == INVALID_INSN || size > buf_len ) { michael@0: /* TODO: set errno */ michael@0: return 0; michael@0: } michael@0: michael@0: /* copy opcode bytes to buffer */ michael@0: memcpy( inv->bytes, buf, size ); michael@0: michael@0: /* set mnemonic type and group */ michael@0: type = raw_insn->mnem_flag & ~INS_FLAG_MASK; michael@0: inv->group = (enum x86_insn_group) (INS_GROUP(type)) >> 12; michael@0: inv->type = (enum x86_insn_type) INS_TYPE(type); michael@0: michael@0: /* handle operands */ michael@0: size += ia32_decode_invariant( buf + size, buf_len - size, raw_insn, michael@0: &buf[size - 1], prefixes, inv ); michael@0: michael@0: inv->size = size; michael@0: michael@0: return size; /* return size of instruction in bytes */ michael@0: } michael@0: michael@0: size_t ia32_disasm_size( unsigned char *buf, size_t buf_len ) { michael@0: x86_invariant_t inv = { {0} }; michael@0: return( ia32_disasm_invariant( buf, buf_len, &inv ) ); michael@0: }