/* 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); } }
/* Sets modcache->has_debug_info. * No lock is needed as we assume the caller hasn't exposed modcache outside this * thread yet. */ static bool symcache_read_symfile(const module_data_t *mod, const char *modname, mod_cache_t *modcache) { hashtable_t *symtable = &modcache->table; bool res = false; const char *line, *next_line; char symbol[MAX_SYMLEN]; size_t offs; uint64 map_size; size_t actual_size; bool ok; void *map = NULL; char symfile[MAXIMUM_PATH]; file_t f; symcache_get_filename(modname, symfile, BUFFER_SIZE_ELEMENTS(symfile)); f = dr_open_file(symfile, DR_FILE_READ); if (f == INVALID_FILE) goto symcache_read_symfile_done; LOG(2, "processing symbol cache file for %s\n", modname); /* we avoid having to do our own buffering by just mapping the whole file */ ok = dr_file_size(f, &map_size); if (ok) { actual_size = (size_t) map_size; ASSERT(actual_size == map_size, "file size too large"); map = dr_map_file(f, &actual_size, 0, NULL, DR_MEMPROT_READ, 0); } if (!ok || map == NULL || actual_size < map_size) { NOTIFY_ERROR("Error mapping symcache file for %s"NL, modname); goto symcache_read_symfile_done; } if (strncmp((char *)map, SYMCACHE_FILE_HEADER, strlen(SYMCACHE_FILE_HEADER)) != 0) { WARN("WARNING: symbol cache file is corrupted\n"); goto symcache_read_symfile_done; } /* i#1057: We use dr_sscanf() because sscanf() from ntdll will call strlen() * and read off the end of the mapped file if it doesn't hit a null. */ if (dr_sscanf((char *)map + strlen(SYMCACHE_FILE_HEADER) + 1, "%d", (uint *)&offs) != 1 || /* neither forward nor backward compatible */ offs != SYMCACHE_VERSION) { WARN("WARNING: symbol cache file has wrong version\n"); goto symcache_read_symfile_done; } line = strchr((char *) map, '\n'); if (line != NULL) line++; if (line != NULL) { /* Module consistency checks */ uint cache_file_size; uint64 module_file_size; uint timestamp; #ifdef WINDOWS version_number_t file_version; version_number_t product_version; uint checksum; size_t module_internal_size; if (dr_sscanf(line, "%u,"UINT64_FORMAT_STRING","UINT64_FORMAT_STRING"," UINT64_FORMAT_STRING",%u,%u,%lu", &cache_file_size, &module_file_size, &file_version.version, &product_version.version, &checksum, ×tamp, &module_internal_size) != 7) { WARN("WARNING: %s symbol cache file has bad consistency header\n", modname); goto symcache_read_symfile_done; } if (module_file_size != modcache->module_file_size || file_version.version != modcache->file_version.version || product_version.version != modcache->product_version.version || checksum != modcache->checksum || timestamp != modcache->timestamp || module_internal_size != modcache->module_internal_size) { LOG(1, "module version mismatch: %s symbol cache file is stale\n", modname); LOG(2, "\t"UINT64_FORMAT_STRING" vs "UINT64_FORMAT_STRING", " UINT64_FORMAT_STRING" vs "UINT64_FORMAT_STRING", " UINT64_FORMAT_STRING" vs "UINT64_FORMAT_STRING", " "%u vs %u, %u vs %u, %lu vs %lu\n", module_file_size, modcache->module_file_size, file_version.version, modcache->file_version.version, product_version.version, modcache->product_version.version, checksum, modcache->checksum, timestamp, modcache->timestamp, module_internal_size, modcache->module_internal_size); goto symcache_read_symfile_done; } #elif defined(LINUX) if (dr_sscanf(line, "%u,"UINT64_FORMAT_STRING",%u", &cache_file_size, &module_file_size, ×tamp) != 3) { WARN("WARNING: %s symbol cache file has bad consistency header\n", modname); goto symcache_read_symfile_done; } if (module_file_size != modcache->module_file_size || timestamp != modcache->timestamp) { LOG(1, "module version mismatch: %s symbol cache file is stale\n", modname); goto symcache_read_symfile_done; } #elif defined(MACOS) uint current_version; uint compatibility_version; byte uuid[16]; /* XXX: if dr_sscanf supported %n maybe we could split these into * separate scans on the same string and share code w/ Linux. */ if (dr_sscanf(line, "%u,"UINT64_FORMAT_STRING",%u,%u,%u,%x,%x,%x,%x", &cache_file_size, &module_file_size, ×tamp, ¤t_version, &compatibility_version, (uint*)(&uuid[0]), (uint*)(&uuid[4]), (uint*)(&uuid[8]), (uint*)(&uuid[12])) != 9) { WARN("WARNING: %s symbol cache file has bad consistency header B\n", modname); goto symcache_read_symfile_done; } if (current_version != modcache->current_version || compatibility_version != modcache->compatibility_version || memcmp(uuid, modcache->uuid, sizeof(uuid)) != 0) { LOG(1, "module version mismatch: %s symbol cache file is stale\n", modname); goto symcache_read_symfile_done; } #endif /* We could go further w/ CRC or even MD5 but not worth it for dev tool */ if (cache_file_size != (uint)map_size) { WARN("WARNING: %s symbol cache file is corrupted: map=%d vs file=%d\n", modname, (uint)map_size, cache_file_size); goto symcache_read_symfile_done; } } line = strchr(line, '\n'); if (line != NULL) line++; if (line != NULL) { uint has_debug_info; if (dr_sscanf(line, "%u", &has_debug_info) != 1) { WARN("WARNING: %s symbol cache file has bad consistency header\n", modname); goto symcache_read_symfile_done; } if (has_debug_info) { /* We assume that the current availability of debug info doesn't matter */ modcache->has_debug_info = true; } else { /* We delay the costly check for symbols until we've read the symcache * b/c if its entry indicates symbols we don't need to look */ if (module_has_symbols(mod)) { LOG(1, "module now has debug info: %s symbol cache is stale\n", modname); goto symcache_read_symfile_done; } } } line = strchr(line, '\n'); if (line != NULL) line++; symbol[0] = '\0'; for (; line != NULL && line < ((char *)map) + map_size; line = next_line) { const char *comma = strchr(line, ','); const char *newline = strchr(line, '\n'); size_t symlen = (comma != NULL ? comma - line : 0); if (newline == NULL) { next_line = ((char *)map) + map_size + 1; /* handle EOF w/o trailing \n */ } else { next_line = newline + 1; } if (symlen > 0 && symlen < MAX_SYMLEN) { strncpy(symbol, line, symlen); symbol[symlen] = '\0'; } if (comma != NULL && symlen < MAX_SYMLEN && symbol[0] != '\0' && dr_sscanf(comma, ",0x%x", (uint *)&offs) == 1) { #ifdef WINDOWS /* Guard against corrupted files that cause DrMem to crash (i#1465) */ if (offs >= modcache->module_internal_size) { /* This one we want to know about */ NOTIFY("SYMCACHE ERROR: %s file has too-large entry "PIFX" for %s"NL, modname, offs, symbol); goto symcache_read_symfile_done; } #endif symcache_symbol_add(modname, symtable, symbol, offs); } else { WARN("WARNING: malformed symbol cache line \"%.*s\"\n", next_line - line - 1, line); /* We abort in case there were two dueling writes to the file * and it somehow got past the self-consistency check, * putting a header in the middle of the file, and we can't * trust subsequent lines since they may belong to a different * version of the module */ break; /* res should still be true */ } } res = true; symcache_read_symfile_done: if (map != NULL) dr_unmap_file(map, actual_size); if (f != INVALID_FILE) dr_close_file(f); if (!res) modcache->has_debug_info = module_has_symbols(mod); return res; }
static bool symcache_read_symfile(const module_data_t *mod, const char *modname, mod_cache_t *modcache) { hashtable_t *symtable = &modcache->table; bool res = false; const char *line, *next_line; char symbol[MAX_SYMLEN]; size_t offs; uint64 map_size; size_t actual_size; bool ok; void *map = NULL; char symfile[MAXIMUM_PATH]; file_t f; symcache_get_filename(modname, symfile, BUFFER_SIZE_ELEMENTS(symfile)); f = dr_open_file(symfile, DR_FILE_READ); if (f == INVALID_FILE) return res; LOG(2, "processing symbol cache file for %s\n", modname); /* we avoid having to do our own buffering by just mapping the whole file */ ok = dr_file_size(f, &map_size); if (ok) { actual_size = (size_t) map_size; ASSERT(actual_size == map_size, "file size too large"); map = dr_map_file(f, &actual_size, 0, NULL, DR_MEMPROT_READ, 0); } if (!ok || map == NULL || actual_size < map_size) { NOTIFY_ERROR("Error mapping symcache file for %s"NL, modname); goto symcache_read_symfile_done; } if (strncmp((char *)map, SYMCACHE_FILE_HEADER, strlen(SYMCACHE_FILE_HEADER)) != 0) { WARN("WARNING: symbol cache file is corrupted\n"); goto symcache_read_symfile_done; } if (sscanf((char *)map + strlen(SYMCACHE_FILE_HEADER) + 1, "%d", &offs) != 1 || /* neither forward nor backward compatible */ offs != SYMCACHE_VERSION) { WARN("WARNING: symbol cache file has wrong version\n"); goto symcache_read_symfile_done; } line = strchr((char *) map, '\n'); if (line != NULL) line++; if (line != NULL) { /* Module consistency checks */ uint cache_file_size; uint64 module_file_size; #ifdef WINDOWS version_number_t file_version; version_number_t product_version; uint checksum; uint timestamp; size_t module_internal_size; if (sscanf(line, " %u,"UINT64_FORMAT_STRING","UINT64_FORMAT_STRING"," UINT64_FORMAT_STRING",%u,%u,%lu", &cache_file_size, &module_file_size, &file_version.version, &product_version.version, &checksum, ×tamp, &module_internal_size) != 7) { WARN("WARNING: %s symbol cache file has bad consistency header\n", modname); goto symcache_read_symfile_done; } if (module_file_size != modcache->module_file_size || file_version.version != modcache->file_version.version || product_version.version != modcache->product_version.version || checksum != modcache->checksum || timestamp != modcache->timestamp || module_internal_size != modcache->module_internal_size) { LOG(1, "module version mismatch: %s symbol cache file is stale\n", modname); LOG(2, "\t"UINT64_FORMAT_STRING" vs "UINT64_FORMAT_STRING", " UINT64_FORMAT_STRING" vs "UINT64_FORMAT_STRING", " UINT64_FORMAT_STRING" vs "UINT64_FORMAT_STRING", " "%u vs %u, %u vs %u, %lu vs %lu\n", module_file_size, modcache->module_file_size, file_version.version, modcache->file_version.version, product_version.version, modcache->product_version.version, checksum, modcache->checksum, timestamp, modcache->timestamp, module_internal_size, modcache->module_internal_size); goto symcache_read_symfile_done; } #else if (sscanf(line, "%u,"UINT64_FORMAT_STRING, &cache_file_size, &module_file_size) != 2) { WARN("WARNING: %s symbol cache file has bad consistency header\n", modname); goto symcache_read_symfile_done; } if (module_file_size != modcache->module_file_size) { LOG(1, "module version mismatch: %s symbol cache file is stale\n", modname); goto symcache_read_symfile_done; } #endif /* We could go further w/ CRC or even MD5 but not worth it for dev tool */ if (cache_file_size != (uint)map_size) { WARN("WARNING: %s symbol cache file is corrupted\n", modname); goto symcache_read_symfile_done; } } line = strchr(line, '\n'); if (line != NULL) line++; symbol[0] = '\0'; for (; line != NULL && line < ((char *)map) + map_size; line = next_line) { const char *newline = strchr(line, '\n'); if (newline == NULL) { next_line = ((char *)map) + map_size + 1; /* handle EOF w/o trailing \n */ } else { next_line = newline + 1; } if (sscanf(line, "%"MAX_SYMLEN_MINUS_1_STR"[^,],0x%x", symbol, &offs) == 2) { symcache_symbol_add(modname, symtable, symbol, offs); } else if (symbol[0] != '\0' && sscanf(line, ",0x%x", &offs) == 1) { /* duplicate entries are allowed to not list the symbol, to save * space in the file (mainly for post-call caching i#669) */ symcache_symbol_add(modname, symtable, symbol, offs); } else { WARN("WARNING: malformed symbol cache line \"%.*s\"\n", next_line - line - 1, line); /* We abort in case there were two dueling writes to the file * and it somehow got past the self-consistency check, * putting a header in the middle of the file, and we can't * trust subsequent lines since they may belong to a different * version of the module */ break; /* res should still be true */ } } res = true; symcache_read_symfile_done: if (map != NULL) dr_unmap_file(map, actual_size); if (f != INVALID_FILE) dr_close_file(f); return res; }