blob: c13ab17e6278ed65f4c8d385190dce244364b8a7 [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2012-2013 Google, Inc. All rights reserved.
* **********************************************************/
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Google, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
/* Tests the drutil extension */
#include "dr_api.h"
#include "drmgr.h"
#include "drutil.h"
#include <string.h> /* memcpy */
#define CHECK(x, msg) do { \
if (!(x)) { \
dr_fprintf(STDERR, "%s\n", msg); \
dr_abort(); \
} \
} while (0);
static bool verbose;
static int repstr_seen;
#define MAGIC_NOTE 0x9a9b9c9d
dr_instr_label_data_t magic_vals = {
0xdeadbeef, 0xeeeebabe, 0x12345678, 0x8765432
};
static void event_exit(void);
static dr_emit_flags_t event_bb_app2app(void *drcontext, void *tag, instrlist_t *bb,
bool for_trace, bool translating);
static dr_emit_flags_t event_bb_analysis(void *drcontext, void *tag, instrlist_t *bb,
bool for_trace, bool translating,
OUT void **user_data);
static dr_emit_flags_t event_bb_insert(void *drcontext, void *tag, instrlist_t *bb,
instr_t *inst, bool for_trace, bool translating,
void *user_data);
DR_EXPORT void
dr_init(client_id_t id)
{
drmgr_priority_t priority = {sizeof(priority), "drutil-test", NULL, NULL, 0};
bool ok;
drmgr_init();
drutil_init();
dr_register_exit_event(event_exit);
ok = drmgr_register_bb_app2app_event(event_bb_app2app, &priority);
CHECK(ok, "drmgr register bb failed");
ok = drmgr_register_bb_instrumentation_event(event_bb_analysis,
event_bb_insert,
&priority);
CHECK(ok, "drmgr register bb failed");
}
static void
event_exit(void)
{
drutil_exit();
drmgr_exit();
dr_fprintf(STDERR, "all done\n");
if (verbose) {
/* I see 62 for win x64, and 16 for linux x86 */
dr_fprintf(STDERR, "saw %d rep str instrs\n", repstr_seen);
}
}
static bool
instr_is_stringop_loop(instr_t *inst)
{
int opc = instr_get_opcode(inst);
return (opc == OP_rep_ins || opc == OP_rep_outs || opc == OP_rep_movs ||
opc == OP_rep_stos || opc == OP_rep_lods || opc == OP_rep_cmps ||
opc == OP_repne_cmps || opc == OP_rep_scas || opc == OP_repne_scas);
}
static dr_emit_flags_t
event_bb_app2app(void *drcontext, void *tag, instrlist_t *bb,
bool for_trace, bool translating)
{
instr_t *inst;
for (inst = instrlist_first(bb); inst != NULL; inst = instr_get_next(inst)) {
if (instr_is_stringop_loop(inst))
repstr_seen++;
}
/* insert a meta instr to test drutil_expand_rep_string() handling it (i#1055) */
instrlist_meta_preinsert(bb, instrlist_first(bb), INSTR_CREATE_label(drcontext));
if (!drutil_expand_rep_string(drcontext, bb)) {
CHECK(false, "drutil rep expansion failed");
}
return DR_EMIT_DEFAULT;
}
static dr_emit_flags_t
event_bb_analysis(void *drcontext, void *tag, instrlist_t *bb,
bool for_trace, bool translating, OUT void **user_data)
{
/* test label data (i#675) */
instr_t *first = instrlist_first(bb);
if (first != NULL) {
instr_t *l = INSTR_CREATE_label(drcontext);
dr_instr_label_data_t *data = instr_get_label_data_area(l);
CHECK(data != NULL, "failed to get data area");
memcpy(data, &magic_vals, sizeof(*data));
instr_set_note(l, (void *)MAGIC_NOTE);
instrlist_meta_preinsert(bb, first, l);
}
return DR_EMIT_DEFAULT;
}
static void
check_label_data(instrlist_t *bb)
{
instr_t *first = instrlist_first(bb);
if (first != NULL) {
dr_instr_label_data_t *data = instr_get_label_data_area(first);
CHECK(data != NULL, "failed to get data area");
CHECK(instr_is_label(first), "expected label");
CHECK(memcmp(data, &magic_vals, sizeof(*data)) == 0,
"label data was not preserved");
CHECK(instr_get_note(first) == (void *)MAGIC_NOTE,
"label note was not preserved");
}
}
static dr_emit_flags_t
event_bb_insert(void *drcontext, void *tag, instrlist_t *bb, instr_t *instr,
bool for_trace, bool translating, void *user_data)
{
int i;
CHECK(!instr_is_stringop_loop(instr), "rep str conversion missed one");
if (instr_writes_memory(instr)) {
for (i = 0; i < instr_num_dsts(instr); i++) {
if (opnd_is_memory_reference(instr_get_dst(instr, i))) {
dr_save_reg(drcontext, bb, instr, REG_XAX, SPILL_SLOT_1);
dr_save_reg(drcontext, bb, instr, REG_XDX, SPILL_SLOT_2);
/* XXX: should come up w/ some clever way to ensure
* this gets the right address: for now just making sure
* it doesn't crash
*/
drutil_insert_get_mem_addr(drcontext, bb, instr,
instr_get_dst(instr, i), REG_XAX, REG_XDX);
dr_restore_reg(drcontext, bb, instr, REG_XDX, SPILL_SLOT_2);
dr_restore_reg(drcontext, bb, instr, REG_XAX, SPILL_SLOT_1);
}
}
}
check_label_data(bb);
return DR_EMIT_DEFAULT;
}