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