static void test_x86_mode(void *dc) { byte *pc, *end; instr_t *instr; /* create instr that looks different in x86 vs x64 */ instr = INSTR_CREATE_add(dc, opnd_create_reg(REG_RAX), OPND_CREATE_INT32(42)); end = instr_encode(dc, instr, buf); ASSERT(end - buf < BUFFER_SIZE_ELEMENTS(buf)); /* read back in */ set_x86_mode(dc, false/*64-bit*/); instr_reset(dc, instr); pc = decode(dc, buf, instr); ASSERT(pc != NULL); ASSERT(instr_get_opcode(instr) == OP_add); /* now interpret as 32-bit where rex will be an inc */ set_x86_mode(dc, true/*32-bit*/); instr_reset(dc, instr); pc = decode(dc, buf, instr); ASSERT(pc != NULL); ASSERT(instr_get_opcode(instr) == OP_dec); instr_free(dc, instr); set_x86_mode(dc, false/*64-bit*/); }
/* return the branch type of the (branch) inst */ uint instr_branch_type(instr_t *cti_instr) { instr_get_opcode(cti_instr); /* ensure opcode is valid */ if (instr_get_opcode(cti_instr) == OP_blx) { /* To handle the mode switch we go through the ibl. * FIXME i#1551: once we have far linking through stubs we should * remove this and have a faster link through the stub. */ return LINK_INDIRECT|LINK_CALL; } /* We treate a predicated call as a cbr, not a call */ else if (instr_is_cbr_arch(cti_instr) || instr_is_ubr_arch(cti_instr)) return LINK_DIRECT|LINK_JMP; else if (instr_is_call_direct(cti_instr)) return LINK_DIRECT|LINK_CALL; else if (instr_is_call_indirect(cti_instr)) return LINK_INDIRECT|LINK_CALL; else if (instr_is_return(cti_instr)) return LINK_INDIRECT|LINK_RETURN; else if (instr_is_mbr_arch(cti_instr)) return LINK_INDIRECT|LINK_JMP; else CLIENT_ASSERT(false, "instr_branch_type: unknown opcode"); return LINK_INDIRECT; }
/* this is only called when the instrace mode is operand trace (this happens at the instrumentation time) */ static void operand_trace(instr_t * instr, void * drcontext){ int i; char stringop[MAX_STRING_LENGTH]; int pc = 0; per_thread_t * data = drmgr_get_tls_field(drcontext, tls_index); module_data_t * module_data = dr_lookup_module(instr_get_app_pc(instr)); if (module_data != NULL){ pc = instr_get_app_pc(instr) - module_data->start; } instr_disassemble_to_buffer(drcontext, instr, stringop, MAX_STRING_LENGTH); if (client_arg->instrace_mode == OPERAND_TRACE){ dr_fprintf(data->outfile, "%s\n", stringop); for (i = 0; i < instr_num_dsts(instr); i++){ opnd_disassemble_to_buffer(drcontext, instr_get_dst(instr, i), stringop, MAX_STRING_LENGTH); if ((instr_get_opcode(instr) == OP_lea) && opnd_is_base_disp(instr_get_dst(instr,i))){ dr_fprintf(data->outfile, "dst-\n"); print_base_disp_for_lea(data->outfile, instr_get_dst(instr, i)); } else{ dr_fprintf(data->outfile, "dst-%d-%s\n", i, stringop); } } for (i = 0; i < instr_num_srcs(instr); i++){ opnd_disassemble_to_buffer(drcontext, instr_get_src(instr, i), stringop, MAX_STRING_LENGTH); if ((instr_get_opcode(instr) == OP_lea) && opnd_is_base_disp(instr_get_src(instr, i))){ dr_fprintf(data->outfile, "src-\n"); print_base_disp_for_lea(data->outfile, instr_get_src(instr, i)); } else{ dr_fprintf(data->outfile, "src-%d-%s\n", i, stringop); } } if (module_data != NULL){ dr_fprintf(data->outfile, "app_pc-%d\n", pc); } } else if (client_arg->instrace_mode == INS_DISASM_TRACE){ if (module_data != NULL){ if (md_get_module_position(instrace_head, module_data->full_path) == -1){ md_add_module(instrace_head, module_data->full_path, MAX_BBS_PER_MODULE); } dr_fprintf(data->outfile, "%d,%d,%s\n", md_get_module_position(instrace_head, module_data->full_path), pc, stringop); } else{ dr_fprintf(data->outfile, "%d,%d,%s\n",0, 0, stringop); } } dr_free_module_data(module_data); }
/* PR 332254: test xchg vs nop */ static void test_nop_xchg(void *dc) { /* 0x0000000000671460 87 c0 xchg %eax %eax -> %eax %eax * 0x0000000000671460 48 87 c0 xchg %rax %rax -> %rax %rax * 0x0000000000671460 41 87 c0 xchg %r8d %eax -> %r8d %eax * 0x0000000000671460 46 90 nop * 0x0000000000671460 4e 90 nop * 0x0000000000671460 41 90 xchg %r8d %eax -> %r8d %eax */ instr_t *instr; instr = INSTR_CREATE_xchg(dc, opnd_create_reg(REG_EAX), opnd_create_reg(REG_EAX)); test_instr_encode(dc, instr, 2); #ifdef X64 /* we don't do the optimal "48 90" instead of "48 87 c0" */ instr = INSTR_CREATE_xchg(dc, opnd_create_reg(REG_RAX), opnd_create_reg(REG_RAX)); test_instr_encode(dc, instr, 3); /* we don't do the optimal "41 90" instead of "41 87 c0" */ instr = INSTR_CREATE_xchg(dc, opnd_create_reg(REG_R8D), opnd_create_reg(REG_EAX)); test_instr_encode(dc, instr, 3); /* ensure we treat as nop and NOT xchg if doesn't have rex.b */ buf[0] = 0x46; buf[1] = 0x90; instr = instr_create(dc); # if VERBOSE disassemble_with_info(dc, buf, STDOUT, true, true); # endif decode(dc, buf, instr); ASSERT(instr_get_opcode(instr) == OP_nop); instr_destroy(dc, instr); buf[0] = 0x4e; buf[1] = 0x90; instr = instr_create(dc); # if VERBOSE disassemble_with_info(dc, buf, STDOUT, true, true); # endif decode(dc, buf, instr); ASSERT(instr_get_opcode(instr) == OP_nop); instr_destroy(dc, instr); buf[0] = 0x41; buf[1] = 0x90; instr = instr_create(dc); # if VERBOSE disassemble_with_info(dc, buf, STDOUT, true, true); # endif decode(dc, buf, instr); ASSERT(instr_get_opcode(instr) == OP_xchg); instr_destroy(dc, instr); #endif }
/* Called by slow_path() after initial decode. Expected to free inst. */ bool slow_path_for_staleness(void *drcontext, dr_mcontext_t *mc, instr_t *inst, app_loc_t *loc) { opnd_t opnd; int opc, i, num_srcs, num_dsts; uint sz; bool pushpop_stackop; opc = instr_get_opcode(inst); num_srcs = num_true_srcs(inst, mc); for (i = 0; i < num_srcs; i++) { opnd = instr_get_src(inst, i); if (opnd_is_memory_reference(opnd)) { opnd = adjust_memop(inst, opnd, false, &sz, &pushpop_stackop); check_mem_opnd_nouninit(opc, 0, loc, opnd, sz, mc); } } num_dsts = num_true_dsts(inst, mc); for (i = 0; i < num_dsts; i++) { opnd = instr_get_dst(inst, i); if (opnd_is_memory_reference(opnd)) { opnd = adjust_memop(inst, opnd, true, &sz, &pushpop_stackop); check_mem_opnd_nouninit(opc, 0, loc, opnd, sz, mc); } } instr_free(drcontext, inst); /* we're not sharing xl8 so no need to call slow_path_xl8_sharing */ return true; }
static dr_emit_flags_t bb_event(void* drcontext, void *tag, instrlist_t* bb, bool for_trace, bool translating) { instr_t *instr; instr_t *next_instr; reg_t in_eax = -1; for (instr = instrlist_first(bb); instr != NULL; instr = next_instr) { next_instr = instr_get_next(instr); if (instr_get_opcode(instr) == OP_mov_imm && opnd_get_reg(instr_get_dst(instr, 0)) == REG_EAX) in_eax = opnd_get_immed_int(instr_get_src(instr, 0)); if (instr_is_syscall(instr) && in_eax == SYS_getpid) { instr_t *myval = INSTR_CREATE_mov_imm (drcontext, opnd_create_reg(REG_EAX), OPND_CREATE_INT32(-7)); instr_set_translation(myval, instr_get_app_pc(instr)); instrlist_preinsert(bb, instr, myval); instrlist_remove(bb, instr); instr_destroy(drcontext, instr); } } return DR_EMIT_DEFAULT; }
/* XXX: exporting this so drwrap can use it but I might prefer to have * this in drutil or the upcoming drsys */ DR_EXPORT int drmgr_decode_sysnum_from_wrapper(app_pc entry) { void *drcontext = dr_get_current_drcontext(); int num = -1; byte *pc = entry; uint opc; instr_t instr; instr_init(drcontext, &instr); do { instr_reset(drcontext, &instr); pc = decode(drcontext, pc, &instr); if (!instr_valid(&instr)) break; /* unknown system call sequence */ opc = instr_get_opcode(&instr); /* sanity check: wrapper should be short */ if (pc - entry > 20) break; /* unknown system call sequence */ if (opc == OP_mov_imm && opnd_is_reg(instr_get_dst(&instr, 0)) && opnd_get_reg(instr_get_dst(&instr, 0)) == DR_REG_EAX && opnd_is_immed_int(instr_get_src(&instr, 0))) { num = (int) opnd_get_immed_int(instr_get_src(&instr, 0)); break; /* success */ } /* stop at call to vsyscall (wow64) or at int itself */ } while (opc != OP_call_ind && opc != OP_int && opc != OP_sysenter && opc != OP_syscall); instr_free(drcontext, &instr); return num; }
/* insert inline code to add an instruction entry into the buffer */ static void instrument_instr(void *drcontext, instrlist_t *ilist, instr_t *where) { /* We need two scratch registers */ reg_id_t reg_ptr, reg_tmp; /* we don't want to predicate this, because an instruction fetch always occurs */ instrlist_set_auto_predicate(ilist, DR_PRED_NONE); if (drreg_reserve_register(drcontext, ilist, where, NULL, ®_ptr) != DRREG_SUCCESS || drreg_reserve_register(drcontext, ilist, where, NULL, ®_tmp) != DRREG_SUCCESS) { DR_ASSERT(false); /* cannot recover */ return; } insert_load_buf_ptr(drcontext, ilist, where, reg_ptr); insert_save_type(drcontext, ilist, where, reg_ptr, reg_tmp, (ushort)instr_get_opcode(where)); insert_save_size(drcontext, ilist, where, reg_ptr, reg_tmp, (ushort)instr_length(drcontext, where)); insert_save_pc(drcontext, ilist, where, reg_ptr, reg_tmp, instr_get_app_pc(where)); insert_update_buf_ptr(drcontext, ilist, where, reg_ptr, sizeof(mem_ref_t)); /* Restore scratch registers */ if (drreg_unreserve_register(drcontext, ilist, where, reg_ptr) != DRREG_SUCCESS || drreg_unreserve_register(drcontext, ilist, where, reg_tmp) != DRREG_SUCCESS) DR_ASSERT(false); instrlist_set_auto_predicate(ilist, instr_get_predicate(where)); }
/* * Header: Decode the specified instruction * * Params: tsk - the task to obtain an instruction from * opcode - the addr at which to save opcode *mask* * params - the addr at which to save the parameters * Return: 0 - success of execution * ETRINS - malformed instruction * * Description: * * Decodes the fields of the instructions and saves them in the provided * locations. If any field is malformed, that error will be returned, and * the state of opcode and params is undefined. */ static char task_instr_decode(struct task *tsk, unsigned char *opcode, unsigned char *params) { char result = 0; unsigned char instr = tsk->recipe[tsk->pc]; switch(instr_get_opcode(instr, opcode)) { case EINVOP: led_set(tsk, LED_RECIPE_CMD_ERROR); tsk->state = ST_ERROR; result = ETRINS; goto yield; } switch(instr_get_params(instr, params)) { case EINVOP: case EINVTG: case EINVDL: case EINVLP: led_set(tsk, LED_RECIPE_CMD_ERROR); tsk->state = ST_ERROR; result = ETRINS; goto yield; case ENOPRM: /* ENOPRM just means the opcode has no param! */ break; } yield: return result; }
/* replaces all inc with add 1, dec with sub 1 * if cannot replace (eflags constraints), leaves original instruction alone */ static dr_emit_flags_t event_trace(void *drcontext, void *tag, instrlist_t *trace, bool translating) { instr_t *instr, *next_instr; int opcode; if (!enable) return DR_EMIT_DEFAULT; #ifdef VERBOSE dr_printf("in dynamorio_trace(tag="PFX")\n", tag); instrlist_disassemble(drcontext, tag, trace, STDOUT); #endif for (instr = instrlist_first(trace); instr != NULL; instr = next_instr) { /* grab next now so we don't go over instructions we insert */ next_instr = instr_get_next(instr); opcode = instr_get_opcode(instr); if (opcode == OP_inc || opcode == OP_dec) { if (!translating) ATOMIC_INC(num_examined); if (replace_inc_with_add(drcontext, instr, trace)) { if (!translating) ATOMIC_INC(num_converted); } } } #ifdef VERBOSE dr_printf("after dynamorio_trace(tag="PFX"):\n", tag); instrlist_disassemble(drcontext, tag, trace, STDOUT); #endif return DR_EMIT_DEFAULT; }
bool instr_is_mov_constant(instr_t *instr, ptr_int_t *value) { int opc = instr_get_opcode(instr); if (opc == OP_eor) { /* We include OP_eor for symmetry w/ x86, but on ARM "mov reg, #0" is * just as compact and there's no reason to use an xor. */ if (opnd_same(instr_get_src(instr, 0), instr_get_dst(instr, 0)) && opnd_same(instr_get_src(instr, 0), instr_get_src(instr, 1)) && /* Must be the form with "sh2, i5_7" and no shift */ instr_num_srcs(instr) == 4 && opnd_get_immed_int(instr_get_src(instr, 2)) == DR_SHIFT_NONE && opnd_get_immed_int(instr_get_src(instr, 3)) == 0) { *value = 0; return true; } else return false; } else if (opc == OP_mvn || opc == OP_mvns) { opnd_t op = instr_get_src(instr, 0); if (opnd_is_immed_int(op)) { *value = -opnd_get_immed_int(op); return true; } else return false; } else if (opc == OP_mov || opc == OP_movs || opc == OP_movw) { opnd_t op = instr_get_src(instr, 0); if (opnd_is_immed_int(op)) { *value = opnd_get_immed_int(op); return true; } else return false; } return false; }
bool instr_is_cti_short_rewrite(instr_t *instr, byte *pc) { /* We assume all app's cbz/cbnz have been mangled. * See comments in x86/'s version of this routine. */ dcontext_t *dcontext; dr_isa_mode_t old_mode; if (pc == NULL) { if (instr == NULL || !instr_has_allocated_bits(instr) || instr->length != CTI_SHORT_REWRITE_LENGTH) return false; pc = instr_get_raw_bits(instr); } if (instr != NULL && instr_opcode_valid(instr)) { int opc = instr_get_opcode(instr); if (opc != OP_cbz && opc != OP_cbnz) return false; } if ((*(pc+1) != CBNZ_BYTE_A && *(pc+1) != CBZ_BYTE_A) || /* Further verify by checking for a disp of 1 */ (*pc & 0xf8) != 0x08) return false; /* XXX: this would be easier if decode_raw_is_jmp took in isa_mode */ dcontext = get_thread_private_dcontext(); if (instr != NULL) dr_set_isa_mode(dcontext, instr_get_isa_mode(instr), &old_mode); if (!decode_raw_is_jmp(dcontext, pc + CTI_SHORT_REWRITE_B_OFFS)) return false; if (instr != NULL) dr_set_isa_mode(dcontext, old_mode, NULL); return true; }
bool instr_is_prefetch(instr_t *instr) { int opcode = instr_get_opcode(instr); if (opcode == OP_pld || opcode == OP_pldw || opcode == OP_pli) return true; return false; }
static dr_emit_flags_t bb_event(void* drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) { instr_t *instr, *next_instr; int opcode; for (instr = instrlist_first(bb); instr != NULL; instr = next_instr) { next_instr = instr_get_next(instr); opcode = instr_get_opcode(instr); if(instr_is_floating(instr)){ // dr_fprintf(logF, "Has seen FPU instruction with opcode %d\n",opcode); } else if(is_SIMD_packed(opcode)){ // dr_fprintf(logF, "Has seen SIMD packed instruction with opcode %d\n",opcode); } //AVX?rcpps? else if(is_SIMD_arithm(opcode)){ int is_single = 0; // printf("opcode is %d\n", opcode); // printf("number of sources %d\n", instr_num_srcs(instr)); // printf("number of dests %d\n", instr_num_dsts(instr)); //assert(number of sources = 2); opnd_t source1 = instr_get_src(instr,0); opnd_t source2 = instr_get_src(instr,1); opnd_t dest = instr_get_dst(instr,0); if(opnd_is_memory_reference(source1)){ // dr_print_instr(drcontext, logF, instr, "INSTR: "); // dr_print_opnd(drcontext, logF, source1, "OPND1: "); // dr_print_opnd(drcontext, logF, source2, "OPND2: "); reg_id_t rd = opnd_get_reg(source2); reg_id_t rs = opnd_get_reg_used(source1, 0); dr_insert_clean_call(drcontext, bb, instr, (void*) callback, true, 5, OPND_CREATE_INTPTR(rs), OPND_CREATE_INTPTR(opnd_get_disp(source1)), OPND_CREATE_INTPTR(rd), OPND_CREATE_INTPTR(opcode), OPND_CREATE_INTPTR(instr_get_app_pc(instr))); } else if(opnd_is_reg(source1) && opnd_is_reg(source2)){ reg_id_t reg1 = opnd_get_reg(source1); reg_id_t reg2 = opnd_get_reg(source2); dr_insert_clean_call(drcontext,bb,instr, (void*)getRegReg, true, 4, OPND_CREATE_INTPTR(reg1), OPND_CREATE_INTPTR(reg2) ,OPND_CREATE_INTPTR(opcode), OPND_CREATE_INTPTR(instr_get_app_pc(instr)) ); } else{ //should not be the case, throw an exception } fp_count++; } } return DR_EMIT_DEFAULT; }
int instr_length_arch(dcontext_t *dcontext, instr_t *instr) { if (instr_get_opcode(instr) == OP_LABEL) return 0; /* Avoid encoding OP_b to avoid reachability checks for added fall-through * jumps, whose targets are later changed to the stub prior to emit. * Another option is to remove the assert on bad encoding, so that the * instr_encode_check_reachability() call in private_instr_encode() can * gracefully fail: which we now do, but this is a nice optimization. */ if (instr_get_opcode(instr) == OP_b) return 4; if (instr_get_isa_mode(instr) == DR_ISA_ARM_THUMB) { /* We have to encode to find the size */ return -1; } else return ARM_INSTR_SIZE; }
bool instr_is_near_call_direct(instr_t *instr) { int opc = instr_get_opcode(instr); /* Mode-switch call is not "near". * FIXME i#1551: once we switch OP_blx to use far-stub linking instead of * ibl we can then consider it "near". */ return (opc == OP_bl); }
static bool instr_is_rep_string(instr_t *instr) { #ifdef X86 uint opc = instr_get_opcode(instr); return (opc == OP_rep_ins || opc == OP_rep_outs || opc == OP_rep_movs || opc == OP_rep_stos || opc == OP_rep_lods || opc == OP_rep_cmps || opc == OP_repne_cmps || opc == OP_rep_scas || opc == OP_repne_scas); #else return false; #endif }
static dr_emit_flags_t event_app_instruction(void* drcontext, void *tag, instrlist_t *bb, instr_t *instr, bool for_trace, bool translating, void *user_data) { /* if find div, insert a clean call to our instrumentation routine */ if (instr_get_opcode(instr) == OP_div) { dr_insert_clean_call(drcontext, bb, instr, (void *)callback, false /*no fp save*/, 2, OPND_CREATE_INTPTR(instr_get_app_pc(instr)), instr_get_src(instr, 0) /*divisor is 1st src*/); } return DR_EMIT_DEFAULT; }
/* Our version, versus Dr. Memory's version in drmemory/fastpath.c */ bool instr_ok_for_instrument_fastpath(instr_t *inst, fastpath_info_t *mi, bb_info_t *bi) { uint opc = instr_get_opcode(inst); int i; initialize_fastpath_info(mi, bi, inst); if (!options.fastpath) return false; if (opc == OP_xlat) { /* can't use base-disp "%ds:(%ebx,%al,1)" for lea: would have to expand * to multiple instrs. not worth supporting since pretty rare though I * do see 3K in twolf test on windows. */ return false; } /* We assume that any one memory reference, even in a rep string form, * will only access one heap allocation. Since we're taking the most * recent access to any part of a heap alloc we thus don't care about the * size of a memory reference. */ for (i=0; i<instr_num_dsts(inst); i++) { if (opnd_uses_memory_we_track(instr_get_dst(inst, i))) { mi->store = true; if (!opnd_is_null(mi->dst[0].app)) { /* FIXME: we could handle 2 dsts if no srcs easily, * and even more dsts if we really wanted to w/o too * much trouble. also something like pusha w/ * consecutive dsts is easy: just take first one. */ return false; } mi->dst[0].app = instr_get_dst(inst, i); } } for (i=0; i<instr_num_srcs(inst); i++) { if (opnd_uses_memory_we_track(instr_get_src(inst, i))) { if (mi->store) mi->mem2mem = true; else mi->load = true; if (!opnd_is_null(mi->src[0].app)) { /* see notes above about handling this: in particular cmps */ return false; } mi->src[0].app = instr_get_src(inst, i); } } return true; }
static instr_t * static_info_instrumentation(void * drcontext, instr_t* instr){ /* for each src and dest add the information accordingly this should return canonicalized static info about an instruction; breaking down any complex instructions if necessary 1) check whether this instruction needs to be instrumented 2) if yes, then get a location and then proceed to instrument -> return the struct 3) if no, return null */ /* main variables */ per_thread_t * data = drmgr_get_tls_field(drcontext,tls_index); /* helper variables */ int opcode; instr_t * ret; /* loop variables */ int i; /* 1) */ opcode = instr_get_opcode(instr); if (client_arg->instrace_mode == OPCODE_TRACE){ opcodes_visited[opcode] = true; return NULL; } if ( (client_arg->instrace_mode == OPERAND_TRACE) || (client_arg->instrace_mode == INS_DISASM_TRACE) ){ operand_trace(instr, drcontext); return NULL; } /* check whether this instr needs instrumentation - check for ones to skip and skip if */ /*switch(opcode){ case OP_jecxz: return NULL; }*/ /* 2) */ data->static_array[data->static_ptr++] = instr_clone(drcontext,instr); DR_ASSERT(data->static_ptr < data->static_array_size); return data->static_array[data->static_ptr - 1]; }
static dr_emit_flags_t drmgr_event_bb_insert(void *drcontext, void *tag, instrlist_t *bb, instr_t *inst, bool for_trace, bool translating, void *user_data) { if (instr_get_app_pc(inst) == addr_KiCallback) { dr_insert_clean_call(drcontext, bb, inst, (void *)drmgr_cls_stack_push, false, 0); } if (instr_get_opcode(inst) == OP_int && opnd_get_immed_int(instr_get_src(inst, 0)) == CBRET_INTERRUPT_NUM) { dr_insert_clean_call(drcontext, bb, inst, (void *)drmgr_cls_stack_pop, false, 0); } return DR_EMIT_DEFAULT; }
static bool instr_is_exclusive_load(instr_t *instr) { switch (instr_get_opcode(instr)) { case OP_ldaxp: case OP_ldaxr: case OP_ldaxrb: case OP_ldaxrh: case OP_ldxp: case OP_ldxr: case OP_ldxrb: case OP_ldxrh: return true; } return false; }
bool instr_writes_gpr_list(instr_t *instr) { int opc = instr_get_opcode(instr); switch (opc) { case OP_ldm: case OP_ldmib: case OP_ldmda: case OP_ldmdb: case OP_ldm_priv: case OP_ldmib_priv: case OP_ldmda_priv: case OP_ldmdb_priv: return true; default: return false; } }
static void look_for_usercall(void *dcontext, byte *entry, const char *sym, LOADED_IMAGE *img, const char *modpath) { bool found_push_imm = false; int imm = 0; app_pc pc, pre_pc; instr_t *instr; if (entry == NULL) return; instr = instr_create(dcontext); pc = entry; while (true) { instr_reset(dcontext, instr); pre_pc = pc; pc = decode(dcontext, pc, instr); if (verbose) { instr_set_translation(instr, pre_pc); dr_print_instr(dcontext, STDOUT, instr, ""); } if (pc == NULL || !instr_valid(instr)) break; if (instr_get_opcode(instr) == OP_push_imm) { found_push_imm = true; imm = (int) opnd_get_immed_int(instr_get_src(instr, 0)); } else if (instr_is_call_direct(instr) && found_push_imm) { app_pc tgt = opnd_get_pc(instr_get_target(instr)); bool found = false; int i; for (i = 0; i < NUM_USERCALL; i++) { if (tgt == usercall_addr[i]) { dr_printf("Call #0x%02x to %s at %s+0x%x\n", imm, usercall_names[i], sym, pre_pc - entry); found = true; break; } } if (found) break; } else if (instr_is_return(instr)) break; if (pc - entry > MAX_BYTES_BEFORE_USERCALL) break; } instr_destroy(dcontext, instr); }
bool instr_uses_memory_we_track(instr_t *inst) { int i; ASSERT(options.staleness, "should not be called"); if (instr_get_opcode(inst) == OP_lea) /* not a real mem access */ return false; for (i = 0; i < instr_num_srcs(inst); i++) { if (opnd_uses_memory_we_track(instr_get_src(inst, i))) return true; } for (i = 0; i < instr_num_dsts(inst); i++) { if (opnd_uses_memory_we_track(instr_get_dst(inst, i))) return true; } return false; }
static bool instr_is_reg_restore(instr_t *instr, reg_id_t reg, umbra_info_t *info) { opnd_t opnd; int slot; if (instr_get_opcode(instr) != OP_mov_ld) return false; opnd = instr_get_dst(instr, 0); if (!opnd_is_reg(opnd) || opnd_get_reg(opnd) != reg) return false; slot = reg - REG_SPILL_START; opnd = OPND_CREATE_ABSMEM(&info->spill_regs[slot], OPSZ_PTR); if (opnd_same(opnd, instr_get_src(instr, 0))) return true; return false; }
bool instr_reads_reg_list(instr_t *instr) { int opc = instr_get_opcode(instr); switch (opc) { case OP_stm: case OP_stmib: case OP_stmda: case OP_stmdb: case OP_stm_priv: case OP_stmib_priv: case OP_stmda_priv: case OP_stmdb_priv: case OP_vstm: case OP_vstmdb: return true; default: return false; } }
static app_pc get_function_entry(app_pc C_var) { void *drcontext = dr_get_current_drcontext(); byte *pc; instr_t inst; instr_init(drcontext, &inst); pc = decode(drcontext, C_var, &inst); ASSERT(pc != NULL, "invalid instr at function entry"); if (instr_get_opcode(&inst) == OP_jmp) { /* skip jmp in ILT */ ASSERT(opnd_is_pc(instr_get_target(&inst)), "decoded jmp should have pc tgt"); pc = opnd_get_pc(instr_get_target(&inst)); } else pc = C_var; instr_free(drcontext, &inst); return pc; }
bool instr_is_return(instr_t *instr) { /* There is no "return" opcode so we consider a return to be either: * A) An indirect branch through lr; * B) An instr that reads lr and writes pc; * (XXX: should we limit to a move and rule out an add or shift or whatever?) * C) A pop into pc. */ int opc = instr_get_opcode(instr); if ((opc == OP_bx || opc == OP_bxj) && opnd_get_reg(instr_get_src(instr, 0)) == DR_REG_LR) return true; if (!instr_writes_to_reg(instr, DR_REG_PC, DR_QUERY_INCLUDE_ALL)) return false; return (instr_reads_from_reg(instr, DR_REG_LR, DR_QUERY_INCLUDE_ALL) || instr_is_pop(instr)); }
static dr_emit_flags_t bb_event(void* drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) { instr_t *instr; if (!translating) increment(tag); /* I'm looking for a specific BB in the test .exe. I've marked * it with a couple nops. */ #ifdef WINDOWS if ((app_pc)tag >= start && (app_pc)tag < end) { #endif instr = instrlist_first(bb); if (instr_is_nop(instr)) { instr_t *next = instr_get_next(instr); /* The test app uses two nops as a marker to identify a specific bb. Since * 2 nop instructions in a row aren't that uncommon on Linux (where we can't * restrict our search to just the test.exe module) we use an unusual nop * for the second one: xchg xbp, xbp */ if (next != NULL && instr_is_nop(next) && instr_get_opcode(next) == OP_xchg && instr_writes_to_exact_reg(next, REG_XBP, DR_QUERY_DEFAULT)) { bb_build_count++; if (delay_flush_at_next_build) { delay_flush_at_next_build = false; dr_delay_flush_region((app_pc)tag - 20, 30, callback_count, flush_event); } dr_insert_clean_call(drcontext, bb, instr, (void *)callback, false, 2, OPND_CREATE_INTPTR(tag), OPND_CREATE_INTPTR(instr_get_app_pc(instr))); } } #ifdef WINDOWS } #endif return DR_EMIT_DEFAULT; }