/* 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; }
/* XXX: exporting this so drwrap can use it but I might prefer to have * this in drutil or the upcoming drsys */ DR_EXPORT int drmgr_decode_sysnum_from_wrapper(app_pc entry) { void *drcontext = dr_get_current_drcontext(); int num = -1; byte *pc = entry; uint opc; instr_t instr; instr_init(drcontext, &instr); do { instr_reset(drcontext, &instr); pc = decode(drcontext, pc, &instr); if (!instr_valid(&instr)) break; /* unknown system call sequence */ opc = instr_get_opcode(&instr); /* sanity check: wrapper should be short */ if (pc - entry > 20) break; /* unknown system call sequence */ if (opc == OP_mov_imm && opnd_is_reg(instr_get_dst(&instr, 0)) && opnd_get_reg(instr_get_dst(&instr, 0)) == DR_REG_EAX && opnd_is_immed_int(instr_get_src(&instr, 0))) { num = (int) opnd_get_immed_int(instr_get_src(&instr, 0)); break; /* success */ } /* stop at call to vsyscall (wow64) or at int itself */ } while (opc != OP_call_ind && opc != OP_int && opc != OP_sysenter && opc != OP_syscall); instr_free(drcontext, &instr); return num; }
static dr_emit_flags_t event_app_analysis(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating, OUT void **user_data) { instr_t *inst, *label; bool prev_was_mov_const = false; ptr_int_t val1, val2; *user_data = NULL; /* Look for duplicate mov immediates telling us which subtest we're in */ for (inst = instrlist_first_app(bb); inst != NULL; inst = instr_get_next_app(inst)) { if (instr_is_mov_constant(inst, prev_was_mov_const ? &val2 : &val1)) { if (prev_was_mov_const && val1 == val2 && val1 != 0 && /* rule out xor w/ self */ opnd_is_reg(instr_get_dst(inst, 0)) && opnd_get_reg(instr_get_dst(inst, 0)) == TEST_REG) { *user_data = (void *) val1; label = INSTR_CREATE_label(drcontext); instr_set_translation(label, instr_get_app_pc(inst)); instrlist_meta_postinsert(bb, inst, label); } else prev_was_mov_const = true; } else prev_was_mov_const = false; } return DR_EMIT_DEFAULT; }
/* this is only called when the instrace mode is operand trace (this happens at the instrumentation time) */ static void operand_trace(instr_t * instr, void * drcontext){ int i; char stringop[MAX_STRING_LENGTH]; int pc = 0; per_thread_t * data = drmgr_get_tls_field(drcontext, tls_index); module_data_t * module_data = dr_lookup_module(instr_get_app_pc(instr)); if (module_data != NULL){ pc = instr_get_app_pc(instr) - module_data->start; } instr_disassemble_to_buffer(drcontext, instr, stringop, MAX_STRING_LENGTH); if (client_arg->instrace_mode == OPERAND_TRACE){ dr_fprintf(data->outfile, "%s\n", stringop); for (i = 0; i < instr_num_dsts(instr); i++){ opnd_disassemble_to_buffer(drcontext, instr_get_dst(instr, i), stringop, MAX_STRING_LENGTH); if ((instr_get_opcode(instr) == OP_lea) && opnd_is_base_disp(instr_get_dst(instr,i))){ dr_fprintf(data->outfile, "dst-\n"); print_base_disp_for_lea(data->outfile, instr_get_dst(instr, i)); } else{ dr_fprintf(data->outfile, "dst-%d-%s\n", i, stringop); } } for (i = 0; i < instr_num_srcs(instr); i++){ opnd_disassemble_to_buffer(drcontext, instr_get_src(instr, i), stringop, MAX_STRING_LENGTH); if ((instr_get_opcode(instr) == OP_lea) && opnd_is_base_disp(instr_get_src(instr, i))){ dr_fprintf(data->outfile, "src-\n"); print_base_disp_for_lea(data->outfile, instr_get_src(instr, i)); } else{ dr_fprintf(data->outfile, "src-%d-%s\n", i, stringop); } } if (module_data != NULL){ dr_fprintf(data->outfile, "app_pc-%d\n", pc); } } else if (client_arg->instrace_mode == INS_DISASM_TRACE){ if (module_data != NULL){ if (md_get_module_position(instrace_head, module_data->full_path) == -1){ md_add_module(instrace_head, module_data->full_path, MAX_BBS_PER_MODULE); } dr_fprintf(data->outfile, "%d,%d,%s\n", md_get_module_position(instrace_head, module_data->full_path), pc, stringop); } else{ dr_fprintf(data->outfile, "%d,%d,%s\n",0, 0, stringop); } } dr_free_module_data(module_data); }
/* Our version, versus Dr. Memory's version in drmemory/fastpath.c */ bool instr_ok_for_instrument_fastpath(instr_t *inst, fastpath_info_t *mi, bb_info_t *bi) { uint opc = instr_get_opcode(inst); int i; initialize_fastpath_info(mi, bi, inst); if (!options.fastpath) return false; if (opc == OP_xlat) { /* can't use base-disp "%ds:(%ebx,%al,1)" for lea: would have to expand * to multiple instrs. not worth supporting since pretty rare though I * do see 3K in twolf test on windows. */ return false; } /* We assume that any one memory reference, even in a rep string form, * will only access one heap allocation. Since we're taking the most * recent access to any part of a heap alloc we thus don't care about the * size of a memory reference. */ for (i=0; i<instr_num_dsts(inst); i++) { if (opnd_uses_memory_we_track(instr_get_dst(inst, i))) { mi->store = true; if (!opnd_is_null(mi->dst[0].app)) { /* FIXME: we could handle 2 dsts if no srcs easily, * and even more dsts if we really wanted to w/o too * much trouble. also something like pusha w/ * consecutive dsts is easy: just take first one. */ return false; } mi->dst[0].app = instr_get_dst(inst, i); } } for (i=0; i<instr_num_srcs(inst); i++) { if (opnd_uses_memory_we_track(instr_get_src(inst, i))) { if (mi->store) mi->mem2mem = true; else mi->load = true; if (!opnd_is_null(mi->src[0].app)) { /* see notes above about handling this: in particular cmps */ return false; } mi->src[0].app = instr_get_src(inst, i); } } return true; }
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 dr_emit_flags_t event_basic_block(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) { int i; instr_t *instr, *first = instrlist_first(bb); for (instr = first; instr != NULL; instr = instr_get_next(instr)) { if (instr_reads_memory(instr)) { for (i = 0; i < instr_num_srcs(instr); i++) { if (opnd_is_memory_reference(instr_get_src(instr, i))) { instrument_mem(drcontext, bb, instr, i, false); } } } if (instr_writes_memory(instr)) { for (i = 0; i < instr_num_dsts(instr); i++) { if (opnd_is_memory_reference(instr_get_dst(instr, i))) { instrument_mem(drcontext, bb, instr, i, true); } } } } // dr_printf("count %d\n",count); return DR_EMIT_DEFAULT; }
/* emits the instruction to buf (for tests that wish to do additional checks on * the output) */ static void test_instr_encode_and_decode(void *dc, instr_t *instr, uint len_expect, /* also checks one operand's size */ bool src, uint opnum, opnd_size_t sz, uint bytes) { opnd_t op; opnd_size_t opsz; instr_t *decin; uint len; byte *pc = instr_encode(dc, instr, buf); len = (int) (pc - (byte *)buf); #if VERBOSE disassemble_with_info(dc, buf, STDOUT, true, true); #endif ASSERT(len == len_expect); decin = instr_create(dc); decode(dc, buf, decin); ASSERT(instr_same(instr, decin)); /* PR 245805: variable sizes should be resolved on decode */ if (src) op = instr_get_src(decin, opnum); else op = instr_get_dst(decin, opnum); opsz = opnd_get_size(op); ASSERT(opsz == sz && opnd_size_in_bytes(opsz) == bytes); instr_destroy(dc, instr); instr_destroy(dc, decin); }
/* Called by slow_path() after initial decode. Expected to free inst. */ bool slow_path_for_staleness(void *drcontext, dr_mcontext_t *mc, instr_t *inst, app_loc_t *loc) { opnd_t opnd; int opc, i, num_srcs, num_dsts; uint sz; bool pushpop_stackop; opc = instr_get_opcode(inst); num_srcs = num_true_srcs(inst, mc); for (i = 0; i < num_srcs; i++) { opnd = instr_get_src(inst, i); if (opnd_is_memory_reference(opnd)) { opnd = adjust_memop(inst, opnd, false, &sz, &pushpop_stackop); check_mem_opnd_nouninit(opc, 0, loc, opnd, sz, mc); } } num_dsts = num_true_dsts(inst, mc); for (i = 0; i < num_dsts; i++) { opnd = instr_get_dst(inst, i); if (opnd_is_memory_reference(opnd)) { opnd = adjust_memop(inst, opnd, true, &sz, &pushpop_stackop); check_mem_opnd_nouninit(opc, 0, loc, opnd, sz, mc); } } instr_free(drcontext, inst); /* we're not sharing xl8 so no need to call slow_path_xl8_sharing */ return true; }
void memory_write(void *pc, void *prev_pc) { void *drcontext = dr_get_current_drcontext(); instr_t *instr = instr_create(drcontext); dr_mcontext_t mctx; opnd_t dst; ctx_t ctx; pc = dr_app_pc_for_decoding(pc); mctx.flags = DR_MC_CONTROL|DR_MC_INTEGER; mctx.size = sizeof(mctx); dr_get_mcontext(drcontext, &mctx); instr_init(drcontext, instr); if (!decode(drcontext, pc, instr)) { dr_printf("Decode of instruction at %p failed\n", pc); return; } ctx.addr = prev_pc; ctx.dr_addr = prev_pc; for (int i = 0; i < instr_num_dsts(instr); i++) { dst = instr_get_dst(instr, i); check_opnd(dst, pc, 0, drcontext, &mctx, &ctx); } instr_destroy(drcontext, instr); }
static dr_emit_flags_t bb_event(void* drcontext, void *tag, instrlist_t* bb, bool for_trace, bool translating) { instr_t *instr; instr_t *next_instr; reg_t in_eax = -1; for (instr = instrlist_first(bb); instr != NULL; instr = next_instr) { next_instr = instr_get_next(instr); if (instr_get_opcode(instr) == OP_mov_imm && opnd_get_reg(instr_get_dst(instr, 0)) == REG_EAX) in_eax = opnd_get_immed_int(instr_get_src(instr, 0)); if (instr_is_syscall(instr) && in_eax == SYS_getpid) { instr_t *myval = INSTR_CREATE_mov_imm (drcontext, opnd_create_reg(REG_EAX), OPND_CREATE_INT32(-7)); instr_set_translation(myval, instr_get_app_pc(instr)); instrlist_preinsert(bb, instr, myval); instrlist_remove(bb, instr); instr_destroy(drcontext, instr); } } return DR_EMIT_DEFAULT; }
static void instr_create_ldstex(dcontext_t *dcontext, int len, uint *pc, instr_t *instr, OUT instr_t *instr_ldstex) { int num_dsts = 0; int num_srcs = 0; int i, d, s, j; for (i = 0; i < len; i++) { ASSERT(instr[i].length == AARCH64_INSTR_SIZE && instr[i].bytes == instr[0].bytes + AARCH64_INSTR_SIZE * i); num_dsts += instr_num_dsts(&instr[i]); num_srcs += instr_num_srcs(&instr[i]); } instr_set_opcode(instr_ldstex, OP_ldstex); instr_set_num_opnds(dcontext, instr_ldstex, num_dsts, num_srcs); d = 0; s = 0; for (i = 0; i < len; i++) { int dsts = instr_num_dsts(&instr[i]); int srcs = instr_num_srcs(&instr[i]); for (j = 0; j < dsts; j++) instr_set_dst(instr_ldstex, d++, instr_get_dst(&instr[i], j)); for (j = 0; j < srcs; j++) instr_set_src(instr_ldstex, s++, instr_get_src(&instr[i], j)); } ASSERT(d == num_dsts && s == num_srcs); /* Set raw bits to original encoding. */ instr_set_raw_bits(instr_ldstex, instr[0].bytes, len * AARCH64_INSTR_SIZE); /* Conservatively assume all flags are read and written. */ instr_ldstex->eflags = EFLAGS_READ_ALL | EFLAGS_WRITE_ALL; instr_set_eflags_valid(instr_ldstex, true); }
bool instr_is_mov_constant(instr_t *instr, ptr_int_t *value) { int opc = instr_get_opcode(instr); if (opc == OP_eor) { /* We include OP_eor for symmetry w/ x86, but on ARM "mov reg, #0" is * just as compact and there's no reason to use an xor. */ if (opnd_same(instr_get_src(instr, 0), instr_get_dst(instr, 0)) && opnd_same(instr_get_src(instr, 0), instr_get_src(instr, 1)) && /* Must be the form with "sh2, i5_7" and no shift */ instr_num_srcs(instr) == 4 && opnd_get_immed_int(instr_get_src(instr, 2)) == DR_SHIFT_NONE && opnd_get_immed_int(instr_get_src(instr, 3)) == 0) { *value = 0; return true; } else return false; } else if (opc == OP_mvn || opc == OP_mvns) { opnd_t op = instr_get_src(instr, 0); if (opnd_is_immed_int(op)) { *value = -opnd_get_immed_int(op); return true; } else return false; } else if (opc == OP_mov || opc == OP_movs || opc == OP_movw) { opnd_t op = instr_get_src(instr, 0); if (opnd_is_immed_int(op)) { *value = opnd_get_immed_int(op); return true; } else return false; } return false; }
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; }
/* 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 bool instr_is_nonbranch_pcrel(instr_t *instr) { int i, n; n = instr_num_dsts(instr); for (i = 0; i < n; i++) ASSERT(!OPND_IS_REL_ADDR(instr_get_dst(instr, i))); n = instr_num_srcs(instr); for (i = 0; i < n; i++) { if (OPND_IS_REL_ADDR(instr_get_src(instr, i))) return true; } return false; }
/* event_bb_insert calls instrument_mem to instrument every * application memory reference. */ dr_emit_flags_t memtrace_bb_instrumentation(void *drcontext, void *tag, instrlist_t *bb, instr_t *instr, bool for_trace, bool translating, void *user_data) { int i; reg_id_t reg; file_t out_file; instr_t * first = NULL; instr_t * current; //DR_ASSERT(instr_ok_to_mangle(instr)); if (instr_ok_to_mangle(instr)){ /* FIXME - need to generalize the filtering library */ for (current = instrlist_first(bb); current != NULL; current = instr_get_next(current)){ if (instr_ok_to_mangle(current)){ first = current; break; } } if ((first != NULL) && filter_from_list(head, first, client_arg->filter_mode)){ if (instr_reads_memory(instr)) { for (i = 0; i < instr_num_srcs(instr); i++) { if (opnd_is_memory_reference(instr_get_src(instr, i))) { instrument_mem(drcontext, bb, instr, i, false); } } } if (instr_writes_memory(instr)) { for (i = 0; i < instr_num_dsts(instr); i++) { if (opnd_is_memory_reference(instr_get_dst(instr, i))) { instrument_mem(drcontext, bb, instr, i, true); } } } } } return DR_EMIT_DEFAULT; }
static void handle_post_write(void *drcontext, instrlist_t *ilist, instr_t *where, reg_id_t reg_addr) { int i; instr_t *prev_instr = instr_get_prev_app(where); bool seen_memref = false; /* XXX: We assume that no write instruction has multiple distinct memory destinations. * This way we are able to persist a single register across an app instruction. Note * there are instructions which currently do break this assumption, but we punt on * this. */ for (i = 0; i < instr_num_dsts(prev_instr); ++i) { if (opnd_is_memory_reference(instr_get_dst(prev_instr, i))) { if (seen_memref) { DR_ASSERT_MSG(false, "Found inst with multiple memory destinations"); break; } seen_memref = true; instrument_post_write(drcontext, ilist, where, instr_get_dst(prev_instr, i), prev_instr, reg_addr); } } }
bool instr_uses_memory_we_track(instr_t *inst) { int i; ASSERT(options.staleness, "should not be called"); if (instr_get_opcode(inst) == OP_lea) /* not a real mem access */ return false; for (i = 0; i < instr_num_srcs(inst); i++) { if (opnd_uses_memory_we_track(instr_get_src(inst, i))) return true; } for (i = 0; i < instr_num_dsts(inst); i++) { if (opnd_uses_memory_we_track(instr_get_dst(inst, i))) return true; } return false; }
static bool instr_is_reg_restore(instr_t *instr, reg_id_t reg, umbra_info_t *info) { opnd_t opnd; int slot; if (instr_get_opcode(instr) != OP_mov_ld) return false; opnd = instr_get_dst(instr, 0); if (!opnd_is_reg(opnd) || opnd_get_reg(opnd) != reg) return false; slot = reg - REG_SPILL_START; opnd = OPND_CREATE_ABSMEM(&info->spill_regs[slot], OPSZ_PTR); if (opnd_same(opnd, instr_get_src(instr, 0))) return true; return false; }
void modify_instr_for_relocations(void *drcontext, instr_t *inst, ptr_uint_t *immed, ptr_uint_t *disp) { int i; ptr_uint_t limmed = 0, ldisp = 0; for (i = instr_num_srcs(inst) - 1; i >= 0; i--) { opnd_t opnd = instr_get_src(inst, i); if (opnd_is_immed_int(opnd) && opnd_get_immed_int(opnd) > 0x10000) { if (limmed != 0) { ASSERT(false); } else { limmed = opnd_get_immed_int(opnd); } instr_set_src(inst, i, opnd_create_immed_int(0, opnd_get_size(opnd))); } if (opnd_is_base_disp(opnd) && opnd_get_disp(opnd) > 0x10000) { if (ldisp != 0 && ldisp != opnd_get_disp(opnd)) { ASSERT(false); } else { ldisp = opnd_get_disp(opnd); } instr_set_src(inst, i, opnd_create_base_disp(opnd_get_base(opnd), opnd_get_index(opnd), opnd_get_scale(opnd), 0, opnd_get_size(opnd))); } } for (i = instr_num_dsts(inst) - 1; i >= 0; i--) { opnd_t opnd = instr_get_dst(inst, i); ASSERT(!opnd_is_immed(opnd)); if (opnd_is_base_disp(opnd) && opnd_get_disp(opnd) > 0x10000) { if (ldisp != 0 && ldisp != opnd_get_disp(opnd)) { ASSERT(false); } else { ldisp = opnd_get_disp(opnd); } instr_set_dst(inst, i, opnd_create_base_disp(opnd_get_base(opnd), opnd_get_index(opnd), opnd_get_scale(opnd), 0, opnd_get_size(opnd))); } } if (limmed != 0) *immed = limmed; if (ldisp != 0) *disp = ldisp; }
static bool reg_update_is_limited(instr_t *instr, reg_id_t reg) { int opcode; int offset; opcode = instr_get_opcode(instr); if (opcode == OP_inc || opcode == OP_dec) return true; if (opcode == OP_and) /* for 0xffffffd0 & reg => reg */ return true; if ((opcode == OP_add || opcode == OP_sub || opcode == OP_adc || opcode == OP_sbb) && opnd_is_immed_int(instr_get_src(instr, 0))) { offset = opnd_get_immed_int(instr_get_src(instr, 0)); if (offset > PAGE_SIZE || offset < -PAGE_SIZE) return false; return true; } if (reg != DR_REG_XSP) return false; if (opcode >= OP_push && opcode <= OP_popa) { if (opcode == OP_pop && opnd_same(instr_get_dst(instr, 0), opnd_create_reg(DR_REG_XSP))) return false; return true; } if (opcode >= OP_call && opcode <= OP_call_far_ind) return true; if (opcode == OP_ret || opcode == OP_ret_far || opcode == OP_enter || opcode == OP_leave || opcode == OP_pushf || opcode == OP_popf) return true; return false; }
static uint calculate_operands(instr_t * instr,uint dst_or_src){ opnd_t op; int i; int ret = 0; if(dst_or_src == DST_TYPE){ for(i=0; i<instr_num_dsts(instr); i++){ op = instr_get_dst(instr,i); if(opnd_is_immed(op) || opnd_is_memory_reference(op) || opnd_is_reg(op)){ ret++; } } } else if(dst_or_src == SRC_TYPE){ for(i=0; i<instr_num_srcs(instr); i++){ op = instr_get_src(instr,i); if (instr_get_opcode(instr) == OP_lea && opnd_is_base_disp(op)){ ret += 4; } else if (opnd_is_immed(op) || opnd_is_memory_reference(op) || opnd_is_reg(op)){ ret++; } } } return ret; }
/* event_bb_insert calls instrument_mem 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) { int i; if (instr_get_app_pc(instr) == NULL) return DR_EMIT_DEFAULT; if (instr_reads_memory(instr)) { for (i = 0; i < instr_num_srcs(instr); i++) { if (opnd_is_memory_reference(instr_get_src(instr, i))) { instrument_mem(drcontext, bb, instr, i, false); } } } if (instr_writes_memory(instr)) { for (i = 0; i < instr_num_dsts(instr); i++) { if (opnd_is_memory_reference(instr_get_dst(instr, i))) { instrument_mem(drcontext, bb, instr, i, true); } } } return DR_EMIT_DEFAULT; }
/* prints the trace and empties the instruction buffer */ static void ins_trace(void *drcontext) { per_thread_t *data; int num_refs; instr_trace_t *instr_trace; instr_t * instr; int i; int j; #ifdef READABLE_TRACE char disassembly[SHORT_STRING_LENGTH]; #else output_t * output; #endif data = drmgr_get_tls_field(drcontext, tls_index); instr_trace = (instr_trace_t *)data->buf_base; num_refs = (int)((instr_trace_t *)data->buf_ptr - instr_trace); uint mem_type; uint64 mem_addr; opnd_t opnd; #ifdef READABLE_TRACE //TODO for (i = 0; i < num_refs; i++) { instr = instr_trace->static_info_instr; instr_disassemble_to_buffer(drcontext,instr,disassembly,SHORT_STRING_LENGTH); dr_fprintf(data->outfile,"%u",instr_get_opcode(instr)); dr_fprintf(data->outfile,",%u",calculate_operands(instr,DST_TYPE)); for(j=0; j<instr_num_dsts(instr); j++){ get_address(instr_trace, j, DST_TYPE, &mem_type, &mem_addr); output_populator_printer(drcontext,instr_get_dst(instr,j),instr,mem_addr,mem_type,NULL); } dr_fprintf(data->outfile,",%u",calculate_operands(instr,SRC_TYPE)); for(j=0; j<instr_num_srcs(instr); j++){ get_address(instr_trace, j, SRC_TYPE, &mem_type, &mem_addr); opnd = instr_get_src(instr, j); if (instr_get_opcode(instr) == OP_lea && opnd_is_base_disp(opnd)){ /* four operands here for [base + index * scale + disp] */ output_populator_printer(drcontext, opnd_create_reg(opnd_get_base(opnd)), instr, mem_addr, mem_type, NULL); output_populator_printer(drcontext, opnd_create_reg(opnd_get_index(opnd)), instr, mem_addr, mem_type, NULL); output_populator_printer(drcontext, opnd_create_immed_int(opnd_get_scale(opnd),OPSZ_PTR), instr, mem_addr, mem_type, NULL); output_populator_printer(drcontext, opnd_create_immed_int(opnd_get_disp(opnd), OPSZ_PTR), instr, mem_addr, mem_type, NULL); } else{ output_populator_printer(drcontext, opnd, instr, mem_addr, mem_type, NULL); } } dr_fprintf(data->outfile,",%u,%u\n",instr_trace->eflags,instr_trace->pc); ++instr_trace; } #else /* we need to fill up the output array here */ for(i = 0; i< num_refs; i++){ instr = instr_trace->static_info_instr; output = &data->output_array[i]; //opcode output->opcode = instr_get_opcode(instr); output->num_dsts = 0; output->num_srcs = 0; for(j=0; j<instr_num_dsts(instr); j++){ output_populator_printer(drcontext,instr_get_dst(instr,j),instr,get_address(instr_trace,j,DST_TYPE),&output->dsts[output->num_dsts]); output->num_dsts++; } for(j=0; j<instr_num_srcs(instr); j++){ output_populator_printer(drcontext,instr_get_src(instr,j),instr,get_address(instr_trace,j,SRC_TYPE),&output->srcs[output->num_srcs]); output->num_srcs++; } output->eflags = instr_trace->eflags; ++instr_trace; } dr_write_file(data->outfile,data->output_array,num_refs * sizeof(output_t)); #endif memset(data->buf_base, 0, INSTR_BUF_SIZE); data->num_refs += num_refs; data->buf_ptr = data->buf_base; }
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); }
/* * 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); }
bool instr_is_jump_mem(instr_t *instr) { return instr_get_opcode(instr) == OP_ldr && opnd_get_reg(instr_get_dst(instr, 0)) == DR_REG_PC; }
/* replaces inc with add 1, dec with sub 1 * returns true if successful, false if not */ static bool replace_inc_with_add(void *drcontext, instr_t *instr, instrlist_t *trace) { instr_t *in; uint eflags; int opcode = instr_get_opcode(instr); bool ok_to_replace = false; DR_ASSERT(opcode == OP_inc || opcode == OP_dec); #ifdef VERBOSE dr_print_instr(drcontext, STDOUT, instr, "in replace_inc_with_add:\n\t"); #endif /* add/sub writes CF, inc/dec does not, make sure that's ok */ for (in = instr; in != NULL; in = instr_get_next(in)) { eflags = instr_get_eflags(in); if ((eflags & EFLAGS_READ_CF) != 0) { #ifdef VERBOSE dr_print_instr(drcontext, STDOUT, in, "\treads CF => cannot replace inc with add: "); #endif return false; } if (instr_is_exit_cti(in)) { /* to be more sophisticated, examine instructions at * target of exit cti (if it is a direct branch). * for this example, we give up if we hit a branch. */ return false; } /* if writes but doesn't read, ok */ if ((eflags & EFLAGS_WRITE_CF) != 0) { ok_to_replace = true; break; } } if (!ok_to_replace) { #ifdef VERBOSE dr_printf("\tno write to CF => cannot replace inc with add\n"); #endif return false; } if (opcode == OP_inc) { #ifdef VERBOSE dr_printf("\treplacing inc with add\n"); #endif in = INSTR_CREATE_add(drcontext, instr_get_dst(instr, 0), OPND_CREATE_INT8(1)); } else { #ifdef VERBOSE dr_printf("\treplacing dec with sub\n"); #endif in = INSTR_CREATE_sub(drcontext, instr_get_dst(instr, 0), OPND_CREATE_INT8(1)); } if (instr_get_prefix_flag(instr, PREFIX_LOCK)) instr_set_prefix_flag(in, PREFIX_LOCK); instr_set_translation(in, instr_get_app_pc(instr)); instrlist_replace(trace, instr, in); instr_destroy(drcontext, instr); return true; }