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; }
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; }
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; }
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; }
/* 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; }
dr_emit_flags_t inscount_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); uint num_instrs = 0; if(instr != first) return DR_EMIT_DEFAULT; if(filter_from_list(head,instr,client_arg->filter_mode)){ for(instr = first ; instr!=NULL ; instr = instr_get_next(instr)){ num_instrs++; } bbcount++; } if(num_instrs > 0){ dr_insert_clean_call(drcontext, bb, instrlist_first(bb), (void *)inscount, false /* save fpstate */, 1, OPND_CREATE_INT32(num_instrs)); } 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; uint num_instrs; #ifdef VERBOSE dr_printf("in dynamorio_basic_block(tag="PFX")\n", tag); # ifdef VERBOSE_VERBOSE instrlist_disassemble(drcontext, tag, bb, STDOUT); # endif #endif for (instr = instrlist_first(bb), num_instrs = 0; instr != NULL; instr = instr_get_next(instr)) { num_instrs++; } dr_insert_clean_call(drcontext, bb, instrlist_first(bb), (void *)inscount, false /* save fpstate */, 1, OPND_CREATE_INT32(num_instrs)); #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; }
/* code cache to hold the call to "clean_call" and return to DR code cache */ static void code_cache_init(void) { void *drcontext; instrlist_t *ilist; instr_t *where; byte *end; drcontext = dr_get_current_drcontext(); code_cache = dr_nonheap_alloc(PAGE_SIZE, DR_MEMPROT_READ | DR_MEMPROT_WRITE | DR_MEMPROT_EXEC); ilist = instrlist_create(drcontext); /* The lean procecure simply performs a clean call, and then jump back */ /* jump back to the DR's code cache */ where = INSTR_CREATE_jmp_ind(drcontext, opnd_create_reg(DR_REG_XCX)); instrlist_meta_append(ilist, where); /* clean call */ dr_insert_clean_call(drcontext, ilist, where, (void *)clean_call_ins_trace, false, 0); /* Encodes the instructions into memory and then cleans up. */ end = instrlist_encode(drcontext, ilist, code_cache, false); DR_ASSERT((end - code_cache) < PAGE_SIZE); instrlist_clear_and_destroy(drcontext, ilist); /* set the memory as just +rx now */ dr_memory_protect(code_cache, PAGE_SIZE, DR_MEMPROT_READ | DR_MEMPROT_EXEC); }
/* * 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) { app_pc pc = dr_fragment_app_pc(tag); if (pc == start_pc) { dr_fprintf(STDERR, "starting syscall monitoring\n"); monitoring = true; } else if (pc == stop_pc) { dr_fprintf(STDERR, "stopping syscall monitoring\n"); monitoring = false; } else { instr_t* instr; instr_t* next_instr; for (instr = instrlist_first(bb); instr != NULL; instr = next_instr) { next_instr = instr_get_next(instr); /* Insert a callback to at_syscall before every system call */ if (instr_is_syscall(instr)) { dr_insert_clean_call(drcontext, bb, instr, at_syscall, false, 0); } } } return DR_EMIT_DEFAULT; }
static dr_emit_flags_t bb_event(void* drcontext, void* tag, instrlist_t* bb, bool for_trace, bool translating) { dr_fprintf(STDERR, "got BB event\n"); dr_insert_clean_call(drcontext, bb, instrlist_first(bb), (void *)unregister, false, 0); 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 *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 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, *mbr = NULL; uint num_instrs; bool bb_in_app; #ifdef VERBOSE dr_printf("in dynamorio_basic_block(tag="PFX")\n", tag); # ifdef VERBOSE_VERBOSE instrlist_disassemble(drcontext, tag, bb, STDOUT); # endif #endif for (instr = instrlist_first(bb), num_instrs = 0; instr != NULL; instr = instr_get_next(instr)) { /* only care about app instr */ if (!instr_ok_to_mangle(instr)) continue; num_instrs++; /* Assuming most of the transfers between app and lib are paired, we * instrument indirect branches but not returns for better performance. */ if (instr_is_mbr(instr) && !instr_is_return(instr)) mbr = instr; } if (dr_fragment_app_pc(tag) >= app_base && dr_fragment_app_pc(tag) < app_end) bb_in_app = true; else bb_in_app = false; dr_insert_clean_call(drcontext, bb, instrlist_first(bb), (void *)(bb_in_app ? app_update : lib_update), false /* save fpstate */, 1, OPND_CREATE_INT32(num_instrs)); if (mbr != NULL) { dr_insert_mbr_instrumentation(drcontext, bb, mbr, (void *)(bb_in_app ? app_mbr : lib_mbr), SPILL_SLOT_1); } #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; }
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 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 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; }
/* For each memory reference app instr, we insert inline code to fill the buffer * with an instruction entry and memory reference entries. */ 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) { int i; if (!instr_is_app(instr)) return DR_EMIT_DEFAULT; if (!instr_reads_memory(instr) && !instr_writes_memory(instr)) return DR_EMIT_DEFAULT; /* insert code to add an entry for app instruction */ instrument_instr(drcontext, bb, instr); /* insert code to add an entry for each memory reference opnd */ for (i = 0; i < instr_num_srcs(instr); i++) { if (opnd_is_memory_reference(instr_get_src(instr, i))) instrument_mem(drcontext, bb, instr, instr_get_src(instr, i), false); } for (i = 0; i < instr_num_dsts(instr); i++) { if (opnd_is_memory_reference(instr_get_dst(instr, i))) instrument_mem(drcontext, bb, instr, instr_get_dst(instr, i), true); } /* insert code to call clean_call for processing the buffer */ if (/* XXX i#1698: there are constraints for code between ldrex/strex pairs, * so we minimize the instrumentation in between by skipping the clean call. * As we're only inserting instrumentation on a memory reference, and the * app should be avoiding memory accesses in between the ldrex...strex, * the only problematic point should be before the strex. * However, there is still a chance that the instrumentation code may clear the * exclusive monitor state. * Using a fault to handle a full buffer should be more robust, and the * forthcoming buffer filling API (i#513) will provide that. */ IF_AARCHXX_ELSE(!instr_is_exclusive_store(instr), true)) dr_insert_clean_call(drcontext, bb, instr, (void *)clean_call, false, 0); 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; }
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; }
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); }
/* * 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 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; }
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; }