void cfi_call_instrumentation_snapahot(void *dcontext, instrlist_t *ilist, instr_t *where, void *callee) { uint dstack_offs = 0, pad = 0; unsigned int eflags_offs; CFI_ASSERT(dcontext != NULL, "cfi_call_instrumentation_snapahot: dcontext cannot be NULL"); instr_t *next_snapshot = INSTR_CREATE_label(dcontext); PRE(ilist, where, INSTR_CREATE_pushf(dcontext)); dstack_offs += XSP_SZ; eflags_offs = dstack_offs; PRE(ilist, where, INSTR_CREATE_push(dcontext, opnd_create_reg(DR_REG_RDI))); dstack_offs += XSP_SZ; PRE(ilist, where, INSTR_CREATE_mov_imm(dcontext, opnd_create_reg(DR_REG_RDI), OPND_CREATE_INT64(&flag_memory_snapshot))); PRE(ilist, where, INSTR_CREATE_mov_ld(dcontext, opnd_create_reg(DR_REG_RDI), opnd_create_base_disp(DR_REG_RDI, DR_REG_NULL, 0, 0, OPSZ_PTR))); PRE(ilist, where, INSTR_CREATE_cmp(dcontext, opnd_create_reg(DR_REG_RDI), OPND_CREATE_INT8(0x0))); PRE(ilist, where, INSTR_CREATE_jcc(dcontext, OP_je, opnd_create_instr(next_snapshot))); PRE(ilist, where, INSTR_CREATE_push(dcontext, opnd_create_base_disp(DR_REG_RSP, DR_REG_NULL, 0, dstack_offs - eflags_offs, OPSZ_STACK))); PRE(ilist, where, INSTR_CREATE_and(dcontext, opnd_create_base_disp(DR_REG_RSP, DR_REG_NULL, 0, 0, OPSZ_STACK), OPND_CREATE_INT32(~(EFLAGS_NON_SYSTEM | EFLAGS_IF)))); PRE(ilist, where, INSTR_CREATE_popf(dcontext)); instrlist_set_our_mangling(ilist, true); cfi_insert_meta_native_call_vargs(dcontext, ilist, where, true/*clean*/, callee); instrlist_set_our_mangling(ilist, false); cfi_insert_native_call(dcontext, ilist, where, callee /*, opnd_create_reg(DR_REG_RAX)*/); PRE(ilist, where, next_snapshot); PRE(ilist, where, INSTR_CREATE_pop(dcontext, opnd_create_reg(DR_REG_RDI))); PRE(ilist, where, INSTR_CREATE_popf(dcontext)); /* dstack_offs = cfi_prepare_for_native_call(dcontext, ilist, where); instrlist_set_our_mangling(ilist, true); cfi_insert_meta_native_call_vargs(dcontext, ilist, where, true*//*clean*//*, callee); instrlist_set_our_mangling(ilist, false); cfi_cleanup_after_native_call(dcontext, ilist, where);*/ }
static void test_modrm16_helper(void *dc, reg_id_t base, reg_id_t scale, uint disp, uint len) { instr_t *instr; /* Avoid REG_EAX b/c of the special 0xa0-0xa3 opcodes */ instr = INSTR_CREATE_mov_ld(dc, opnd_create_reg(REG_EBX), opnd_create_base_disp(base, scale, (scale == REG_NULL ? 0 : 1), /* we need OPSZ_4_short2 to match * instr_same on decode! */ disp, OPSZ_4_short2)); if (base == REG_NULL && scale == REG_NULL) { /* Don't need _ex unless abs addr, in which case should get 32-bit * disp! Test both sides. */ test_instr_encode(dc, instr, len + 1/*32-bit disp but no prefix*/); instr = INSTR_CREATE_mov_ld(dc, opnd_create_reg(REG_EBX), opnd_create_base_disp_ex(base, scale, (scale == REG_NULL ? 0 : 1), /* we need OPSZ_4_short2 to match * instr_same on decode! */ disp, OPSZ_4_short2, false, false, true)); test_instr_encode(dc, instr, len); } else { test_instr_encode(dc, instr, len); } }
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 void test_instr_as_immed(void) { void *drcontext = dr_get_current_drcontext(); instrlist_t *ilist = instrlist_create(drcontext); byte *pc; instr_t *ins0, *ins1, *ins2; opnd_t opnd; byte *highmem = PREFERRED_ADDR; pc = dr_raw_mem_alloc(PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE| DR_MEMPROT_EXEC, highmem); ASSERT(pc == highmem); /* Test push_imm of instr */ ins0 = INSTR_CREATE_nop(drcontext); instrlist_append(ilist, ins0); instrlist_insert_push_instr_addr(drcontext, ins0, highmem, ilist, NULL, &ins1, &ins2); ASSERT(ins2 != NULL); instrlist_append(ilist, INSTR_CREATE_pop (drcontext, opnd_create_reg(DR_REG_RAX))); instrlist_append(ilist, INSTR_CREATE_ret(drcontext)); pc = instrlist_encode(drcontext, ilist, highmem, true); instrlist_clear(drcontext, ilist); ASSERT(pc < highmem + PAGE_SIZE); pc = ((byte* (*)(void))highmem)(); ASSERT(pc == highmem); /* Test mov_imm of instr */ ins0 = INSTR_CREATE_nop(drcontext); instrlist_append(ilist, ins0); /* Beyond TOS, but a convenient mem dest */ opnd = opnd_create_base_disp(DR_REG_RSP, DR_REG_NULL, 0, -8, OPSZ_8); instrlist_insert_mov_instr_addr(drcontext, ins0, highmem, opnd, ilist, NULL, &ins1, &ins2); ASSERT(ins2 != NULL); instrlist_append(ilist, INSTR_CREATE_mov_ld (drcontext, opnd_create_reg(DR_REG_RAX), opnd)); instrlist_append(ilist, INSTR_CREATE_ret(drcontext)); pc = instrlist_encode(drcontext, ilist, highmem, true); instrlist_clear(drcontext, ilist); ASSERT(pc < highmem + PAGE_SIZE); pc = ((byte* (*)(void))highmem)(); ASSERT(pc == highmem); instrlist_clear_and_destroy(drcontext, ilist); dr_raw_mem_free(highmem, PAGE_SIZE); }
opnd_t callee_info_slot_opnd(callee_info_t *ci, slot_kind_t kind, reg_id_t value) { uint i; if (kind == SLOT_REG) value = dr_reg_fixer[value]; for (i = 0; i < BUFFER_SIZE_ELEMENTS(ci->scratch_slots); i++) { if (ci->scratch_slots[i].kind == kind && ci->scratch_slots[i].value == value) { int disp = (int)offsetof(unprotected_context_t, inline_spill_slots[i]); return opnd_create_base_disp(ci->spill_reg, DR_REG_NULL, 0, disp, OPSZ_PTR); } } ASSERT_MESSAGE(CHKLVL_ASSERTS, "Tried to find scratch slot for value " "without calling callee_info_reserve_slot for it", false); return opnd_create_null(); }
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); }
opnd_t opnd_create_sized_tls_slot(int offs, opnd_size_t size) { return opnd_create_base_disp(dr_reg_stolen, REG_NULL, 0, offs, size); }
/* PR 215143: auto-magically add size prefixes */ static void test_size_changes(void *dc) { /* * 0x004299d4 67 51 addr16 push %ecx %sp -> %sp (%sp) * 0x004299d4 66 51 data16 push %cx %esp -> %esp (%esp) * 0x004299d4 66 67 51 data16 addr16 push %cx %sp -> %sp (%sp) * 0x004298a4 e3 fe jecxz $0x004298a4 %ecx * 0x004298a4 67 e3 fd addr16 jecxz $0x004298a4 %cx * 0x080a5260 67 e2 fd addr16 loop $0x080a5260 %cx -> %cx * 0x080a5260 67 e1 fd addr16 loope $0x080a5260 %cx -> %cx * 0x080a5260 67 e0 fd addr16 loopne $0x080a5260 %cx -> %cx */ instr_t *instr; /* push addr16 */ instr = instr_create_2dst_2src(dc, OP_push, opnd_create_reg(IF_X64_ELSE(REG_ESP, REG_SP)), opnd_create_base_disp(IF_X64_ELSE(REG_ESP, REG_SP), REG_NULL, 0, -(int)sizeof(void*), OPSZ_ret), opnd_create_reg(REG_XCX), opnd_create_reg(IF_X64_ELSE(REG_ESP, REG_SP))); test_instr_encode(dc, instr, 2); #ifndef X64 /* can only shorten on AMD */ /* push data16 */ instr = instr_create_2dst_2src(dc, OP_push, opnd_create_reg(REG_XSP), opnd_create_base_disp(REG_XSP, REG_NULL, 0, -2, OPSZ_2), opnd_create_reg(REG_CX), opnd_create_reg(REG_XSP)); test_instr_encode(dc, instr, 2); /* push addr16 and data16 */ instr = instr_create_2dst_2src(dc, OP_push, opnd_create_reg(REG_SP), opnd_create_base_disp(REG_SP, REG_NULL, 0, -2, OPSZ_2), opnd_create_reg(REG_CX), opnd_create_reg(REG_SP)); test_instr_encode(dc, instr, 3); #endif /* jecxz and jcxz */ test_instr_encode(dc, INSTR_CREATE_jecxz(dc, opnd_create_pc(buf)), 2); /* test non-default count register size (requires addr prefix) */ instr = instr_create_0dst_2src (dc, OP_jecxz, opnd_create_pc(buf), opnd_create_reg(IF_X64_ELSE(REG_ECX, REG_CX))); test_instr_encode(dc, instr, 3); instr = instr_create_1dst_2src (dc, OP_loop, opnd_create_reg(IF_X64_ELSE(REG_ECX, REG_CX)), opnd_create_pc(buf), opnd_create_reg(IF_X64_ELSE(REG_ECX, REG_CX))); test_instr_encode(dc, instr, 3); instr = instr_create_1dst_2src (dc, OP_loope, opnd_create_reg(IF_X64_ELSE(REG_ECX, REG_CX)), opnd_create_pc(buf), opnd_create_reg(IF_X64_ELSE(REG_ECX, REG_CX))); test_instr_encode(dc, instr, 3); instr = instr_create_1dst_2src (dc, OP_loopne, opnd_create_reg(IF_X64_ELSE(REG_ECX, REG_CX)), opnd_create_pc(buf), opnd_create_reg(IF_X64_ELSE(REG_ECX, REG_CX))); test_instr_encode(dc, instr, 3); /* * 0x004ee0b8 a6 cmps %ds:(%esi) %es:(%edi) %esi %edi -> %esi %edi * 0x004ee0b8 67 a6 addr16 cmps %ds:(%si) %es:(%di) %si %di -> %si %di * 0x004ee0b8 66 a7 data16 cmps %ds:(%esi) %es:(%edi) %esi %edi -> %esi %edi * 0x004ee0b8 d7 xlat %ds:(%ebx,%al,1) -> %al * 0x004ee0b8 67 d7 addr16 xlat %ds:(%bx,%al,1) -> %al * 0x004ee0b8 0f f7 c1 maskmovq %mm0 %mm1 -> %ds:(%edi) * 0x004ee0b8 67 0f f7 c1 addr16 maskmovq %mm0 %mm1 -> %ds:(%di) * 0x004ee0b8 66 0f f7 c1 maskmovdqu %xmm0 %xmm1 -> %ds:(%edi) * 0x004ee0b8 67 66 0f f7 c1 addr16 maskmovdqu %xmm0 %xmm1 -> %ds:(%di) */ test_instr_encode(dc, INSTR_CREATE_cmps_1(dc), 1); instr = instr_create_2dst_4src (dc, OP_cmps, opnd_create_reg(IF_X64_ELSE(REG_ESI, REG_SI)), opnd_create_reg(IF_X64_ELSE(REG_EDI, REG_DI)), opnd_create_far_base_disp(SEG_DS, IF_X64_ELSE(REG_ESI, REG_SI), REG_NULL, 0, 0, OPSZ_1), opnd_create_far_base_disp(SEG_ES, IF_X64_ELSE(REG_EDI, REG_DI), REG_NULL, 0, 0, OPSZ_1), opnd_create_reg(IF_X64_ELSE(REG_ESI, REG_SI)), opnd_create_reg(IF_X64_ELSE(REG_EDI, REG_DI))); test_instr_encode(dc, instr, 2); instr = instr_create_2dst_4src (dc, OP_cmps, opnd_create_reg(REG_XSI), opnd_create_reg(REG_XDI), opnd_create_far_base_disp(SEG_DS, REG_XSI, REG_NULL, 0, 0, OPSZ_2), opnd_create_far_base_disp(SEG_ES, REG_XDI, REG_NULL, 0, 0, OPSZ_2), opnd_create_reg(REG_XSI), opnd_create_reg(REG_XDI)); test_instr_encode_and_decode(dc, instr, 2, true/*src*/, 0, OPSZ_2, 2); test_instr_encode(dc, INSTR_CREATE_xlat(dc), 1); instr = instr_create_1dst_1src (dc, OP_xlat, opnd_create_reg(REG_AL), opnd_create_far_base_disp(SEG_DS, IF_X64_ELSE(REG_EBX, REG_BX), REG_AL, 1, 0, OPSZ_1)); test_instr_encode(dc, instr, 2); instr = INSTR_CREATE_maskmovq(dc, opnd_create_reg(REG_MM0), opnd_create_reg(REG_MM1)); test_instr_encode(dc, instr, 3); instr = instr_create_1dst_2src (dc, OP_maskmovq, opnd_create_far_base_disp(SEG_DS, IF_X64_ELSE(REG_EDI, REG_DI), REG_NULL, 0, 0, OPSZ_8), opnd_create_reg(REG_MM0), opnd_create_reg(REG_MM1)); test_instr_encode(dc, instr, 4); instr = INSTR_CREATE_maskmovdqu(dc, opnd_create_reg(REG_XMM0), opnd_create_reg(REG_XMM1)); test_instr_encode(dc, instr, 4); instr = instr_create_1dst_2src (dc, OP_maskmovdqu, opnd_create_far_base_disp(SEG_DS, IF_X64_ELSE(REG_EDI, REG_DI), REG_NULL, 0, 0, OPSZ_16), opnd_create_reg(REG_XMM0), opnd_create_reg(REG_XMM1)); test_instr_encode(dc, instr, 5); /* Test iretw, iretd, iretq (unlike most stack operation iretd (and lretd on AMD) * exist and are the default in 64-bit mode. As such, it has a different size/type * then most other stack operations). Our instr_create routine should match stack * (iretq on 64-bit, iretd on 32-bit). See PR 191977. */ instr = INSTR_CREATE_iret(dc); #ifdef X64 test_instr_encode_and_decode(dc, instr, 2, true /*src*/, 1, OPSZ_40, 40); ASSERT(buf[0] == 0x48); /* check for rex.w prefix */ #else test_instr_encode_and_decode(dc, instr, 1, true /*src*/, 1, OPSZ_12, 12); #endif instr = instr_create_1dst_2src (dc, OP_iret, opnd_create_reg(REG_XSP), opnd_create_reg(REG_XSP), opnd_create_base_disp(REG_XSP, REG_NULL, 0, 0, OPSZ_12)); test_instr_encode_and_decode(dc, instr, 1, true /*src*/, 1, OPSZ_12, 12); instr = instr_create_1dst_2src (dc, OP_iret, opnd_create_reg(REG_XSP), opnd_create_reg(REG_XSP), opnd_create_base_disp(REG_XSP, REG_NULL, 0, 0, OPSZ_6)); test_instr_encode_and_decode(dc, instr, 2, true /*src*/, 1, OPSZ_6, 6); ASSERT(buf[0] == 0x66); /* check for data prefix */ }
static void test_indirect_cti(void *dc) { /* 0x004275f4 ff d1 call %ecx %esp -> %esp (%esp) 0x004275f4 66 ff d1 data16 call %cx %esp -> %esp (%esp) 0x004275f4 67 ff d1 addr16 call %ecx %sp -> %sp (%sp) 0x00427794 ff 19 lcall (%ecx) %esp -> %esp (%esp) 0x00427794 66 ff 19 data16 lcall (%ecx) %esp -> %esp (%esp) 0x00427794 67 ff 1f addr16 lcall (%bx) %sp -> %sp (%sp) */ instr_t *instr; instr = INSTR_CREATE_call_ind(dc, opnd_create_reg(REG_XCX)); test_instr_encode(dc, instr, 2); #ifndef X64 /* only on AMD can we shorten, so we don't test it */ instr = instr_create_2dst_2src(dc, OP_call_ind, opnd_create_reg(REG_XSP), opnd_create_base_disp(REG_XSP, REG_NULL, 0, -2, OPSZ_2), opnd_create_reg(REG_CX), opnd_create_reg(REG_XSP)); test_instr_encode(dc, instr, 3); #endif instr = instr_create_2dst_2src(dc, OP_call_ind, opnd_create_reg(IF_X64_ELSE(REG_ESP, REG_SP)), opnd_create_base_disp(IF_X64_ELSE(REG_ESP, REG_SP), REG_NULL, 0, -(int)sizeof(void*), OPSZ_ret), /* only on AMD can we shorten, so we don't test it */ opnd_create_reg(REG_XCX), opnd_create_reg(IF_X64_ELSE(REG_ESP, REG_SP))); test_instr_encode(dc, instr, 3); /* invalid to have far call go through reg since needs 6 bytes */ instr = INSTR_CREATE_call_far_ind(dc, opnd_create_base_disp(REG_XCX, REG_NULL, 0, 0, OPSZ_6)); test_instr_encode(dc, instr, 2); instr = instr_create_2dst_2src(dc, OP_call_far_ind, opnd_create_reg(REG_XSP), opnd_create_base_disp(REG_XSP, REG_NULL, 0, -4, OPSZ_4), opnd_create_base_disp(REG_XCX, REG_NULL, 0, 0, OPSZ_4), opnd_create_reg(REG_XSP)); test_instr_encode(dc, instr, 3); instr = instr_create_2dst_2src(dc, OP_call_far_ind, opnd_create_reg(IF_X64_ELSE(REG_ESP, REG_SP)), opnd_create_base_disp(IF_X64_ELSE(REG_ESP, REG_SP), REG_NULL, 0, -8, OPSZ_8_rex16_short4), opnd_create_base_disp(IF_X64_ELSE(REG_EBX, REG_BX), REG_NULL, 0, 0, OPSZ_6), opnd_create_reg(IF_X64_ELSE(REG_ESP, REG_SP))); test_instr_encode(dc, instr, 3); /* case 10710: make sure we can encode these guys 0x00428844 0e push %cs %esp -> %esp (%esp) 0x00428844 1e push %ds %esp -> %esp (%esp) 0x00428844 16 push %ss %esp -> %esp (%esp) 0x00428844 06 push %es %esp -> %esp (%esp) 0x00428844 0f a0 push %fs %esp -> %esp (%esp) 0x00428844 0f a8 push %gs %esp -> %esp (%esp) 0x00428844 1f pop %esp (%esp) -> %ds %esp 0x00428844 17 pop %esp (%esp) -> %ss %esp 0x00428844 07 pop %esp (%esp) -> %es %esp 0x00428844 0f a1 pop %esp (%esp) -> %fs %esp 0x00428844 0f a9 pop %esp (%esp) -> %gs %esp */ #ifndef X64 test_instr_encode(dc, INSTR_CREATE_push(dc, opnd_create_reg(SEG_CS)), 1); test_instr_encode(dc, INSTR_CREATE_push(dc, opnd_create_reg(SEG_DS)), 1); test_instr_encode(dc, INSTR_CREATE_push(dc, opnd_create_reg(SEG_SS)), 1); test_instr_encode(dc, INSTR_CREATE_push(dc, opnd_create_reg(SEG_ES)), 1); #endif test_instr_encode(dc, INSTR_CREATE_push(dc, opnd_create_reg(SEG_FS)), 2); test_instr_encode(dc, INSTR_CREATE_push(dc, opnd_create_reg(SEG_GS)), 2); #ifndef X64 test_instr_encode(dc, INSTR_CREATE_pop(dc, opnd_create_reg(SEG_DS)), 1); test_instr_encode(dc, INSTR_CREATE_pop(dc, opnd_create_reg(SEG_SS)), 1); test_instr_encode(dc, INSTR_CREATE_pop(dc, opnd_create_reg(SEG_ES)), 1); #endif test_instr_encode(dc, INSTR_CREATE_pop(dc, opnd_create_reg(SEG_FS)), 2); test_instr_encode(dc, INSTR_CREATE_pop(dc, opnd_create_reg(SEG_GS)), 2); }
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); }
static dr_emit_flags_t event_basic_block(void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) { instr_t *first = instrlist_first(bb); app_pc pc = dr_fragment_app_pc(tag); instr_t *mov1, *mov2; /* We try to avoid register stealing by using "dead" register if possible. * However, technically, a fault could come in and want the original value * of the "dead" register, but that's too corner-case for us. */ reg_id_t reg = bb_find_dead_reg(bb); bool steal = (reg == DR_REG_NULL); if (reg == DR_REG_NULL) reg = DR_REG_XCX; /* randomly use one if no dead reg found */ /* save register if necessary */ if (steal) dr_save_reg(drcontext, bb, first, reg, SPILL_SLOT_1); /* load buffer pointer from TLS field */ MINSERT(bb, first, INSTR_CREATE_mov_ld (drcontext, opnd_create_reg(reg), opnd_create_far_base_disp(tls_seg, DR_REG_NULL, DR_REG_NULL, 0, tls_offs, OPSZ_PTR))); /* store bb's start pc into the buffer */ instrlist_insert_mov_immed_ptrsz(drcontext, (ptr_int_t)pc, OPND_CREATE_MEMPTR(reg, 0), bb, first, &mov1, &mov2); DR_ASSERT(mov1 != NULL); instr_set_ok_to_mangle(mov1, false); if (mov2 != NULL) instr_set_ok_to_mangle(mov2, false); /* update the TLS buffer pointer by incrementing just the bottom 16 bits of * the pointer */ if (bb_aflags_are_dead(bb, first)) { /* if aflags are dead, we use add directly */ MINSERT(bb, first, INSTR_CREATE_add (drcontext, opnd_create_far_base_disp(tls_seg, DR_REG_NULL, DR_REG_NULL, 0, tls_offs, OPSZ_2), OPND_CREATE_INT8(sizeof(app_pc)))); } else { reg_id_t reg_16; #ifdef X64 reg_16 = reg_32_to_16(reg_64_to_32(reg)); #else reg_16 = reg_32_to_16(reg); #endif /* we use lea to avoid aflags save/restore */ MINSERT(bb, first, INSTR_CREATE_lea (drcontext, opnd_create_reg(reg_16), opnd_create_base_disp(reg, DR_REG_NULL, 0, sizeof(app_pc), OPSZ_lea))); MINSERT(bb, first, INSTR_CREATE_mov_st (drcontext, opnd_create_far_base_disp(tls_seg, DR_REG_NULL, DR_REG_NULL, 0, tls_offs, OPSZ_PTR), opnd_create_reg(reg))); } /* restore register if necessary */ if (steal) dr_restore_reg(drcontext, bb, first, reg, SPILL_SLOT_1); return DR_EMIT_DEFAULT; }
static void test_indirect_cti(void *dc) { /* 0x004275f4 ff d1 call %ecx %esp -> %esp (%esp) 0x004275f4 66 ff d1 data16 call %cx %esp -> %esp (%esp) 0x004275f4 67 ff d1 addr16 call %ecx %sp -> %sp (%sp) 0x00427794 ff 19 lcall (%ecx) %esp -> %esp (%esp) 0x00427794 66 ff 19 data16 lcall (%ecx) %esp -> %esp (%esp) 0x00427794 67 ff 1f addr16 lcall (%bx) %sp -> %sp (%sp) */ instr_t *instr; instr = INSTR_CREATE_call_ind(dc, opnd_create_reg(REG_ECX)); test_instr_encode(dc, instr, 2); instr = instr_create_2dst_2src(dc, OP_call_ind, opnd_create_reg(REG_ESP), opnd_create_base_disp(REG_ESP, REG_NULL, 0, 0, OPSZ_2), opnd_create_reg(REG_CX), opnd_create_reg(REG_ESP)); test_instr_encode(dc, instr, 3); instr = instr_create_2dst_2src(dc, OP_call_ind, opnd_create_reg(REG_SP), opnd_create_base_disp(REG_SP, REG_NULL, 0, 0, OPSZ_4_short2), opnd_create_reg(REG_ECX), opnd_create_reg(REG_SP)); test_instr_encode(dc, instr, 3); /* invalid to have far call go through reg since needs 6 bytes */ instr = INSTR_CREATE_call_far_ind(dc, opnd_create_base_disp(REG_ECX, REG_NULL, 0, 0, OPSZ_6)); test_instr_encode(dc, instr, 2); instr = instr_create_2dst_2src(dc, OP_call_far_ind, opnd_create_reg(REG_ESP), opnd_create_base_disp(REG_ESP, REG_NULL, 0, 0, OPSZ_2), opnd_create_base_disp(REG_ECX, REG_NULL, 0, 0, OPSZ_4), opnd_create_reg(REG_ESP)); test_instr_encode(dc, instr, 3); instr = instr_create_2dst_2src(dc, OP_call_far_ind, opnd_create_reg(REG_SP), opnd_create_base_disp(REG_SP, REG_NULL, 0, 0, OPSZ_4_short2), opnd_create_base_disp(REG_BX, REG_NULL, 0, 0, OPSZ_6), opnd_create_reg(REG_SP)); test_instr_encode(dc, instr, 3); /* case 10710: make sure we can encode these guys 0x00428844 0e push %cs %esp -> %esp (%esp) 0x00428844 1e push %ds %esp -> %esp (%esp) 0x00428844 16 push %ss %esp -> %esp (%esp) 0x00428844 06 push %es %esp -> %esp (%esp) 0x00428844 0f a0 push %fs %esp -> %esp (%esp) 0x00428844 0f a8 push %gs %esp -> %esp (%esp) 0x00428844 1f pop %esp (%esp) -> %ds %esp 0x00428844 17 pop %esp (%esp) -> %ss %esp 0x00428844 07 pop %esp (%esp) -> %es %esp 0x00428844 0f a1 pop %esp (%esp) -> %fs %esp 0x00428844 0f a9 pop %esp (%esp) -> %gs %esp */ test_instr_encode(dc, INSTR_CREATE_push(dc, opnd_create_reg(SEG_CS)), 1); test_instr_encode(dc, INSTR_CREATE_push(dc, opnd_create_reg(SEG_DS)), 1); test_instr_encode(dc, INSTR_CREATE_push(dc, opnd_create_reg(SEG_SS)), 1); test_instr_encode(dc, INSTR_CREATE_push(dc, opnd_create_reg(SEG_ES)), 1); test_instr_encode(dc, INSTR_CREATE_push(dc, opnd_create_reg(SEG_FS)), 2); test_instr_encode(dc, INSTR_CREATE_push(dc, opnd_create_reg(SEG_GS)), 2); test_instr_encode(dc, INSTR_CREATE_pop(dc, opnd_create_reg(SEG_DS)), 1); test_instr_encode(dc, INSTR_CREATE_pop(dc, opnd_create_reg(SEG_SS)), 1); test_instr_encode(dc, INSTR_CREATE_pop(dc, opnd_create_reg(SEG_ES)), 1); test_instr_encode(dc, INSTR_CREATE_pop(dc, opnd_create_reg(SEG_FS)), 2); test_instr_encode(dc, INSTR_CREATE_pop(dc, opnd_create_reg(SEG_GS)), 2); }
unsigned int cfi_prepare_for_native_call(void *drcontext, instrlist_t *ilist, instr_t *where) { unsigned int eflags_offs, dstack_offs = 0; instr_t *in = (where == NULL) ? instrlist_last(ilist) : instr_get_prev(where); CFI_ASSERT(drcontext != NULL, "cfi_prepare_for_native_call: drcontext cannot be NULL"); //PRE(ilist, where, INSTR_CREATE_push_imm(dcontext, OPND_CREATE_INT32(0))); //dstack_offs += XSP_SZ; PRE(ilist, where, INSTR_CREATE_pushf(drcontext)); PRE(ilist, where, INSTR_CREATE_push(drcontext, opnd_create_base_disp(REG_XSP, REG_NULL, 0, 0, OPSZ_STACK))); PRE(ilist, where, INSTR_CREATE_and(drcontext, opnd_create_base_disp(REG_XSP, REG_NULL, 0, 0, OPSZ_STACK), OPND_CREATE_INT32(~(EFLAGS_NON_SYSTEM | EFLAGS_IF)))); PRE(ilist, where, INSTR_CREATE_popf(drcontext)); /* dstack_offs += XSP_SZ; eflags_offs = dstack_offs; PRE(ilist, where, INSTR_CREATE_lea(drcontext, opnd_create_reg(REG_XSP), OPND_CREATE_MEM_lea(REG_XSP, REG_NULL, 0, - XMM_SLOTS_SIZE))); PRE(ilist, where, INSTR_CREATE_push(drcontext, opnd_create_reg(REG_R15))); PRE(ilist, where, INSTR_CREATE_push(drcontext, opnd_create_reg(REG_R14))); PRE(ilist, where, INSTR_CREATE_push(drcontext, opnd_create_reg(REG_R13))); PRE(ilist, where, INSTR_CREATE_push(drcontext, opnd_create_reg(REG_R12))); PRE(ilist, where, INSTR_CREATE_push(drcontext, opnd_create_reg(REG_R11))); PRE(ilist, where, INSTR_CREATE_push(drcontext, opnd_create_reg(REG_R10))); PRE(ilist, where, INSTR_CREATE_push(drcontext, opnd_create_reg(REG_R9))); PRE(ilist, where, INSTR_CREATE_push(drcontext, opnd_create_reg(REG_R8))); PRE(ilist, where, INSTR_CREATE_push(drcontext, opnd_create_reg(REG_RAX))); PRE(ilist, where, INSTR_CREATE_push(drcontext, opnd_create_reg(REG_RCX))); PRE(ilist, where, INSTR_CREATE_push(drcontext, opnd_create_reg(REG_RDX))); PRE(ilist, where, INSTR_CREATE_push(drcontext, opnd_create_reg(REG_RBX)));*/ /* we do NOT match pusha xsp value *//* PRE(ilist, where, INSTR_CREATE_push(drcontext, opnd_create_reg(REG_RSP))); PRE(ilist, where, INSTR_CREATE_push(drcontext, opnd_create_reg(REG_RBP))); PRE(ilist, where, INSTR_CREATE_push(drcontext, opnd_create_reg(REG_RSI))); PRE(ilist, where, INSTR_CREATE_push(drcontext, opnd_create_reg(REG_RDI))); dstack_offs += 16*XSP_SZ + XMM_SLOTS_SIZE; PRE(ilist, where, INSTR_CREATE_push(drcontext, opnd_create_base_disp(REG_XSP, REG_NULL, 0, dstack_offs - eflags_offs, OPSZ_STACK))); PRE(ilist, where, INSTR_CREATE_and(drcontext, opnd_create_base_disp(REG_XSP, REG_NULL, 0, 0, OPSZ_STACK), OPND_CREATE_INT32(~(EFLAGS_NON_SYSTEM | EFLAGS_IF)))); PRE(ilist, where, INSTR_CREATE_popf(drcontext));*/ /* now go through and mark inserted instrs as meta */ if (in == NULL) in = instrlist_first(ilist); else in = instr_get_next(in); while (in != where) { instr_set_ok_to_mangle(in, false); in = instr_get_next(in); } return dstack_offs; }
void watchpoint_indirect_call_event(dcontext_t *drcontext, instrlist_t *ilist, instr_t *instr, instr_t *next_instr, bool mangle_calls, uint flags) { opnd_t instr_opnd; reg_id_t base_reg; unsigned long used_registers = 0; app_pc pc; struct memory_operand_modifier ops = {0}; instr_t *begin_instrumenting = INSTR_CREATE_label(drcontext); instr_t *done_instrumenting = INSTR_CREATE_label(drcontext); instr_t *nop = INSTR_CREATE_nop(drcontext); instr_t *emulated; memset(&ops, 0, sizeof(struct memory_operand_modifier)); if(instr_reads_memory(instr)) { instr_opnd = instr_get_src(instr, 0); if (opnd_is_rel_addr(instr_opnd) || opnd_is_abs_addr(instr_opnd)) { }else if (opnd_is_base_disp(instr_opnd)) { for_each_src_operand(instr, &ops, (opnd_callback_t *) memory_src_operand_finder); // base_reg = opnd_get_reg(instr_opnd); switch(ops.found_operand.value.base_disp.base_reg) { case DR_REG_RSP: case DR_REG_ESP: case DR_REG_SP: case DR_REG_RBP: case DR_REG_EBP: case DR_REG_BP: return; default: break; } collect_regs(&used_registers, instr, instr_num_srcs, instr_get_src ); collect_regs(&used_registers, instr, instr_num_dsts, instr_get_dst ); reg_id_t reg_watched_addr = get_next_free_reg(&used_registers); opnd_t opnd_watched_addr = opnd_create_reg(reg_watched_addr); reg_id_t reg_unwatched_addr = get_next_free_reg(&used_registers); opnd_t opnd_unwatched_addr = opnd_create_reg(reg_unwatched_addr); PRE(ilist, instr, begin_instrumenting); PRE(ilist, instr, INSTR_CREATE_push(drcontext, opnd_watched_addr)); PRE(ilist, instr, INSTR_CREATE_push(drcontext, opnd_unwatched_addr)); PRE(ilist, instr, INSTR_CREATE_pushf(drcontext)); PRE(ilist, instr, INSTR_CREATE_lea(drcontext, opnd_watched_addr, opnd_create_base_disp(opnd_get_base(ops.found_operand), opnd_get_index(ops.found_operand), opnd_get_scale(ops.found_operand), opnd_get_disp(ops.found_operand), OPSZ_lea))); PRE(ilist, instr, INSTR_CREATE_mov_imm(drcontext, opnd_unwatched_addr, OPND_CREATE_INT64(WATCHPOINT_INDEX_MASK))); PRE(ilist, instr, INSTR_CREATE_or(drcontext, opnd_unwatched_addr, opnd_watched_addr)); emulated = instr_clone(drcontext, instr); emulated->translation = 0; ops.replacement_operand = opnd_create_base_disp( reg_unwatched_addr, DR_REG_NULL,1, 0 , ops.found_operand.size); for_each_operand(emulated, &ops, (opnd_callback_t *) memory_operand_replacer); PRE(ilist, instr, INSTR_CREATE_popf(drcontext)); PRE(ilist, instr, emulated); PRE(ilist, instr, INSTR_CREATE_pop(drcontext, opnd_unwatched_addr)); PRE(ilist, instr, INSTR_CREATE_pop(drcontext, opnd_watched_addr)); PRE(ilist, instr, INSTR_CREATE_jmp_short(drcontext, opnd_create_instr(done_instrumenting))); pc = instr->translation; instr->translation = 0; // hack! instr_being_modified(instr, false); instr_set_ok_to_mangle(instr, false); if(NULL != pc){ nop->translation = pc + instr->length; } POST(ilist, instr, nop); POST(ilist, instr, done_instrumenting); } } }
/* instrument_instr 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_instr(void *drcontext, instrlist_t *ilist, instr_t *where) { instr_t *instr, *call, *restore; opnd_t opnd1, opnd2; reg_id_t reg1, reg2; drvector_t allowed; per_thread_t *data; app_pc pc; data = drmgr_get_tls_field(drcontext, tls_index); /* Steal two scratch registers. * reg2 must be ECX or RCX for jecxz. */ drreg_init_and_fill_vector(&allowed, false); drreg_set_vector_entry(&allowed, DR_REG_XCX, true); if (drreg_reserve_register(drcontext, ilist, where, &allowed, ®2) != DRREG_SUCCESS || drreg_reserve_register(drcontext, ilist, where, NULL, ®1) != DRREG_SUCCESS) { DR_ASSERT(false); /* cannot recover */ drvector_delete(&allowed); return; } drvector_delete(&allowed); /* The following assembly performs the following instructions * buf_ptr->pc = pc; * buf_ptr->opcode = opcode; * 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); /* Store pc */ 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(ins_ref_t, pc)); instrlist_insert_mov_immed_ptrsz(drcontext, (ptr_int_t) pc, opnd1, ilist, where, NULL, NULL); /* Store opcode */ opnd1 = OPND_CREATE_MEMPTR(reg2, offsetof(ins_ref_t, opcode)); opnd2 = OPND_CREATE_INT32(instr_get_opcode(where)); instr = INSTR_CREATE_mov_st(drcontext, opnd1, opnd2); instrlist_meta_preinsert(ilist, where, instr); /* 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(ins_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 the lea + jecxz trick for better performance. * lea and jecxz won't disturb the eflags, so we won't need * code to save and restore the 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 our generated lean procedure which performs a 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 the 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 scratch registers */ instrlist_meta_preinsert(ilist, where, restore); if (drreg_unreserve_register(drcontext, ilist, where, reg1) != DRREG_SUCCESS || drreg_unreserve_register(drcontext, ilist, where, reg2) != DRREG_SUCCESS) DR_ASSERT(false); }
/* * 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); }