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; }
dr_emit_flags_t memdump_bb_instrumentation(void *drcontext, void *tag, instrlist_t *bb, instr_t *instr, bool for_trace, bool translating, void *user_data) { reg_id_t reg1 = DR_REG_XAX; reg_id_t reg2 = DR_REG_XBX; int i = 0; if(filter_bb_level_from_list(app_pc_head, instr)){ dr_save_reg(drcontext, bb, instr, reg1, SPILL_SLOT_1); dr_save_reg(drcontext, bb, instr, reg2, SPILL_SLOT_2); dr_mutex_lock(mutex); DEBUG_PRINT("instrumenting %x pc\n", instr_get_app_pc(instr)); instr_clones[instr_clone_amount] = instr_clone(drcontext, instr); for (i = 0; i < instr_num_srcs(instr); i++){ if (opnd_is_memory_reference(instr_get_src(instr, i))) { drutil_insert_get_mem_addr(drcontext, bb, instr, instr_get_src(instr,i), reg1, reg2); dr_insert_clean_call(drcontext, bb, instr, clean_call_mem_information, false, 3, OPND_CREATE_INTPTR(instr_clones[instr_clone_amount]), opnd_create_reg(reg1), OPND_CREATE_INTPTR(false)); } } for (i = 0; i < instr_num_dsts(instr); i++){ if (opnd_is_memory_reference(instr_get_dst(instr, i))) { drutil_insert_get_mem_addr(drcontext, bb, instr, instr_get_dst(instr, i), reg1, reg2); dr_insert_clean_call(drcontext, bb, instr, clean_call_mem_information, false, 3, OPND_CREATE_INTPTR(instr_clones[instr_clone_amount]), opnd_create_reg(reg1), OPND_CREATE_INTPTR(true)); } } instr_clone_amount++; dr_mutex_unlock(mutex); dr_restore_reg(drcontext, bb, instr, reg1, SPILL_SLOT_1); dr_restore_reg(drcontext, bb, instr, reg2, SPILL_SLOT_2); } return DR_EMIT_DEFAULT; }
/* * Function that tests a single operand of an instruction to see if * it's a memory reference, and if so, adds a call to log_mem. */ static void try_mem_opnd( void *drcontext, instrlist_t *bb, instr_t *instr, char **loc, opnd_t opnd, bool write) { if (!opnd_is_memory_reference(opnd)) return; instr_format_location(instr, loc); reg_id_t r0, r1; drreg_status_t st; st = drreg_reserve_register(drcontext, bb, instr, NULL, &r0); DR_ASSERT(st == DRREG_SUCCESS); st = drreg_reserve_register(drcontext, bb, instr, NULL, &r1); DR_ASSERT(st == DRREG_SUCCESS); bool ok = drutil_insert_get_mem_addr(drcontext, bb, instr, opnd, r0, r1); DR_ASSERT(ok); uint size = drutil_opnd_mem_size_in_bytes(opnd, instr); dr_insert_clean_call( drcontext, bb, instr, (void *)log_mem, false, 4, opnd_create_reg(r0), OPND_CREATE_INT32(size), OPND_CREATE_INT32(write), OPND_CREATE_INTPTR(*loc)); st = drreg_unreserve_register(drcontext, bb, instr, r1); DR_ASSERT(st == DRREG_SUCCESS); st = drreg_unreserve_register(drcontext, bb, instr, r0); DR_ASSERT(st == DRREG_SUCCESS); }
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; }
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 */ opnd_t opnd; if (instr_is_div(instr, &opnd)) { dr_insert_clean_call(drcontext, bb, instr, (void *)callback, false /*no fp save*/, 2, OPND_CREATE_INTPTR(instr_get_app_pc(instr)), opnd); } return DR_EMIT_DEFAULT; }
dr_emit_flags_t funcwrap_bb_instrumentation(void *drcontext, void *tag, instrlist_t *bb, instr_t *instr, bool for_trace, bool translating, void *user_data) { instr_t * first = instrlist_first(bb); app_pc pc = instr_get_app_pc(first); module_data_t * module_data; per_thread_t * data = drmgr_get_tls_field(dr_get_current_drcontext(), tls_index); module_t * md; app_pc offset; if (instr != first || data->nesting != 0){ return DR_EMIT_DEFAULT; } module_data = dr_lookup_module(pc); data = drmgr_get_tls_field(drcontext, tls_index); if (module_data != NULL){ md = md_lookup_module(head, module_data->full_path); if (md != NULL){ offset = pc - module_data->start; for (int i = 1; i <= md->bbs[0].start_addr; i++){ if (offset == md->bbs[i].start_addr){ DEBUG_PRINT("bb instrumenting function\n"); data->filter_func = true; dr_insert_clean_call(drcontext, bb, instr, clean_call, false, 1, OPND_CREATE_INTPTR(instr_get_app_pc(instr))); wrap_thread_id = dr_get_thread_id(drcontext); DEBUG_PRINT("done bb instrumenting function\n"); } } } } /*if (data->filter_func){ instrlist_disassemble(drcontext, instr_get_app_pc(instrlist_first(bb)), bb, logfile); }*/ dr_free_module_data(module_data); return DR_EMIT_DEFAULT; }
static dr_emit_flags_t event_app_instruction(void *drcontext, void *tag, instrlist_t *bb, instr_t *inst, bool for_trace, bool translating, void *user_data) { app_pc pc = dr_fragment_app_pc(tag); reg_id_t reg; /* We need a 2nd scratch reg for several operations on AArch32 and AArch64 only. */ reg_id_t reg2 = DR_REG_NULL; /* We do all our work at the start of the block prior to the first instr */ if (!drmgr_is_first_instr(drcontext, inst)) return DR_EMIT_DEFAULT; /* We need a scratch register */ if (drreg_reserve_register(drcontext, bb, inst, NULL, ®) != DRREG_SUCCESS) { DR_ASSERT(false); /* cannot recover */ return DR_EMIT_DEFAULT; } #ifdef AARCHXX /* We need a second register here, because the drx_buf routines need a scratch reg * for AArch32 and AArch64. */ if (drreg_reserve_register(drcontext, bb, inst, NULL, ®2) != DRREG_SUCCESS) { DR_ASSERT(false); /* cannot recover */ return DR_EMIT_DEFAULT; } #endif /* load buffer pointer from TLS field */ drx_buf_insert_load_buf_ptr(drcontext, buf, bb, inst, reg); /* store bb's start pc into the buffer */ drx_buf_insert_buf_store(drcontext, buf, bb, inst, reg, reg2, OPND_CREATE_INTPTR(pc), OPSZ_PTR, 0); /* Internally this will update the TLS buffer pointer by incrementing just the bottom * 16 bits of the pointer. */ drx_buf_insert_update_buf_ptr(drcontext, buf, bb, inst, reg, reg2, sizeof(app_pc)); if (drreg_unreserve_register(drcontext, bb, inst, reg) != DRREG_SUCCESS) DR_ASSERT(false); #ifdef AARCHXX if (drreg_unreserve_register(drcontext, bb, inst, reg2) != DRREG_SUCCESS) DR_ASSERT(false); #endif return DR_EMIT_DEFAULT; }
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; }
static dr_emit_flags_t bb_event(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) { static int64 bb_count; if (++bb_count % 10 == 0) { for (instr_t *inst = instrlist_first(bb); inst != NULL; inst = instr_get_next(inst)) { if (instr_is_exclusive_store(inst)) return DR_EMIT_DEFAULT; } dr_insert_clean_call(drcontext, bb, instrlist_first(bb), (void *)cleancallee, false, 1, OPND_CREATE_INTPTR(tag)); } return DR_EMIT_DEFAULT; }
static dr_emit_flags_t event_basic_block(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) { instr_t *instr; for (instr = instrlist_first_app(bb); instr != NULL; instr = instr_get_next_app(instr)) { if (instr_is_cbr(instr)) { dr_insert_cbr_instrumentation_ex (drcontext, bb, instr, (void *)at_cbr, OPND_CREATE_INTPTR(dr_fragment_app_pc(tag))); } } return DR_EMIT_DEFAULT; }
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 find div, insert a clean call to our instrumentation routine */ if (opcode == 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; }
static dr_emit_flags_t bb_event(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) { instr_t *instr, *next_instr; for (instr = instrlist_first(bb); instr != NULL; instr = next_instr) { next_instr = instr_get_next(instr); if (instr_is_cbr(instr)) { /* Conditional branch. We can determine the target and * fallthrough addresses here, but we want to note the * edge if and when it actually executes at runtime. * Instead of using dr_insert_cbr_instrumentation(), we'll * insert separate instrumentation for the taken and not * taken cases and remove the instrumentation for an edge * after it executes. */ cbr_state_t state; bool insert_taken, insert_not_taken; app_pc src = instr_get_app_pc(instr); /* First look up the state of this branch so we * know what instrumentation to insert, if any. */ elem_t *elem = lookup(table, src); if (elem == NULL) { state = CBR_NEITHER; insert(table, src, CBR_NEITHER); } else { state = elem->state; } insert_taken = (state & CBR_TAKEN) == 0; insert_not_taken = (state & CBR_NOT_TAKEN) == 0; if (insert_taken || insert_not_taken) { app_pc fall = (app_pc)decode_next_pc(drcontext, (byte *)src); app_pc targ = instr_get_branch_target_pc(instr); /* Redirect the existing cbr to jump to a callout for * the 'taken' case. We'll insert a 'not-taken' * callout at the fallthrough address. */ instr_t *label = INSTR_CREATE_label(drcontext); /* should be meta, and meta-instrs shouldn't have translations */ instr_set_meta_no_translation(instr); /* it may not reach (in particular for x64) w/ our added clean call */ if (instr_is_cti_short(instr)) { /* if jecxz/loop we want to set the target of the long-taken * so set instr to the return value */ instr = instr_convert_short_meta_jmp_to_long(drcontext, bb, instr); } instr_set_target(instr, opnd_create_instr(label)); if (insert_not_taken) { /* Callout for the not-taken case. Insert after * the cbr (i.e., 3rd argument is NULL). */ dr_insert_clean_call(drcontext, bb, NULL, (void*)at_not_taken, false /* don't save fp state */, 2 /* 2 args for at_not_taken */, OPND_CREATE_INTPTR(src), OPND_CREATE_INTPTR(fall)); } /* After the callout, jump to the original fallthrough * address. Note that this is an exit cti, and should * not be a meta-instruction. Therefore, we use * preinsert instead of meta_preinsert, and we must * set the translation field. On Windows, this jump * and the final jump below never execute since the * at_taken and at_not_taken callouts redirect * execution and never return. However, since the API * expects clients to produced well-formed code, we * insert explicit exits from the block for Windows as * well as Linux. */ instrlist_preinsert(bb, NULL, INSTR_XL8(INSTR_CREATE_jmp (drcontext, opnd_create_pc(fall)), fall)); /* label goes before the 'taken' callout */ MINSERT(bb, NULL, label); if (insert_taken) { /* Callout for the taken case */ dr_insert_clean_call(drcontext, bb, NULL, (void*)at_taken, false /* don't save fp state */, 2 /* 2 args for at_taken */, OPND_CREATE_INTPTR(src), OPND_CREATE_INTPTR(targ)); } /* After the callout, jump to the original target * block (this should not be a meta-instruction). */ instrlist_preinsert(bb, NULL, INSTR_XL8(INSTR_CREATE_jmp (drcontext, opnd_create_pc(targ)), targ)); } } } /* since our added instrumentation is not constant, we ask to store * translations now */ return DR_EMIT_STORE_TRANSLATIONS; }
dr_emit_flags_t bb_event(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) { instr_t *instr, *next_instr; app_pc bb_addr = dr_fragment_app_pc(tag); if (bb_addr == start_pc) { instrument = true; } else if (bb_addr == stop_pc) { instrument = false; } if (!instrument) { return DR_EMIT_DEFAULT; } for (instr = instrlist_first(bb); instr != NULL; instr = next_instr) { next_instr = instr_get_next(instr); /* * Conditional branch. We can determine the target and * fallthrough addresses here, but we need to instrument if we * want to record the edge only if it actually executes at * runtime. Instead of using dr_insert_cbr_instrumentation, * we'll insert separate instrumentation for the taken and not * taken cases and remove it separately after we see each * case. */ if (instr_is_cbr(instr)) { app_pc src = instr_get_app_pc(instr); cbr_state_t state; bool insert_taken, insert_not_taken; /* First look up the state of this branch so we * know what instrumentation to insert, if any. */ elem_t *elem = lookup(table, src); if (elem == NULL) { state = CBR_NONE; insert(table, src, CBR_NONE); } else { state = elem->state; } insert_taken = (state & CBR_TAKEN) == 0; insert_not_taken = (state & CBR_NOT_TAKEN) == 0; if (insert_taken || insert_not_taken) { app_pc fall = (app_pc)decode_next_pc(drcontext, (byte *)src); app_pc targ = instr_get_branch_target_pc(instr); /* * Redirect the cbr to jump to the 'taken' callout. * We'll insert a 'not-taken' callout at fallthrough * address. */ instr_t *label = INSTR_CREATE_label(drcontext); instr_set_meta(instr); instr_set_translation(instr, NULL); /* If this is a short cti, make sure it can reach its new target */ if (instr_is_cti_short(instr)) { /* if jecxz/loop we want to set the target of the long-taken * so set instr to the return value */ instr = instr_convert_short_meta_jmp_to_long(drcontext, bb, instr); } instr_set_target(instr, opnd_create_instr(label)); if (insert_not_taken) { /* * Callout for the not-taken case */ dr_insert_clean_call(drcontext, bb, NULL, (void *)at_not_taken, false /* don't save fp state */, 2 /* 2 args for at_not_taken */, OPND_CREATE_INTPTR((ptr_uint_t)src), OPND_CREATE_INTPTR((ptr_uint_t)fall)); } /* * Jump to the original fall-through address. * (This should not be a meta-instruction). */ instrlist_preinsert( bb, NULL, INSTR_XL8(INSTR_CREATE_jmp(drcontext, opnd_create_pc(fall)), fall)); /* label goes before the 'taken' callout */ MINSERT(bb, NULL, label); if (insert_taken) { /* * Callout for the taken case */ dr_insert_clean_call(drcontext, bb, NULL, (void *)at_taken, false /* don't save fp state */, 2 /* 2 args for at_taken */, OPND_CREATE_INTPTR((ptr_uint_t)src), OPND_CREATE_INTPTR((ptr_uint_t)targ)); } /* * Jump to the original target block (this should * not be a meta-instruction). */ instrlist_preinsert( bb, NULL, INSTR_XL8(INSTR_CREATE_jmp(drcontext, opnd_create_pc(targ)), targ)); } } } /* since our added instrumentation is not constant, we ask to store * translations now */ return DR_EMIT_STORE_TRANSLATIONS; }
static void instrument_mem(void *drcontext, instrlist_t *ilist, instr_t *where, int pos, bool write) { instr_t *instr; opnd_t ref, opnd1, opnd2; reg_id_t reg1 = DR_REG_XAX; /* We can optimize it by picking dead reg */ reg_id_t reg2 = DR_REG_XCX; /* reg2 must be ECX or RCX for jecxz */ if (write) ref = instr_get_dst(where, pos); else ref = instr_get_src(where, pos); dr_save_reg(drcontext, ilist, where, reg1, SPILL_SLOT_2); dr_save_reg(drcontext, ilist, where, reg2, SPILL_SLOT_3); // reg2 = RBufIdx opnd1 = opnd_create_reg(reg2); opnd2 = OPND_CREATE_ABSMEM((byte *)&RBufIdx, OPSZ_4); instr = INSTR_CREATE_mov_ld(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); // save flags since we are using inc, and dr_save_arith_flags_to_xax(drcontext, ilist, where); // reg2 = reg2 & RBUF_SIZE opnd1 = opnd_create_reg(reg2); opnd2 = OPND_CREATE_INT32(RBUF_SIZE); instr = INSTR_CREATE_and(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); dr_restore_arith_flags_from_xax(drcontext, ilist, where); // reg1 = &RBuf opnd1 = opnd_create_reg(reg1); opnd2 = OPND_CREATE_INTPTR(RBuf); instr = INSTR_CREATE_mov_imm(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); // reg1 = reg1 + reg2 * sizeof(uint) // = RBuf + RBufIdx * sizeof(uint) // = RBuf[RBufIdx] opnd1 = opnd_create_reg(reg1); opnd2 = opnd_create_base_disp(reg1, reg2, sizeof(uint), 0, OPSZ_lea); instr = INSTR_CREATE_lea(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); // RBuf[RBufIdx].addr = addr; opnd1 = OPND_CREATE_MEMPTR(reg1, 0); drutil_insert_get_mem_addr(drcontext, ilist, where, ref, reg2, reg1); opnd2 = opnd_create_reg(reg2); instr = INSTR_CREATE_mov_st(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); dr_save_arith_flags_to_xax(drcontext, ilist, where); // reg2 = RBufIdx opnd1 = opnd_create_reg(reg2); opnd2 = OPND_CREATE_ABSMEM((byte *)&RBufIdx, OPSZ_4); instr = INSTR_CREATE_mov_ld(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); // reg2 = reg2 + 1 opnd1 = opnd_create_reg(reg2); instr = INSTR_CREATE_inc(drcontext, opnd1); instrlist_meta_preinsert(ilist, where, instr); // RBufIdx = reg2 opnd1 = OPND_CREATE_ABSMEM((byte *)&RBufIdx, OPSZ_4); opnd2 = opnd_create_reg(reg2); instr = INSTR_CREATE_mov_st(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); dr_restore_arith_flags_from_xax(drcontext, ilist, where); dr_restore_reg(drcontext, ilist, where, reg1, SPILL_SLOT_2); dr_restore_reg(drcontext, ilist, where, reg2, SPILL_SLOT_3); }
/* * The main function called to instrument each machine instruction. */ static dr_emit_flags_t instrument_instr( void *drcontext, void *tag, instrlist_t *bb, instr_t *instr, bool for_trace, bool translating, void *user_data) { char *loc = NULL; /* * If this instruction is the first in its basic block, call * log_pc to record that we're executing this block at all. */ if (drmgr_is_first_instr(drcontext, instr)) { instr_format_location(instr, &loc); dr_insert_clean_call( drcontext, bb, instr, (void *)log_pc, false, 1, OPND_CREATE_INTPTR(loc)); } /* * If the instruction reads or writes memory, log its access. */ if (instr_reads_memory(instr) || instr_writes_memory(instr)) { for (int i = 0, limit = instr_num_srcs(instr); i < limit; i++) try_mem_opnd(drcontext, bb, instr, &loc, instr_get_src(instr, i), false); for (int i = 0, limit = instr_num_dsts(instr); i < limit; i++) try_mem_opnd(drcontext, bb, instr, &loc, instr_get_dst(instr, i), false); } /* * Now do opcode-specific checks. */ int opcode = instr_get_opcode(instr); switch (opcode) { case OP_div: case OP_idiv: /* * x86 hardware divisions. The operand order for DR's * representation of these seem to be: 0 = denominator, 1 = * numerator MSW, 2 = numerator LSW. */ instr_format_location(instr, &loc); dr_insert_clean_call( drcontext, bb, instr, (void *)log_div, false, 3, instr_get_src(instr, 2), instr_get_src(instr, 0), OPND_CREATE_INTPTR(loc)); break; case OP_shl: case OP_shr: case OP_sar: case OP_shlx: case OP_shrx: case OP_sarx: case OP_rol: case OP_ror: case OP_rcl: case OP_rcr: /* * Shift instructions. If they're register-controlled, log the * shift count. */ { opnd_t shiftcount = instr_get_src(instr, 0); if (!opnd_is_immed(shiftcount)) { reg_id_t r0; drreg_status_t st; st = drreg_reserve_register(drcontext, bb, instr, NULL, &r0); DR_ASSERT(st == DRREG_SUCCESS); opnd_t op_r0 = opnd_create_reg(r0); instrlist_preinsert(bb, instr, INSTR_CREATE_movzx( drcontext, op_r0, shiftcount)); instr_format_location(instr, &loc); dr_insert_clean_call( drcontext, bb, instr, (void *)log_var_shift, false, 2, op_r0, OPND_CREATE_INTPTR(loc)); st = drreg_unreserve_register(drcontext, bb, instr, r0); DR_ASSERT(st == DRREG_SUCCESS); } } break; } return DR_EMIT_DEFAULT; }
static dr_emit_flags_t event_app_instruction(void *drcontext, void *tag, instrlist_t *bb, instr_t *inst, bool for_trace, bool translating, void *user_data) { reg_id_t reg_ptr = IF_X86_ELSE(DR_REG_XDX, TEST_REG); reg_id_t reg_tmp = IF_X86_ELSE(DR_REG_XCX, DR_REG_R3); /* We need a third register on ARM, because updating the buf pointer * requires a second scratch reg. */ reg_id_t scratch = IF_X86_ELSE(reg_tmp, DR_REG_R5); ptr_int_t subtest = (ptr_int_t) user_data; if (!instr_is_label(inst)) return DR_EMIT_DEFAULT; #ifdef X86 scratch = reg_resize_to_opsz(scratch, OPSZ_4); #endif if (subtest == DRX_BUF_TEST_1_C) { /* testing fast circular buffer */ /* test to make sure that on first invocation, the buffer is empty */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_empty, false, 1, OPND_CREATE_INTPTR(circular_fast)); /* load the buf pointer, and then write a garbage element to the buffer */ drx_buf_insert_load_buf_ptr(drcontext, circular_fast, bb, inst, reg_ptr); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_4, 0); drx_buf_insert_update_buf_ptr(drcontext, circular_fast, bb, inst, reg_ptr, reg_tmp, sizeof(int)); /* verify the buffer was written to */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_dirty, false, 2, OPND_CREATE_INTPTR(circular_fast), opnd_create_reg(scratch)); /* fast circular buffer: trigger an overflow */ drx_buf_insert_load_buf_ptr(drcontext, circular_fast, bb, inst, reg_ptr); drx_buf_insert_update_buf_ptr(drcontext, circular_fast, bb, inst, reg_ptr, reg_tmp, CIRCULAR_FAST_SZ - sizeof(int)); /* the buffer is now clean */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_empty, false, 1, OPND_CREATE_INTPTR(circular_fast)); } else if (subtest == DRX_BUF_TEST_2_C) { /* testing slow circular buffer */ /* test to make sure that on first invocation, the buffer is empty */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_empty, false, 1, OPND_CREATE_INTPTR(circular_slow)); /* load the buf pointer, and then write an element to the buffer */ drx_buf_insert_load_buf_ptr(drcontext, circular_slow, bb, inst, reg_ptr); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_4, 0); drx_buf_insert_update_buf_ptr(drcontext, circular_slow, bb, inst, reg_ptr, DR_REG_NULL, sizeof(int)); /* verify the buffer was written to */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_dirty, false, 2, OPND_CREATE_INTPTR(circular_slow), opnd_create_reg(scratch)); /* slow circular buffer: trigger a fault */ drx_buf_insert_load_buf_ptr(drcontext, circular_slow, bb, inst, reg_ptr); drx_buf_insert_update_buf_ptr(drcontext, circular_slow, bb, inst, reg_ptr, DR_REG_NULL, CIRCULAR_SLOW_SZ - sizeof(int)); /* the "trigger" is a write, so we write whatever garbage is in reg_tmp */ drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_4, 0); /* the buffer is now clean */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_empty, false, 1, OPND_CREATE_INTPTR(circular_slow)); } else if (subtest == DRX_BUF_TEST_3_C) { /* testing trace buffer */ /* test to make sure that on first invocation, the buffer is empty */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_empty, false, 1, OPND_CREATE_INTPTR(trace)); /* load the buf pointer, and then write an element to the buffer */ drx_buf_insert_load_buf_ptr(drcontext, trace, bb, inst, reg_ptr); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_4, 0); drx_buf_insert_update_buf_ptr(drcontext, trace, bb, inst, reg_ptr, DR_REG_NULL, sizeof(int)); /* verify the buffer was written to */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_dirty, false, 2, OPND_CREATE_INTPTR(trace), opnd_create_reg(scratch)); /* trace buffer: trigger a fault and verify */ drx_buf_insert_load_buf_ptr(drcontext, trace, bb, inst, reg_ptr); drx_buf_insert_update_buf_ptr(drcontext, trace, bb, inst, reg_ptr, DR_REG_NULL, TRACE_SZ - sizeof(int)); /* the "trigger" is a write, so we write whatever garbage is in reg_tmp */ drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_4, 0); /* the buffer is now clean */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_empty, false, 1, OPND_CREATE_INTPTR(trace)); } else if (subtest == DRX_BUF_TEST_4_C) { /* test immediate store: 8 bytes (if possible), 4 bytes, 2 bytes and 1 byte */ /* "ABCDEFGH\x00" (x2 for x64) */ drx_buf_insert_load_buf_ptr(drcontext, circular_fast, bb, inst, reg_ptr); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x41, OPSZ_1), OPSZ_1, 0); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x42, OPSZ_1), OPSZ_1, 1); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x4443, OPSZ_2), OPSZ_2, 2); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x48474645, OPSZ_4), OPSZ_4, 4); #ifdef X64 drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x4847464544434241, OPSZ_8), OPSZ_8, 8); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x00, OPSZ_1), OPSZ_1, 17); #else drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x00, OPSZ_1), OPSZ_1, 9); #endif dr_insert_clean_call(drcontext, bb, inst, verify_store, false, 1, OPND_CREATE_INTPTR(circular_fast)); } else if (subtest == DRX_BUF_TEST_5_C) { /* test register store: 8 bytes (if possible), 4 bytes, 2 bytes and 1 byte */ /* "ABCDEFGH\x00" (x2 for x64) */ drx_buf_insert_load_buf_ptr(drcontext, circular_fast, bb, inst, reg_ptr); scratch = reg_resize_to_opsz(scratch, OPSZ_1); MINSERT(bb, inst, XINST_CREATE_load_int (drcontext, opnd_create_reg(scratch), opnd_create_immed_int(0x41, OPSZ_1))); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_1, 0); MINSERT(bb, inst, XINST_CREATE_load_int (drcontext, opnd_create_reg(scratch), opnd_create_immed_int(0x42, OPSZ_1))); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_1, 1); scratch = reg_resize_to_opsz(scratch, OPSZ_2); MINSERT(bb, inst, XINST_CREATE_load_int (drcontext, opnd_create_reg(scratch), opnd_create_immed_int(0x4443, OPSZ_2))); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_2, 2); scratch = reg_resize_to_opsz(scratch, OPSZ_4); #ifdef X86 MINSERT(bb, inst, XINST_CREATE_load_int (drcontext, opnd_create_reg(scratch), opnd_create_immed_int(0x48474645, OPSZ_4))); #else instrlist_insert_mov_immed_ptrsz(drcontext, 0x48474645, opnd_create_reg(reg_resize_to_opsz (scratch, OPSZ_PTR)), bb, inst, NULL, NULL); #endif drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_4, 4); #ifdef X64 scratch = reg_resize_to_opsz(scratch, OPSZ_8); /* only way to reliably move a 64 bit int into a register */ instrlist_insert_mov_immed_ptrsz(drcontext, 0x4847464544434241, opnd_create_reg(scratch), bb, inst, NULL, NULL); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_8, 8); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x00, OPSZ_1), OPSZ_1, 17); #else drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x00, OPSZ_1), OPSZ_1, 9); #endif dr_insert_clean_call(drcontext, bb, inst, verify_store, false, 1, OPND_CREATE_INTPTR(circular_fast)); } else if (subtest == DRX_BUF_TEST_6_C) { /* Currently, the fast circular buffer does not recommend variable-size * writes, for good reason. We don't test the memcpy operation on the * fast circular buffer. */ /* verify memcpy works on the slow clrcular buffer */ drx_buf_insert_load_buf_ptr(drcontext, circular_slow, bb, inst, reg_ptr); instrlist_insert_mov_immed_ptrsz(drcontext, (ptr_int_t)test_copy, opnd_create_reg(reg_resize_to_opsz (scratch, OPSZ_PTR)), bb, inst, NULL, NULL); drx_buf_insert_buf_memcpy(drcontext, circular_slow, bb, inst, reg_ptr, reg_resize_to_opsz(scratch, OPSZ_PTR), sizeof(test_copy)); /* NULL out the buffer */ drx_buf_insert_load_buf_ptr(drcontext, circular_slow, bb, inst, reg_ptr); instrlist_insert_mov_immed_ptrsz(drcontext, (ptr_int_t)test_null, opnd_create_reg(reg_resize_to_opsz (scratch, OPSZ_PTR)), bb, inst, NULL, NULL); drx_buf_insert_buf_memcpy(drcontext, circular_slow, bb, inst, reg_ptr, reg_resize_to_opsz(scratch, OPSZ_PTR), sizeof(test_null)); /* Unfortunately, we can't just use the check in verify_buffer_empty, because * drx_buf_insert_buf_memcpy() incrememnts the buffer pointer internally, unlike * drx_buf_insert_buf_store(). We simply check that the buffer was NULLed out. */ dr_insert_clean_call(drcontext, bb, inst, (void *)verify_buffers_nulled, false, 1, OPND_CREATE_INTPTR(circular_slow)); /* verify memcpy works on the trace buffer */ drx_buf_insert_load_buf_ptr(drcontext, trace, bb, inst, reg_ptr); instrlist_insert_mov_immed_ptrsz(drcontext, (ptr_int_t)test_copy, opnd_create_reg(reg_resize_to_opsz (scratch, OPSZ_PTR)), bb, inst, NULL, NULL); drx_buf_insert_buf_memcpy(drcontext, trace, bb, inst, reg_ptr, reg_resize_to_opsz(scratch, OPSZ_PTR), sizeof(test_copy)); /* NULL out the buffer */ drx_buf_insert_load_buf_ptr(drcontext, trace, bb, inst, reg_ptr); instrlist_insert_mov_immed_ptrsz(drcontext, (ptr_int_t)test_null, opnd_create_reg(reg_resize_to_opsz (scratch, OPSZ_PTR)), bb, inst, NULL, NULL); drx_buf_insert_buf_memcpy(drcontext, trace, bb, inst, reg_ptr, reg_resize_to_opsz(scratch, OPSZ_PTR), sizeof(test_null)); /* verify buffer was NULLed */ dr_insert_clean_call(drcontext, bb, inst, (void *)verify_buffers_nulled, false, 1, OPND_CREATE_INTPTR(trace)); } return DR_EMIT_DEFAULT; }
static reg_id_t instrument_mem(void *drcontext, instrlist_t *ilist, instr_t *where, opnd_t ref) { reg_id_t reg_ptr, reg_tmp, reg_addr; ushort type, size; bool ok; if (drreg_reserve_register(drcontext, ilist, where, NULL, ®_tmp) != DRREG_SUCCESS) { DR_ASSERT(false); return DR_REG_NULL; } if (drreg_reserve_register(drcontext, ilist, where, NULL, ®_ptr) != DRREG_SUCCESS) { DR_ASSERT(false); return DR_REG_NULL; } /* i#2449: In the situation that instrument_post_write, instrument_mem and ref all * have the same register reserved, drutil_insert_get_mem_addr will compute the * address of an operand using an incorrect register value, as drreg will elide the * save/restore. */ if (opnd_uses_reg(ref, reg_tmp) && drreg_get_app_value(drcontext, ilist, where, reg_tmp, reg_tmp) != DRREG_SUCCESS) { DR_ASSERT(false); return DR_REG_NULL; } if (opnd_uses_reg(ref, reg_ptr) && drreg_get_app_value(drcontext, ilist, where, reg_ptr, reg_ptr) != DRREG_SUCCESS) { DR_ASSERT(false); return DR_REG_NULL; } /* We use reg_ptr as scratch to get addr. Note we do this first as reg_ptr or reg_tmp * may be used in ref. */ ok = drutil_insert_get_mem_addr(drcontext, ilist, where, ref, reg_tmp, reg_ptr); DR_ASSERT(ok); drx_buf_insert_load_buf_ptr(drcontext, trace_buffer, ilist, where, reg_ptr); /* inserts memref addr */ drx_buf_insert_buf_store(drcontext, trace_buffer, ilist, where, reg_ptr, DR_REG_NULL, opnd_create_reg(reg_tmp), OPSZ_PTR, offsetof(mem_ref_t, addr)); if (IF_AARCHXX_ELSE(true, false)) { /* At this point we save the write address for later, because reg_tmp's value * will get clobbered on ARM. */ if (drreg_reserve_register(drcontext, ilist, where, NULL, ®_addr) != DRREG_SUCCESS) { DR_ASSERT(false); return DR_REG_NULL; } MINSERT(ilist, where, XINST_CREATE_move(drcontext, opnd_create_reg(reg_addr), opnd_create_reg(reg_tmp))); } /* inserts type */ type = (ushort)instr_get_opcode(where); drx_buf_insert_buf_store(drcontext, trace_buffer, ilist, where, reg_ptr, reg_tmp, OPND_CREATE_INT16(type), OPSZ_2, offsetof(mem_ref_t, type)); /* inserts size */ size = (ushort)drutil_opnd_mem_size_in_bytes(ref, where); drx_buf_insert_buf_store(drcontext, trace_buffer, ilist, where, reg_ptr, reg_tmp, OPND_CREATE_INT16(size), OPSZ_2, offsetof(mem_ref_t, size)); drx_buf_insert_update_buf_ptr(drcontext, trace_buffer, ilist, where, reg_ptr, DR_REG_NULL, sizeof(mem_ref_t)); if (instr_is_call(where)) { app_pc pc; /* Note that on ARM the call instruction writes only to the link register, so * we would never even get into instrument_mem() on ARM if this was a call. */ IF_AARCHXX(DR_ASSERT(false)); /* We simulate the call instruction's written memory by writing the next app_pc * to the written buffer, since we can't do this after the call has happened. */ drx_buf_insert_load_buf_ptr(drcontext, write_buffer, ilist, where, reg_ptr); pc = decode_next_pc(drcontext, instr_get_app_pc(where)); /* note that for a circular buffer, we don't need to specify a scratch register */ drx_buf_insert_buf_store(drcontext, trace_buffer, ilist, where, reg_ptr, DR_REG_NULL, OPND_CREATE_INTPTR((ptr_int_t)pc), OPSZ_PTR, 0); drx_buf_insert_update_buf_ptr(drcontext, write_buffer, ilist, where, reg_ptr, reg_tmp, sizeof(app_pc)); /* we don't need to persist reg_tmp to the next instruction */ if (drreg_unreserve_register(drcontext, ilist, where, reg_tmp) != DRREG_SUCCESS) DR_ASSERT(false); reg_tmp = DR_REG_NULL; } else if (IF_AARCHXX_ELSE(true, false)) { /* Now reg_tmp has the address of the write again. */ MINSERT(ilist, where, XINST_CREATE_move(drcontext, opnd_create_reg(reg_tmp), opnd_create_reg(reg_addr))); if (drreg_unreserve_register(drcontext, ilist, where, reg_addr) != DRREG_SUCCESS) DR_ASSERT(false); } if (drreg_unreserve_register(drcontext, ilist, where, reg_ptr) != DRREG_SUCCESS) DR_ASSERT(false); return reg_tmp; }