/* Library offset has to be computed before the probe library is loaded * into memory. Reading it from the map file is one of the easiest ways to * do it. */ unsigned int get_symbol_offset_from_map(const char *map_file, const char *symbol) { const char *pref_addr_str = "Preferred load address is "; unsigned int pref_base, sym_addr, offset = 0xdeadc0de; ssize_t file_sz; file_t fd = INVALID_FILE; char *buf, *temp; fd = dr_open_file(map_file, DR_FILE_READ); if (fd == INVALID_FILE) goto _get_module_offset_exit; /* This seems to be the easiest way to get the size of the file. */ if (!dr_file_seek(fd, 0, DR_SEEK_END)) goto _get_module_offset_exit; file_sz = (ssize_t) dr_file_tell(fd); if (file_sz <= 0) goto _get_module_offset_exit; if (!dr_file_seek(fd, 0, DR_SEEK_SET)) goto _get_module_offset_exit; /* Read the whole file. */ buf = dr_global_alloc(file_sz + 1); if (buf == NULL) goto _get_module_offset_exit; dr_read_file(fd, buf, file_sz); buf[file_sz] = '\0'; /* Locate preferred base & symbol address. */ temp = strstr(buf, pref_addr_str); if (temp != NULL) { pref_base = strtoul(temp + strlen(pref_addr_str), NULL, 16); temp = strstr(buf, symbol); if (temp != NULL) sym_addr = strtoul(temp + strlen(symbol), NULL, 16); offset = sym_addr - pref_base; } dr_global_free(buf, file_sz + 1); _get_module_offset_exit: if (fd != INVALID_FILE) dr_close_file(fd); return offset; }
static void event_exit(void) { /* ensure our file was not closed by the app */ if (!dr_file_seek(file, 0, DR_SEEK_SET)) dr_fprintf(STDERR, "seek error in exit event\n"); dr_close_file(file); dr_fprintf(STDERR, "file separation check\n"); /* i#1213: test float i/o. * Technically we should save fpstate (for detach) but we're not bothering. */ dr_fprintf(STDERR, "float i/o test: %6.5g\n", 3.1415916); dr_fprintf(STDERR, "done\n"); }
/* caller must hold symcache_lock */ static void symcache_write_symfile(const char *modname, mod_cache_t *modcache) { uint i; file_t f; hashtable_t *symtable = &modcache->table; char buf[SYMCACHE_BUFFER_SIZE]; size_t sofar = 0; ssize_t len; size_t bsz = BUFFER_SIZE_ELEMENTS(buf); size_t filesz_loc; char symfile[MAXIMUM_PATH]; char symfile_tmp[MAXIMUM_PATH]; int64 file_size; ASSERT(dr_mutex_self_owns(symcache_lock), "missing symcache lock"); /* if from file, we assume it's a waste of time to re-write file: * the version matched after all, unless we appended to it. */ if (modcache->from_file && !modcache->appended) return; if (symtable->entries == 0) return; /* nothing to write */ /* Open the temp symcache that we will rename. */ symcache_get_filename(modname, symfile, BUFFER_SIZE_ELEMENTS(symfile)); f = INVALID_FILE; i = 0; while (f == INVALID_FILE && i < SYMCACHE_MAX_TMP_TRIES) { dr_snprintf(symfile_tmp, BUFFER_SIZE_ELEMENTS(symfile_tmp), "%s.%04d.tmp", symfile, i); NULL_TERMINATE_BUFFER(symfile_tmp); f = dr_open_file(symfile_tmp, DR_FILE_WRITE_REQUIRE_NEW); i++; } if (f == INVALID_FILE) { NOTIFY("WARNING: Unable to create symcache temp file %s"NL, symfile_tmp); return; } BUFFERED_WRITE(f, buf, bsz, sofar, len, "%s %d\n", SYMCACHE_FILE_HEADER, SYMCACHE_VERSION); /* Leave room for file size for self-consistency check */ filesz_loc = sofar; /* XXX: Assumes that the buffer hasn't been flushed. */ BUFFERED_WRITE(f, buf, bsz, sofar, len, "%"STRINGIFY(SYMCACHE_SIZE_DIGITS)"u,", 0); #ifdef WINDOWS BUFFERED_WRITE(f, buf, bsz, sofar, len, UINT64_FORMAT_STRING","UINT64_FORMAT_STRING"," UINT64_FORMAT_STRING",%u,%u,%lu\n", modcache->module_file_size, modcache->file_version.version, modcache->product_version.version, modcache->checksum, modcache->timestamp, modcache->module_internal_size); #else BUFFERED_WRITE(f, buf, bsz, sofar, len, UINT64_FORMAT_STRING",%u", modcache->module_file_size, modcache->timestamp); # ifdef MACOS BUFFERED_WRITE(f, buf, bsz, sofar, len, ",%u,%u,", modcache->current_version, modcache->compatibility_version); /* For easy sscanf we print as 4 ints */ for (i = 0; i < 4; i++) BUFFERED_WRITE(f, buf, bsz, sofar, len, "%08x,", *(int*)(&modcache->uuid[i*4])); # endif BUFFERED_WRITE(f, buf, bsz, sofar, len, "\n"); #endif BUFFERED_WRITE(f, buf, bsz, sofar, len, "%u\n", modcache->has_debug_info); for (i = 0; i < HASHTABLE_SIZE(symtable->table_bits); i++) { hash_entry_t *he; for (he = symtable->table[i]; he != NULL; he = he->next) { offset_list_t *olist = (offset_list_t *) he->payload; offset_entry_t *e; if (olist == NULL) continue; /* skip symbol in dup entries to save space */ BUFFERED_WRITE(f, buf, bsz, sofar, len, "%s", he->key); e = olist->list; while (e != NULL) { BUFFERED_WRITE(f, buf, bsz, sofar, len, ",0x%x\n", e->offs); e = e->next; } } } /* now update size */ FLUSH_BUFFER(f, buf, sofar); if ((file_size = dr_file_tell(f)) < 0 || dr_snprintf(buf, BUFFER_SIZE_ELEMENTS(buf), "%"STRINGIFY(SYMCACHE_SIZE_DIGITS)"u", (uint)file_size) < 0 || !dr_file_seek(f, filesz_loc, DR_SEEK_SET) || dr_write_file(f, buf, SYMCACHE_SIZE_DIGITS) != SYMCACHE_SIZE_DIGITS) { /* If any steps fail, warn and give up. */ NOTIFY("WARNING: Unable to write symcache file size."NL); dr_close_file(f); dr_delete_file(symfile_tmp); return; } else { LOG(3, "Wrote symcache %s file size %u to pos "SZFMT"\n", modname, (uint)file_size, filesz_loc); ASSERT(strlen(buf) <= SYMCACHE_SIZE_DIGITS, "not enough space for file size"); } dr_close_file(f); if (!dr_rename_file(symfile_tmp, symfile, /*replace*/true)) { NOTIFY_ERROR("WARNING: Failed to rename the symcache file."NL); dr_delete_file(symfile_tmp); } }
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 */