michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "libdis.h" michael@0: #include "ia32_insn.h" michael@0: #include "ia32_invariant.h" michael@0: #include "x86_operand_list.h" michael@0: michael@0: michael@0: #ifdef _MSC_VER michael@0: #define snprintf _snprintf michael@0: #define inline __inline michael@0: #endif michael@0: michael@0: unsigned int x86_disasm( unsigned char *buf, unsigned int buf_len, michael@0: uint32_t buf_rva, unsigned int offset, michael@0: x86_insn_t *insn ){ michael@0: int len, size; michael@0: unsigned char bytes[MAX_INSTRUCTION_SIZE]; michael@0: michael@0: if ( ! buf || ! insn || ! buf_len ) { michael@0: /* caller screwed up somehow */ michael@0: return 0; michael@0: } michael@0: michael@0: michael@0: /* ensure we are all NULLed up */ michael@0: memset( insn, 0, sizeof(x86_insn_t) ); michael@0: insn->addr = buf_rva + offset; michael@0: insn->offset = offset; michael@0: /* default to invalid insn */ michael@0: insn->type = insn_invalid; michael@0: insn->group = insn_none; michael@0: michael@0: if ( offset >= buf_len ) { michael@0: /* another caller screwup ;) */ michael@0: x86_report_error(report_disasm_bounds, (void*)(long)buf_rva+offset); michael@0: return 0; michael@0: } michael@0: michael@0: len = buf_len - offset; michael@0: michael@0: /* copy enough bytes for disassembly into buffer : this michael@0: * helps prevent buffer overruns at the end of a file */ michael@0: memset( bytes, 0, MAX_INSTRUCTION_SIZE ); michael@0: memcpy( bytes, &buf[offset], (len < MAX_INSTRUCTION_SIZE) ? len : michael@0: MAX_INSTRUCTION_SIZE ); michael@0: michael@0: /* actually do the disassembly */ michael@0: /* TODO: allow switching when more disassemblers are added */ michael@0: size = ia32_disasm_addr( bytes, len, insn); michael@0: michael@0: /* check and see if we had an invalid instruction */ michael@0: if (! size ) { michael@0: x86_report_error(report_invalid_insn, (void*)(long)buf_rva+offset ); michael@0: return 0; michael@0: } michael@0: michael@0: /* check if we overran the end of the buffer */ michael@0: if ( size > len ) { michael@0: x86_report_error( report_insn_bounds, (void*)(long)buf_rva + offset ); michael@0: MAKE_INVALID( insn, bytes ); michael@0: return 0; michael@0: } michael@0: michael@0: /* fill bytes field of insn */ michael@0: memcpy( insn->bytes, bytes, size ); michael@0: michael@0: return size; michael@0: } michael@0: michael@0: unsigned int x86_disasm_range( unsigned char *buf, uint32_t buf_rva, michael@0: unsigned int offset, unsigned int len, michael@0: DISASM_CALLBACK func, void *arg ) { michael@0: x86_insn_t insn; michael@0: unsigned int buf_len, size, count = 0, bytes = 0; michael@0: michael@0: /* buf_len is implied by the arguments */ michael@0: buf_len = len + offset; michael@0: michael@0: while ( bytes < len ) { michael@0: size = x86_disasm( buf, buf_len, buf_rva, offset + bytes, michael@0: &insn ); michael@0: if ( size ) { michael@0: /* invoke callback if it exists */ michael@0: if ( func ) { michael@0: (*func)( &insn, arg ); michael@0: } michael@0: bytes += size; michael@0: count ++; michael@0: } else { michael@0: /* error */ michael@0: bytes++; /* try next byte */ michael@0: } michael@0: michael@0: x86_oplist_free( &insn ); michael@0: } michael@0: michael@0: return( count ); michael@0: } michael@0: michael@0: static inline int follow_insn_dest( x86_insn_t *insn ) { michael@0: if ( insn->type == insn_jmp || insn->type == insn_jcc || michael@0: insn->type == insn_call || insn->type == insn_callcc ) { michael@0: return(1); michael@0: } michael@0: return(0); michael@0: } michael@0: michael@0: static inline int insn_doesnt_return( x86_insn_t *insn ) { michael@0: return( (insn->type == insn_jmp || insn->type == insn_return) ? 1: 0 ); michael@0: } michael@0: michael@0: static int32_t internal_resolver( x86_op_t *op, x86_insn_t *insn ){ michael@0: int32_t next_addr = -1; michael@0: if ( x86_optype_is_address(op->type) ) { michael@0: next_addr = op->data.sdword; michael@0: } else if ( op->type == op_relative_near ) { michael@0: next_addr = insn->addr + insn->size + op->data.relative_near; michael@0: } else if ( op->type == op_relative_far ) { michael@0: next_addr = insn->addr + insn->size + op->data.relative_far; michael@0: } michael@0: return( next_addr ); michael@0: } michael@0: michael@0: unsigned int x86_disasm_forward( unsigned char *buf, unsigned int buf_len, michael@0: uint32_t buf_rva, unsigned int offset, michael@0: DISASM_CALLBACK func, void *arg, michael@0: DISASM_RESOLVER resolver, void *r_arg ){ michael@0: x86_insn_t insn; michael@0: x86_op_t *op; michael@0: int32_t next_addr; michael@0: uint32_t next_offset; michael@0: unsigned int size, count = 0, bytes = 0, cont = 1; michael@0: michael@0: while ( cont && bytes < buf_len ) { michael@0: size = x86_disasm( buf, buf_len, buf_rva, offset + bytes, michael@0: &insn ); michael@0: michael@0: if ( size ) { michael@0: /* invoke callback if it exists */ michael@0: if ( func ) { michael@0: (*func)( &insn, arg ); michael@0: } michael@0: bytes += size; michael@0: count ++; michael@0: } else { michael@0: /* error */ michael@0: bytes++; /* try next byte */ michael@0: } michael@0: michael@0: if ( follow_insn_dest(&insn) ) { michael@0: op = x86_get_dest_operand( &insn ); michael@0: next_addr = -1; michael@0: michael@0: /* if caller supplied a resolver, use it to determine michael@0: * the address to disassemble */ michael@0: if ( resolver ) { michael@0: next_addr = resolver(op, &insn, r_arg); michael@0: } else { michael@0: next_addr = internal_resolver(op, &insn); michael@0: } michael@0: michael@0: if (next_addr != -1 ) { michael@0: next_offset = next_addr - buf_rva; michael@0: /* if offset is in this buffer... */ michael@0: if ( next_addr >= buf_rva && michael@0: next_offset < buf_len ) { michael@0: /* go ahead and disassemble */ michael@0: count += x86_disasm_forward( buf, michael@0: buf_len, michael@0: buf_rva, michael@0: next_offset, michael@0: func, arg, michael@0: resolver, r_arg ); michael@0: } else { michael@0: /* report unresolved address */ michael@0: x86_report_error( report_disasm_bounds, michael@0: (void*)(long)next_addr ); michael@0: } michael@0: } michael@0: } /* end follow_insn */ michael@0: michael@0: if ( insn_doesnt_return(&insn) ) { michael@0: /* stop disassembling */ michael@0: cont = 0; michael@0: } michael@0: michael@0: x86_oplist_free( &insn ); michael@0: } michael@0: return( count ); michael@0: } michael@0: michael@0: /* invariant instruction representation */ michael@0: size_t x86_invariant_disasm( unsigned char *buf, int buf_len, michael@0: x86_invariant_t *inv ){ michael@0: if (! buf || ! buf_len || ! inv ) { michael@0: return(0); michael@0: } michael@0: michael@0: return ia32_disasm_invariant(buf, buf_len, inv); michael@0: } michael@0: size_t x86_size_disasm( unsigned char *buf, unsigned int buf_len ) { michael@0: if (! buf || ! buf_len ) { michael@0: return(0); michael@0: } michael@0: michael@0: return ia32_disasm_size(buf, buf_len); michael@0: }