/* 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 void test_disp_control_helper(void *dc, int disp, bool encode_zero_disp, bool force_full_disp, bool disp16, uint len_expect) { byte *pc; uint len; instr_t *instr = INSTR_CREATE_mov_ld (dc, opnd_create_reg(REG_ECX), opnd_create_base_disp_ex(disp16 ? IF_X64_ELSE(REG_EBX, REG_BX) : REG_XBX, REG_NULL, 0, disp, OPSZ_4, encode_zero_disp, force_full_disp, disp16)); pc = instr_encode(dc, instr, buf); len = (int) (pc - (byte *)buf); #if VERBOSE pc = disassemble_with_info(dc, buf, STDOUT, true, true); #endif ASSERT(len == len_expect); instr_reset(dc, instr); decode(dc, buf, instr); ASSERT(instr_num_srcs(instr) == 1 && opnd_is_base_disp(instr_get_src(instr, 0)) && BOOLS_MATCH(encode_zero_disp, opnd_is_disp_encode_zero(instr_get_src(instr, 0))) && BOOLS_MATCH(force_full_disp, opnd_is_disp_force_full(instr_get_src(instr, 0))) && BOOLS_MATCH(disp16, opnd_is_disp_short_addr(instr_get_src(instr, 0)))); instr_destroy(dc, instr); }
/* 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); }
static void process_ret(instr_t *instr, syscall_info_t *info) { assert(instr_is_return(instr)); if (opnd_is_immed_int(instr_get_src(instr, 0))) info->num_args = (int) opnd_get_immed_int(instr_get_src(instr, 0)); else info->num_args = 0; }
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; }
/* 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; }
/* If has_instr_jmp_targets is true, this routine trashes the note field * of each instr_t to store the offset in order to properly encode * the relative pc for an instr_t jump target */ byte * instrlist_encode_to_copy(dcontext_t *dcontext, instrlist_t *ilist, byte *copy_pc, byte *final_pc, byte *max_pc, bool has_instr_jmp_targets) { instr_t *inst; int len = 0; #ifdef ARM /* XXX i#1734: reset encode state to avoid any stale encode state * or dangling pointer. */ if (instr_get_isa_mode(instrlist_first(ilist)) == DR_ISA_ARM_THUMB) encode_reset_it_block(dcontext); #endif /* Do an extra pass over the instrlist so we can determine if an instr opnd * was erroneously used with has_instr_jmp_targets = false. */ DOCHECK(2, { if (!has_instr_jmp_targets) { for (inst = instrlist_first(ilist); inst; inst = instr_get_next(inst)) { if (TEST(INSTR_OPERANDS_VALID, (inst)->flags)) { int i; for (i = 0; i < instr_num_srcs(inst); ++i) { CLIENT_ASSERT(!opnd_is_instr(instr_get_src(inst, i)), "has_instr_jmp_targets was unset " "but an instr opnd was found"); } } } } });
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 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; }
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); }
/* 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_read(void *pc, void *next_pc) { void *drcontext = dr_get_current_drcontext(); instr_t *instr = instr_create(drcontext); dr_mcontext_t mctx; opnd_t src; 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 = next_pc; ctx.dr_addr = next_pc; for (int i = 0; i < instr_num_srcs(instr); i++) { src = instr_get_src(instr, i); check_opnd(src, pc, 1, drcontext, &mctx, &ctx); } instr_destroy(drcontext, instr); }
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; }
/* If instr is unsigned division, return true and set *opnd to divisor. */ static bool instr_is_div(instr_t *instr, OUT opnd_t *opnd) { int opc = instr_get_opcode(instr); #if defined(X86) if (opc == OP_div) { *opnd = instr_get_src(instr, 0); /* divisor is 1st src */ return true; } #elif defined(AARCHXX) if (opc == OP_udiv) { *opnd = instr_get_src(instr, 1); /* divisor is 2nd src */ return true; } #else # error NYI #endif return false; }
/* First tried something like this, but we hit too many issues in decode and encode */ bool compare_pages(void *drcontext, byte *start1, byte *start2) { byte *p1 = start1, *p2 = start2; int skipped_bytes = 0, identical_skipped_bytes = 0; while (p1 < start1 + PAGE_SIZE) { int instr_size = decode_sizeof(drcontext, p1, NULL _IF_X64(NULL)); if (p1 + instr_size > start1 + PAGE_SIZE) { /* We're overlapping the end of the page, skip these. */ int end_skip = start1 + PAGE_SIZE - p1; VVERBOSE_PRINT("Passing PAGE_END %d bytes", end_skip); skipped_bytes += end_skip; if (memcmp(p1, p2, end_skip) == 0) identical_skipped_bytes += end_skip; break; } if (decode_sizeof(drcontext, p2, NULL _IF_X64(NULL)) != instr_size) { VVERBOSE_PRINT("Instruction alignment mismatch\n"); return false; } /* assumption - instructions <= 4 bytes in size won't have relocations */ if (instr_size < 5) { if (memcmp(p1, p2, instr_size) != 0) { VVERBOSE_PRINT("Difference found in small instr\n"); return false; } p1 += size; p2 += size; } else { /* guess if there could be a relocation */ instr_t *instr1 = instr_create(drcontext); instr_t *instr2 = instr_create(drcontext); p1 = decode(drcontext, p1, instr1); p2 = decode(drcontext, p2, instr2); if (p1 - start1 != p2 - start2) { VVERBOSE_PRINT("Instruction alignment mismatch on full decode\n"); /* Fixme - free instr, don't expect this to happen */ return false; } if (instr_get_num_srcs(instr1) != instr_get_num_srcs(instr2) || instr_get_num_dsts(instr1) != instr_get_num_dsts(instr2)) { VVERBOSE_PRINT("Full decode operand mismatch"); return false; } for (i = instr_get_num_srcs(instr1); i > 0; i--) { opnd_t opnd = instr_get_src(instr1, i); if (opnd_is_immed_int(opnd) && opnd_get_immed_int(opnd) > 0x10000) { instr_set_src(instr1, i, opnd_create_immed_int(opnd_get_immed_int(opnd), opnd_get_size(opnd))); } } } } }
instrlist_t* instrlist_clone(dcontext_t *dcontext, instrlist_t *old) { instr_t *inst, *copy; instrlist_t *newlist = instrlist_create(dcontext); inst = instrlist_first(old); while (inst != NULL) { copy = instr_clone(dcontext, inst); /* to copy instr targets we temporarily clobber note field */ instr_set_note(inst, (void *)copy); instrlist_append(newlist, copy); inst = instr_get_next(inst); } /* Fix up instr src if it is an instr and restore note field */ /* Note: we do not allows instruction update code cache, * which is very dangerous. * So we do not support instr as dst opnd and won't fix up here if any. */ for (inst = instrlist_first(old), copy = instrlist_first(newlist); inst != NULL && copy != NULL; inst = instr_get_next(inst), copy = instr_get_next(copy)) { int i; for (i = 0; i < inst->num_srcs; i++) { instr_t *tgt; opnd_t op = instr_get_src(copy, i); if (!opnd_is_instr(op)) continue; CLIENT_ASSERT(opnd_get_instr(op) != NULL, "instrlist_clone: NULL instr operand"); tgt = (instr_t *) instr_get_note(opnd_get_instr(op)); CLIENT_ASSERT(tgt != NULL, "instrlist_clone: operand instr not in instrlist"); if (opnd_is_far_instr(op)) { instr_set_src(copy, i, opnd_create_far_instr (opnd_get_segment_selector(op), tgt)); } else instr_set_src(copy, i, opnd_create_instr(tgt)); } } for (inst = instrlist_first(old), copy = instrlist_first(newlist); inst != NULL && copy != NULL; inst = instr_get_next(inst), copy = instr_get_next(copy)) { /* restore note field */ instr_set_note(inst, instr_get_note(copy)); } #ifdef CLIENT_INTERFACE newlist->fall_through_bb = old->fall_through_bb; #endif return newlist; }
bool instr_is_pop(instr_t *instr) { opnd_t memop; if (instr_num_srcs(instr) == 0) return false; memop = instr_get_src(instr, 0); if (!opnd_is_base_disp(memop)) return false; return opnd_get_base(memop) == DR_REG_SP; }
/* 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) { /* 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 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 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 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; }
static void look_for_usercall(void *dcontext, byte *entry, const char *sym, LOADED_IMAGE *img, const char *modpath) { bool found_push_imm = false; int imm = 0; app_pc pc, pre_pc; instr_t *instr; if (entry == NULL) return; instr = instr_create(dcontext); pc = entry; while (true) { instr_reset(dcontext, instr); pre_pc = pc; pc = decode(dcontext, pc, instr); if (verbose) { instr_set_translation(instr, pre_pc); dr_print_instr(dcontext, STDOUT, instr, ""); } if (pc == NULL || !instr_valid(instr)) break; if (instr_get_opcode(instr) == OP_push_imm) { found_push_imm = true; imm = (int) opnd_get_immed_int(instr_get_src(instr, 0)); } else if (instr_is_call_direct(instr) && found_push_imm) { app_pc tgt = opnd_get_pc(instr_get_target(instr)); bool found = false; int i; for (i = 0; i < NUM_USERCALL; i++) { if (tgt == usercall_addr[i]) { dr_printf("Call #0x%02x to %s at %s+0x%x\n", imm, usercall_names[i], sym, pre_pc - entry); found = true; break; } } if (found) break; } else if (instr_is_return(instr)) break; if (pc - entry > MAX_BYTES_BEFORE_USERCALL) break; } instr_destroy(dcontext, instr); }
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; }
bool instr_is_return(instr_t *instr) { /* There is no "return" opcode so we consider a return to be either: * A) An indirect branch through lr; * B) An instr that reads lr and writes pc; * (XXX: should we limit to a move and rule out an add or shift or whatever?) * C) A pop into pc. */ int opc = instr_get_opcode(instr); if ((opc == OP_bx || opc == OP_bxj) && opnd_get_reg(instr_get_src(instr, 0)) == DR_REG_LR) return true; if (!instr_writes_to_reg(instr, DR_REG_PC, DR_QUERY_INCLUDE_ALL)) return false; return (instr_reads_from_reg(instr, DR_REG_LR, DR_QUERY_INCLUDE_ALL) || instr_is_pop(instr)); }
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; }