static dr_emit_flags_t event_app_instruction(void *drcontext, void *tag, instrlist_t *bb, instr_t *inst, bool for_trace, bool translating, void *user_data) { app_pc pc = dr_fragment_app_pc(tag); reg_id_t reg; /* We need a 2nd scratch reg for several operations on AArch32 and AArch64 only. */ reg_id_t reg2 = DR_REG_NULL; /* We do all our work at the start of the block prior to the first instr */ if (!drmgr_is_first_instr(drcontext, inst)) return DR_EMIT_DEFAULT; /* We need a scratch register */ if (drreg_reserve_register(drcontext, bb, inst, NULL, ®) != DRREG_SUCCESS) { DR_ASSERT(false); /* cannot recover */ return DR_EMIT_DEFAULT; } #ifdef AARCHXX /* We need a second register here, because the drx_buf routines need a scratch reg * for AArch32 and AArch64. */ if (drreg_reserve_register(drcontext, bb, inst, NULL, ®2) != DRREG_SUCCESS) { DR_ASSERT(false); /* cannot recover */ return DR_EMIT_DEFAULT; } #endif /* load buffer pointer from TLS field */ drx_buf_insert_load_buf_ptr(drcontext, buf, bb, inst, reg); /* store bb's start pc into the buffer */ drx_buf_insert_buf_store(drcontext, buf, bb, inst, reg, reg2, OPND_CREATE_INTPTR(pc), OPSZ_PTR, 0); /* Internally this will update the TLS buffer pointer by incrementing just the bottom * 16 bits of the pointer. */ drx_buf_insert_update_buf_ptr(drcontext, buf, bb, inst, reg, reg2, sizeof(app_pc)); if (drreg_unreserve_register(drcontext, bb, inst, reg) != DRREG_SUCCESS) DR_ASSERT(false); #ifdef AARCHXX if (drreg_unreserve_register(drcontext, bb, inst, reg2) != DRREG_SUCCESS) DR_ASSERT(false); #endif return DR_EMIT_DEFAULT; }
static dr_emit_flags_t event_app_instruction(void *drcontext, void *tag, instrlist_t *bb, instr_t *inst, bool for_trace, bool translating, void *user_data) { reg_id_t reg_ptr = IF_X86_ELSE(DR_REG_XDX, TEST_REG); reg_id_t reg_tmp = IF_X86_ELSE(DR_REG_XCX, DR_REG_R3); /* We need a third register on ARM, because updating the buf pointer * requires a second scratch reg. */ reg_id_t scratch = IF_X86_ELSE(reg_tmp, DR_REG_R5); ptr_int_t subtest = (ptr_int_t) user_data; if (!instr_is_label(inst)) return DR_EMIT_DEFAULT; #ifdef X86 scratch = reg_resize_to_opsz(scratch, OPSZ_4); #endif if (subtest == DRX_BUF_TEST_1_C) { /* testing fast circular buffer */ /* test to make sure that on first invocation, the buffer is empty */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_empty, false, 1, OPND_CREATE_INTPTR(circular_fast)); /* load the buf pointer, and then write a garbage element to the buffer */ drx_buf_insert_load_buf_ptr(drcontext, circular_fast, bb, inst, reg_ptr); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_4, 0); drx_buf_insert_update_buf_ptr(drcontext, circular_fast, bb, inst, reg_ptr, reg_tmp, sizeof(int)); /* verify the buffer was written to */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_dirty, false, 2, OPND_CREATE_INTPTR(circular_fast), opnd_create_reg(scratch)); /* fast circular buffer: trigger an overflow */ drx_buf_insert_load_buf_ptr(drcontext, circular_fast, bb, inst, reg_ptr); drx_buf_insert_update_buf_ptr(drcontext, circular_fast, bb, inst, reg_ptr, reg_tmp, CIRCULAR_FAST_SZ - sizeof(int)); /* the buffer is now clean */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_empty, false, 1, OPND_CREATE_INTPTR(circular_fast)); } else if (subtest == DRX_BUF_TEST_2_C) { /* testing slow circular buffer */ /* test to make sure that on first invocation, the buffer is empty */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_empty, false, 1, OPND_CREATE_INTPTR(circular_slow)); /* load the buf pointer, and then write an element to the buffer */ drx_buf_insert_load_buf_ptr(drcontext, circular_slow, bb, inst, reg_ptr); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_4, 0); drx_buf_insert_update_buf_ptr(drcontext, circular_slow, bb, inst, reg_ptr, DR_REG_NULL, sizeof(int)); /* verify the buffer was written to */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_dirty, false, 2, OPND_CREATE_INTPTR(circular_slow), opnd_create_reg(scratch)); /* slow circular buffer: trigger a fault */ drx_buf_insert_load_buf_ptr(drcontext, circular_slow, bb, inst, reg_ptr); drx_buf_insert_update_buf_ptr(drcontext, circular_slow, bb, inst, reg_ptr, DR_REG_NULL, CIRCULAR_SLOW_SZ - sizeof(int)); /* the "trigger" is a write, so we write whatever garbage is in reg_tmp */ drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_4, 0); /* the buffer is now clean */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_empty, false, 1, OPND_CREATE_INTPTR(circular_slow)); } else if (subtest == DRX_BUF_TEST_3_C) { /* testing trace buffer */ /* test to make sure that on first invocation, the buffer is empty */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_empty, false, 1, OPND_CREATE_INTPTR(trace)); /* load the buf pointer, and then write an element to the buffer */ drx_buf_insert_load_buf_ptr(drcontext, trace, bb, inst, reg_ptr); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_4, 0); drx_buf_insert_update_buf_ptr(drcontext, trace, bb, inst, reg_ptr, DR_REG_NULL, sizeof(int)); /* verify the buffer was written to */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_dirty, false, 2, OPND_CREATE_INTPTR(trace), opnd_create_reg(scratch)); /* trace buffer: trigger a fault and verify */ drx_buf_insert_load_buf_ptr(drcontext, trace, bb, inst, reg_ptr); drx_buf_insert_update_buf_ptr(drcontext, trace, bb, inst, reg_ptr, DR_REG_NULL, TRACE_SZ - sizeof(int)); /* the "trigger" is a write, so we write whatever garbage is in reg_tmp */ drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_4, 0); /* the buffer is now clean */ dr_insert_clean_call(drcontext, bb, inst, verify_buffers_empty, false, 1, OPND_CREATE_INTPTR(trace)); } else if (subtest == DRX_BUF_TEST_4_C) { /* test immediate store: 8 bytes (if possible), 4 bytes, 2 bytes and 1 byte */ /* "ABCDEFGH\x00" (x2 for x64) */ drx_buf_insert_load_buf_ptr(drcontext, circular_fast, bb, inst, reg_ptr); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x41, OPSZ_1), OPSZ_1, 0); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x42, OPSZ_1), OPSZ_1, 1); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x4443, OPSZ_2), OPSZ_2, 2); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x48474645, OPSZ_4), OPSZ_4, 4); #ifdef X64 drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x4847464544434241, OPSZ_8), OPSZ_8, 8); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x00, OPSZ_1), OPSZ_1, 17); #else drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x00, OPSZ_1), OPSZ_1, 9); #endif dr_insert_clean_call(drcontext, bb, inst, verify_store, false, 1, OPND_CREATE_INTPTR(circular_fast)); } else if (subtest == DRX_BUF_TEST_5_C) { /* test register store: 8 bytes (if possible), 4 bytes, 2 bytes and 1 byte */ /* "ABCDEFGH\x00" (x2 for x64) */ drx_buf_insert_load_buf_ptr(drcontext, circular_fast, bb, inst, reg_ptr); scratch = reg_resize_to_opsz(scratch, OPSZ_1); MINSERT(bb, inst, XINST_CREATE_load_int (drcontext, opnd_create_reg(scratch), opnd_create_immed_int(0x41, OPSZ_1))); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_1, 0); MINSERT(bb, inst, XINST_CREATE_load_int (drcontext, opnd_create_reg(scratch), opnd_create_immed_int(0x42, OPSZ_1))); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_1, 1); scratch = reg_resize_to_opsz(scratch, OPSZ_2); MINSERT(bb, inst, XINST_CREATE_load_int (drcontext, opnd_create_reg(scratch), opnd_create_immed_int(0x4443, OPSZ_2))); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_2, 2); scratch = reg_resize_to_opsz(scratch, OPSZ_4); #ifdef X86 MINSERT(bb, inst, XINST_CREATE_load_int (drcontext, opnd_create_reg(scratch), opnd_create_immed_int(0x48474645, OPSZ_4))); #else instrlist_insert_mov_immed_ptrsz(drcontext, 0x48474645, opnd_create_reg(reg_resize_to_opsz (scratch, OPSZ_PTR)), bb, inst, NULL, NULL); #endif drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_4, 4); #ifdef X64 scratch = reg_resize_to_opsz(scratch, OPSZ_8); /* only way to reliably move a 64 bit int into a register */ instrlist_insert_mov_immed_ptrsz(drcontext, 0x4847464544434241, opnd_create_reg(scratch), bb, inst, NULL, NULL); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, DR_REG_NULL, opnd_create_reg(scratch), OPSZ_8, 8); drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x00, OPSZ_1), OPSZ_1, 17); #else drx_buf_insert_buf_store(drcontext, circular_fast, bb, inst, reg_ptr, scratch, opnd_create_immed_int(0x00, OPSZ_1), OPSZ_1, 9); #endif dr_insert_clean_call(drcontext, bb, inst, verify_store, false, 1, OPND_CREATE_INTPTR(circular_fast)); } else if (subtest == DRX_BUF_TEST_6_C) { /* Currently, the fast circular buffer does not recommend variable-size * writes, for good reason. We don't test the memcpy operation on the * fast circular buffer. */ /* verify memcpy works on the slow clrcular buffer */ drx_buf_insert_load_buf_ptr(drcontext, circular_slow, bb, inst, reg_ptr); instrlist_insert_mov_immed_ptrsz(drcontext, (ptr_int_t)test_copy, opnd_create_reg(reg_resize_to_opsz (scratch, OPSZ_PTR)), bb, inst, NULL, NULL); drx_buf_insert_buf_memcpy(drcontext, circular_slow, bb, inst, reg_ptr, reg_resize_to_opsz(scratch, OPSZ_PTR), sizeof(test_copy)); /* NULL out the buffer */ drx_buf_insert_load_buf_ptr(drcontext, circular_slow, bb, inst, reg_ptr); instrlist_insert_mov_immed_ptrsz(drcontext, (ptr_int_t)test_null, opnd_create_reg(reg_resize_to_opsz (scratch, OPSZ_PTR)), bb, inst, NULL, NULL); drx_buf_insert_buf_memcpy(drcontext, circular_slow, bb, inst, reg_ptr, reg_resize_to_opsz(scratch, OPSZ_PTR), sizeof(test_null)); /* Unfortunately, we can't just use the check in verify_buffer_empty, because * drx_buf_insert_buf_memcpy() incrememnts the buffer pointer internally, unlike * drx_buf_insert_buf_store(). We simply check that the buffer was NULLed out. */ dr_insert_clean_call(drcontext, bb, inst, (void *)verify_buffers_nulled, false, 1, OPND_CREATE_INTPTR(circular_slow)); /* verify memcpy works on the trace buffer */ drx_buf_insert_load_buf_ptr(drcontext, trace, bb, inst, reg_ptr); instrlist_insert_mov_immed_ptrsz(drcontext, (ptr_int_t)test_copy, opnd_create_reg(reg_resize_to_opsz (scratch, OPSZ_PTR)), bb, inst, NULL, NULL); drx_buf_insert_buf_memcpy(drcontext, trace, bb, inst, reg_ptr, reg_resize_to_opsz(scratch, OPSZ_PTR), sizeof(test_copy)); /* NULL out the buffer */ drx_buf_insert_load_buf_ptr(drcontext, trace, bb, inst, reg_ptr); instrlist_insert_mov_immed_ptrsz(drcontext, (ptr_int_t)test_null, opnd_create_reg(reg_resize_to_opsz (scratch, OPSZ_PTR)), bb, inst, NULL, NULL); drx_buf_insert_buf_memcpy(drcontext, trace, bb, inst, reg_ptr, reg_resize_to_opsz(scratch, OPSZ_PTR), sizeof(test_null)); /* verify buffer was NULLed */ dr_insert_clean_call(drcontext, bb, inst, (void *)verify_buffers_nulled, false, 1, OPND_CREATE_INTPTR(trace)); } return DR_EMIT_DEFAULT; }
static reg_id_t instrument_mem(void *drcontext, instrlist_t *ilist, instr_t *where, opnd_t ref) { reg_id_t reg_ptr, reg_tmp, reg_addr; ushort type, size; bool ok; if (drreg_reserve_register(drcontext, ilist, where, NULL, ®_tmp) != DRREG_SUCCESS) { DR_ASSERT(false); return DR_REG_NULL; } if (drreg_reserve_register(drcontext, ilist, where, NULL, ®_ptr) != DRREG_SUCCESS) { DR_ASSERT(false); return DR_REG_NULL; } /* i#2449: In the situation that instrument_post_write, instrument_mem and ref all * have the same register reserved, drutil_insert_get_mem_addr will compute the * address of an operand using an incorrect register value, as drreg will elide the * save/restore. */ if (opnd_uses_reg(ref, reg_tmp) && drreg_get_app_value(drcontext, ilist, where, reg_tmp, reg_tmp) != DRREG_SUCCESS) { DR_ASSERT(false); return DR_REG_NULL; } if (opnd_uses_reg(ref, reg_ptr) && drreg_get_app_value(drcontext, ilist, where, reg_ptr, reg_ptr) != DRREG_SUCCESS) { DR_ASSERT(false); return DR_REG_NULL; } /* We use reg_ptr as scratch to get addr. Note we do this first as reg_ptr or reg_tmp * may be used in ref. */ ok = drutil_insert_get_mem_addr(drcontext, ilist, where, ref, reg_tmp, reg_ptr); DR_ASSERT(ok); drx_buf_insert_load_buf_ptr(drcontext, trace_buffer, ilist, where, reg_ptr); /* inserts memref addr */ drx_buf_insert_buf_store(drcontext, trace_buffer, ilist, where, reg_ptr, DR_REG_NULL, opnd_create_reg(reg_tmp), OPSZ_PTR, offsetof(mem_ref_t, addr)); if (IF_AARCHXX_ELSE(true, false)) { /* At this point we save the write address for later, because reg_tmp's value * will get clobbered on ARM. */ if (drreg_reserve_register(drcontext, ilist, where, NULL, ®_addr) != DRREG_SUCCESS) { DR_ASSERT(false); return DR_REG_NULL; } MINSERT(ilist, where, XINST_CREATE_move(drcontext, opnd_create_reg(reg_addr), opnd_create_reg(reg_tmp))); } /* inserts type */ type = (ushort)instr_get_opcode(where); drx_buf_insert_buf_store(drcontext, trace_buffer, ilist, where, reg_ptr, reg_tmp, OPND_CREATE_INT16(type), OPSZ_2, offsetof(mem_ref_t, type)); /* inserts size */ size = (ushort)drutil_opnd_mem_size_in_bytes(ref, where); drx_buf_insert_buf_store(drcontext, trace_buffer, ilist, where, reg_ptr, reg_tmp, OPND_CREATE_INT16(size), OPSZ_2, offsetof(mem_ref_t, size)); drx_buf_insert_update_buf_ptr(drcontext, trace_buffer, ilist, where, reg_ptr, DR_REG_NULL, sizeof(mem_ref_t)); if (instr_is_call(where)) { app_pc pc; /* Note that on ARM the call instruction writes only to the link register, so * we would never even get into instrument_mem() on ARM if this was a call. */ IF_AARCHXX(DR_ASSERT(false)); /* We simulate the call instruction's written memory by writing the next app_pc * to the written buffer, since we can't do this after the call has happened. */ drx_buf_insert_load_buf_ptr(drcontext, write_buffer, ilist, where, reg_ptr); pc = decode_next_pc(drcontext, instr_get_app_pc(where)); /* note that for a circular buffer, we don't need to specify a scratch register */ drx_buf_insert_buf_store(drcontext, trace_buffer, ilist, where, reg_ptr, DR_REG_NULL, OPND_CREATE_INTPTR((ptr_int_t)pc), OPSZ_PTR, 0); drx_buf_insert_update_buf_ptr(drcontext, write_buffer, ilist, where, reg_ptr, reg_tmp, sizeof(app_pc)); /* we don't need to persist reg_tmp to the next instruction */ if (drreg_unreserve_register(drcontext, ilist, where, reg_tmp) != DRREG_SUCCESS) DR_ASSERT(false); reg_tmp = DR_REG_NULL; } else if (IF_AARCHXX_ELSE(true, false)) { /* Now reg_tmp has the address of the write again. */ MINSERT(ilist, where, XINST_CREATE_move(drcontext, opnd_create_reg(reg_tmp), opnd_create_reg(reg_addr))); if (drreg_unreserve_register(drcontext, ilist, where, reg_addr) != DRREG_SUCCESS) DR_ASSERT(false); } if (drreg_unreserve_register(drcontext, ilist, where, reg_ptr) != DRREG_SUCCESS) DR_ASSERT(false); return reg_tmp; }