static dr_emit_flags_t event_basic_block(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) { instr_t *instr; trace_head_entry_t *e = NULL; if (translating) return DR_EMIT_DEFAULT; for (instr = instrlist_first(bb); instr != NULL; instr = instr_get_next(instr)) { /* blocks containing calls are trace heads */ if (instr_is_call(instr)) { dr_mark_trace_head(drcontext, tag); dr_mutex_lock(htable_mutex); e = add_trace_head_entry(NULL, tag); e->is_trace_head = true; dr_mutex_unlock(htable_mutex); #ifdef VERBOSE dr_log(drcontext, LOG_ALL, 3, "inline: marking bb "PFX" as trace head\n", tag); #endif /* doesn't matter what's in rest of bb */ return DR_EMIT_DEFAULT; } else if (instr_is_return(instr)) { dr_mutex_lock(htable_mutex); e = add_trace_head_entry(NULL, tag); e->has_ret = true; dr_mutex_unlock(htable_mutex); } } return DR_EMIT_DEFAULT; }
static dr_emit_flags_t event_analyze_bb(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating, void **user_data) { instr_t *instr; trace_head_entry_t *e = NULL; if (translating) return DR_EMIT_DEFAULT; for (instr = instrlist_first_app(bb); instr != NULL; instr = instr_get_next_app(instr)) { /* Blocks containing calls are trace heads. */ if (instr_is_call(instr)) { dr_mark_trace_head(drcontext, tag); hashtable_lock(&head_table); e = hashtable_lookup(&head_table, tag); if (e == NULL) { e = create_trace_head_entry(tag); if (!hashtable_add(&head_table, tag, (void *)e)) DR_ASSERT(false); } else e->refcount++; e->is_trace_head = true; hashtable_unlock(&head_table); #ifdef VERBOSE dr_log(drcontext, DR_LOG_ALL, 3, "inline: marking bb "PFX" as call trace head\n", tag); #endif /* Doesn't matter what's in rest of the bb. */ return DR_EMIT_DEFAULT; } else if (instr_is_return(instr)) { hashtable_lock(&head_table); e = hashtable_lookup(&head_table, tag); if (e == NULL) { e = create_trace_head_entry(tag); if (!hashtable_add(&head_table, tag, (void *)e)) DR_ASSERT(false); } else e->refcount++; e->has_ret = true; hashtable_unlock(&head_table); #ifdef VERBOSE dr_log(drcontext, DR_LOG_ALL, 3, "inline: marking bb "PFX" as return trace head\n", tag); #endif } } return DR_EMIT_DEFAULT; }
// return the addr of the previous instructions // a bad addr can be return in some specific case but generally work good void *get_prev_instr_pc(void *pc, void *drc) { instr_t *instr = instr_create(drc); void *tmp_pc; for (int ct = 1; ; ct++) { tmp_pc = dr_app_pc_for_decoding(pc - ct); if (decode(drc, tmp_pc, instr)) { if (instr_is_call(instr) && decode_next_pc(drc, tmp_pc) == pc) break; } instr_reuse(drc, instr); } instr_destroy(drc, instr); return tmp_pc; }
static reg_id_t instrument_mem(void *drcontext, instrlist_t *ilist, instr_t *where, opnd_t ref) { reg_id_t reg_ptr, reg_tmp, reg_addr; ushort type, size; bool ok; if (drreg_reserve_register(drcontext, ilist, where, NULL, ®_tmp) != DRREG_SUCCESS) { DR_ASSERT(false); return DR_REG_NULL; } if (drreg_reserve_register(drcontext, ilist, where, NULL, ®_ptr) != DRREG_SUCCESS) { DR_ASSERT(false); return DR_REG_NULL; } /* i#2449: In the situation that instrument_post_write, instrument_mem and ref all * have the same register reserved, drutil_insert_get_mem_addr will compute the * address of an operand using an incorrect register value, as drreg will elide the * save/restore. */ if (opnd_uses_reg(ref, reg_tmp) && drreg_get_app_value(drcontext, ilist, where, reg_tmp, reg_tmp) != DRREG_SUCCESS) { DR_ASSERT(false); return DR_REG_NULL; } if (opnd_uses_reg(ref, reg_ptr) && drreg_get_app_value(drcontext, ilist, where, reg_ptr, reg_ptr) != DRREG_SUCCESS) { DR_ASSERT(false); return DR_REG_NULL; } /* We use reg_ptr as scratch to get addr. Note we do this first as reg_ptr or reg_tmp * may be used in ref. */ ok = drutil_insert_get_mem_addr(drcontext, ilist, where, ref, reg_tmp, reg_ptr); DR_ASSERT(ok); drx_buf_insert_load_buf_ptr(drcontext, trace_buffer, ilist, where, reg_ptr); /* inserts memref addr */ drx_buf_insert_buf_store(drcontext, trace_buffer, ilist, where, reg_ptr, DR_REG_NULL, opnd_create_reg(reg_tmp), OPSZ_PTR, offsetof(mem_ref_t, addr)); if (IF_AARCHXX_ELSE(true, false)) { /* At this point we save the write address for later, because reg_tmp's value * will get clobbered on ARM. */ if (drreg_reserve_register(drcontext, ilist, where, NULL, ®_addr) != DRREG_SUCCESS) { DR_ASSERT(false); return DR_REG_NULL; } MINSERT(ilist, where, XINST_CREATE_move(drcontext, opnd_create_reg(reg_addr), opnd_create_reg(reg_tmp))); } /* inserts type */ type = (ushort)instr_get_opcode(where); drx_buf_insert_buf_store(drcontext, trace_buffer, ilist, where, reg_ptr, reg_tmp, OPND_CREATE_INT16(type), OPSZ_2, offsetof(mem_ref_t, type)); /* inserts size */ size = (ushort)drutil_opnd_mem_size_in_bytes(ref, where); drx_buf_insert_buf_store(drcontext, trace_buffer, ilist, where, reg_ptr, reg_tmp, OPND_CREATE_INT16(size), OPSZ_2, offsetof(mem_ref_t, size)); drx_buf_insert_update_buf_ptr(drcontext, trace_buffer, ilist, where, reg_ptr, DR_REG_NULL, sizeof(mem_ref_t)); if (instr_is_call(where)) { app_pc pc; /* Note that on ARM the call instruction writes only to the link register, so * we would never even get into instrument_mem() on ARM if this was a call. */ IF_AARCHXX(DR_ASSERT(false)); /* We simulate the call instruction's written memory by writing the next app_pc * to the written buffer, since we can't do this after the call has happened. */ drx_buf_insert_load_buf_ptr(drcontext, write_buffer, ilist, where, reg_ptr); pc = decode_next_pc(drcontext, instr_get_app_pc(where)); /* note that for a circular buffer, we don't need to specify a scratch register */ drx_buf_insert_buf_store(drcontext, trace_buffer, ilist, where, reg_ptr, DR_REG_NULL, OPND_CREATE_INTPTR((ptr_int_t)pc), OPSZ_PTR, 0); drx_buf_insert_update_buf_ptr(drcontext, write_buffer, ilist, where, reg_ptr, reg_tmp, sizeof(app_pc)); /* we don't need to persist reg_tmp to the next instruction */ if (drreg_unreserve_register(drcontext, ilist, where, reg_tmp) != DRREG_SUCCESS) DR_ASSERT(false); reg_tmp = DR_REG_NULL; } else if (IF_AARCHXX_ELSE(true, false)) { /* Now reg_tmp has the address of the write again. */ MINSERT(ilist, where, XINST_CREATE_move(drcontext, opnd_create_reg(reg_tmp), opnd_create_reg(reg_addr))); if (drreg_unreserve_register(drcontext, ilist, where, reg_addr) != DRREG_SUCCESS) DR_ASSERT(false); } if (drreg_unreserve_register(drcontext, ilist, where, reg_ptr) != DRREG_SUCCESS) DR_ASSERT(false); return reg_tmp; }
static void module_load_event(void *drcontext, const module_data_t *mod, bool loaded) { if (strstr(dr_module_preferred_name(mod), "client.drwrap-test.appdll.") != NULL) { bool ok; instr_t inst; app_pc init_pc, pc, next_pc; load_count++; if (load_count == 2) { /* test no-frills */ drwrap_set_global_flags(DRWRAP_NO_FRILLS); } addr_replace = (app_pc) dr_get_proc_address(mod->handle, "replaceme"); CHECK(addr_replace != NULL, "cannot find lib export"); ok = drwrap_replace(addr_replace, (app_pc) replacewith, false); CHECK(ok, "replace failed"); addr_replace2 = (app_pc) dr_get_proc_address(mod->handle, "replaceme2"); CHECK(addr_replace2 != NULL, "cannot find lib export"); ok = drwrap_replace_native(addr_replace2, (app_pc) replacewith2, true/*at entry*/, 0, (void *)(ptr_int_t)DRWRAP_NATIVE_PARAM, false); CHECK(ok, "replace_native failed"); init_pc = (app_pc) dr_get_proc_address(mod->handle, "replace_callsite"); CHECK(init_pc != NULL, "cannot find lib export"); /* Find callsite: we assume we'll linearly hit a ret. We take final call * to skip any PIC call. */ instr_init(drcontext, &inst); pc = init_pc; do { instr_reset(drcontext, &inst); next_pc = decode(drcontext, pc, &inst); if (!instr_valid(&inst)) break; /* if initial jmp, follow it to handle ILT-indirection */ if (pc == init_pc && instr_is_ubr(&inst)) next_pc = opnd_get_pc(instr_get_target(&inst)); else if (instr_is_call(&inst)) addr_replace_callsite = pc; pc = next_pc; } while (instr_valid(&inst) && !instr_is_return(&inst)); CHECK(addr_replace_callsite != NULL, "cannot find lib export"); ok = drwrap_replace_native(addr_replace_callsite, (app_pc) replace_callsite, false/*!at entry*/, 0, (void *)(ptr_int_t)DRWRAP_NATIVE_PARAM, false); CHECK(ok, "replace_native failed"); instr_free(drcontext, &inst); wrap_addr(&addr_level0, "level0", mod, true, true); wrap_addr(&addr_level1, "level1", mod, true, true); wrap_addr(&addr_level2, "level2", mod, true, true); wrap_addr(&addr_tailcall, "makes_tailcall", mod, true, true); wrap_addr(&addr_skipme, "skipme", mod, true, true); wrap_addr(&addr_repeat, "repeatme", mod, true, true); wrap_addr(&addr_preonly, "preonly", mod, true, false); wrap_addr(&addr_postonly, "postonly", mod, false, true); wrap_addr(&addr_runlots, "runlots", mod, false, true); /* test longjmp */ wrap_unwindtest_addr(&addr_long0, "long0", mod); wrap_unwindtest_addr(&addr_long1, "long1", mod); wrap_unwindtest_addr(&addr_long2, "long2", mod); wrap_unwindtest_addr(&addr_long3, "long3", mod); wrap_unwindtest_addr(&addr_longdone, "longdone", mod); drmgr_set_tls_field(drcontext, tls_idx, (void *)(ptr_uint_t)0); #ifdef WINDOWS /* test SEH */ /* we can't do this test for no-frills b/c only one wrap per addr */ if (load_count == 1) { ok = drwrap_wrap_ex(addr_long0, wrap_unwindtest_seh_pre, wrap_unwindtest_seh_post, NULL, DRWRAP_UNWIND_ON_EXCEPTION); CHECK(ok, "wrap failed"); ok = drwrap_wrap_ex(addr_long1, wrap_unwindtest_seh_pre, wrap_unwindtest_seh_post, NULL, DRWRAP_UNWIND_ON_EXCEPTION); CHECK(ok, "wrap failed"); ok = drwrap_wrap_ex(addr_long2, wrap_unwindtest_seh_pre, wrap_unwindtest_seh_post, NULL, DRWRAP_UNWIND_ON_EXCEPTION); CHECK(ok, "wrap failed"); ok = drwrap_wrap_ex(addr_long3, wrap_unwindtest_seh_pre, wrap_unwindtest_seh_post, NULL, DRWRAP_UNWIND_ON_EXCEPTION); CHECK(ok, "wrap failed"); ok = drwrap_wrap_ex(addr_longdone, wrap_unwindtest_seh_pre, wrap_unwindtest_seh_post, NULL, DRWRAP_UNWIND_ON_EXCEPTION); CHECK(ok, "wrap failed"); } #endif /* test leaner wrapping */ if (load_count == 2) drwrap_set_global_flags(DRWRAP_NO_FRILLS | DRWRAP_FAST_CLEANCALLS); wrap_addr(&addr_skip_flags, "skip_flags", mod, true, false); } }