michael@0: #include michael@0: #include michael@0: #include michael@0: #include "qword.h" michael@0: michael@0: #include "ia32_insn.h" michael@0: #include "ia32_opcode_tables.h" michael@0: michael@0: #include "ia32_reg.h" michael@0: #include "ia32_operand.h" michael@0: #include "ia32_implicit.h" michael@0: #include "ia32_settings.h" michael@0: michael@0: #include "libdis.h" michael@0: michael@0: extern ia32_table_desc_t ia32_tables[]; michael@0: extern ia32_settings_t ia32_settings; michael@0: michael@0: #define IS_SP( op ) (op->type == op_register && \ michael@0: (op->data.reg.id == REG_ESP_INDEX || \ michael@0: op->data.reg.alias == REG_ESP_INDEX) ) michael@0: #define IS_IMM( op ) (op->type == op_immediate ) michael@0: michael@0: #ifdef WIN32 michael@0: # define INLINE michael@0: #else michael@0: # define INLINE inline michael@0: #endif michael@0: michael@0: /* for calculating stack modification based on an operand */ michael@0: static INLINE int32_t long_from_operand( x86_op_t *op ) { michael@0: michael@0: if (! IS_IMM(op) ) { michael@0: return 0L; michael@0: } michael@0: michael@0: switch ( op->datatype ) { michael@0: case op_byte: michael@0: return (int32_t) op->data.sbyte; michael@0: case op_word: michael@0: return (int32_t) op->data.sword; michael@0: case op_qword: michael@0: return (int32_t) op->data.sqword; michael@0: case op_dword: michael@0: return op->data.sdword; michael@0: default: michael@0: /* these are not used in stack insn */ michael@0: break; michael@0: } michael@0: michael@0: return 0L; michael@0: } michael@0: michael@0: michael@0: /* determine what this insn does to the stack */ michael@0: static void ia32_stack_mod(x86_insn_t *insn) { michael@0: x86_op_t *dest, *src = NULL; michael@0: michael@0: if (! insn || ! insn->operands ) { michael@0: return; michael@0: } michael@0: michael@0: dest = &insn->operands->op; michael@0: if ( dest ) { michael@0: src = &insn->operands->next->op; michael@0: } michael@0: michael@0: insn->stack_mod = 0; michael@0: insn->stack_mod_val = 0; michael@0: michael@0: switch ( insn->type ) { michael@0: case insn_call: michael@0: case insn_callcc: michael@0: insn->stack_mod = 1; michael@0: insn->stack_mod_val = insn->addr_size * -1; michael@0: break; michael@0: case insn_push: michael@0: insn->stack_mod = 1; michael@0: insn->stack_mod_val = insn->addr_size * -1; michael@0: break; michael@0: case insn_return: michael@0: insn->stack_mod = 1; michael@0: insn->stack_mod_val = insn->addr_size; michael@0: case insn_int: case insn_intcc: michael@0: case insn_iret: michael@0: break; michael@0: case insn_pop: michael@0: insn->stack_mod = 1; michael@0: if (! IS_SP( dest ) ) { michael@0: insn->stack_mod_val = insn->op_size; michael@0: } /* else we don't know the stack change in a pop esp */ michael@0: break; michael@0: case insn_enter: michael@0: insn->stack_mod = 1; michael@0: insn->stack_mod_val = 0; /* TODO : FIX */ michael@0: break; michael@0: case insn_leave: michael@0: insn->stack_mod = 1; michael@0: insn->stack_mod_val = 0; /* TODO : FIX */ michael@0: break; michael@0: case insn_pushregs: michael@0: insn->stack_mod = 1; michael@0: insn->stack_mod_val = 0; /* TODO : FIX */ michael@0: break; michael@0: case insn_popregs: michael@0: insn->stack_mod = 1; michael@0: insn->stack_mod_val = 0; /* TODO : FIX */ michael@0: break; michael@0: case insn_pushflags: michael@0: insn->stack_mod = 1; michael@0: insn->stack_mod_val = 0; /* TODO : FIX */ michael@0: break; michael@0: case insn_popflags: michael@0: insn->stack_mod = 1; michael@0: insn->stack_mod_val = 0; /* TODO : FIX */ michael@0: break; michael@0: case insn_add: michael@0: if ( IS_SP( dest ) ) { michael@0: insn->stack_mod = 1; michael@0: insn->stack_mod_val = long_from_operand( src ); michael@0: } michael@0: break; michael@0: case insn_sub: michael@0: if ( IS_SP( dest ) ) { michael@0: insn->stack_mod = 1; michael@0: insn->stack_mod_val = long_from_operand( src ); michael@0: insn->stack_mod_val *= -1; michael@0: } michael@0: break; michael@0: case insn_inc: michael@0: if ( IS_SP( dest ) ) { michael@0: insn->stack_mod = 1; michael@0: insn->stack_mod_val = 1; michael@0: } michael@0: break; michael@0: case insn_dec: michael@0: if ( IS_SP( dest ) ) { michael@0: insn->stack_mod = 1; michael@0: insn->stack_mod_val = 1; michael@0: } michael@0: break; michael@0: case insn_mov: case insn_movcc: michael@0: case insn_xchg: case insn_xchgcc: michael@0: case insn_mul: case insn_div: michael@0: case insn_shl: case insn_shr: michael@0: case insn_rol: case insn_ror: michael@0: case insn_and: case insn_or: michael@0: case insn_not: case insn_neg: michael@0: case insn_xor: michael@0: if ( IS_SP( dest ) ) { michael@0: insn->stack_mod = 1; michael@0: } michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: if (! strcmp("enter", insn->mnemonic) ) { michael@0: insn->stack_mod = 1; michael@0: } else if (! strcmp("leave", insn->mnemonic) ) { michael@0: insn->stack_mod = 1; michael@0: } michael@0: michael@0: /* for mov, etc we return 0 -- unknown stack mod */ michael@0: michael@0: return; michael@0: } michael@0: michael@0: /* get the cpu details for this insn from cpu flags int */ michael@0: static void ia32_handle_cpu( x86_insn_t *insn, unsigned int cpu ) { michael@0: insn->cpu = (enum x86_insn_cpu) CPU_MODEL(cpu); michael@0: insn->isa = (enum x86_insn_isa) (ISA_SUBSET(cpu)) >> 16; michael@0: return; michael@0: } michael@0: michael@0: /* handle mnemonic type and group */ michael@0: static void ia32_handle_mnemtype(x86_insn_t *insn, unsigned int mnemtype) { michael@0: unsigned int type = mnemtype & ~INS_FLAG_MASK; michael@0: insn->group = (enum x86_insn_group) (INS_GROUP(type)) >> 12; michael@0: insn->type = (enum x86_insn_type) INS_TYPE(type); michael@0: michael@0: return; michael@0: } michael@0: michael@0: static void ia32_handle_notes(x86_insn_t *insn, unsigned int notes) { michael@0: insn->note = (enum x86_insn_note) notes; michael@0: return; michael@0: } michael@0: michael@0: static void ia32_handle_eflags( x86_insn_t *insn, unsigned int eflags) { michael@0: unsigned int flags; michael@0: michael@0: /* handle flags effected */ michael@0: flags = INS_FLAGS_TEST(eflags); michael@0: /* handle weird OR cases */ michael@0: /* these are either JLE (ZF | SF<>OF) or JBE (CF | ZF) */ michael@0: if (flags & INS_TEST_OR) { michael@0: flags &= ~INS_TEST_OR; michael@0: if ( flags & INS_TEST_ZERO ) { michael@0: flags &= ~INS_TEST_ZERO; michael@0: if ( flags & INS_TEST_CARRY ) { michael@0: flags &= ~INS_TEST_CARRY ; michael@0: flags |= (int)insn_carry_or_zero_set; michael@0: } else if ( flags & INS_TEST_SFNEOF ) { michael@0: flags &= ~INS_TEST_SFNEOF; michael@0: flags |= (int)insn_zero_set_or_sign_ne_oflow; michael@0: } michael@0: } michael@0: } michael@0: insn->flags_tested = (enum x86_flag_status) flags; michael@0: michael@0: insn->flags_set = (enum x86_flag_status) INS_FLAGS_SET(eflags) >> 16; michael@0: michael@0: return; michael@0: } michael@0: michael@0: static void ia32_handle_prefix( x86_insn_t *insn, unsigned int prefixes ) { michael@0: michael@0: insn->prefix = (enum x86_insn_prefix) prefixes & PREFIX_MASK; // >> 20; michael@0: if (! (insn->prefix & PREFIX_PRINT_MASK) ) { michael@0: /* no printable prefixes */ michael@0: insn->prefix = insn_no_prefix; michael@0: } michael@0: michael@0: /* concat all prefix strings */ michael@0: if ( (unsigned int)insn->prefix & PREFIX_LOCK ) { michael@0: strncat(insn->prefix_string, "lock ", 32 - michael@0: strlen(insn->prefix_string)); michael@0: } michael@0: michael@0: if ( (unsigned int)insn->prefix & PREFIX_REPNZ ) { michael@0: strncat(insn->prefix_string, "repnz ", 32 - michael@0: strlen(insn->prefix_string)); michael@0: } else if ( (unsigned int)insn->prefix & PREFIX_REPZ ) { michael@0: strncat(insn->prefix_string, "repz ", 32 - michael@0: strlen(insn->prefix_string)); michael@0: } michael@0: michael@0: return; michael@0: } michael@0: michael@0: michael@0: static void reg_32_to_16( x86_op_t *op, x86_insn_t *insn, void *arg ) { michael@0: michael@0: /* if this is a 32-bit register and it is a general register ... */ michael@0: if ( op->type == op_register && op->data.reg.size == 4 && michael@0: (op->data.reg.type & reg_gen) ) { michael@0: /* WORD registers are 8 indices off from DWORD registers */ michael@0: ia32_handle_register( &(op->data.reg), michael@0: op->data.reg.id + 8 ); michael@0: } michael@0: } michael@0: michael@0: static void handle_insn_metadata( x86_insn_t *insn, ia32_insn_t *raw_insn ) { michael@0: ia32_handle_mnemtype( insn, raw_insn->mnem_flag ); michael@0: ia32_handle_notes( insn, raw_insn->notes ); michael@0: ia32_handle_eflags( insn, raw_insn->flags_effected ); michael@0: ia32_handle_cpu( insn, raw_insn->cpu ); michael@0: ia32_stack_mod( insn ); michael@0: } michael@0: michael@0: static size_t ia32_decode_insn( unsigned char *buf, size_t buf_len, michael@0: ia32_insn_t *raw_insn, x86_insn_t *insn, michael@0: unsigned int prefixes ) { michael@0: size_t size, op_size; michael@0: unsigned char modrm; michael@0: michael@0: /* this should never happen, but just in case... */ michael@0: if ( raw_insn->mnem_flag == INS_INVALID ) { michael@0: return 0; michael@0: } michael@0: michael@0: if (ia32_settings.options & opt_16_bit) { michael@0: insn->op_size = ( prefixes & PREFIX_OP_SIZE ) ? 4 : 2; michael@0: insn->addr_size = ( prefixes & PREFIX_ADDR_SIZE ) ? 4 : 2; michael@0: } else { michael@0: insn->op_size = ( prefixes & PREFIX_OP_SIZE ) ? 2 : 4; michael@0: insn->addr_size = ( prefixes & PREFIX_ADDR_SIZE ) ? 2 : 4; michael@0: } michael@0: michael@0: michael@0: /* ++++ 1. Copy mnemonic and mnemonic-flags to CODE struct */ michael@0: if ((ia32_settings.options & opt_att_mnemonics) && raw_insn->mnemonic_att[0]) { michael@0: strncpy( insn->mnemonic, raw_insn->mnemonic_att, 16 ); michael@0: } michael@0: else { michael@0: strncpy( insn->mnemonic, raw_insn->mnemonic, 16 ); michael@0: } michael@0: ia32_handle_prefix( insn, prefixes ); michael@0: michael@0: handle_insn_metadata( insn, raw_insn ); michael@0: michael@0: /* prefetch the next byte in case it is a modr/m byte -- saves michael@0: * worrying about whether the 'mod/rm' operand or the 'reg' operand michael@0: * occurs first */ michael@0: modrm = GET_BYTE( buf, buf_len ); michael@0: michael@0: /* ++++ 2. Decode Explicit Operands */ michael@0: /* Intel uses up to 3 explicit operands in its instructions; michael@0: * the first is 'dest', the second is 'src', and the third michael@0: * is an additional source value (usually an immediate value, michael@0: * e.g. in the MUL instructions). These three explicit operands michael@0: * are encoded in the opcode tables, even if they are not used michael@0: * by the instruction. Additional implicit operands are stored michael@0: * in a supplemental table and are handled later. */ michael@0: michael@0: op_size = ia32_decode_operand( buf, buf_len, insn, raw_insn->dest, michael@0: raw_insn->dest_flag, prefixes, modrm ); michael@0: /* advance buffer, increase size if necessary */ michael@0: buf += op_size; michael@0: buf_len -= op_size; michael@0: size = op_size; michael@0: michael@0: op_size = ia32_decode_operand( buf, buf_len, insn, raw_insn->src, michael@0: raw_insn->src_flag, prefixes, modrm ); michael@0: buf += op_size; michael@0: buf_len -= op_size; michael@0: size += op_size; michael@0: michael@0: op_size = ia32_decode_operand( buf, buf_len, insn, raw_insn->aux, michael@0: raw_insn->aux_flag, prefixes, modrm ); michael@0: size += op_size; michael@0: michael@0: michael@0: /* ++++ 3. Decode Implicit Operands */ michael@0: /* apply implicit operands */ michael@0: ia32_insn_implicit_ops( insn, raw_insn->implicit_ops ); michael@0: /* we have one small inelegant hack here, to deal with michael@0: * the two prefixes that have implicit operands. If Intel michael@0: * adds more, we'll change the algorithm to suit :) */ michael@0: if ( (prefixes & PREFIX_REPZ) || (prefixes & PREFIX_REPNZ) ) { michael@0: ia32_insn_implicit_ops( insn, IDX_IMPLICIT_REP ); michael@0: } michael@0: michael@0: michael@0: /* 16-bit hack: foreach operand, if 32-bit reg, make 16-bit reg */ michael@0: if ( insn->op_size == 2 ) { michael@0: x86_operand_foreach( insn, reg_32_to_16, NULL, op_any ); michael@0: } michael@0: michael@0: return size; michael@0: } michael@0: michael@0: michael@0: /* convenience routine */ michael@0: #define USES_MOD_RM(flag) \ michael@0: (flag == ADDRMETH_E || flag == ADDRMETH_M || flag == ADDRMETH_Q || \ michael@0: flag == ADDRMETH_W || flag == ADDRMETH_R) michael@0: michael@0: static int uses_modrm_flag( unsigned int flag ) { michael@0: unsigned int meth; michael@0: if ( flag == ARG_NONE ) { michael@0: return 0; michael@0: } michael@0: meth = (flag & ADDRMETH_MASK); michael@0: if ( USES_MOD_RM(meth) ) { michael@0: return 1; michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: /* This routine performs the actual byte-by-byte opcode table lookup. michael@0: * Originally it was pretty simple: get a byte, adjust it to a proper michael@0: * index into the table, then check the table row at that index to michael@0: * determine what to do next. But is anything that simple with Intel? michael@0: * This is now a huge, convoluted mess, mostly of bitter comments. */ michael@0: /* buf: pointer to next byte to read from stream michael@0: * buf_len: length of buf michael@0: * table: index of table to use for lookups michael@0: * raw_insn: output pointer that receives opcode definition michael@0: * prefixes: output integer that is encoded with prefixes in insn michael@0: * returns : number of bytes consumed from stream during lookup */ michael@0: 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: unsigned char *next, op = buf[0]; /* byte value -- 'opcode' */ michael@0: size_t size = 1, sub_size = 0, next_len; michael@0: ia32_table_desc_t *table_desc; michael@0: unsigned int subtable, prefix = 0, recurse_table = 0; michael@0: michael@0: table_desc = &ia32_tables[table]; michael@0: michael@0: op = GET_BYTE( buf, buf_len ); michael@0: michael@0: if ( table_desc->type == tbl_fpu && op > table_desc->maxlim) { michael@0: /* one of the fucking FPU tables out of the 00-BH range */ michael@0: /* OK,. this is a bit of a hack -- the proper way would michael@0: * have been to use subtables in the 00-BF FPU opcode tables, michael@0: * but that is rather wasteful of space... */ michael@0: table_desc = &ia32_tables[table +1]; michael@0: } michael@0: michael@0: /* PERFORM TABLE LOOKUP */ michael@0: michael@0: /* ModR/M trick: shift extension bits into lowest bits of byte */ michael@0: /* Note: non-ModR/M tables have a shift value of 0 */ michael@0: op >>= table_desc->shift; michael@0: michael@0: /* ModR/M trick: mask out high bits to turn extension into an index */ michael@0: /* Note: non-ModR/M tables have a mask value of 0xFF */ michael@0: op &= table_desc->mask; michael@0: michael@0: michael@0: /* Sparse table trick: check that byte is <= max value */ michael@0: /* Note: full (256-entry) tables have a maxlim of 155 */ michael@0: if ( op > table_desc->maxlim ) { michael@0: /* this is a partial table, truncated at the tail, michael@0: and op is out of range! */ michael@0: return INVALID_INSN; michael@0: } michael@0: michael@0: /* Sparse table trick: check that byte is >= min value */ michael@0: /* Note: full (256-entry) tables have a minlim of 0 */ michael@0: if ( table_desc->minlim > op ) { michael@0: /* this is a partial table, truncated at the head, michael@0: and op is out of range! */ michael@0: return INVALID_INSN; michael@0: } michael@0: /* adjust op to be an offset from table index 0 */ michael@0: op -= table_desc->minlim; michael@0: michael@0: /* Yay! 'op' is now fully adjusted to be an index into 'table' */ michael@0: *raw_insn = &(table_desc->table[op]); michael@0: //printf("BYTE %X TABLE %d OP %X\n", buf[0], table, op ); michael@0: michael@0: if ( (*raw_insn)->mnem_flag & INS_FLAG_PREFIX ) { michael@0: prefix = (*raw_insn)->mnem_flag & PREFIX_MASK; michael@0: } michael@0: michael@0: michael@0: /* handle escape to a multibyte/coproc/extension/etc table */ michael@0: /* NOTE: if insn is a prefix and has a subtable, then we michael@0: * only recurse if this is the first prefix byte -- michael@0: * that is, if *prefixes is 0. michael@0: * NOTE also that suffix tables are handled later */ michael@0: subtable = (*raw_insn)->table; michael@0: michael@0: if ( subtable && ia32_tables[subtable].type != tbl_suffix && michael@0: (! prefix || ! *prefixes) ) { michael@0: michael@0: if ( ia32_tables[subtable].type == tbl_ext_ext || michael@0: ia32_tables[subtable].type == tbl_fpu_ext ) { michael@0: /* opcode extension: reuse current byte in buffer */ michael@0: next = buf; michael@0: next_len = buf_len; michael@0: } else { michael@0: /* "normal" opcode: advance to next byte in buffer */ michael@0: if ( buf_len > 1 ) { michael@0: next = &buf[1]; michael@0: next_len = buf_len - 1; michael@0: } michael@0: else { michael@0: // buffer is truncated michael@0: return INVALID_INSN; michael@0: } michael@0: } michael@0: /* we encountered a multibyte opcode: recurse using the michael@0: * table specified in the opcode definition */ michael@0: sub_size = ia32_table_lookup( next, next_len, subtable, michael@0: raw_insn, prefixes ); michael@0: michael@0: /* SSE/prefix hack: if the original opcode def was a michael@0: * prefix that specified a subtable, and the subtable michael@0: * lookup returned a valid insn, then we have encountered michael@0: * an SSE opcode definition; otherwise, we pretend we michael@0: * never did the subtable lookup, and deal with the michael@0: * prefix normally later */ michael@0: if ( prefix && ( sub_size == INVALID_INSN || michael@0: INS_TYPE((*raw_insn)->mnem_flag) == INS_INVALID ) ) { michael@0: /* this is a prefix, not an SSE insn : michael@0: * lookup next byte in main table, michael@0: * subsize will be reset during the michael@0: * main table lookup */ michael@0: recurse_table = 1; michael@0: } else { michael@0: /* this is either a subtable (two-byte) insn michael@0: * or an invalid insn: either way, set prefix michael@0: * to NULL and end the opcode lookup */ michael@0: prefix = 0; michael@0: // short-circuit lookup on invalid insn michael@0: if (sub_size == INVALID_INSN) return INVALID_INSN; michael@0: } michael@0: } else if ( prefix ) { michael@0: recurse_table = 1; michael@0: } michael@0: michael@0: /* by default, we assume that we have the opcode definition, michael@0: * and there is no need to recurse on the same table, but michael@0: * if we do then a prefix was encountered... */ michael@0: if ( recurse_table ) { michael@0: /* this must have been a prefix: use the same table for michael@0: * lookup of the next byte */ michael@0: sub_size = ia32_table_lookup( &buf[1], buf_len - 1, table, michael@0: raw_insn, prefixes ); michael@0: michael@0: // short-circuit lookup on invalid insn michael@0: if (sub_size == INVALID_INSN) return INVALID_INSN; michael@0: michael@0: /* a bit of a hack for branch hints */ michael@0: if ( prefix & BRANCH_HINT_MASK ) { michael@0: if ( INS_GROUP((*raw_insn)->mnem_flag) == INS_EXEC ) { michael@0: /* segment override prefixes are invalid for michael@0: * all branch instructions, so delete them */ michael@0: prefix &= ~PREFIX_REG_MASK; michael@0: } else { michael@0: prefix &= ~BRANCH_HINT_MASK; michael@0: } michael@0: } michael@0: michael@0: /* apply prefix to instruction */ michael@0: michael@0: /* TODO: implement something enforcing prefix groups */ michael@0: (*prefixes) |= prefix; michael@0: } michael@0: michael@0: /* if this lookup was in a ModR/M table, then an opcode byte is michael@0: * NOT consumed: subtract accordingly. NOTE that if none of the michael@0: * operands used the ModR/M, then we need to consume the byte michael@0: * here, but ONLY in the 'top-level' opcode extension table */ michael@0: michael@0: if ( table_desc->type == tbl_ext_ext ) { michael@0: /* extensions-to-extensions never consume a byte */ michael@0: --size; michael@0: } else if ( (table_desc->type == tbl_extension || michael@0: table_desc->type == tbl_fpu || michael@0: table_desc->type == tbl_fpu_ext ) && michael@0: /* extensions that have an operand encoded in ModR/M michael@0: * never consume a byte */ michael@0: (uses_modrm_flag((*raw_insn)->dest_flag) || michael@0: uses_modrm_flag((*raw_insn)->src_flag) ) ) { michael@0: --size; michael@0: } michael@0: michael@0: size += sub_size; michael@0: michael@0: return size; michael@0: } michael@0: michael@0: static size_t handle_insn_suffix( unsigned char *buf, size_t buf_len, michael@0: ia32_insn_t *raw_insn, x86_insn_t * insn ) { michael@0: ia32_table_desc_t *table_desc; michael@0: ia32_insn_t *sfx_insn; michael@0: size_t size; michael@0: unsigned int prefixes = 0; michael@0: michael@0: table_desc = &ia32_tables[raw_insn->table]; michael@0: size = ia32_table_lookup( buf, buf_len, raw_insn->table, &sfx_insn, michael@0: &prefixes ); michael@0: if (size == INVALID_INSN || sfx_insn->mnem_flag == INS_INVALID ) { michael@0: return 0; michael@0: } michael@0: michael@0: strncpy( insn->mnemonic, sfx_insn->mnemonic, 16 ); michael@0: handle_insn_metadata( insn, sfx_insn ); michael@0: michael@0: return 1; michael@0: } michael@0: michael@0: /* invalid instructions are handled by returning 0 [error] from the michael@0: * function, setting the size of the insn to 1 byte, and copying michael@0: * the byte at the start of the invalid insn into the x86_insn_t. michael@0: * if the caller is saving the x86_insn_t for invalid instructions, michael@0: * instead of discarding them, this will maintain a consistent michael@0: * address space in the x86_insn_ts */ michael@0: michael@0: /* this function is called by the controlling disassembler, so its name and michael@0: * calling convention cannot be changed */ michael@0: /* buf points to the loc of the current opcode (start of the michael@0: * instruction) in the instruction stream. The instruction michael@0: * stream is assumed to be a buffer of bytes read directly michael@0: * from the file for the purpose of disassembly; a mem-mapped michael@0: * file is ideal for * this. michael@0: * insn points to a code structure to be filled by instr_decode michael@0: * returns the size of the decoded instruction in bytes */ michael@0: size_t ia32_disasm_addr( unsigned char * buf, size_t buf_len, michael@0: x86_insn_t *insn ) { michael@0: ia32_insn_t *raw_insn = NULL; michael@0: unsigned int prefixes = 0; michael@0: size_t size, sfx_size; michael@0: michael@0: if ( (ia32_settings.options & opt_ignore_nulls) && buf_len > 3 && michael@0: !buf[0] && !buf[1] && !buf[2] && !buf[3]) { michael@0: /* IF IGNORE_NULLS is set AND michael@0: * first 4 bytes in the intruction stream are NULL michael@0: * THEN return 0 (END_OF_DISASSEMBLY) */ michael@0: /* TODO: set errno */ michael@0: MAKE_INVALID( insn, buf ); michael@0: return 0; /* 4 00 bytes in a row? This isn't code! */ michael@0: } michael@0: michael@0: /* Perform recursive table lookup starting with main table (0) */ michael@0: size = ia32_table_lookup(buf, buf_len, idx_Main, &raw_insn, &prefixes); michael@0: if ( size == INVALID_INSN || size > buf_len || raw_insn->mnem_flag == INS_INVALID ) { michael@0: MAKE_INVALID( insn, buf ); michael@0: /* TODO: set errno */ michael@0: return 0; michael@0: } michael@0: michael@0: /* We now have the opcode itself figured out: we can decode michael@0: * the rest of the instruction. */ michael@0: size += ia32_decode_insn( &buf[size], buf_len - size, raw_insn, insn, michael@0: prefixes ); michael@0: if ( raw_insn->mnem_flag & INS_FLAG_SUFFIX ) { michael@0: /* AMD 3DNow! suffix -- get proper operand type here */ michael@0: sfx_size = handle_insn_suffix( &buf[size], buf_len - size, michael@0: raw_insn, insn ); michael@0: if (! sfx_size ) { michael@0: /* TODO: set errno */ michael@0: MAKE_INVALID( insn, buf ); michael@0: return 0; michael@0: } michael@0: michael@0: size += sfx_size; michael@0: } michael@0: michael@0: if (! size ) { michael@0: /* invalid insn */ michael@0: MAKE_INVALID( insn, buf ); michael@0: return 0; michael@0: } michael@0: michael@0: michael@0: insn->size = size; michael@0: return size; /* return size of instruction in bytes */ michael@0: }