Beispiel #1
0
static bool
handle_GdiCreateDIBSection(bool pre, void *drcontext, cls_syscall_t *pt)
{
    byte *dib;
    if (pre)
        return true;
    if (safe_read((byte *) syscall_get_param(drcontext, 8), sizeof(dib), &dib)) {
        /* XXX: move this into common/alloc.c since that's currently
         * driving all the known allocs, heap and otherwise
         */
        byte *dib_base;
        size_t dib_size;
        if (dr_query_memory(dib, &dib_base, &dib_size, NULL)) {
            LOG(SYSCALL_VERBOSE, "NtGdiCreateDIBSection created "PFX"-"PFX"\n",
                dib_base, dib_base+dib_size);
            client_handle_mmap(drcontext, dib_base, dib_size,
                               /* XXX: may not be file-backed but treating as
                                * all-defined and non-heap which is what this param
                                * does today.  could do dr_virtual_query().
                                */
                               true/*file-backed*/);
        } else
            WARN("WARNING: unable to query DIB section "PFX"\n", dib);
    } else
        WARN("WARNING: unable to read NtGdiCreateDIBSection param\n");
    /* When passed-in section pointer is NULL, the return value is
     * HBITMAP but doesn't seem to be a real memory address, which is
     * odd, b/c presumably when a section is used it would be a real
     * memory address, right?  The value is typically large so clearly
     * not just a table index.  Xref i#539.
     */
    return true;
}
Beispiel #2
0
void clean_call_mem_information(instr_t * instr, app_pc mem_val, uint write){

	void * drcontext = dr_get_current_drcontext();
	module_data_t * data = dr_lookup_module(instr_get_app_pc(instr));
	uint offset;

	app_pc base_pc;
	size_t size;
	uint prot;
	file_t dump_file;
	char * dump_filename;
	
	DR_ASSERT(data != NULL);
	offset = instr_get_app_pc(instr) - data->start;

	dr_mutex_lock(mutex);

	//if (!md_lookup_bb_in_module(done_head, data->full_path, offset)){

		//md_add_bb_to_module(done_head, data->full_path, offset, MAX_BBS_PER_MODULE, false); 
		dr_query_memory(mem_val, &base_pc, &size, &prot);
		//DEBUG_PRINT("base pc - %x, size - %u, write - %u\n", base_pc, size, write);
		if (write){  /* postpone till the end of the function */
			if (!is_mem_region_present(write_regions, base_pc, size, write_region_size)){
				DEBUG_PRINT("write registered - offset - %x memval %x\n", offset, mem_val);
				add_to_mem_region(write_regions, base_pc, size, &write_region_size);
				DEBUG_PRINT("base pc %x, size %d\n", base_pc, size); 
			}
		}
		else{
			if (!is_mem_region_present(read_regions, base_pc, size, read_region_size)){
				add_to_mem_region(read_regions, base_pc, size, &read_region_size);
				//DEBUG_PRINT("size - %d\n", read_region_size);
				//DEBUG_PRINT("present - %d\n", is_mem_region_present(read_regions, base_pc, size, read_region_size));
				//dr_abort();
				DEBUG_PRINT("read registered - offset - %x memval %x\n", offset,  mem_val);
				DEBUG_PRINT("base pc %x, size %d\n", base_pc, size);
				dump_filename = get_mem_dump_filename(base_pc, size, write,0);
				dump_file = dr_open_file(dump_filename, DR_FILE_WRITE_OVERWRITE);
				DEBUG_PRINT("%s dumping file\n", dump_filename);
				do_mem_dump(dump_file, base_pc, size);
				DEBUG_PRINT("file dumped\n");
				dr_global_free(dump_filename, sizeof(char) * MAX_STRING_LENGTH);
				dr_close_file(dump_file);
			}
		}



	//}

	dr_mutex_unlock(mutex);
	dr_free_module_data(data);


}
Beispiel #3
0
/* WARNING i#262: if you use the cmake binary package, ctest is built
 * without a GNU_STACK section, which causes the linux kernel to set
 * the READ_IMPLIES_EXEC personality flag, which is propagated to
 * children and causes all mmaps to be +x, breaking all these tests
 * that check for mmapped memory to be +rw or +r!
 */
static
void global_test(void)
{
    char *array;
    uint prot;
    dr_fprintf(STDERR, "  testing global memory alloc...");
    array = dr_global_alloc(SIZE);
    write_array(array);
    dr_query_memory((const byte *)array, NULL, NULL, &prot);
    if (prot != get_os_mem_prot(DR_MEMPROT_READ|DR_MEMPROT_WRITE))
        dr_fprintf(STDERR, "[error: prot %d doesn't match rw] ", prot);
    dr_global_free(array, SIZE);
    dr_fprintf(STDERR, "success\n");
}
Beispiel #4
0
static
void nonheap_test(void)
{
    uint prot;
    char *array =
        dr_nonheap_alloc(SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE|DR_MEMPROT_EXEC);
    dr_fprintf(STDERR, "  testing nonheap memory alloc...");
    write_array(array);
    dr_query_memory((const byte *)array, NULL, NULL, &prot);
    if (prot != get_os_mem_prot((DR_MEMPROT_READ|DR_MEMPROT_WRITE|DR_MEMPROT_EXEC)))
        dr_fprintf(STDERR, "[error: prot %d doesn't match rwx] ", prot);
    dr_memory_protect(array, SIZE, DR_MEMPROT_NONE);
    dr_query_memory((const byte *)array, NULL, NULL, &prot);
    if (prot != get_os_mem_prot(DR_MEMPROT_NONE))
        dr_fprintf(STDERR, "[error: prot %d doesn't match none] ", prot);
    dr_memory_protect(array, SIZE, DR_MEMPROT_READ);
    dr_query_memory((const byte *)array, NULL, NULL, &prot);
    if (prot != get_os_mem_prot(DR_MEMPROT_READ))
        dr_fprintf(STDERR, "[error: prot %d doesn't match r] ", prot);
    if (dr_safe_write(array, 1, (const void *) &prot, NULL))
        dr_fprintf(STDERR, "[error: should not be writable] ");
    dr_nonheap_free(array, SIZE);
    dr_fprintf(STDERR, "success\n");
}
Beispiel #5
0
byte * find_prot_edge(const byte *start, uint prot_flag)
{
    uint prot;
    byte *base = (byte *)start;
    byte *last;
    size_t size = 0;

    do {
        last = base + size;
    } while (dr_query_memory(last, &base, &size, &prot) && TESTALL(prot_flag, prot));

    if (last == start)
        dr_fprintf(STDERR, "error in find_prot_edge");

    return last;
}
Beispiel #6
0
size_t
allocation_size(app_pc start, app_pc *base)
{
#ifdef WINDOWS
    app_pc pc = start;
    MEMORY_BASIC_INFORMATION mbi;
    app_pc alloc_base;
    size_t size;

    if (dr_virtual_query(pc, &mbi, sizeof(mbi)) != sizeof(mbi))
        return 0;
    if (mbi.State == MEM_FREE) {
        if (base != NULL)
            *base = NULL;
        return mbi.RegionSize;
    }

    alloc_base = mbi.AllocationBase;
    pc = (app_pc) mbi.BaseAddress + mbi.RegionSize;
    size = pc - alloc_base;

    /* keep querying until reach next alloc base */
    do {
        if (dr_virtual_query(pc, &mbi, sizeof(mbi)) != sizeof(mbi))
            break;
        if (mbi.State == MEM_FREE || mbi.AllocationBase != alloc_base)
            break;
        ASSERT(mbi.RegionSize > 0, "error querying memory");
        size += mbi.RegionSize;
        if (POINTER_OVERFLOW_ON_ADD(pc, mbi.RegionSize))
            break;
        pc += mbi.RegionSize;
    } while (true);
    ASSERT(alloc_base + size > start || alloc_base + size == NULL, "query mem error");
    if (base != NULL)
        *base = alloc_base;
    return size;
#else /* WINDOWS */
    size_t size;
    if (dr_query_memory(start, base, &size, NULL))
        return size;
    else
        return 0;
#endif /* WINDOWS */
}
Beispiel #7
0
static
void raw_alloc_test(void)
{
    uint prot;
    char *array = PREFERRED_ADDR;
    dr_mem_info_t info;
    bool res;
    dr_fprintf(STDERR, "  testing raw memory alloc...");
    res = dr_raw_mem_alloc(PAGE_SIZE, DR_MEMPROT_READ | DR_MEMPROT_WRITE,
                           array) != NULL;
    if (!res) {
        dr_fprintf(STDERR, "[error: fail to alloc at "PFX"]\n", array);
        return;
    }
    write_array(array);
    dr_query_memory((const byte *)array, NULL, NULL, &prot);
    if (prot != get_os_mem_prot(DR_MEMPROT_READ|DR_MEMPROT_WRITE))
        dr_fprintf(STDERR, "[error: prot %d doesn't match rw]\n", prot);
    dr_raw_mem_free(array, PAGE_SIZE);
    dr_query_memory_ex((const byte *)array, &info);
    if (info.prot != DR_MEMPROT_NONE)
        dr_fprintf(STDERR, "[error: prot %d doesn't match none]\n", info.prot);
    dr_fprintf(STDERR, "success\n");
}
Beispiel #8
0
static
void raw_alloc_test(void)
{
    uint prot;
    char *array, *preferred;
    dr_mem_info_t info;
    bool res;
    dr_fprintf(STDERR, "  testing raw memory alloc...");

    /* Find a free region of memory without inadvertently "preloading" it.
     * First probe by allocating 2x the platform allocation alignment unit.
     */
    array = dr_raw_mem_alloc(HINT_ALLOC_SIZE, DR_MEMPROT_READ | DR_MEMPROT_WRITE, NULL);
    /* Then select the second half as the preferred address for the allocation test. */
    preferred = (void *)((ptr_uint_t)array + HINT_OFFSET);
    /* Free the probe allocation. */
    dr_raw_mem_free(array, HINT_ALLOC_SIZE);
    array = preferred;

    /* Now `array` is guaranteed to be available. */
    res = dr_raw_mem_alloc(PAGE_SIZE, DR_MEMPROT_READ | DR_MEMPROT_WRITE,
                           array) != NULL;
    if (!res) {
        dr_fprintf(STDERR, "[error: fail to alloc at "PFX"]\n", array);
        return;
    }
    write_array(array);
    dr_query_memory((const byte *)array, NULL, NULL, &prot);
    if (prot != get_os_mem_prot(DR_MEMPROT_READ|DR_MEMPROT_WRITE))
        dr_fprintf(STDERR, "[error: prot %d doesn't match rw]\n", prot);
    dr_raw_mem_free(array, PAGE_SIZE);
    dr_query_memory_ex((const byte *)array, &info);
    if (info.prot != DR_MEMPROT_NONE)
        dr_fprintf(STDERR, "[error: prot %d doesn't match none]\n", info.prot);
    dr_fprintf(STDERR, "success\n");
}
Beispiel #9
0
static
void custom_test(void)
{
    void *drcontext = dr_get_current_drcontext();
    void *array;
    size_t size;
    uint prot;

    dr_fprintf(STDERR, "  testing custom memory alloc....");

    /* test global */
    array = dr_custom_alloc(NULL, 0, SIZE, 0, NULL);
    write_array(array);
    dr_custom_free(NULL, 0, array, SIZE);

    array = dr_custom_alloc(NULL, DR_ALLOC_CACHE_REACHABLE, SIZE, 0, NULL);
    ASSERT(reachable_from_client(array));
    write_array(array);
    dr_custom_free(NULL, DR_ALLOC_CACHE_REACHABLE, array, SIZE);

    /* test thread-local */
    array = dr_custom_alloc(drcontext, DR_ALLOC_THREAD_PRIVATE, SIZE, 0, NULL);
    write_array(array);
    dr_custom_free(drcontext, DR_ALLOC_THREAD_PRIVATE, array, SIZE);

    array = dr_custom_alloc(drcontext, DR_ALLOC_THREAD_PRIVATE|DR_ALLOC_CACHE_REACHABLE,
                            SIZE, 0, NULL);
    ASSERT(reachable_from_client(array));
    write_array(array);
    dr_custom_free(drcontext, DR_ALLOC_THREAD_PRIVATE|DR_ALLOC_CACHE_REACHABLE,
                   array, SIZE);

    /* test non-heap */
    array = dr_custom_alloc(NULL, DR_ALLOC_NON_HEAP, PAGE_SIZE,
                            DR_MEMPROT_READ|DR_MEMPROT_WRITE, NULL);
    write_array(array);
    dr_custom_free(NULL, DR_ALLOC_NON_HEAP, array, PAGE_SIZE);

    array = dr_custom_alloc(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_FIXED_LOCATION, PAGE_SIZE,
                            DR_MEMPROT_READ|DR_MEMPROT_WRITE, PREFERRED_ADDR);
    ASSERT(array == (void *)PREFERRED_ADDR);
    write_array(array);
    dr_custom_free(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_FIXED_LOCATION, array, PAGE_SIZE);

    array = dr_custom_alloc(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_CACHE_REACHABLE,
                            PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE, NULL);
    ASSERT(reachable_from_client(array));
    write_array(array);
    dr_custom_free(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_CACHE_REACHABLE,
                   array, PAGE_SIZE);

    array = dr_custom_alloc(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_LOW_2GB,
                            PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE, NULL);
#ifdef X64
    ASSERT((ptr_uint_t)array < 0x80000000);
#endif
    write_array(array);
    dr_custom_free(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_LOW_2GB, array, PAGE_SIZE);

    array = dr_custom_alloc(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_NON_DR,
                            PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE, NULL);
    write_array(array);
    dr_custom_free(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_NON_DR,
                   array, PAGE_SIZE);

    array = dr_custom_alloc(NULL, DR_ALLOC_NON_HEAP, PAGE_SIZE,
                            DR_MEMPROT_READ|DR_MEMPROT_WRITE|DR_MEMPROT_EXEC, NULL);
    ASSERT(dr_query_memory((byte *)array, NULL, &size, &prot) &&
           size == PAGE_SIZE && prot == (DR_MEMPROT_READ|DR_MEMPROT_WRITE|
                                         DR_MEMPROT_EXEC));
    write_array(array);
    dr_custom_free(NULL, DR_ALLOC_NON_HEAP, array, PAGE_SIZE);

    dr_fprintf(STDERR, "success\n");
}
Beispiel #10
0
static
void custom_test(void)
{
    void *drcontext = dr_get_current_drcontext();
    void *array, *preferred;
    size_t size;
    uint prot;

    dr_fprintf(STDERR, "  testing custom memory alloc....");

    /* test global */
    array = dr_custom_alloc(NULL, 0, SIZE, 0, NULL);
    write_array(array);
    dr_custom_free(NULL, 0, array, SIZE);

    array = dr_custom_alloc(NULL, DR_ALLOC_CACHE_REACHABLE, SIZE, 0, NULL);
    ASSERT(reachable_from_client(array));
    write_array(array);
    dr_custom_free(NULL, DR_ALLOC_CACHE_REACHABLE, array, SIZE);

    /* test thread-local */
    array = dr_custom_alloc(drcontext, DR_ALLOC_THREAD_PRIVATE, SIZE, 0, NULL);
    write_array(array);
    dr_custom_free(drcontext, DR_ALLOC_THREAD_PRIVATE, array, SIZE);

    array = dr_custom_alloc(drcontext, DR_ALLOC_THREAD_PRIVATE|DR_ALLOC_CACHE_REACHABLE,
                            SIZE, 0, NULL);
    ASSERT(reachable_from_client(array));
    write_array(array);
    dr_custom_free(drcontext, DR_ALLOC_THREAD_PRIVATE|DR_ALLOC_CACHE_REACHABLE,
                   array, SIZE);

    /* test non-heap */
    array = dr_custom_alloc(NULL, DR_ALLOC_NON_HEAP, PAGE_SIZE,
                            DR_MEMPROT_READ|DR_MEMPROT_WRITE, NULL);
    write_array(array);
    dr_custom_free(NULL, DR_ALLOC_NON_HEAP, array, PAGE_SIZE);

    /* Find a free region of memory without inadvertently "preloading" it.
     * First probe by allocating 2x the platform allocation alignment unit.
     */
    array = dr_custom_alloc(NULL, DR_ALLOC_NON_HEAP | DR_ALLOC_NON_DR, HINT_ALLOC_SIZE,
                            DR_MEMPROT_READ|DR_MEMPROT_WRITE, NULL);
    /* Then select the second half as the preferred address for the allocation test. */
    preferred = (void *)((ptr_uint_t)array + HINT_OFFSET);
    /* Free the probe allocation. */
    dr_custom_free(NULL, DR_ALLOC_NON_HEAP | DR_ALLOC_NON_DR, array, HINT_ALLOC_SIZE);

    /* Now `preferred` is guaranteed to be available. */
    array = dr_custom_alloc(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_FIXED_LOCATION, PAGE_SIZE,
                            DR_MEMPROT_READ|DR_MEMPROT_WRITE, preferred);
    ASSERT(array == preferred);
    write_array(array);
    dr_custom_free(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_FIXED_LOCATION, array, PAGE_SIZE);

    array = dr_custom_alloc(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_CACHE_REACHABLE,
                            PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE, NULL);
    ASSERT(reachable_from_client(array));
    write_array(array);
    dr_custom_free(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_CACHE_REACHABLE,
                   array, PAGE_SIZE);

    array = dr_custom_alloc(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_LOW_2GB,
                            PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE, NULL);
#ifdef X64
    ASSERT((ptr_uint_t)array < 0x80000000);
#endif
    write_array(array);
    dr_custom_free(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_LOW_2GB, array, PAGE_SIZE);

    array = dr_custom_alloc(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_NON_DR,
                            PAGE_SIZE, DR_MEMPROT_READ|DR_MEMPROT_WRITE, NULL);
    write_array(array);
    dr_custom_free(NULL, DR_ALLOC_NON_HEAP|DR_ALLOC_NON_DR,
                   array, PAGE_SIZE);

    array = dr_custom_alloc(NULL, DR_ALLOC_NON_HEAP, PAGE_SIZE,
                            DR_MEMPROT_READ|DR_MEMPROT_WRITE|DR_MEMPROT_EXEC, NULL);
    ASSERT(dr_query_memory((byte *)array, NULL, &size, &prot) &&
           size == PAGE_SIZE && prot == (DR_MEMPROT_READ|DR_MEMPROT_WRITE|
                                         DR_MEMPROT_EXEC));
    write_array(array);
    dr_custom_free(NULL, DR_ALLOC_NON_HEAP, array, PAGE_SIZE);

    dr_fprintf(STDERR, "success\n");
}
Beispiel #11
0
static void
handle_clone(void *drcontext, dr_mcontext_t *mc)
{
    uint flags = (uint) dr_syscall_get_param(drcontext, 0);
    app_pc newsp = (app_pc) dr_syscall_get_param(drcontext, 1);

    if (!options.shadowing)
        return;

    /* PR 418629: we need to change the stack from defined (marked when it
     * was allocated) to unaddressable.  Originally we couldn't get the stack
     * bounds in the thread init event (xref PR 395156) so we watch here:
     * we could move this code now but not worth it.
     * FIXME: should we watch SYS_exit and put stack back to defined
     * in case it's re-used?  Seems better to leave it unaddressable
     * since may be more common to have racy accesses we want to flag
     * rather than legitimate re-use?
     */
    if (TEST(CLONE_VM, flags) && newsp != NULL) {
        app_pc stack_base = NULL;
        size_t stack_size;
        /* newsp is TOS */
        ASSERT(options.track_heap, "now relying on -track_heap in general");
        if (is_in_heap_region(newsp)) {
            /* How find base of malloc chunk to then find size?
             * Don't want to store all mallocs in an interval data structure
             * (shown to be slow in PR 535568).
             * Maybe hardcode knowledge of how far from upper address
             * glibc clone() sets newsp?
             * Actually, should just walk shadow memory until hit
             * unaddressable.
             */
            /* FIXME: NEVER TESTED! */
            app_pc pc;
            ssize_t sz;
            /* PR 525807 added an interval tree of "large mallocs" */
            if (malloc_large_lookup(newsp, &pc, (size_t*)&sz)) {
                stack_base = pc;
                stack_size = sz;
            } else {
                /* Should be rare so we just do brute force and slow */
                pc = shadow_prev_dword(newsp, newsp - options.stack_swap_threshold,
                                       SHADOW_UNADDRESSABLE);
                sz = malloc_chunk_size(pc+1);
                if (sz > 0) { /* returns -1 on failure */
                    stack_base = pc + 1;
                    stack_size = sz;
                }
            }
        } else {
            /* On linux a pre-adjacent mmap w/ same prot will be merged into the
             * same region as returned by dr_query_memory() and we'll mark it as
             * unaddressable => many false positives (on FC10, adding a printf
             * to suite/tests/linux/clone.c between the stack mmap and the clone
             * call resulted in the merge).  My solution is to track mmaps and
             * assume a stack will be a single mmap (maybe separate guard page
             * but that should be noprot so ok to not mark unaddress: xref PR
             * 406328).
             */
            if (!mmap_anon_lookup(newsp, &stack_base, &stack_size)) {
                /* Fall back to a query */
                LOG(2, "thread stack "PFX" not in mmap table, querying\n", newsp);
                if (!dr_query_memory(newsp - 1, &stack_base, &stack_size, NULL)) {
                    /* We can estimate the stack end by assuming that clone()
                     * puts less than a page on the stack, but the base is harder:
                     * instead we rely on PR 525807 handle_push_addressable() to
                     * mark the stack unaddr one page at a time.
                     */
                    stack_base = NULL;
                }
            }
        }
        if (stack_base != NULL) {
            LOG(2, "changing thread stack "PFX"-"PFX" -"PFX" to unaddressable\n",
                stack_base, stack_base + stack_size, newsp);
            ASSERT(stack_base + stack_size >= newsp,
                   "new thread's stack alloc messed up");
            if (options.check_stack_bounds) {
                /* assume that above newsp should stay defined */
                shadow_set_range(stack_base, newsp, SHADOW_UNADDRESSABLE);
                check_stack_size_vs_threshold(drcontext, stack_size);
                if (BEYOND_TOS_REDZONE_SIZE > 0) {
                    size_t redzone_sz = BEYOND_TOS_REDZONE_SIZE;
                    if (newsp - BEYOND_TOS_REDZONE_SIZE < stack_base)
                        redzone_sz = newsp - stack_base;
                    shadow_set_range(newsp - redzone_sz, newsp, SHADOW_UNDEFINED);
                }
            }
        } else {
            LOG(0, "ERROR: cannot find bounds of new thread's stack "PFX"\n",
                newsp);
            ASSERT(false, "can't find bounds of thread's stack");
        }
    }
}
Beispiel #12
0
DR_EXPORT
void dr_init(client_id_t id)
{
    char buf[MAXIMUM_PATH];
    int64 pos;
    int i;
    uint prot;
    byte *base_pc;
    size_t size;
    size_t bytes_read, bytes_written;
    byte *edge, *mbuf;
    bool ok;
    byte *f_map;

    /* The Makefile will pass a full absolute path (for Windows and Linux) as the client
     * option to a dummy file in the which we use to exercise the file api routines.
     * TODO - these tests should be a lot more thorough, but the basic functionality
     * is there (should add write tests, file_exists, directory etc. tests). */
    file = dr_open_file(dr_get_options(id), DR_FILE_READ);
    if (file == INVALID_FILE)
        dr_fprintf(STDERR, "Error opening file\n");
    memset(buf, 0, sizeof(buf));
    dr_read_file(file, buf, 10);
    pos = dr_file_tell(file);
    if (pos < 0)
        dr_fprintf(STDERR, "tell error\n");
    dr_fprintf(STDERR, "%s\n", buf);
    if (!dr_file_seek(file, 0, DR_SEEK_SET))
        dr_fprintf(STDERR, "seek error\n");
    memset(buf, 0, sizeof(buf));
    dr_read_file(file, buf, 5);
    dr_fprintf(STDERR, "%s\n", buf);
    for (i = 0; i < 100; i++) buf[i] = 0;
    if (!dr_file_seek(file, pos - 5, DR_SEEK_CUR))
        dr_fprintf(STDERR, "seek error\n");
    memset(buf, 0, sizeof(buf));
    dr_read_file(file, buf, 7);
    dr_fprintf(STDERR, "%s\n", buf);
    if (!dr_file_seek(file, -6, DR_SEEK_END))
        dr_fprintf(STDERR, "seek error\n");
    memset(buf, 0, sizeof(buf));
    /* read "x\nEOF\n" from the data file */
    dr_read_file(file, buf, 6);
    /* check for DOS line ending */
    if (buf[4] == '\r') {
        /* Account for two line endings: the snippet is "x\r\nEOF\r\n".
         * No conversion required--ctest will discard the '\r' when comparing results.
         */
        if (!dr_file_seek(file, -8, DR_SEEK_END))
            dr_fprintf(STDERR, "seek error\n");
            memset(buf, 0, sizeof(buf));
            dr_read_file(file, buf, 8);
    }
    dr_fprintf(STDERR, "%s\n", buf);
#define EXTRA_SIZE 0x60
    size  = PAGE_SIZE + EXTRA_SIZE;
    f_map = dr_map_file(file, &size, 0, NULL, DR_MEMPROT_READ, DR_MAP_PRIVATE);
    if (f_map == NULL || size < (PAGE_SIZE + EXTRA_SIZE))
        dr_fprintf(STDERR, "map error\n");
    /* test unaligned unmap */
    if (!dr_unmap_file(f_map + PAGE_SIZE, EXTRA_SIZE))
        dr_fprintf(STDERR, "unmap error\n");

    /* leave file open and check in exit event that it's still open after
     * app tries to close it
     */
    dr_register_exit_event(event_exit);

    /* Test dr_rename_file. */
    test_dr_rename_delete();

    /* Test the memory query routines */
    dummy_func();
    if (!dr_memory_is_readable((byte *)dummy_func, 1) ||
        !dr_memory_is_readable(read_only_buf+1000, 4000) ||
        !dr_memory_is_readable(writable_buf+1000, 4000)) {
        dr_fprintf(STDERR, "ERROR : dr_memory_is_readable() incorrect results\n");
    }

    if (!dr_query_memory((byte *)dummy_func, &base_pc, &size, &prot))
        dr_fprintf(STDERR, "ERROR : can't find dummy_func mem region\n");
    dr_fprintf(STDERR, "dummy_func is %s%s%s\n", TEST(DR_MEMPROT_READ, prot) ? "r" : "",
              TEST(DR_MEMPROT_WRITE, prot) ? "w" : "",
              TEST(DR_MEMPROT_EXEC, prot) ? "x" : "");
    if (base_pc > (byte *)dummy_func || base_pc + size < (byte *)dummy_func)
        dr_fprintf(STDERR, "dummy_func region mismatch");

    memset(writable_buf, 0, sizeof(writable_buf)); /* strip off write copy */
    if (!dr_query_memory(writable_buf+100, &base_pc, &size, &prot))
        dr_fprintf(STDERR, "ERROR : can't find dummy_func mem region\n");
    dr_fprintf(STDERR, "writable_buf is %s%s%s\n", TEST(DR_MEMPROT_READ, prot) ? "r" : "",
              TEST(DR_MEMPROT_WRITE, prot) ? "w" : "",
#ifdef UNIX
              /* Linux sometimes (probably depends on version and hardware NX
               * support) lists all readable regions as also exectuable in the
               * maps file.  We just skip checking here for Linux to make
               * matching the template file easier. */
              ""
#else
              TEST(DR_MEMPROT_EXEC, prot) ? "x" : ""
#endif
              );
    if (base_pc > writable_buf || base_pc + size < writable_buf)
        dr_fprintf(STDERR, "writable_buf region mismatch\n");
    if (base_pc + size < writable_buf + sizeof(writable_buf))
        dr_fprintf(STDERR, "writable_buf size mismatch "PFX" "PFX" "PFX" "PFX"\n",
                  base_pc, size, writable_buf, sizeof(writable_buf));

    if (!dr_query_memory(read_only_buf+100, &base_pc, &size, &prot))
        dr_fprintf(STDERR, "ERROR : can't find dummy_func mem region\n");
    dr_fprintf(STDERR, "read_only_buf is %s%s\n", TEST(DR_MEMPROT_READ, prot) ? "r" : "",
              TEST(DR_MEMPROT_WRITE, prot) ? "w" : "");
    if (base_pc > read_only_buf || base_pc + size < read_only_buf)
        dr_fprintf(STDERR, "read_only_buf region mismatch");
    if (base_pc + size < read_only_buf + sizeof(read_only_buf))
        dr_fprintf(STDERR, "read_only_buf size mismatch");

    /* test the safe_read functions */
    /* TODO - extend test to cover racy writes and reads (won't work on Linux yet). */
    memset(safe_buf, 0xcd, sizeof(safe_buf));
    if (!dr_safe_read(read_only_buf + 4000, 1000, safe_buf, &bytes_read) ||
        bytes_read != 1000 || !memchk(safe_buf, 0, 1000) || *(safe_buf+1000) != 0xcd) {
        dr_fprintf(STDERR, "ERROR in plain dr_safe_read()\n");
    }
    memset(safe_buf, 0xcd, sizeof(safe_buf));
    /* read_only_buf will be in .rodata on Linux, and can be followed by string
     * constants with the same page protections.  In order to be sure that we're
     * copying zeroes, we map our own memory.
     */
    mbuf = dr_nonheap_alloc(PAGE_SIZE*3, DR_MEMPROT_READ|DR_MEMPROT_WRITE);
    memset(mbuf, 0, PAGE_SIZE*3);
    dr_memory_protect(mbuf + PAGE_SIZE*2, PAGE_SIZE, DR_MEMPROT_NONE);
    edge = find_prot_edge(mbuf, DR_MEMPROT_READ);
    bytes_read = 0xcdcdcdcd;
    if (dr_safe_read(edge - (PAGE_SIZE + 10), PAGE_SIZE+20, safe_buf, &bytes_read) ||
        bytes_read == 0xcdcdcdcd || bytes_read > PAGE_SIZE+10 ||
        !memchk(safe_buf, 0, bytes_read)) {
        dr_fprintf(STDERR, "ERROR in overlap dr_safe_read()\n");
    }
    dr_nonheap_free(mbuf, PAGE_SIZE*3);
    dr_fprintf(STDERR, "dr_safe_read() check\n");

    /* test DR_TRY_EXCEPT */
    DR_TRY_EXCEPT(dr_get_current_drcontext(), {
        ok = false;
        *((int *)4) = 37;
    }, { /* EXCEPT */