Beispiel #1
0
/* Check if current instrumentation can be merged into previous aflags
 * (or on ARM, GPR) save/restore inserted by drx_restore_arith_flags.
 * Returns NULL if cannot merge. Otherwise, returns the right insertion point,
 * i.e., DRX_NOTE_AFLAGS_RESTORE_BEGIN label instr.
 *
 * This routine looks for labels inserted by drx_restore_arith_flags,
 * so changes to drx_restore_arith_flags may affect this routine.
 * On ARM the labels are from drx_insert_counter_update.
 */
static instr_t *
merge_prev_drx_spill(instr_t *where, bool aflags)
{
    instr_t *instr;
#ifdef DEBUG
    bool has_sahf = false;
#endif

    if (where == NULL)
        return NULL;
    instr = instr_get_prev(where);
    if (instr == NULL)
        return NULL;
    if (!instr_is_label(instr))
        return NULL;
    /* Check if prev instr is DRX_NOTE_AFLAGS_RESTORE_END.
     * We bail even there is only a label instr in between, which
     * might be a target of internal cti.
     */
    if (instr_get_note(instr) != NOTE_VAL(DRX_NOTE_AFLAGS_RESTORE_END))
        return NULL;

    /* find DRX_NOTE_AFLAGS_RESTORE_BEGIN */
    for (instr  = instr_get_prev(instr);
         instr != NULL;
         instr  = instr_get_prev(instr)) {
        if (instr_is_app(instr)) {
            /* we do not expect any app instr */
            ASSERT(false, "drx aflags restore is corrupted");
            return NULL;
        }
        if (instr_is_label(instr)) {
            if (instr_get_note(instr) == NOTE_VAL(DRX_NOTE_AFLAGS_RESTORE_BEGIN)) {
                ASSERT(!aflags || has_sahf, "missing sahf");
                return instr;
            }
            /* we do not expect any other label instr */
            ASSERT(false, "drx aflags restore is corrupted");
            return NULL;
#ifdef DEBUG
        } else {
            if (instr_get_note(instr) == NOTE_VAL(DRX_NOTE_AFLAGS_RESTORE_SAHF))
                has_sahf = true;
#endif
        }
    }
    return NULL;
}
Beispiel #2
0
static instr_t *
analyze_client_code(void *drcontext, 
                    instrlist_t *ilist,
                    instr_t *where,
                    ref_info_t *ref_info)
{
    instr_t *next, *lea, *and, *cmp, *jcc, *sub;
    opnd_t   ref, opnd;
    ref_cache_t *cache;
    reg_id_t reg;
    int      pos, i;

    next = instr_get_next(where);
    if (next == NULL)
        return NULL;
    
    if (instr_get_opcode(where) != OP_lea)
        return next;

    /* lea [ref] => r1 */
    ref = instr_get_src(where, 0);
    if (!opnd_is_base_disp(ref) || opnd_get_index(ref) != DR_REG_NULL)
        return next;

    lea = where;
    and = next;
    cmp = instr_get_next(and);
    jcc = instr_get_next(cmp);

    if (instr_get_app_pc(and) == NULL   &&
        instr_get_opcode(and) == OP_and &&
        instr_get_app_pc(cmp) == NULL   &&
        instr_get_opcode(cmp) == OP_cmp &&
        instr_get_app_pc(jcc) == NULL   &&
        instr_get_opcode(jcc) == OP_jz) {
        /* find pattern of 
         *   lea [ref] => reg
         *   and 0xffffffff00000000 reg
         *   cmp cache->tag  reg
         *   jz
         */
        opnd  = instr_get_src(cmp, 1);
        cache = opnd_get_addr(opnd) - offsetof(ref_cache_t, tag);
        for (i = 0; i < 10; ) {
            lea = instr_get_next(lea);
            if (!instr_is_label(lea))
                i++;
        }
        DR_ASSERT(instr_get_opcode(lea) == OP_lea);
    } else if (instr_get_app_pc(next) == NULL &&
               instr_get_opcode(next) == OP_sub) {
        opnd  = instr_get_src(next, 0);
        cache = opnd_get_addr(opnd) - offsetof(ref_cache_t, offset);
    } else {
        return next;
    }

    reg = opnd_get_base(ref);
    UMBRA_REG_TO_POS(reg, pos);
    if (ref_info[pos].cache == NULL) {
        ref_info[pos].cache = cache;
    } else {
        sub = instr_get_next(lea);
        DR_ASSERT(instr_get_opcode(sub) == OP_sub);
        while (lea != where) {
            next = instr_get_next(where);
            instrlist_remove(ilist, where);
            instr_destroy(drcontext, where);
            where = next;
        }
        opnd = OPND_CREATE_ABSMEM((void *)(reg_t)ref_info[pos].cache + 
                                  offsetof(ref_cache_t, offset),
                                  OPSZ_PTR);
        instr_set_src(sub, 0, opnd);
        if (proc_info.client.app_unit_bits  > 0 && 
            proc_info.client.shd_unit_bits != 0)
            next = instr_get_next(sub); /* reg & mask => reg */
        if (proc_info.client.orig_addr) {
            next = instr_get_next(next); /* mov reg => r2 */
            next = instr_get_next(next); /* r2 & bit_mask => r2 */
        }
    }

    next = instr_get_next(lea);
    return instr_get_next(next);
}
Beispiel #3
0
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;
}