Пример #1
0
static void
event_module_load(void *drcontext, const module_data_t *data, bool loaded)
{
    module_entry_t *entry = NULL;
    module_data_t  *mod;
    int i;
    /* Some apps repeatedly unload and reload the same module,
     * so we will try to re-use the old one.
     */
    ASSERT(data != NULL, "data must not be NULL");
    drvector_lock(&module_table.vector);
    /* Assuming most recently loaded entries are most likely to be unloaded,
     * we iterate the module table in a backward way for better performance.
     */
    for (i = module_table.vector.entries-1; i >= 0; i--) {
        entry = drvector_get_entry(&module_table.vector, i);
        mod   = entry->data;
        if (entry->unload &&
            /* If the same module is re-loaded at the same address,
             * we will try to use the existing entry.
             */
            mod->start       == data->start        &&
            mod->end         == data->end          &&
            mod->entry_point == data->entry_point  &&
#ifdef WINDOWS
            mod->checksum    == data->checksum     &&
            mod->timestamp   == data->timestamp    &&
#endif
            /* If a module w/ no name (there are some) is loaded, we will
             * keep making new entries.
             */
            dr_module_preferred_name(data) != NULL &&
            dr_module_preferred_name(mod)  != NULL &&
            strcmp(dr_module_preferred_name(data),
                   dr_module_preferred_name(mod)) == 0) {
            entry->unload = false;
            break;
        }
        entry = NULL;
    }
    if (entry == NULL) {
        entry = dr_global_alloc(sizeof(*entry));
        entry->id = module_table.vector.entries;
        entry->unload = false;
        entry->data = dr_copy_module_data(data);
        drvector_append(&module_table.vector, entry);
    }
    drvector_unlock(&module_table.vector);
    global_module_cache_add(module_table.cache, entry);
}
Пример #2
0
DR_EXPORT void
dr_init(client_id_t id)
{
    /* Look up start_monitor() and stop_monitor() in the target app.
     * These functions are dummy markers that tell us when to start
     * and stop printing syscalls.
     */
    /* NOTE - we could use dr_module_lookup_by_name, but we use the iterator instead
     * to test it out. */
    dr_module_iterator_t *iter = dr_module_iterator_start();
    while (dr_module_iterator_hasnext(iter)) {
        module_data_t *data = dr_module_iterator_next(iter);
        if (strcmp(dr_module_preferred_name(data), TEST_NAME) == 0) {
            module_handle_t lib = data->handle;
            start_pc = (app_pc)dr_get_proc_address(lib, "start_monitor");
            stop_pc = (app_pc)dr_get_proc_address(lib, "stop_monitor");
        }
        dr_free_module_data(data);
    }
    dr_module_iterator_stop(iter);

    if (start_pc == NULL || stop_pc == NULL) {
        dr_fprintf(STDERR, "ERROR: did not find start/stop markers\n");
    }

    /* Register the BB hook */
    dr_register_bb_event(bb_event);
#ifdef LINUX  /* With early injection, libc won't be loaded until later. */
    dr_register_module_load_event(event_module_load);
#endif
}
Пример #3
0
static void
replace_routine(bool add, const module_data_t *mod,
                app_pc addr, int index)
{
    IF_DEBUG(const char *modname = dr_module_preferred_name(mod);)
    /* look for partial map (i#730) */
    if (addr >= mod->end) {
Пример #4
0
static void
event_module_load(void *drcontext, const module_data_t *info, bool loaded)
{
    uint64 early_inject;

    /* do some more dr_get_proc_address testing */
    if (strncmp(dr_module_preferred_name(info), "libc.", 5) == 0) {
        module_handle_t lib = info->handle;
        dr_export_info_t fn_info;
        dr_fprintf(STDERR, "found libc\n");
        if (dr_get_proc_address(lib, "malloc") == NULL)
            dr_fprintf(STDERR, "ERROR: can't find malloc in libc\n");
        if (dr_get_proc_address(lib, "free") == NULL)
            dr_fprintf(STDERR, "ERROR: can't find free in libc\n");
        if (dr_get_proc_address(lib, "printf") == NULL)
            dr_fprintf(STDERR, "ERROR: can't find printf in libc\n");

        /* i#884: gettimeofday is indirect code on my system, and calling it
         * will crash unless we wait until libc is fully relocated.
         * dr_get_proc_address() wraps the fault in a try/except and returns
         * NULL.  The _ex variant does not, so we use that to test the lookup.
         */
        if (!dr_get_proc_address_ex(lib, "gettimeofday", &fn_info, sizeof(fn_info)))
            dr_fprintf(STDERR, "ERROR: can't find gettimeofday in libc\n");

        dr_unregister_module_load_event(event_module_load);
    }
}
Пример #5
0
/* assuming caller holds the lock */
static void
module_table_entry_print(module_entry_t *entry, file_t log, bool print_all_info)
{
    const char *name;
    module_data_t *data;
    const char *full_path = "<unknown>";
    data = entry->data;
    name = dr_module_preferred_name(data);
    if (data->full_path != NULL && data->full_path[0] != '\0')
        full_path = data->full_path;

    if (print_all_info) {
        dr_fprintf(log, "%3u, "PFX", "PFX", "PFX", %s, %s",
                   entry->id, data->start, data->end, data->entry_point,
                   (name == NULL || name[0] == '\0') ? "<unknown>" : name,
                   full_path);
#ifdef WINDOWS
        dr_fprintf(log, ", 0x%08x, 0x%08x", data->checksum, data->timestamp);
#endif /* WINDOWS */
        dr_fprintf(log, "\n");
    } else {
        dr_fprintf(log, " %u, %llu, %s\n", entry->id,
                   (uint64)(data->end - data->start), full_path);
    }
}
Пример #6
0
static
void module_load_event(void *dcontext, const module_data_t *data, bool loaded)
{
    if (string_match(dr_module_preferred_name(data), "client.flush.exe")) {
        start = data->start;
        end = data->end;
    }
}
Пример #7
0
static bool
module_data_same(const module_data_t *d1, const module_data_t *d2)
{
    if (d1->start == d2->start &&
        d1->end   == d2->end   &&
        d1->entry_point == d2->entry_point &&
#ifdef WINDOWS
        d1->checksum  == d2->checksum  &&
        d1->timestamp == d2->timestamp &&
#endif
        /* treat two modules w/ no name (there are some) as different */
        dr_module_preferred_name(d1) != NULL &&
        dr_module_preferred_name(d2) != NULL &&
        strcmp(dr_module_preferred_name(d1),
               dr_module_preferred_name(d2)) == 0)
        return true;
    return false;
}
Пример #8
0
static void
module_unload_event(void *drcontext, const module_data_t *mod)
{
    if (strstr(dr_module_preferred_name(mod),
               "client.drwrap-test.appdll.") != NULL) {
        bool ok;
        ok = drwrap_replace(addr_replace, NULL, true);
        CHECK(ok, "un-replace failed");
        ok = drwrap_replace_native(addr_replace2, NULL, true, 0, NULL, true);
        CHECK(ok, "un-replace_native failed");
        ok = drwrap_replace_native(addr_replace_callsite, NULL, false, 0, NULL, true);
        CHECK(ok, "un-replace_native failed");

        unwrap_addr(addr_skip_flags, "skip_flags", mod, true, false);
        unwrap_addr(addr_level0, "level0", mod, true, true);
        unwrap_addr(addr_level1, "level1", mod, true, true);
        unwrap_addr(addr_level2, "level2", mod, true, true);
        unwrap_addr(addr_tailcall, "makes_tailcall", mod, true, true);
        unwrap_addr(addr_preonly, "preonly", mod, true, false);
        /* skipme, postonly, and runlots were already unwrapped */

        /* test longjmp */
        unwrap_unwindtest_addr(addr_long0, "long0", mod);
        unwrap_unwindtest_addr(addr_long1, "long1", mod);
        unwrap_unwindtest_addr(addr_long2, "long2", mod);
        unwrap_unwindtest_addr(addr_long3, "long3", mod);
        unwrap_unwindtest_addr(addr_longdone, "longdone", mod);
        drmgr_set_tls_field(drcontext, tls_idx, (void *)(ptr_uint_t)0);
#ifdef WINDOWS
        /* test SEH */
        if (load_count == 1) {
            ok = drwrap_unwrap(addr_long0, wrap_unwindtest_seh_pre,
                               wrap_unwindtest_seh_post);
            CHECK(ok, "unwrap failed");
            ok = drwrap_unwrap(addr_long1, wrap_unwindtest_seh_pre,
                               wrap_unwindtest_seh_post);
            CHECK(ok, "unwrap failed");
            ok = drwrap_unwrap(addr_long2, wrap_unwindtest_seh_pre,
                               wrap_unwindtest_seh_post);
            CHECK(ok, "unwrap failed");
            ok = drwrap_unwrap(addr_long3, wrap_unwindtest_seh_pre,
                               wrap_unwindtest_seh_post);
            CHECK(ok, "unwrap failed");
            ok = drwrap_unwrap(addr_longdone, wrap_unwindtest_seh_pre,
                               wrap_unwindtest_seh_post);
        }
        CHECK(ok, "unwrap failed");
#endif
    }
}
Пример #9
0
static
void module_load_event_perm(void *drcontext, const module_data_t *info, bool loaded)
{
    /* Test i#138 */
    if (info->full_path == NULL || info->full_path[0] == '\0')
        dr_fprintf(STDERR, "ERROR: full_path empty for %s\n", dr_module_preferred_name(info));
#ifdef WINDOWS
    /* We do not expect \\server-style paths for this test */
    else if (info->full_path[0] == '\\' || info->full_path[1] != ':')
        dr_fprintf(STDERR, "ERROR: full_path is not in DOS format: %s\n", info->full_path);
#else
    else if (info->full_path[0] != '/')
        dr_fprintf(STDERR, "ERROR: full_path is not absolute: %s\n", info->full_path);
#endif
}
Пример #10
0
/* Lookup the module containing addr and see if we have a table entry for it */
static table_entry_t *
get_entry_for_address(app_pc addr)
{
    module_data_t *data = dr_lookup_module(addr);
    table_entry_t *entry = NULL;
    if (data != NULL) {
        entry = table;
        while (entry != NULL &&
               _stricmp(dr_module_preferred_name(data), entry->value.module_name) != 0) {
            entry = entry->next;
        }
        dr_free_module_data(data);
    }
    return entry;
}
Пример #11
0
void
symcache_module_unload(void *drcontext, const module_data_t *mod)
{
    mod_cache_t *modcache;
    const char *modname = dr_module_preferred_name(mod);
    if (modname == NULL)
        return; /* don't support caching */
    ASSERT(initialized, "symcache was not initialized");
    dr_mutex_lock(symcache_lock);
    modcache = (mod_cache_t *) hashtable_lookup(&symcache_table, (void *)mod->full_path);
    if (modcache != NULL) {
        symcache_write_symfile(modname, modcache);
        hashtable_remove(&symcache_table, (void *)mod->full_path);
    }
    dr_mutex_unlock(symcache_lock);
}
Пример #12
0
static bool
symcache_module_has_data(const module_data_t *mod, bool require_syms)
{
    mod_cache_t *modcache;
    bool res = false;
    const char *modname = dr_module_preferred_name(mod);
    if (modname == NULL)
        return false; /* don't support caching */
    ASSERT(initialized, "symcache was not initialized");
    dr_mutex_lock(symcache_lock);
    modcache = (mod_cache_t *) hashtable_lookup(&symcache_table, (void *)mod->full_path);
    if (modcache != NULL)
        res = (modcache->table.entries > 0 && (!require_syms || modcache->has_debug_info));
    dr_mutex_unlock(symcache_lock);
    return res;
}
Пример #13
0
bool
symcache_module_is_cached(const module_data_t *mod)
{
    mod_cache_t *modcache;
    bool res = false;
    const char *modname = dr_module_preferred_name(mod);
    if (modname == NULL)
        return false; /* don't support caching */
    ASSERT(initialized, "symcache was not initialized");
    dr_mutex_lock(symcache_lock);
    modcache = (mod_cache_t *) hashtable_lookup(&symcache_table, (void *)mod->full_path);
    if (modcache != NULL)
        res = modcache->table.entries > 0;
    dr_mutex_unlock(symcache_lock);
    return res;
}
Пример #14
0
static bool
symcache_module_save_common(const module_data_t *mod, bool remove)
{
    mod_cache_t *modcache;
    const char *modname = dr_module_preferred_name(mod);
    if (modname == NULL)
        return false; /* don't support caching */
    ASSERT(initialized, "symcache was not initialized");
    dr_mutex_lock(symcache_lock);
    modcache = (mod_cache_t *) hashtable_lookup(&symcache_table, (void *)mod->full_path);
    if (modcache != NULL) {
        symcache_write_symfile(modname, modcache);
        if (remove)
            hashtable_remove(&symcache_table, (void *)mod->full_path);
    }
    dr_mutex_unlock(symcache_lock);
    return true;
}
Пример #15
0
/* If an entry already exists and is 0, replaces it; else adds a new
 * offset for that symbol.
 */
bool
symcache_add(const module_data_t *mod, const char *symbol, size_t offs)
{
    mod_cache_t *modcache;
    const char *modname = dr_module_preferred_name(mod);
    if (modname == NULL)
        return false; /* don't support caching */
    ASSERT(initialized, "symcache was not initialized");
    dr_mutex_lock(symcache_lock);
    modcache = (mod_cache_t *) hashtable_lookup(&symcache_table, (void *)mod->full_path);
    if (modcache == NULL) {
        LOG(2, "%s: there is no cache for %s\n", __FUNCTION__, modname);
        dr_mutex_unlock(symcache_lock);
        return false;
    }
    if (symcache_symbol_add(modname, &modcache->table, symbol, offs) &&
        modcache->from_file)
        modcache->appended = true;
    dr_mutex_unlock(symcache_lock);
    return true;    
}
Пример #16
0
static void
print_address(file_t f, app_pc addr, const char *prefix)
{
    drsym_error_t symres;
    drsym_info_t sym;
    char name[MAX_SYM_RESULT];
    char file[MAXIMUM_PATH];
    module_data_t *data;
    data = dr_lookup_module(addr);
    if (data == NULL) {
        dr_fprintf(f, "%s "PFX" ? ??:0\n", prefix, addr);
        return;
    }
    sym.struct_size = sizeof(sym);
    sym.name = name;
    sym.name_size = MAX_SYM_RESULT;
    sym.file = file;
    sym.file_size = MAXIMUM_PATH;
    symres = drsym_lookup_address(data->full_path, addr - data->start, &sym,
                                  DRSYM_DEFAULT_FLAGS);
    if (symres == DRSYM_SUCCESS || symres == DRSYM_ERROR_LINE_NOT_AVAILABLE) {
        const char *modname = dr_module_preferred_name(data);
        if (modname == NULL)
            modname = "<noname>";
        dr_fprintf(f, "%s "PFX" %s!%s+"PIFX, prefix, addr,
                   modname, sym.name, addr - data->start - sym.start_offs);
        if (symres == DRSYM_ERROR_LINE_NOT_AVAILABLE) {
            dr_fprintf(f, " ??:0\n");
        } else {
            dr_fprintf(f, " %s:%"UINT64_FORMAT_CODE"+"PIFX"\n",
                       sym.file, sym.line, sym.line_offs);
        }
    } else
        dr_fprintf(f, "%s "PFX" ? ??:0\n", prefix, addr);
    dr_free_module_data(data);
}
Пример #17
0
/* assuming caller holds the lock */
static int
module_table_entry_print(module_entry_t *entry, char *buf, size_t size)
{
    const char *name;
    module_data_t *data;
    const char *full_path = "<unknown>";
    int len, total_len = 0;
    data = entry->data;
    name = dr_module_preferred_name(data);
    if (data->full_path != NULL && data->full_path[0] != '\0')
        full_path = data->full_path;

    len = dr_snprintf(buf, size, "%3u, " PFX ", " PFX ", " PFX "",
                      entry->id, data->start, data->end, data->entry_point);
    if (len == -1)
        return -1;
    buf += len;
    total_len += len;
    size -= len;
#ifdef WINDOWS
    len = dr_snprintf(buf, size, ", 0x%08x, 0x%08x",
                      data->checksum, data->timestamp);
    if (len == -1)
        return -1;
    buf += len;
    total_len += len;
    size -= len;
#endif
    len = dr_snprintf(buf, size, ", %s\n", full_path);
    if (len == -1)
        return -1;
    buf += len;
    total_len += len;
    size -= len;
    return total_len;
}
Пример #18
0
static void
event_exit(void)
{
    int i;
    char msg[512];
    int len;
    int j;
    uint64 xmod_xfer = 0;
    uint64 self_xfer = 0;
    for (i = 0; i < num_mods; i++) {
        dr_fprintf(logfile, "module %3d: %s\n", i,
                   dr_module_preferred_name(mod_array[i].info) == NULL ?
                   "<unknown>" : dr_module_preferred_name(mod_array[i].info));
        dr_fprintf(logfile, "%20llu instruction executed\n", mod_cnt[i]);
    }
    if (mod_cnt[UNKNOW_MODULE_IDX] != 0) {
        dr_fprintf(logfile, "unknown modules:\n%20llu instruction executed\n",
                   mod_cnt[UNKNOW_MODULE_IDX]);
    }
    for (i = 0; i < MAX_NUM_MODULES; i++) {
        for (j = 0; j < num_mods; j++) {
            if (xfer_cnt[i][j] != 0) {
                dr_fprintf(logfile, "mod %3d => mod %3d: %8u\n",
                           i, j, xfer_cnt[i][j]);
                if (i == j)
                    self_xfer += xfer_cnt[i][j];
                else
                    xmod_xfer += xfer_cnt[i][j];
            }
        }
    }
    len = dr_snprintf(msg, sizeof(msg)/sizeof(msg[0]),
                      "Instrumentation results:\n"
                      "\t%10llu instructions executed\n"
                      "\t%10llu (%2.3f%%) cross module indirect branches\n"
                      "\t%10llu (%2.3f%%) intra-module indirect branches\n",
                      ins_count,
                      xmod_xfer, 100*(float)xmod_xfer/ins_count,
                      self_xfer, 100*(float)self_xfer/ins_count);
    DR_ASSERT(len > 0);
    NULL_TERMINATE_BUFFER(msg);
#ifdef SHOW_RESULTS
    DISPLAY_STRING(msg);
#endif /* SHOW_RESULTS */
    dr_fprintf(logfile, "%s\n", msg);
    dr_mutex_lock(mod_lock);
    for (i = 0; i < num_mods; i++) {
        DR_ASSERT(mod_array[i].info != NULL);
        dr_free_module_data(mod_array[i].info);
    }
    dr_mutex_unlock(mod_lock);
    dr_mutex_destroy(mod_lock);
    log_file_close(logfile);
    drx_exit();
    if (!drmgr_unregister_bb_instrumentation_event(event_analyze_bb) ||
        !drmgr_unregister_module_load_event(event_module_load) ||
        !drmgr_unregister_module_unload_event(event_module_unload) ||
        drreg_exit() != DRREG_SUCCESS)
        DR_ASSERT(false);
    drmgr_exit();
}
Пример #19
0
void
symcache_module_load(void *drcontext, const module_data_t *mod, bool loaded)
{
    /* look for cache file for this module.
     * fill in hashtable: key is string, value is list of offsets
     */
    mod_cache_t *modcache;
    const char *modname = dr_module_preferred_name(mod);
    file_t f;
    if (modname == NULL)
        return; /* don't support caching */

    /* if smaller than threshold, not worth caching */
    if (mod->end - mod->start < op_modsize_cache_threshold) {
        LOG(1, "%s: module %s too small to cache\n", __FUNCTION__, modname);
        return;
    }

    ASSERT(initialized, "symcache was not initialized");

    /* support initializing prior to module events => called twice */
    dr_mutex_lock(symcache_lock);
    modcache = (mod_cache_t *) hashtable_lookup(&symcache_table,
                                                (void *)mod->full_path);
    dr_mutex_unlock(symcache_lock);
    if (modcache != NULL)
        return;

    modcache = (mod_cache_t *) global_alloc(sizeof(*modcache), HEAPSTAT_HASHTABLE);
    memset(modcache, 0, sizeof(*modcache));
    hashtable_init_ex(&modcache->table, SYMCACHE_MODULE_TABLE_HASH_BITS,
                      HASH_STRING, true/*strdup*/, true/*synch*/,
                      symcache_free_list, NULL, NULL);

    /* store consistency fields */
    f = dr_open_file(mod->full_path, DR_FILE_READ);
    if (f != INVALID_FILE) {
        bool ok = dr_file_size(f, &modcache->module_file_size);
        if (!ok)
            WARN("WARNING: unable to determine size of %s\n", mod->full_path);
        dr_close_file(f);
    } else
        WARN("WARNING: unable to open %s\n", mod->full_path);
#ifdef WINDOWS
    modcache->file_version = mod->file_version;
    modcache->product_version = mod->product_version;
    modcache->checksum = mod->checksum;
    modcache->timestamp = mod->timestamp;
    modcache->module_internal_size = mod->module_internal_size;
#endif

    modcache->modname = drmem_strdup(modname, HEAPSTAT_HASHTABLE);
    modcache->from_file = symcache_read_symfile(mod, modname, modcache);

    dr_mutex_lock(symcache_lock);
    if (!hashtable_add(&symcache_table, (void *)mod->full_path, (void *)modcache)) {
        /* this should be really rare to have dup paths (xref i#729) -- and
         * actually we now have a lookup up above so we should only get here
         * on a race while we let go of the lock
         */
        WARN("WARNING: duplicate module paths: only caching symbols from first\n");
        hashtable_delete(&modcache->table);
        global_free(modcache, sizeof(*modcache), HEAPSTAT_HASHTABLE);
    }
    dr_mutex_unlock(symcache_lock);
}
Пример #20
0
 */
/* Some symbols have multiple offsets.  The majority have just one.
 * Rather than allocate an array and have the caller free it, or use
 * callbacks or an iterator across which we hold our lock, we have the
 * caller pass us an index as a stateless iterator.  The table is
 * unlikely to be changed in between so we avoid complexity there.
 */
bool
symcache_lookup(const module_data_t *mod, const char *symbol, uint idx,
                size_t *offs OUT, uint *num OUT)
{
    offset_list_t *olist;
    offset_entry_t *e;
    mod_cache_t *modcache;
    uint i;
    const char *modname = dr_module_preferred_name(mod);
    if (modname == NULL)
        return false; /* don't support caching */
    ASSERT(initialized, "symcache was not initialized");
    if (offs == NULL || num == NULL) {
        ASSERT(false, "invalid params");
        return false;
    }
    dr_mutex_lock(symcache_lock);
    modcache = (mod_cache_t *) hashtable_lookup(&symcache_table, (void *)mod->full_path);
    if (modcache == NULL) {
        dr_mutex_unlock(symcache_lock);
        return false;
    }
    olist = (offset_list_t *) hashtable_lookup(&modcache->table, (void *)symbol);
    if (olist == NULL) {
Пример #21
0
static void
module_load_event(void *drcontext, const module_data_t *mod, bool loaded)
{
    if (strstr(dr_module_preferred_name(mod),
               "client.drwrap-test.appdll.") != NULL) {
        bool ok;
        instr_t inst;
        app_pc init_pc, pc, next_pc;

        load_count++;
        if (load_count == 2) {
            /* test no-frills */
            drwrap_set_global_flags(DRWRAP_NO_FRILLS);
        }

        addr_replace = (app_pc) dr_get_proc_address(mod->handle, "replaceme");
        CHECK(addr_replace != NULL, "cannot find lib export");
        ok = drwrap_replace(addr_replace, (app_pc) replacewith, false);
        CHECK(ok, "replace failed");

        addr_replace2 = (app_pc) dr_get_proc_address(mod->handle, "replaceme2");
        CHECK(addr_replace2 != NULL, "cannot find lib export");
        ok = drwrap_replace_native(addr_replace2, (app_pc) replacewith2, true/*at entry*/,
                                   0, (void *)(ptr_int_t)DRWRAP_NATIVE_PARAM, false);
        CHECK(ok, "replace_native failed");

        init_pc = (app_pc) dr_get_proc_address(mod->handle, "replace_callsite");
        CHECK(init_pc != NULL, "cannot find lib export");
        /* Find callsite: we assume we'll linearly hit a ret.  We take final call
         * to skip any PIC call.
         */
        instr_init(drcontext, &inst);
        pc = init_pc;
        do {
            instr_reset(drcontext, &inst);
            next_pc = decode(drcontext, pc, &inst);
            if (!instr_valid(&inst))
                break;
            /* if initial jmp, follow it to handle ILT-indirection */
            if (pc == init_pc && instr_is_ubr(&inst))
                next_pc = opnd_get_pc(instr_get_target(&inst));
            else if (instr_is_call(&inst))
                addr_replace_callsite = pc;
            pc = next_pc;
        } while (instr_valid(&inst) && !instr_is_return(&inst));
        CHECK(addr_replace_callsite != NULL, "cannot find lib export");
        ok = drwrap_replace_native(addr_replace_callsite, (app_pc) replace_callsite,
                                   false/*!at entry*/, 0,
                                   (void *)(ptr_int_t)DRWRAP_NATIVE_PARAM, false);
        CHECK(ok, "replace_native failed");
        instr_free(drcontext, &inst);

        wrap_addr(&addr_level0, "level0", mod, true, true);
        wrap_addr(&addr_level1, "level1", mod, true, true);
        wrap_addr(&addr_level2, "level2", mod, true, true);
        wrap_addr(&addr_tailcall, "makes_tailcall", mod, true, true);
        wrap_addr(&addr_skipme, "skipme", mod, true, true);
        wrap_addr(&addr_repeat, "repeatme", mod, true, true);
        wrap_addr(&addr_preonly, "preonly", mod, true, false);
        wrap_addr(&addr_postonly, "postonly", mod, false, true);
        wrap_addr(&addr_runlots, "runlots", mod, false, true);

        /* test longjmp */
        wrap_unwindtest_addr(&addr_long0, "long0", mod);
        wrap_unwindtest_addr(&addr_long1, "long1", mod);
        wrap_unwindtest_addr(&addr_long2, "long2", mod);
        wrap_unwindtest_addr(&addr_long3, "long3", mod);
        wrap_unwindtest_addr(&addr_longdone, "longdone", mod);
        drmgr_set_tls_field(drcontext, tls_idx, (void *)(ptr_uint_t)0);
#ifdef WINDOWS
        /* test SEH */
        /* we can't do this test for no-frills b/c only one wrap per addr */
        if (load_count == 1) {
            ok = drwrap_wrap_ex(addr_long0, wrap_unwindtest_seh_pre,
                                wrap_unwindtest_seh_post,
                                NULL, DRWRAP_UNWIND_ON_EXCEPTION);
            CHECK(ok, "wrap failed");
            ok = drwrap_wrap_ex(addr_long1, wrap_unwindtest_seh_pre,
                                wrap_unwindtest_seh_post,
                                NULL, DRWRAP_UNWIND_ON_EXCEPTION);
            CHECK(ok, "wrap failed");
            ok = drwrap_wrap_ex(addr_long2, wrap_unwindtest_seh_pre,
                                wrap_unwindtest_seh_post,
                                NULL, DRWRAP_UNWIND_ON_EXCEPTION);
            CHECK(ok, "wrap failed");
            ok = drwrap_wrap_ex(addr_long3, wrap_unwindtest_seh_pre,
                                wrap_unwindtest_seh_post,
                                NULL, DRWRAP_UNWIND_ON_EXCEPTION);
            CHECK(ok, "wrap failed");
            ok = drwrap_wrap_ex(addr_longdone, wrap_unwindtest_seh_pre,
                                wrap_unwindtest_seh_post,
                                NULL, DRWRAP_UNWIND_ON_EXCEPTION);
            CHECK(ok, "wrap failed");
        }
#endif
        /* test leaner wrapping */
        if (load_count == 2)
            drwrap_set_global_flags(DRWRAP_NO_FRILLS | DRWRAP_FAST_CLEANCALLS);
        wrap_addr(&addr_skip_flags, "skip_flags", mod, true, false);
    }
}