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