| #include <stdlib.h> |
| #include "libdis.h" |
| |
| |
| static void x86_oplist_append( x86_insn_t *insn, x86_oplist_t *op ) { |
| x86_oplist_t *list; |
| |
| if (! insn ) { |
| return; |
| } |
| |
| list = insn->operands; |
| if (! list ) { |
| insn->operand_count = 1; |
| /* Note that we have no way of knowing if this is an |
| * exlicit operand or not, since the caller fills |
| * the x86_op_t after we return. We increase the |
| * explicit count automatically, and ia32_insn_implicit_ops |
| * decrements it */ |
| insn->explicit_count = 1; |
| insn->operands = op; |
| return; |
| } |
| |
| /* get to end of list */ |
| for ( ; list->next; list = list->next ) |
| ; |
| |
| insn->operand_count = insn->operand_count + 1; |
| insn->explicit_count = insn->explicit_count + 1; |
| list->next = op; |
| |
| return; |
| } |
| |
| x86_op_t * x86_operand_new( x86_insn_t *insn ) { |
| x86_oplist_t *op; |
| |
| if (! insn ) { |
| return(NULL); |
| } |
| op = calloc( sizeof(x86_oplist_t), 1 ); |
| op->op.insn = insn; |
| x86_oplist_append( insn, op ); |
| return( &(op->op) ); |
| } |
| |
| void x86_oplist_free( x86_insn_t *insn ) { |
| x86_oplist_t *op, *list; |
| |
| if (! insn ) { |
| return; |
| } |
| |
| for ( list = insn->operands; list; ) { |
| op = list; |
| list = list->next; |
| free(op); |
| } |
| |
| insn->operands = NULL; |
| insn->operand_count = 0; |
| insn->explicit_count = 0; |
| |
| return; |
| } |
| |
| /* ================================================== LIBDISASM API */ |
| /* these could probably just be #defines, but that means exposing the |
| enum... yet one more confusing thing in the API */ |
| int x86_operand_foreach( x86_insn_t *insn, x86_operand_fn func, void *arg, |
| enum x86_op_foreach_type type ){ |
| x86_oplist_t *list; |
| char explicit = 1, implicit = 1; |
| |
| if (! insn || ! func ) { |
| return 0; |
| } |
| |
| /* note: explicit and implicit can be ORed together to |
| * allow an "all" limited by access type, even though the |
| * user is stupid to do this since it is default behavior :) */ |
| if ( (type & op_explicit) && ! (type & op_implicit) ) { |
| implicit = 0; |
| } |
| if ( (type & op_implicit) && ! (type & op_explicit) ) { |
| explicit = 0; |
| } |
| |
| type = type & 0x0F; /* mask out explicit/implicit operands */ |
| |
| for ( list = insn->operands; list; list = list->next ) { |
| if (! implicit && (list->op.flags & op_implied) ) { |
| /* operand is implicit */ |
| continue; |
| } |
| |
| if (! explicit && ! (list->op.flags & op_implied) ) { |
| /* operand is not implicit */ |
| continue; |
| } |
| |
| switch ( type ) { |
| case op_any: |
| break; |
| case op_dest: |
| if (! (list->op.access & op_write) ) { |
| continue; |
| } |
| break; |
| case op_src: |
| if (! (list->op.access & op_read) ) { |
| continue; |
| } |
| break; |
| case op_ro: |
| if (! (list->op.access & op_read) || |
| (list->op.access & op_write ) ) { |
| continue; |
| } |
| break; |
| case op_wo: |
| if (! (list->op.access & op_write) || |
| (list->op.access & op_read ) ) { |
| continue; |
| } |
| break; |
| case op_xo: |
| if (! (list->op.access & op_execute) ) { |
| continue; |
| } |
| break; |
| case op_rw: |
| if (! (list->op.access & op_write) || |
| ! (list->op.access & op_read ) ) { |
| continue; |
| } |
| break; |
| case op_implicit: case op_explicit: /* make gcc happy */ |
| break; |
| } |
| /* any non-continue ends up here: invoke the callback */ |
| (*func)( &list->op, insn, arg ); |
| } |
| |
| return 1; |
| } |
| |
| static void count_operand( x86_op_t *op, x86_insn_t *insn, void *arg ) { |
| size_t * count = (size_t *) arg; |
| *count = *count + 1; |
| } |
| |
| size_t x86_operand_count( x86_insn_t *insn, enum x86_op_foreach_type type ) { |
| size_t count = 0; |
| |
| /* save us a list traversal for common counts... */ |
| if ( type == op_any ) { |
| return insn->operand_count; |
| } else if ( type == op_explicit ) { |
| return insn->explicit_count; |
| } |
| |
| x86_operand_foreach( insn, count_operand, &count, type ); |
| return count; |
| } |
| |
| /* accessor functions */ |
| x86_op_t * x86_operand_1st( x86_insn_t *insn ) { |
| if (! insn->explicit_count ) { |
| return NULL; |
| } |
| |
| return &(insn->operands->op); |
| } |
| |
| x86_op_t * x86_operand_2nd( x86_insn_t *insn ) { |
| if ( insn->explicit_count < 2 ) { |
| return NULL; |
| } |
| |
| return &(insn->operands->next->op); |
| } |
| |
| x86_op_t * x86_operand_3rd( x86_insn_t *insn ) { |
| if ( insn->explicit_count < 3 ) { |
| return NULL; |
| } |
| |
| return &(insn->operands->next->next->op); |
| } |