DR_EXPORT bool drx_aflags_are_dead(instr_t *where) { instr_t *instr; uint flags; for (instr = where; instr != NULL; instr = instr_get_next(instr)) { /* we treat syscall/interrupt as aflags read */ if (instr_is_syscall(instr) || instr_is_interrupt(instr)) return false; flags = instr_get_arith_flags(instr, DR_QUERY_DEFAULT); if (TESTANY(EFLAGS_READ_ARITH, flags)) return false; if (TESTALL(EFLAGS_WRITE_ARITH, flags)) return true; if (instr_is_cti(instr)) { if (instr_is_app(instr) && (instr_is_ubr(instr) || instr_is_call_direct(instr))) { instr_t *next = instr_get_next(instr); opnd_t tgt = instr_get_target(instr); /* continue on elision */ if (next != NULL && instr_is_app(next) && opnd_is_pc(tgt) && opnd_get_pc(tgt) == instr_get_app_pc(next)) continue; } /* unknown target, assume aflags is live */ return false; } } return false; }
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) { /* We test reserving across app instrs by reserving on each store and * unreserving on the subsequent instr. */ drvector_t allowed; if (reg != DR_REG_NULL) { if (drreg_unreserve_register(drcontext, bb, instr, reg) != DRREG_SUCCESS) CHECK(false, "failed to unreserve"); reg = DR_REG_NULL; } if (!instr_is_app(instr)) return DR_EMIT_DEFAULT; if (!instr_writes_memory(instr)) return DR_EMIT_DEFAULT; if (!drmgr_is_last_instr(drcontext, instr)) { drreg_init_and_fill_vector(&allowed, true); /* Limit the registers for more of a stress test: */ drreg_set_vector_entry(&allowed, IF_X86_ELSE(DR_REG_XCX, DR_REG_R0), false); drreg_set_vector_entry(&allowed, IF_X86_ELSE(DR_REG_XDX, DR_REG_R1), false); if (drreg_reserve_register(drcontext, bb, instr, &allowed, ®) != DRREG_SUCCESS) DR_ASSERT(false); drvector_delete(&allowed); } 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; reg_id_t *reg_next = (reg_id_t *)user_data; bool seen_memref = false; /* If the previous instruction was a write, we should handle it. */ if (*reg_next != DR_REG_NULL) handle_post_write(drcontext, bb, instr, *reg_next); *reg_next = DR_REG_NULL; if (!instr_is_app(instr)) return DR_EMIT_DEFAULT; if (!instr_writes_memory(instr)) return DR_EMIT_DEFAULT; /* XXX: See above, in handle_post_write(). To simplify the handling of registers, we * assume no instruction has multiple distinct memory destination operands. */ for (i = 0; i < instr_num_dsts(instr); ++i) { if (opnd_is_memory_reference(instr_get_dst(instr, i))) { if (seen_memref) { DR_ASSERT_MSG(false, "Found inst with multiple memory destinations"); break; } *reg_next = instrument_mem(drcontext, bb, instr, instr_get_dst(instr, i)); seen_memref = true; } } return DR_EMIT_DEFAULT; }
instr_t * instrlist_last_app(instrlist_t *ilist) { instr_t *last = ilist->last; if (last == NULL) return NULL; if (instr_is_app(last)) return last; return instr_get_prev_app(last); }
/* event_bb_insert calls instrument_instr to instrument every * application memory reference. */ static dr_emit_flags_t event_bb_insert(void *drcontext, void *tag, instrlist_t *bb, instr_t *instr, bool for_trace, bool translating, void *user_data) { if (instr_get_app_pc(instr) == NULL || !instr_is_app(instr)) return DR_EMIT_DEFAULT; instrument_instr(drcontext, bb, instr); return DR_EMIT_DEFAULT; }
/* returns the first app (non-meta) inst in the list */ instr_t * instrlist_first_app(instrlist_t *ilist) { instr_t *first = ilist->first; if (first == NULL) return NULL; if (instr_is_app(first)) return first; return instr_get_next_app(first); }
/* Check if current instrumentation can be merged into previous aflags * (or on ARM, GPR) save/restore inserted by drx_restore_arith_flags. * Returns NULL if cannot merge. Otherwise, returns the right insertion point, * i.e., DRX_NOTE_AFLAGS_RESTORE_BEGIN label instr. * * This routine looks for labels inserted by drx_restore_arith_flags, * so changes to drx_restore_arith_flags may affect this routine. * On ARM the labels are from drx_insert_counter_update. */ static instr_t * merge_prev_drx_spill(instr_t *where, bool aflags) { instr_t *instr; #ifdef DEBUG bool has_sahf = false; #endif if (where == NULL) return NULL; instr = instr_get_prev(where); if (instr == NULL) return NULL; if (!instr_is_label(instr)) return NULL; /* Check if prev instr is DRX_NOTE_AFLAGS_RESTORE_END. * We bail even there is only a label instr in between, which * might be a target of internal cti. */ if (instr_get_note(instr) != NOTE_VAL(DRX_NOTE_AFLAGS_RESTORE_END)) return NULL; /* find DRX_NOTE_AFLAGS_RESTORE_BEGIN */ for (instr = instr_get_prev(instr); instr != NULL; instr = instr_get_prev(instr)) { if (instr_is_app(instr)) { /* we do not expect any app instr */ ASSERT(false, "drx aflags restore is corrupted"); return NULL; } if (instr_is_label(instr)) { if (instr_get_note(instr) == NOTE_VAL(DRX_NOTE_AFLAGS_RESTORE_BEGIN)) { ASSERT(!aflags || has_sahf, "missing sahf"); return instr; } /* we do not expect any other label instr */ ASSERT(false, "drx aflags restore is corrupted"); return NULL; #ifdef DEBUG } else { if (instr_get_note(instr) == NOTE_VAL(DRX_NOTE_AFLAGS_RESTORE_SAHF)) has_sahf = true; #endif } } return NULL; }
/* 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_instruction(void *drcontext, void *tag, instrlist_t *bb, instr_t *instr, bool for_trace, bool translating, void *user_data) { /* ignore tool-inserted instrumentation */ if (!instr_is_app(instr)) return DR_EMIT_DEFAULT; /* instrument calls and returns -- ignore far calls/rets */ if (instr_is_call_direct(instr)) { insert_counter_update(drcontext, bb, instr, offsetof(per_thread_t, num_direct_calls)); } else if (instr_is_call_indirect(instr)) { insert_counter_update(drcontext, bb, instr, offsetof(per_thread_t, num_indirect_calls)); } else if (instr_is_return(instr)) { insert_counter_update(drcontext, bb, instr, offsetof(per_thread_t, num_returns)); } 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; }