/* 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_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; }
// We stored the instr count in *bb_field in bb_analysis(). int offline_instru_t::instrument_instr(void *drcontext, void *tag, void **bb_field, instrlist_t *ilist, instr_t *where, reg_id_t reg_ptr, reg_id_t reg_tmp, int adjust, instr_t *app) { app_pc pc, modbase; uint modidx; offline_entry_t entry; // We write just once per bb. if ((ptr_uint_t)*bb_field > MAX_INSTR_COUNT) return adjust; pc = dr_fragment_app_pc(tag); if (drmodtrack_lookup(drcontext, pc, &modidx, &modbase) != DRCOVLIB_SUCCESS) { // FIXME i#2062: add non-module support. The plan for instrs is to have // one entry w/ the start abs pc, and subsequent entries that pack the instr // length for 10 instrs, 4 bits each, into a pc.modoffs field. We will // also need to store the type (read/write/prefetch*) and size for the // memrefs. modidx = 0; modbase = pc; } entry.pc.type = OFFLINE_TYPE_PC; // We put the ARM vs Thumb mode into the modoffs to ensure proper decoding. entry.pc.modoffs = dr_app_pc_as_jump_target(instr_get_isa_mode(where), pc) - modbase; entry.pc.modidx = modidx; entry.pc.instr_count = (ptr_uint_t)*bb_field; insert_save_pc(drcontext, ilist, where, reg_ptr, reg_tmp, adjust, entry.combined_value); *(ptr_uint_t*)bb_field = MAX_INSTR_COUNT + 1; return (adjust + sizeof(offline_entry_t)); }
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 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; }
/* We collect the basic block information including offset from module base, * size, and num of instructions, and add it into a basic block table without * instrumentation. */ static dr_emit_flags_t event_basic_block_analysis(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating, OUT void **user_data) { per_thread_t *data; instr_t *instr; app_pc tag_pc, start_pc, end_pc; /* do nothing for translation */ if (translating) return DR_EMIT_DEFAULT; data = (per_thread_t *)drmgr_get_tls_field(drcontext, tls_idx); /* Collect the number of instructions and the basic block size, * assuming the basic block does not have any elision on control * transfer instructions, which is true for default options passed * to DR but not for -opt_speed. */ /* We separate the tag from the instr pc ranges to handle displaced code * such as for the vsyscall hook. */ tag_pc = dr_fragment_app_pc(tag); start_pc = instr_get_app_pc(instrlist_first_app(bb)); end_pc = start_pc; /* for finding the size */ for (instr = instrlist_first_app(bb); instr != NULL; instr = instr_get_next_app(instr)) { app_pc pc = instr_get_app_pc(instr); int len = instr_length(drcontext, instr); /* -opt_speed (elision) is not supported */ /* For rep str expansion pc may be one back from start pc but equal to the tag. */ ASSERT(pc != NULL && (pc >= start_pc || pc == tag_pc), "-opt_speed is not supported"); if (pc + len > end_pc) end_pc = pc + len; } /* We allow duplicated basic blocks for the following reasons: * 1. Avoids handling issues like code cache consistency, e.g., * module load/unload, self-modifying code, etc. * 2. Avoids the overhead on duplication check. * 3. Stores more information on code cache events, e.g., trace building, * repeated bb building, etc. * 4. The duplication can be easily handled in a post-processing step, * which is required anyway. */ bb_table_entry_add(drcontext, data, tag_pc, (uint)(end_pc - start_pc)); if (go_native) return DR_EMIT_GO_NATIVE; else return DR_EMIT_DEFAULT; }
static dr_emit_flags_t event_basic_block(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) { instr_t *instr; for (instr = instrlist_first_app(bb); instr != NULL; instr = instr_get_next_app(instr)) { if (instr_is_cbr(instr)) { dr_insert_cbr_instrumentation_ex (drcontext, bb, instr, (void *)at_cbr, OPND_CREATE_INTPTR(dr_fragment_app_pc(tag))); } } return DR_EMIT_DEFAULT; }
static dr_emit_flags_t bb_event(void *drcontext, app_pc tag, instrlist_t *bb, bool for_trace, bool translating) { instr_t *instr, *next_instr; app_pc bb_addr, instr_addr; bool found_section; bb_addr = dr_fragment_app_pc(tag); /* vsyscall: some versions of windows jump to 0x7ffe0300 to * execute a syscall; this address is not contained in any module. */ if ((ptr_uint_t)bb_addr == 0x7ffe0300 || (ptr_uint_t)bb_addr == 0x7ffe0302) return DR_EMIT_DEFAULT; if (!is_in_known_module(bb_addr, &found_section, §ion)) { dr_fprintf(STDERR, "ERROR: BB addr "PFX" in unknown module\n", bb_addr); } if (!found_section) { dr_fprintf(STDERR, "ERROR: BB addr "PFX" isn't within a module section\n", bb_addr); } if ((section.Characteristics & IMAGE_SCN_CNT_CODE) == 0) { dr_fprintf(STDERR, "ERROR: BB addr "PFX" isn't within a code section\n", bb_addr); } for (instr = instrlist_first(bb); instr != NULL; instr = next_instr) { next_instr = instr_get_next(instr); instr_addr = instr_get_app_pc(instr); if (!is_in_known_module(instr_addr, &found_section, §ion)) { dr_fprintf(STDERR, "ERROR: instr addr "PFX" in unknown module\n", instr_addr); } if (!found_section) { dr_fprintf(STDERR, "ERROR: instr addr "PFX" isn't within a module section\n", instr_addr); } if ((section.Characteristics & IMAGE_SCN_CNT_CODE) == 0) { dr_fprintf(STDERR, "ERROR: instr addr "PFX" isn't within a code section\n", instr_addr); } if (instr_addr == exit_proc_addr) { dr_fprintf(STDERR, "Hit kernel32!ExitProcess\n"); exit_proc_addr = NULL; } } return DR_EMIT_DEFAULT; }
dr_emit_flags_t bb_event(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) { app_pc bb_addr = dr_fragment_app_pc(tag); dr_emit_flags_t flags = DR_EMIT_DEFAULT; if (bb_addr == start_pc) { instrument = true; } else if (bb_addr == stop_pc) { instrument = false; } if (instrument) { flags = instrument_bb(drcontext, tag, bb, for_trace, translating); } return flags; }
/* 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; }
dr_emit_flags_t bb_event(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) { instr_t *instr, *next_instr; app_pc bb_addr = dr_fragment_app_pc(tag); if (bb_addr == start_pc) { instrument = true; } else if (bb_addr == stop_pc) { instrument = false; } if (!instrument) { return DR_EMIT_DEFAULT; } for (instr = instrlist_first(bb); instr != NULL; instr = next_instr) { next_instr = instr_get_next(instr); /* * Conditional branch. We can determine the target and * fallthrough addresses here, but we need to instrument if we * want to record the edge only if it actually executes at * runtime. Instead of using dr_insert_cbr_instrumentation, * we'll insert separate instrumentation for the taken and not * taken cases and remove it separately after we see each * case. */ if (instr_is_cbr(instr)) { app_pc src = instr_get_app_pc(instr); cbr_state_t state; bool insert_taken, insert_not_taken; /* First look up the state of this branch so we * know what instrumentation to insert, if any. */ elem_t *elem = lookup(table, src); if (elem == NULL) { state = CBR_NONE; insert(table, src, CBR_NONE); } else { state = elem->state; } insert_taken = (state & CBR_TAKEN) == 0; insert_not_taken = (state & CBR_NOT_TAKEN) == 0; if (insert_taken || insert_not_taken) { app_pc fall = (app_pc)decode_next_pc(drcontext, (byte *)src); app_pc targ = instr_get_branch_target_pc(instr); /* * Redirect the cbr to jump to the 'taken' callout. * We'll insert a 'not-taken' callout at fallthrough * address. */ instr_t *label = INSTR_CREATE_label(drcontext); instr_set_meta(instr); instr_set_translation(instr, NULL); /* If this is a short cti, make sure it can reach its new target */ if (instr_is_cti_short(instr)) { /* if jecxz/loop we want to set the target of the long-taken * so set instr to the return value */ instr = instr_convert_short_meta_jmp_to_long(drcontext, bb, instr); } instr_set_target(instr, opnd_create_instr(label)); if (insert_not_taken) { /* * Callout for the not-taken case */ dr_insert_clean_call(drcontext, bb, NULL, (void *)at_not_taken, false /* don't save fp state */, 2 /* 2 args for at_not_taken */, OPND_CREATE_INTPTR((ptr_uint_t)src), OPND_CREATE_INTPTR((ptr_uint_t)fall)); } /* * Jump to the original fall-through address. * (This should not be a meta-instruction). */ instrlist_preinsert( bb, NULL, INSTR_XL8(INSTR_CREATE_jmp(drcontext, opnd_create_pc(fall)), fall)); /* label goes before the 'taken' callout */ MINSERT(bb, NULL, label); if (insert_taken) { /* * Callout for the taken case */ dr_insert_clean_call(drcontext, bb, NULL, (void *)at_taken, false /* don't save fp state */, 2 /* 2 args for at_taken */, OPND_CREATE_INTPTR((ptr_uint_t)src), OPND_CREATE_INTPTR((ptr_uint_t)targ)); } /* * Jump to the original target block (this should * not be a meta-instruction). */ instrlist_preinsert( bb, NULL, INSTR_XL8(INSTR_CREATE_jmp(drcontext, opnd_create_pc(targ)), targ)); } } } /* since our added instrumentation is not constant, we ask to store * translations now */ return DR_EMIT_STORE_TRANSLATIONS; }
static 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_bb_analysis(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating, void **user_data) { 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 /* Only count in app BBs */ if (only_from_app.get_value()) { module_data_t *mod = dr_lookup_module(dr_fragment_app_pc(tag)); if (mod != NULL) { bool from_exe = (mod->start == exe_start); dr_free_module_data(mod); if (!from_exe) { *user_data = NULL; return DR_EMIT_DEFAULT; } } } /* Count instructions. If an emulation client is running with this client, * we want to count all the original native instructions and the emulated * instruction but NOT the introduced native instructions used for emulation. */ bool is_emulation = false; for (instr = instrlist_first(bb), num_instrs = 0; instr != NULL; instr = instr_get_next(instr)) { if (drmgr_is_emulation_start(instr)) { /* Each emulated instruction is replaced by a series of native * instructions delimited by labels indicating when the emulation * sequence begins and ends. It is the responsibility of the * emulation client to place the start/stop labels correctly. */ num_instrs++; is_emulation = true; /* Data about the emulated instruction can be extracted from the * start label using the accessor function: * drmgr_get_emulated_instr_data() */ continue; } if (drmgr_is_emulation_end(instr)) { is_emulation = false; continue; } if (is_emulation) continue; if (!instr_is_app(instr)) continue; num_instrs++; } *user_data = (void *)(ptr_uint_t)num_instrs; #if defined(VERBOSE) && defined(VERBOSE_VERBOSE) dr_printf("Finished counting for dynamorio_basic_block(tag=" PFX ")\n", tag); instrlist_disassemble(drcontext, tag, bb, STDOUT); #endif return DR_EMIT_DEFAULT; }