static dr_emit_flags_t event_bb_insert(void *drcontext, void *tag, instrlist_t *bb, instr_t *inst, bool for_trace, bool translating, void *user_data) { static int freq; reg_id_t reg1 = IF_X86_ELSE(DR_REG_XAX, DR_REG_R0); reg_id_t reg2 = IF_X86_ELSE(DR_REG_XCX, DR_REG_R1); CHECK(drmgr_is_first_instr(drcontext, instrlist_first_app(bb)), "first incorrect"); CHECK(!drmgr_is_first_instr(drcontext, instrlist_last(bb)) || instrlist_first_app(bb) == instrlist_last(bb), "first incorrect"); CHECK(drmgr_is_last_instr(drcontext, instrlist_last(bb)), "last incorrect"); CHECK(!drmgr_is_last_instr(drcontext, instrlist_first_app(bb)) || instrlist_first_app(bb) == instrlist_last(bb), "last incorrect"); /* hack to instrument every nth bb. assumes DR serializes bb events. */ freq++; if (freq % 100 == 0 && inst == (instr_t*)user_data/*first instr*/) { /* test read from cache */ dr_save_reg(drcontext, bb, inst, reg1, SPILL_SLOT_1); drmgr_insert_read_tls_field(drcontext, tls_idx, bb, inst, reg1); dr_insert_clean_call(drcontext, bb, inst, (void *)check_tls_from_cache, false, 1, opnd_create_reg(reg1)); drmgr_insert_read_cls_field(drcontext, cls_idx, bb, inst, reg1); dr_insert_clean_call(drcontext, bb, inst, (void *)check_cls_from_cache, false, 1, opnd_create_reg(reg1)); dr_restore_reg(drcontext, bb, inst, reg1, SPILL_SLOT_1); } if (freq % 300 == 0 && inst == (instr_t*)user_data/*first instr*/) { instr_t *first, *second; /* test write from cache */ dr_save_reg(drcontext, bb, inst, reg1, SPILL_SLOT_1); dr_save_reg(drcontext, bb, inst, reg2, SPILL_SLOT_2); instrlist_insert_mov_immed_ptrsz(drcontext, (ptr_int_t)MAGIC_NUMBER_FROM_CACHE, opnd_create_reg(reg1), bb, inst, &first, &second); instr_set_meta(first); if (second != NULL) instr_set_meta(second); drmgr_insert_write_tls_field(drcontext, tls_idx, bb, inst, reg1, reg2); dr_insert_clean_call(drcontext, bb, inst, (void *)check_tls_write_from_cache, false, 0); drmgr_insert_write_cls_field(drcontext, cls_idx, bb, inst, reg1, reg2); dr_insert_clean_call(drcontext, bb, inst, (void *)check_cls_write_from_cache, false, 0); dr_restore_reg(drcontext, bb, inst, reg2, SPILL_SLOT_2); dr_restore_reg(drcontext, bb, inst, reg1, SPILL_SLOT_1); } return DR_EMIT_DEFAULT; }
/* This event is called separately for each individual instruction in the bb. */ static dr_emit_flags_t event_insert_instrumentation(void *drcontext, void *tag, instrlist_t *bb, instr_t *instr, bool for_trace, bool translating, void *user_data) { per_bb_data_t *per_bb = (per_bb_data_t *)user_data; /* We increment the per-bb counters just once, at the top of the bb. */ if (drmgr_is_first_instr(drcontext, instr)) { /* drx will analyze whether to save the flags for us. */ uint flags = DRX_COUNTER_LOCK; if (per_bb->num_instrs > 0) { drx_insert_counter_update(drcontext, bb, instr, SPILL_SLOT_MAX+1, &stats->num_instrs, per_bb->num_instrs, flags); } if (per_bb->num_flops > 0) { drx_insert_counter_update(drcontext, bb, instr, SPILL_SLOT_MAX+1, &stats->num_flops, per_bb->num_flops, flags); } if (per_bb->num_syscalls > 0) { drx_insert_counter_update(drcontext, bb, instr, SPILL_SLOT_MAX+1, &stats->num_syscalls, per_bb->num_syscalls, flags); } } if (drmgr_is_last_instr(drcontext, instr)) dr_thread_free(drcontext, per_bb, sizeof(*per_bb)); return DR_EMIT_DEFAULT; }
/* This event is called separately for each individual instruction in the bb. */ static dr_emit_flags_t event_insert_instrumentation(void *drcontext, void *tag, instrlist_t *bb, instr_t *instr, bool for_trace, bool translating, void *user_data) { bool bb_in_app; if (dr_fragment_app_pc(tag) >= app_base && dr_fragment_app_pc(tag) < app_end) bb_in_app = true; else bb_in_app = false; if (drmgr_is_first_instr(drcontext, instr)) { uint num_instrs = (uint)(ptr_uint_t)user_data; dr_insert_clean_call(drcontext, bb, instr, (void *)(bb_in_app ? app_update : lib_update), false /* save fpstate */, 1, OPND_CREATE_INT32(num_instrs)); } if (instr_is_mbr(instr) && !instr_is_return(instr)) { /* Assuming most of the transfers between app and lib are paired, we * instrument indirect branches but not returns for better performance. */ dr_insert_mbr_instrumentation( drcontext, bb, instr, (void *)(bb_in_app ? app_mbr : lib_mbr), SPILL_SLOT_1); } 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 *inst, bool for_trace, bool translating, void *user_data) { if (!drmgr_is_first_instr(drcontext, inst)) return DR_EMIT_DEFAULT; /* Exercise drreg's adjacent increment aflags spill removal code */ drx_insert_counter_update(drcontext, bb, inst, SPILL_SLOT_MAX + 1, IF_NOT_X86_(SPILL_SLOT_MAX + 1) & counterA, 1, 0); drx_insert_counter_update(drcontext, bb, inst, SPILL_SLOT_MAX + 1, IF_NOT_X86_(SPILL_SLOT_MAX + 1) & counterB, 3, 0); 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) { #ifdef SHOW_RESULTS bool aflags_dead; #endif if (!drmgr_is_first_instr(drcontext, inst)) return DR_EMIT_DEFAULT; #ifdef VERBOSE dr_printf("in dynamorio_basic_block(tag="PFX")\n", tag); # ifdef VERBOSE_VERBOSE instrlist_disassemble(drcontext, tag, bb, STDOUT); # endif #endif #ifdef SHOW_RESULTS if (drreg_are_aflags_dead(drcontext, inst, &aflags_dead) == DRREG_SUCCESS && !aflags_dead) bbs_eflags_saved++; else bbs_no_eflags_saved++; #endif /* We demonstrate how to use drreg for aflags save/restore here. * We could use drx_insert_counter_update instead of drreg. * Xref sample opcodes.c as an example of using drx_insert_counter_update. */ if (drreg_reserve_aflags(drcontext, bb, inst) != DRREG_SUCCESS) DR_ASSERT(false && "fail to reserve aflags!"); /* racy update on the counter for better performance */ instrlist_meta_preinsert (bb, inst, INSTR_CREATE_inc(drcontext, OPND_CREATE_ABSMEM ((byte *)&global_count, OPSZ_4))); if (drreg_unreserve_aflags(drcontext, bb, inst) != DRREG_SUCCESS) DR_ASSERT(false && "fail to unreserve aflags!"); #if defined(VERBOSE) && defined(VERBOSE_VERBOSE) dr_printf("Finished instrumenting dynamorio_basic_block(tag="PFX")\n", tag); instrlist_disassemble(drcontext, tag, bb, STDOUT); #endif return DR_EMIT_DEFAULT; }
/* This event is called separately for each individual instruction in the bb. */ static dr_emit_flags_t event_insert_instrumentation(void *drcontext, void *tag, instrlist_t *bb, instr_t *instr, bool for_trace, bool translating, void *user_data) { if (drmgr_is_first_instr(drcontext, instr)) { uint num_instrs = (uint)(ptr_uint_t)user_data; int i; app_pc bb_addr = dr_fragment_app_pc(tag); for (i = 0; i < num_mods; i++) { if (mod_array[i].loaded && mod_array[i].base <= bb_addr && mod_array[i].end > bb_addr) break; } if (i == num_mods) i = UNKNOW_MODULE_IDX; /* We pass SPILL_SLOT_MAX+1 as drx will use drreg for spilling. */ drx_insert_counter_update(drcontext, bb, instr, SPILL_SLOT_MAX+1, (void *)&mod_cnt[i], num_instrs, DRX_COUNTER_64BIT); drx_insert_counter_update(drcontext, bb, instr, SPILL_SLOT_MAX+1, (void *)&ins_count, num_instrs, DRX_COUNTER_64BIT); } if (instr_is_mbr(instr) && !instr_is_return(instr)) { /* Assuming most of the transfers between modules are paired, we * instrument indirect branches but not returns for better performance. * We assume that most cross module transfers happens via indirect * branches. * Direct branch with DGC or self-modify may also cross modules, but * it should be ok to ignore, and we can handle them more efficiently. */ /* dr_insert_mbr_instrumentation is going to read app values, so we need a * drreg lazy restore "barrier" here. */ drreg_status_t res = drreg_restore_app_values(drcontext, bb, instr, instr_get_target(instr), NULL); DR_ASSERT(res == DRREG_SUCCESS || res == DRREG_ERROR_NO_APP_VALUE); dr_insert_mbr_instrumentation(drcontext, bb, instr, (void *)mbr_update, SPILL_SLOT_1); } 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) { uint num_instrs; /* By default drmgr enables auto-predication, which predicates all instructions with * the predicate of the current instruction on ARM. * We disable it here because we want to unconditionally execute the following * instrumentation. */ drmgr_disable_auto_predication(drcontext, bb); if (!drmgr_is_first_instr(drcontext, instr)) return DR_EMIT_DEFAULT; /* Only insert calls for in-app BBs */ if (user_data == NULL) return DR_EMIT_DEFAULT; /* Insert clean call */ num_instrs = (uint)(ptr_uint_t)user_data; dr_insert_clean_call(drcontext, bb, instrlist_first_app(bb), (void *)inscount, false /* save fpstate */, 1, OPND_CREATE_INT32(num_instrs)); return DR_EMIT_DEFAULT; }
/* * 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; }