Exemple #1
0
/* 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)));
                }
            }
        }
    }
}
Exemple #2
0
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; 
}
Exemple #3
0
/* 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;
	
}
Exemple #4
0
static void
test_instr_opnds(void *dc)
{
    /* Verbose disasm looks like this:
     * 32-bit:
     *   0x080f1ae0  ff 25 e7 1a 0f 08    jmp    0x080f1ae7
     *   0x080f1ae6  b8 ef be ad de       mov    $0xdeadbeef -> %eax
     *   0x080f1ae0  a0 e6 1a 0f 08       mov    0x080f1ae6 -> %al
     *   0x080f1ae5  b8 ef be ad de       mov    $0xdeadbeef -> %eax
     * 64-bit:
     *   0x00000000006b8de0  ff 25 02 00 00 00    jmp    <rel> 0x00000000006b8de8
     *   0x00000000006b8de6  48 b8 ef be ad de 00 mov    $0x00000000deadbeef -> %rax
     *                       00 00 00
     *   0x00000000006b8de0  8a 05 02 00 00 00    mov    <rel> 0x00000000006b8de8 -> %al
     *   0x00000000006b8de6  48 b8 ef be ad de 00 mov    $0x00000000deadbeef -> %rax
     *                       00 00 00
     */
    instrlist_t *ilist;
    instr_t *tgt, *instr;
    byte *pc;
    short disp;

    ilist = instrlist_create(dc);

    /* test mem instr as ind jmp target */
    tgt = INSTR_CREATE_mov_imm(dc, opnd_create_reg(DR_REG_XAX),
                               opnd_create_immed_int(0xdeadbeef, OPSZ_PTR));
    /* skip rex+opcode */
    disp = IF_X64_ELSE(2,1);
    instrlist_append(ilist, INSTR_CREATE_jmp_ind
                     (dc, opnd_create_mem_instr(tgt, disp, OPSZ_PTR)));
    instrlist_append(ilist, tgt);
    pc = instrlist_encode(dc, ilist, buf, true/*instr targets*/);
    ASSERT(pc != NULL);
    instrlist_clear(dc, ilist);
#if VERBOSE
    pc = disassemble_with_info(dc, buf, STDOUT, true, true);
    pc = disassemble_with_info(dc, pc, STDOUT, true, true);
#endif
    pc = buf;
    instr = instr_create(dc);
    pc = decode(dc, pc, instr);
    ASSERT(pc != NULL);
    ASSERT(instr_get_opcode(instr) == OP_jmp_ind);
#ifdef X64
    ASSERT(opnd_is_rel_addr(instr_get_src(instr, 0)));
    ASSERT(opnd_get_addr(instr_get_src(instr, 0)) == pc + disp);
#else
    ASSERT(opnd_is_base_disp(instr_get_src(instr, 0)));
    ASSERT(opnd_get_base(instr_get_src(instr, 0)) == REG_NULL);
    ASSERT(opnd_get_index(instr_get_src(instr, 0)) == REG_NULL);
    ASSERT(opnd_get_disp(instr_get_src(instr, 0)) == (ptr_int_t)pc + disp);
#endif

    /* test mem instr as TYPE_O */
    tgt = INSTR_CREATE_mov_imm(dc, opnd_create_reg(DR_REG_XAX),
                               opnd_create_immed_int(0xdeadbeef, OPSZ_PTR));
    /* skip rex+opcode */
    disp = IF_X64_ELSE(2,1);
    instrlist_append(ilist, INSTR_CREATE_mov_ld
                     (dc, opnd_create_reg(DR_REG_AL),
                      opnd_create_mem_instr(tgt, disp, OPSZ_1)));
    instrlist_append(ilist, tgt);
    pc = instrlist_encode(dc, ilist, buf, true/*instr targets*/);
    ASSERT(pc != NULL);
    instrlist_clear(dc, ilist);
#if VERBOSE
    pc = disassemble_with_info(dc, buf, STDOUT, true, true);
    pc = disassemble_with_info(dc, pc, STDOUT, true, true);
#endif
    pc = buf;
    instr_reset(dc, instr);
    pc = decode(dc, pc, instr);
    ASSERT(pc != NULL);
    ASSERT(instr_get_opcode(instr) == OP_mov_ld);
#ifdef X64
    ASSERT(opnd_is_rel_addr(instr_get_src(instr, 0)));
    ASSERT(opnd_get_addr(instr_get_src(instr, 0)) == pc + disp);
#else
    ASSERT(opnd_is_base_disp(instr_get_src(instr, 0)));
    ASSERT(opnd_get_base(instr_get_src(instr, 0)) == REG_NULL);
    ASSERT(opnd_get_index(instr_get_src(instr, 0)) == REG_NULL);
    ASSERT(opnd_get_disp(instr_get_src(instr, 0)) == (ptr_int_t)pc + disp);
#endif

    instr_free(dc, instr);
    instrlist_destroy(dc, ilist);
}
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;
}