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; }
/* Insert arithmetic flags restore code with more control. * For x86: * - skip %eax restore if !restore_reg * - restore %eax from reg if reg is not DR_REG_NULL * - restore %eax from slot otherwise * For ARM: * - restores flags from reg * - restores reg to slot, unless !restore_reg. * Routine merge_prev_drx_aflags_switch looks for labels inserted by * drx_restore_arith_flags, so changes to this routine may affect * merge_prev_drx_aflags_switch. */ static void drx_restore_arith_flags(void *drcontext, instrlist_t *ilist, instr_t *where, bool restore_reg, bool restore_oflag, dr_spill_slot_t slot, reg_id_t reg) { instr_t *instr; ilist_insert_note_label(drcontext, ilist, where, NOTE_VAL(DRX_NOTE_AFLAGS_RESTORE_BEGIN)); #ifdef X86 if (restore_oflag) { /* add 0x7f, %al */ instr = INSTR_CREATE_add(drcontext, opnd_create_reg(DR_REG_AL), OPND_CREATE_INT8(0x7f)); MINSERT(ilist, where, instr); } /* sahf */ instr = INSTR_CREATE_sahf(drcontext); instr_set_note(instr, NOTE_VAL(DRX_NOTE_AFLAGS_RESTORE_SAHF)); MINSERT(ilist, where, instr); /* restore eax if necessary */ if (restore_reg) { if (reg != DR_REG_NULL) { ASSERT(reg >= DR_REG_START_GPR && reg <= DR_REG_STOP_GPR && reg != DR_REG_XAX, "wrong dead reg"); MINSERT(ilist, where, INSTR_CREATE_mov_st(drcontext, opnd_create_reg(DR_REG_XAX), opnd_create_reg(reg))); } else { ASSERT(slot >= SPILL_SLOT_1 && slot <= SPILL_SLOT_MAX, "wrong spill slot"); dr_restore_reg(drcontext, ilist, where, DR_REG_XAX, slot); } } #elif defined(ARM) ASSERT(reg >= DR_REG_START_GPR && reg <= DR_REG_STOP_GPR, "reg must be a GPR"); instr = INSTR_CREATE_mrs(drcontext, opnd_create_reg(reg), opnd_create_reg(DR_REG_CPSR)); instr_set_note(instr, NOTE_VAL(DRX_NOTE_AFLAGS_RESTORE_SAHF)); MINSERT(ilist, where, instr); if (restore_reg) { ASSERT(slot >= SPILL_SLOT_1 && slot <= SPILL_SLOT_MAX, "wrong spill slot"); dr_restore_reg(drcontext, ilist, where, reg, slot); } #endif ilist_insert_note_label(drcontext, ilist, where, NOTE_VAL(DRX_NOTE_AFLAGS_RESTORE_END)); }
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; }
static void insert_counter_update(void *drcontext, instrlist_t *bb, instr_t *where, int offset) { /* Since the inc instruction clobbers 5 of the arithmetic eflags, * we have to save them around the inc. We could be more efficient * by not bothering to save the overflow flag and constructing our * own sequence of instructions to save the other 5 flags (using * lahf) or by doing a liveness analysis on the flags and saving * only if live. */ dr_save_reg(drcontext, bb, where, DR_REG_XAX, SPILL_SLOT_1); dr_save_arith_flags_to_xax(drcontext, bb, where); /* Increment the global counter using the lock prefix to make it atomic * across threads. It would be cheaper to aggregate the thread counters * in the exit events, but this sample is intended to illustrate inserted * instrumentation. */ instrlist_meta_preinsert(bb, where, LOCK(INSTR_CREATE_inc (drcontext, OPND_CREATE_ABSMEM(((byte *)&global_count) + offset, OPSZ_4)))); /* Increment the thread private counter. */ if (dr_using_all_private_caches()) { per_thread_t *data = (per_thread_t *) dr_get_tls_field(drcontext); /* private caches - we can use an absolute address */ instrlist_meta_preinsert(bb, where, INSTR_CREATE_inc(drcontext, OPND_CREATE_ABSMEM(((byte *)&data) + offset, OPSZ_4))); } else { /* shared caches - we must indirect via thread local storage */ /* We spill xbx to use a scratch register (we could do a liveness * analysis to try and find a dead register to use). Note that xax * is currently holding the saved eflags. */ dr_save_reg(drcontext, bb, where, DR_REG_XBX, SPILL_SLOT_2); dr_insert_read_tls_field(drcontext, bb, where, DR_REG_XBX); instrlist_meta_preinsert(bb, where, INSTR_CREATE_inc(drcontext, OPND_CREATE_MEM32(DR_REG_XBX, offset))); dr_restore_reg(drcontext, bb, where, DR_REG_XBX, SPILL_SLOT_2); } /* Restore flags and xax. */ dr_restore_arith_flags_from_xax(drcontext, bb, where); dr_restore_reg(drcontext, bb, where, DR_REG_XAX, SPILL_SLOT_1); }
static dr_emit_flags_t event_bb(void *dc, void *tag, instrlist_t *bb, bool for_trace, bool translating) { instr_t *where = instrlist_first(bb); instr_t *ret_label = INSTR_CREATE_label(dc); dr_save_reg(dc, bb, where, DR_REG_XAX, SPILL_SLOT_1); PRE(bb, where, INSTR_CREATE_mov_imm(dc, opnd_create_reg(DR_REG_XAX), opnd_create_instr(ret_label))); PRE(bb, where, INSTR_CREATE_jmp(dc, opnd_create_pc(slowpath))); PRE(bb, where, ret_label); dr_restore_reg(dc, bb, where, DR_REG_XAX, SPILL_SLOT_1); return DR_EMIT_DEFAULT; }
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) { /* hack to instrument every nth bb. assumes DR serializes bb events. */ static int freq; freq++; if (freq % 100 == 0 && inst == (instr_t*)user_data/*first instr*/) { /* test read from cache */ dr_save_reg(drcontext, bb, inst, DR_REG_XAX, SPILL_SLOT_1); drmgr_insert_read_tls_field(drcontext, tls_idx, bb, inst, DR_REG_XAX); dr_insert_clean_call(drcontext, bb, inst, (void *)check_tls_from_cache, false, 1, opnd_create_reg(DR_REG_XAX)); drmgr_insert_read_cls_field(drcontext, cls_idx, bb, inst, DR_REG_XAX); dr_insert_clean_call(drcontext, bb, inst, (void *)check_cls_from_cache, false, 1, opnd_create_reg(DR_REG_XAX)); dr_restore_reg(drcontext, bb, inst, DR_REG_XAX, SPILL_SLOT_1); } if (freq % 300 == 0 && inst == (instr_t*)user_data/*first instr*/) { /* test write from cache */ dr_save_reg(drcontext, bb, inst, DR_REG_XAX, SPILL_SLOT_1); dr_save_reg(drcontext, bb, inst, DR_REG_XCX, SPILL_SLOT_2); instrlist_meta_preinsert(bb, inst, INSTR_CREATE_mov_imm (drcontext, opnd_create_reg(DR_REG_EAX), OPND_CREATE_INT32(MAGIC_NUMBER_FROM_CACHE))); drmgr_insert_write_tls_field(drcontext, tls_idx, bb, inst, DR_REG_XAX, DR_REG_XCX); 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, DR_REG_XAX, DR_REG_XCX); dr_insert_clean_call(drcontext, bb, inst, (void *)check_cls_write_from_cache, false, 0); dr_restore_reg(drcontext, bb, inst, DR_REG_XCX, SPILL_SLOT_2); dr_restore_reg(drcontext, bb, inst, DR_REG_XAX, SPILL_SLOT_1); } 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, *first = instrlist_first(bb), *point = NULL; uint num_instrs; uint flags; uint need_restore; /* count instruction */ for (instr = first, num_instrs = 0; instr != NULL; instr = instr_get_next(instr)) { num_instrs++; } need_restore = 0; flags = instr_get_arith_flags(instr); /* eflags are not dead save eflags to register */ if (!(TESTALL(EFLAGS_WRITE_6, flags) && !TESTANY(EFLAGS_READ_6, flags))) { need_restore = 1; dr_save_reg(drcontext, bb, first, DR_REG_XAX, SPILL_SLOT_1); dr_save_arith_flags_to_xax(drcontext, bb, first); } /* add the instruction count */ instrlist_meta_preinsert (bb, first, INSTR_CREATE_add(drcontext, OPND_CREATE_ABSMEM ((byte *)&global_count, OPSZ_4), OPND_CREATE_INT32(num_instrs))); /* Need to carry since it is a 8 byte variable. */ instrlist_meta_preinsert (bb, first, INSTR_CREATE_adc(drcontext, OPND_CREATE_ABSMEM ((byte *)&global_count + 4, OPSZ_4), OPND_CREATE_INT32(0))); /* resotre eflags */ if (need_restore) { dr_restore_arith_flags_from_xax(drcontext, bb, first); dr_restore_reg(drcontext, bb, first, DR_REG_XAX, SPILL_SLOT_1); } return DR_EMIT_DEFAULT; }
static void dynamic_info_instrumentation(void *drcontext, instrlist_t *ilist, instr_t *where, instr_t * static_info) { /* issues that may arise 1. pc and eflags is uint but in 64 bit mode 8 byte transfers are done -> so far no problem (need to see this) need to see whether there is a better way 2. double check all the printing */ /* this function does the acutal instrumentation arguments - we get a filled pointer here about the operand types for a given instruction (srcs and dests) 1) increment the pointer to the instr_trace buffers 2) add this pointer to instr_trace_t wrapper 3) check whether any of the srcs and dests have memory operations; if so add a lea instruction and get the dynamic address Add this address to instr_trace_t structure 4) if the buffer is full call a function to dump it to the file and restore the head ptr of the buffer (lean function is used utilizing a code cache to limit code bloat needed for a clean call before every instruction.) */ instr_t *instr, *call, *restore, *first, *second; opnd_t ref, opnd1, opnd2; reg_id_t reg1 = DR_REG_XBX; /* We can optimize it by picking dead reg */ reg_id_t reg2 = DR_REG_XCX; /* reg2 must be ECX or RCX for jecxz */ reg_id_t reg3 = DR_REG_XAX; per_thread_t *data; uint pc; uint i; module_data_t * module_data; if (client_arg->instrace_mode == DISASSEMBLY_TRACE){ dr_insert_clean_call(drcontext, ilist, where, clean_call_disassembly_trace, false, 0); return; } data = drmgr_get_tls_field(drcontext, tls_index); /* Steal the register for memory reference address * * We can optimize away the unnecessary register save and restore * by analyzing the code and finding the register is dead. */ dr_save_reg(drcontext, ilist, where, reg1, SPILL_SLOT_2); dr_save_reg(drcontext, ilist, where, reg2, SPILL_SLOT_3); dr_save_reg(drcontext, ilist, where, reg3, SPILL_SLOT_4); drmgr_insert_read_tls_field(drcontext, tls_index, ilist, where, reg2); /* Load data->buf_ptr into reg2 */ opnd1 = opnd_create_reg(reg2); opnd2 = OPND_CREATE_MEMPTR(reg2, offsetof(per_thread_t, buf_ptr)); instr = INSTR_CREATE_mov_ld(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); /* buf_ptr->static_info_instr = static_info; */ /* Move static_info to static_info_instr field of buf (which is a instr_trace_t *) */ opnd1 = OPND_CREATE_MEMPTR(reg2, offsetof(instr_trace_t, static_info_instr)); instrlist_insert_mov_immed_ptrsz(drcontext, (ptr_int_t)static_info, opnd1, ilist, where, &first, &second); /* buf_ptr->num_mem = 0; */ opnd1 = OPND_CREATE_MEMPTR(reg2, offsetof(instr_trace_t, num_mem)); instrlist_insert_mov_immed_ptrsz(drcontext, (ptr_int_t)0, opnd1, ilist, where, &first, &second); for (i = 0; i<instr_num_dsts(where); i++){ if (opnd_is_memory_reference(instr_get_dst(where, i))){ ref = instr_get_dst(where, i); DR_ASSERT(opnd_is_null(ref) == false); dr_restore_reg(drcontext, ilist, where, reg1, SPILL_SLOT_2); dr_restore_reg(drcontext, ilist, where, reg2, SPILL_SLOT_3); #ifdef DEBUG_MEM_REGS dr_insert_clean_call(drcontext, ilist, where, clean_call_disassembly_trace, false, 0); dr_insert_clean_call(drcontext, ilist, where, clean_call_print_regvalues, false, 0); #endif drutil_insert_get_mem_addr(drcontext, ilist, where, ref, reg1, reg2); #ifdef DEBUG_MEM_REGS dr_insert_clean_call(drcontext, ilist, where, clean_call_print_regvalues, false, 0); #endif #ifdef DEBUG_MEM_STATS dr_insert_clean_call(drcontext, ilist, where, clean_call_disassembly_trace, false, 0); dr_insert_clean_call(drcontext, ilist, where, clean_call_mem_stats, false, 1, opnd_create_reg(reg1)); #endif dr_insert_clean_call(drcontext, ilist, where, clean_call_populate_mem, false, 3, opnd_create_reg(reg1), OPND_CREATE_INT32(i), OPND_CREATE_INT32(DST_TYPE)); } } for (i = 0; i<instr_num_srcs(where); i++){ if (opnd_is_memory_reference(instr_get_src(where, i))){ ref = instr_get_src(where, i); DR_ASSERT(opnd_is_null(ref) == false); dr_restore_reg(drcontext, ilist, where, reg1, SPILL_SLOT_2); dr_restore_reg(drcontext, ilist, where, reg2, SPILL_SLOT_3); #ifdef DEBUG_MEM_REGS dr_insert_clean_call(drcontext, ilist, where, clean_call_disassembly_trace, false, 0); dr_insert_clean_call(drcontext, ilist, where, clean_call_print_regvalues, false, 0); #endif drutil_insert_get_mem_addr(drcontext, ilist, where, ref, reg1, reg2); #ifdef DEBUG_MEM_REGS dr_insert_clean_call(drcontext, ilist, where, clean_call_print_regvalues, false, 0); #endif #ifdef DEBUG_MEM_STATS dr_insert_clean_call(drcontext, ilist, where, clean_call_disassembly_trace, false, 0); dr_insert_clean_call(drcontext, ilist, where, clean_call_mem_stats, false, 1, opnd_create_reg(reg1)); #endif dr_insert_clean_call(drcontext, ilist, where, clean_call_populate_mem, false, 3, opnd_create_reg(reg1), OPND_CREATE_INT32(i), OPND_CREATE_INT32(SRC_TYPE)); } } drmgr_insert_read_tls_field(drcontext, tls_index, ilist, where, reg2); /* Load data->buf_ptr into reg2 */ opnd1 = opnd_create_reg(reg2); opnd2 = OPND_CREATE_MEMPTR(reg2, offsetof(per_thread_t, buf_ptr)); instr = INSTR_CREATE_mov_ld(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); /* arithmetic flags are saved here for buf_ptr->eflags filling */ dr_save_arith_flags_to_xax(drcontext, ilist, where); /* load the eflags */ opnd1 = OPND_CREATE_MEMPTR(reg2, offsetof(instr_trace_t, eflags)); opnd2 = opnd_create_reg(reg3); instr = INSTR_CREATE_mov_st(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); /* load the app_pc */ opnd1 = OPND_CREATE_MEMPTR(reg2, offsetof(instr_trace_t, pc)); module_data = dr_lookup_module(instr_get_app_pc(where)); //dynamically generated code - module information not available - then just store 0 at the pc slot of the instr_trace data if (module_data != NULL){ pc = instr_get_app_pc(where) - module_data->start; dr_free_module_data(module_data); } else{ pc = 0; } instrlist_insert_mov_immed_ptrsz(drcontext, (ptr_int_t)pc, opnd1, ilist, where, &first, &second); /* buf_ptr++; */ /* Increment reg value by pointer size using lea instr */ opnd1 = opnd_create_reg(reg2); opnd2 = opnd_create_base_disp(reg2, DR_REG_NULL, 0, sizeof(instr_trace_t), OPSZ_lea); instr = INSTR_CREATE_lea(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); /* Update the data->buf_ptr */ drmgr_insert_read_tls_field(drcontext, tls_index, ilist, where, reg1); opnd1 = OPND_CREATE_MEMPTR(reg1, offsetof(per_thread_t, buf_ptr)); opnd2 = opnd_create_reg(reg2); instr = INSTR_CREATE_mov_st(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); /* we use lea + jecxz trick for better performance * lea and jecxz won't disturb the eflags, so we won't insert * code to save and restore application's eflags. */ /* lea [reg2 - buf_end] => reg2 */ opnd1 = opnd_create_reg(reg1); opnd2 = OPND_CREATE_MEMPTR(reg1, offsetof(per_thread_t, buf_end)); instr = INSTR_CREATE_mov_ld(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); opnd1 = opnd_create_reg(reg2); opnd2 = opnd_create_base_disp(reg1, reg2, 1, 0, OPSZ_lea); instr = INSTR_CREATE_lea(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); /* jecxz call */ call = INSTR_CREATE_label(drcontext); opnd1 = opnd_create_instr(call); instr = INSTR_CREATE_jecxz(drcontext, opnd1); instrlist_meta_preinsert(ilist, where, instr); /* jump restore to skip clean call */ restore = INSTR_CREATE_label(drcontext); opnd1 = opnd_create_instr(restore); instr = INSTR_CREATE_jmp(drcontext, opnd1); instrlist_meta_preinsert(ilist, where, instr); /* clean call */ /* We jump to lean procedure which performs full context switch and * clean call invocation. This is to reduce the code cache size. */ instrlist_meta_preinsert(ilist, where, call); /* mov restore DR_REG_XCX */ opnd1 = opnd_create_reg(reg2); /* this is the return address for jumping back from lean procedure */ opnd2 = opnd_create_instr(restore); /* We could use instrlist_insert_mov_instr_addr(), but with a register * destination we know we can use a 64-bit immediate. */ instr = INSTR_CREATE_mov_imm(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); /* jmp code_cache */ opnd1 = opnd_create_pc(code_cache); instr = INSTR_CREATE_jmp(drcontext, opnd1); instrlist_meta_preinsert(ilist, where, instr); /* restore %reg */ instrlist_meta_preinsert(ilist, where, restore); //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); dr_restore_reg(drcontext, ilist, where, reg3, SPILL_SLOT_4); //instrlist_disassemble(drcontext, instr_get_app_pc(instrlist_first(ilist)), ilist, logfile); }
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); }
static dr_emit_flags_t event_basic_block(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) { instr_t *first = instrlist_first(bb); app_pc pc = dr_fragment_app_pc(tag); instr_t *mov1, *mov2; /* We try to avoid register stealing by using "dead" register if possible. * However, technically, a fault could come in and want the original value * of the "dead" register, but that's too corner-case for us. */ reg_id_t reg = bb_find_dead_reg(bb); bool steal = (reg == DR_REG_NULL); if (reg == DR_REG_NULL) reg = DR_REG_XCX; /* randomly use one if no dead reg found */ /* save register if necessary */ if (steal) dr_save_reg(drcontext, bb, first, reg, SPILL_SLOT_1); /* load buffer pointer from TLS field */ MINSERT(bb, first, INSTR_CREATE_mov_ld (drcontext, opnd_create_reg(reg), opnd_create_far_base_disp(tls_seg, DR_REG_NULL, DR_REG_NULL, 0, tls_offs, OPSZ_PTR))); /* store bb's start pc into the buffer */ instrlist_insert_mov_immed_ptrsz(drcontext, (ptr_int_t)pc, OPND_CREATE_MEMPTR(reg, 0), bb, first, &mov1, &mov2); DR_ASSERT(mov1 != NULL); instr_set_ok_to_mangle(mov1, false); if (mov2 != NULL) instr_set_ok_to_mangle(mov2, false); /* update the TLS buffer pointer by incrementing just the bottom 16 bits of * the pointer */ if (bb_aflags_are_dead(bb, first)) { /* if aflags are dead, we use add directly */ MINSERT(bb, first, INSTR_CREATE_add (drcontext, opnd_create_far_base_disp(tls_seg, DR_REG_NULL, DR_REG_NULL, 0, tls_offs, OPSZ_2), OPND_CREATE_INT8(sizeof(app_pc)))); } else { reg_id_t reg_16; #ifdef X64 reg_16 = reg_32_to_16(reg_64_to_32(reg)); #else reg_16 = reg_32_to_16(reg); #endif /* we use lea to avoid aflags save/restore */ MINSERT(bb, first, INSTR_CREATE_lea (drcontext, opnd_create_reg(reg_16), opnd_create_base_disp(reg, DR_REG_NULL, 0, sizeof(app_pc), OPSZ_lea))); MINSERT(bb, first, INSTR_CREATE_mov_st (drcontext, opnd_create_far_base_disp(tls_seg, DR_REG_NULL, DR_REG_NULL, 0, tls_offs, OPSZ_PTR), opnd_create_reg(reg))); } /* restore register if necessary */ if (steal) dr_restore_reg(drcontext, bb, first, reg, SPILL_SLOT_1); return DR_EMIT_DEFAULT; }
static dr_emit_flags_t event_basic_block(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) { int i; const int MAX_INSTR_LEN = 64; char instr_name[MAX_INSTR_LEN]; instr_t *instr, *first = instrlist_first(bb); uint flags; uint cur_flop_count = 0; uint tracked_instr_count[tracked_instrs_len]; for( i = 0; i < tracked_instrs_len; i++ ) tracked_instr_count[i] = 0; #ifdef VERBOSE dr_printf("in dynamorio_basic_block(tag="PFX")\n", tag); # ifdef VERBOSE_VERBOSE instrlist_disassemble(drcontext, tag, bb, STDOUT); # endif #endif /* we use fp ops so we have to save fp state */ byte fp_raw[512 + 16]; byte *fp_align = (byte *) ( (((ptr_uint_t)fp_raw) + 16) & ((ptr_uint_t)-16) ); if (translating) { return DR_EMIT_DEFAULT; } proc_save_fpstate(fp_align); int my_readfrom[DR_REG_LAST_VALID_ENUM+MY_NUM_EFLAGS+1]; int my_writtento[DR_REG_LAST_VALID_ENUM+MY_NUM_EFLAGS+1]; for (i = 0; i < DR_REG_LAST_VALID_ENUM+MY_NUM_EFLAGS+1; i++) { my_readfrom[i] = 0; my_writtento[i] = 0; } t_glob_reg_state glob_reg_state = {0,0,0,0,0,0,my_readfrom,my_writtento}; int my_cur_size = 0; for (instr = instrlist_first(bb); instr != NULL; instr = instr_get_next(instr)) { my_cur_size++; /* ILP Calculations */ glob_reg_state.raw_setnr = 1; glob_reg_state.war_setnr = 1; glob_reg_state.waw_setnr = 1; glob_reg_state.else_setnr = 1; glob_reg_state.final_setnr = 1; calc_set_num(instr, &glob_reg_state); /* Count flop instr */ if( instr_is_floating( instr ) ) { cur_flop_count += 1; } /* Count mul instructions */ instr_disassemble_to_buffer( drcontext, instr, instr_name, MAX_INSTR_LEN ); for( i = 0; i < tracked_instrs_len; i++ ) { if( strncmp( instr_name, tracked_instrs[i], strlen(tracked_instrs[i])) == 0) { tracked_instr_count[i] += 1; } } } //now we can calculate the ILP. float ilp = ((float)my_cur_size) / ((float)(glob_reg_state.num_sets != 0 ? glob_reg_state.num_sets : 1)); dr_mutex_lock(stats_mutex); // Due to lack of memory, we only store the ILPs for the latest MY_MAX_BB // basic blocks. This enables us to run e.g. firefox. int my_cur_num = my_bbcount % MY_MAX_BB; my_bbcount++; if(my_cur_num == 0 && my_bbcount > 1) { dr_printf("Overflow at %d\n", my_bbcount); } my_bbexecs[my_cur_num] = 0; //initialize my_bbsizes[my_cur_num] = my_cur_size; bb_flop_count[my_cur_num] = cur_flop_count; for( i = 0; i < tracked_instrs_len; i++ ) { bb_instr_count[my_cur_num*tracked_instrs_len+i] = tracked_instr_count[i]; } my_bbilp[my_cur_num] = ilp; dr_mutex_unlock(stats_mutex); #ifdef USE_CLEAN_CALL dr_insert_clean_call(drcontext, bb, instrlist_first(bb), clean_call, false, 1, OPND_CREATE_INT32(my_cur_num)); #else #ifdef INSERT_AT_END instr = NULL; #else // Find place to insert inc instruction for (instr = first; instr != NULL; instr = instr_get_next(instr)) { flags = instr_get_arith_flags(instr); if (TESTALL(EFLAGS_WRITE_6, flags) && !TESTANY(EFLAGS_READ_6, flags)) break; } #endif if (instr == NULL) { // no suitable place found, save regs dr_save_reg(drcontext, bb, first, DR_REG_XAX, SPILL_SLOT_1); dr_save_arith_flags_to_xax(drcontext, bb, first); } // Increment my_bbexecs[my_current_bb] using the lock prefix instrlist_meta_preinsert (bb, (instr == NULL) ? first : instr, LOCK(INSTR_CREATE_inc(drcontext, OPND_CREATE_ABSMEM ((byte *)&(my_bbexecs[my_cur_num]), OPSZ_4)))); if (instr == NULL) { // no suitable place found earlier, restore regs dr_restore_arith_flags_from_xax(drcontext, bb, first); dr_restore_reg(drcontext, bb, first, DR_REG_XAX, SPILL_SLOT_1); } #endif proc_restore_fpstate(fp_align); #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; }
/* * instrument_mem is called whenever a memory reference is identified. * It inserts code before the memory reference to to fill the memory buffer * and jump to our own code cache to call the clean_call when the buffer is full. */ static void instrument_mem(void *drcontext, instrlist_t *ilist, instr_t *where, int pos, bool write) { instr_t *instr, *call, *restore, *first, *second; opnd_t ref, opnd1, opnd2; reg_id_t reg1 = DR_REG_XBX; /* We can optimize it by picking dead reg */ reg_id_t reg2 = DR_REG_XCX; /* reg2 must be ECX or RCX for jecxz */ per_thread_t *data; app_pc pc; data = drmgr_get_tls_field(drcontext, tls_index); /* Steal the register for memory reference address * * We can optimize away the unnecessary register save and restore * by analyzing the code and finding the register is dead. */ dr_save_reg(drcontext, ilist, where, reg1, SPILL_SLOT_2); dr_save_reg(drcontext, ilist, where, reg2, SPILL_SLOT_3); if (write) ref = instr_get_dst(where, pos); else ref = instr_get_src(where, pos); /* use drutil to get mem address */ drutil_insert_get_mem_addr(drcontext, ilist, where, ref, reg1, reg2); /* The following assembly performs the following instructions * buf_ptr->write = write; * buf_ptr->addr = addr; * buf_ptr->size = size; * buf_ptr->pc = pc; * buf_ptr++; * if (buf_ptr >= buf_end_ptr) * clean_call(); */ drmgr_insert_read_tls_field(drcontext, tls_index, ilist, where, reg2); /* Load data->buf_ptr into reg2 */ opnd1 = opnd_create_reg(reg2); opnd2 = OPND_CREATE_MEMPTR(reg2, offsetof(per_thread_t, buf_ptr)); instr = INSTR_CREATE_mov_ld(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); /* Move write/read to write field */ opnd1 = OPND_CREATE_MEM32(reg2, offsetof(mem_ref_t, write)); opnd2 = OPND_CREATE_INT32(write); instr = INSTR_CREATE_mov_imm(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); /* Store address in memory ref */ opnd1 = OPND_CREATE_MEMPTR(reg2, offsetof(mem_ref_t, addr)); opnd2 = opnd_create_reg(reg1); instr = INSTR_CREATE_mov_st(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); /* Store size in memory ref */ opnd1 = OPND_CREATE_MEMPTR(reg2, offsetof(mem_ref_t, size)); /* drutil_opnd_mem_size_in_bytes handles OP_enter */ opnd2 = OPND_CREATE_INT32(drutil_opnd_mem_size_in_bytes(ref, where)); instr = INSTR_CREATE_mov_st(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); /* Store pc in memory ref */ pc = instr_get_app_pc(where); /* For 64-bit, we can't use a 64-bit immediate so we split pc into two halves. * We could alternatively load it into reg1 and then store reg1. * We use a convenience routine that does the two-step store for us. */ opnd1 = OPND_CREATE_MEMPTR(reg2, offsetof(mem_ref_t, pc)); instrlist_insert_mov_immed_ptrsz(drcontext, (ptr_int_t) pc, opnd1, ilist, where, &first, &second); instr_set_ok_to_mangle(first, false/*meta*/); if (second != NULL) instr_set_ok_to_mangle(second, false/*meta*/); /* Increment reg value by pointer size using lea instr */ opnd1 = opnd_create_reg(reg2); opnd2 = opnd_create_base_disp(reg2, DR_REG_NULL, 0, sizeof(mem_ref_t), OPSZ_lea); instr = INSTR_CREATE_lea(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); /* Update the data->buf_ptr */ drmgr_insert_read_tls_field(drcontext, tls_index, ilist, where, reg1); opnd1 = OPND_CREATE_MEMPTR(reg1, offsetof(per_thread_t, buf_ptr)); opnd2 = opnd_create_reg(reg2); instr = INSTR_CREATE_mov_st(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); /* we use lea + jecxz trick for better performance * lea and jecxz won't disturb the eflags, so we won't insert * code to save and restore application's eflags. */ /* lea [reg2 - buf_end] => reg2 */ opnd1 = opnd_create_reg(reg1); opnd2 = OPND_CREATE_MEMPTR(reg1, offsetof(per_thread_t, buf_end)); instr = INSTR_CREATE_mov_ld(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); opnd1 = opnd_create_reg(reg2); opnd2 = opnd_create_base_disp(reg1, reg2, 1, 0, OPSZ_lea); instr = INSTR_CREATE_lea(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); /* jecxz call */ call = INSTR_CREATE_label(drcontext); opnd1 = opnd_create_instr(call); instr = INSTR_CREATE_jecxz(drcontext, opnd1); instrlist_meta_preinsert(ilist, where, instr); /* jump restore to skip clean call */ restore = INSTR_CREATE_label(drcontext); opnd1 = opnd_create_instr(restore); instr = INSTR_CREATE_jmp(drcontext, opnd1); instrlist_meta_preinsert(ilist, where, instr); /* clean call */ /* We jump to lean procedure which performs full context switch and * clean call invocation. This is to reduce the code cache size. */ instrlist_meta_preinsert(ilist, where, call); /* mov restore DR_REG_XCX */ opnd1 = opnd_create_reg(reg2); /* this is the return address for jumping back from lean procedure */ opnd2 = opnd_create_instr(restore); /* We could use instrlist_insert_mov_instr_addr(), but with a register * destination we know we can use a 64-bit immediate. */ instr = INSTR_CREATE_mov_imm(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); /* jmp code_cache */ opnd1 = opnd_create_pc(code_cache); instr = INSTR_CREATE_jmp(drcontext, opnd1); instrlist_meta_preinsert(ilist, where, instr); /* restore %reg */ instrlist_meta_preinsert(ilist, where, restore); dr_restore_reg(drcontext, ilist, where, reg1, SPILL_SLOT_2); dr_restore_reg(drcontext, ilist, where, reg2, SPILL_SLOT_3); }
/** Adds instrumentation that records fragment execution. */ struct instr_info_t instrument_frag(void* ctx, instrlist_t* frag, frag_id_t id) { const size_t offsetof_current = offsetof(struct trace_buffer_t, current); ptr_int_t frag_id = id; // sign-extended for OPND_CREATE_INT32 #ifdef TRACE_DEBUG app_pc frag_pc; #endif app_pc xl8_pc; instr_t* where; instr_t* before; struct instr_info_t instr_info; instr_t* store; instr_t* first; #ifdef TRACE_DEBUG dr_fprintf(STDERR, "debug: instrument_frag(0x%" PRIxPTR ")\n", frag_id); frag_pc = instr_get_app_pc(instrlist_first(frag)); instrlist_disassemble(ctx, frag_pc, frag, STDERR); #endif where = configure_instr(&instr_info, frag); xl8_pc = instr_get_app_pc(where); before = instr_get_prev(where); #define INSERT(instr) prexl8(frag, where, (instr), xl8_pc) // Add instrumentation. // save tls_reg if(instr_info.restore_tls_reg) { dr_save_reg(ctx, frag, where, instr_info.tls_reg, SPILL_SLOT_2); } // save current_reg if(instr_info.restore_current_reg) { dr_save_reg(ctx, frag, where, instr_info.current_reg, SPILL_SLOT_3); } // tls_reg = tb dr_insert_read_tls_field(ctx, frag, where, instr_info.tls_reg); // current_reg = tb->current INSERT( INSTR_CREATE_mov_ld( ctx, opnd_create_reg(instr_info.current_reg), OPND_CREATE_MEMPTR(instr_info.tls_reg, offsetof_current))); // *current_reg = bb_id store = INSERT( INSTR_CREATE_mov_st( ctx, OPND_CREATE_MEMPTR(instr_info.current_reg, 0), OPND_CREATE_INT32(frag_id))); // current_reg += sizeof(bb_id) INSERT( INSTR_CREATE_lea( ctx, opnd_create_reg(instr_info.current_reg), OPND_CREATE_MEM_lea(instr_info.current_reg, DR_REG_NULL, 0, sizeof(frag_id_t)))); // tb->current = current_reg INSERT( INSTR_CREATE_mov_st( ctx, OPND_CREATE_MEMPTR(instr_info.tls_reg, offsetof_current), opnd_create_reg(instr_info.current_reg))); // restore current_reg if(instr_info.restore_current_reg) { dr_restore_reg(ctx, frag, where, instr_info.current_reg, SPILL_SLOT_3); } // restore tls_reg if(instr_info.restore_tls_reg) { dr_restore_reg(ctx, frag, where, instr_info.tls_reg, SPILL_SLOT_2); } #undef INSERT // Compute instrumentation instructions offsets. if(before) { first = instr_get_next(before); } else { first = instrlist_first(frag); } instr_info.first_offset = get_offset(ctx, instrlist_first(frag), first); instr_info.store_offset = get_offset(ctx, instrlist_first(frag), store); #ifdef TRACE_DUMP_BB instrlist_disassemble(ctx, frag_pc, frag, STDERR); #endif #ifdef TRACE_DEBUG dr_fprintf(STDERR, "debug: instrument_frag() done," " first_offset=0x%" PRIx32 " store_offset=0x%" PRIx32 "\n", instr_info.first_offset, instr_info.store_offset); #endif return instr_info; }