/* see win32/tls.c test for alt. method of starting a detach, this way is preferable * since we may at some point disallow the process from detaching itself */ void detach() { char buf[2*MAX_PATH] = "\""; char *tools = getenv("DYNAMORIO_WINTOOLS"); int size; STARTUPINFO sinfo = {sizeof(sinfo), NULL, "", 0, 0, 0, 0, 0, 0, 0, 0, STARTF_USESTDHANDLES, 0, 0, NULL, GetStdHandle(STD_INPUT_HANDLE), GetStdHandle(STD_OUTPUT_HANDLE), GetStdHandle(STD_ERROR_HANDLE)}; PROCESS_INFORMATION pinfo; strncat(buf, tools == NULL ? "" : tools, BUFFER_ROOM_LEFT(buf)); NULL_TERMINATE_BUFFER(buf); strncat(buf, "\\DRcontrol.exe\" -detachexe "PROC_NAME, BUFFER_ROOM_LEFT(buf)); NULL_TERMINATE_BUFFER(buf); print("Detaching\n"); if (CreateProcess(NULL, buf, NULL, NULL, TRUE, 0, NULL, NULL, &sinfo, &pinfo)) { /* This thread will be detached at non-dr intercepted syscall (the wait below) * with multiple stacked callbacks (from SendAsyncProc). */ WaitForSingleObject(pinfo.hProcess, INFINITE); CloseHandle(pinfo.hProcess); CloseHandle(pinfo.hThread); } else { print("Detach Failed!\n"); } }
drfront_status_t drfront_get_app_full_path(const char *app, OUT char *buf, size_t buflen/*# elements*/) { TCHAR wbuf[MAX_PATH]; TCHAR wapp[MAX_PATH]; drfront_status_t status_check = DRFRONT_ERROR; bool is_dir = false; status_check = drfront_char_to_tchar(app, wapp, BUFFER_SIZE_ELEMENTS(wapp)); if (status_check != DRFRONT_SUCCESS) return status_check; _tsearchenv(wapp, _T("PATH"), wbuf); NULL_TERMINATE_BUFFER(wbuf); status_check = drfront_tchar_to_char(wbuf, buf, buflen); if (status_check != DRFRONT_SUCCESS) return status_check; if (wbuf[0] == _T('\0') || /* DrM-i#1617: we might have a same-name directory on the path */ (drfront_dir_exists(buf, &is_dir) == DRFRONT_SUCCESS && is_dir)) { /* may need to append .exe, FIXME : other executable types */ TCHAR tmp_buf[MAX_PATH]; _sntprintf(tmp_buf, BUFFER_SIZE_ELEMENTS(tmp_buf), _T("%s%s"), wapp, _T(".exe")); NULL_TERMINATE_BUFFER(wbuf); _tsearchenv(tmp_buf, _T("PATH"), wbuf); } if (wbuf[0] == _T('\0')) { /* last try: expand w/ cur dir */ GetFullPathName(wapp, BUFFER_SIZE_ELEMENTS(wbuf), wbuf, NULL); NULL_TERMINATE_BUFFER(wbuf); } status_check = drfront_tchar_to_char(wbuf, buf, buflen); return status_check; }
static void generate_process_name(process_info_t *pi, WCHAR *name_buf /* OUT */, uint name_buf_length /* elements */) { WCHAR qual_name[MAX_CMDLINE]; WCHAR qual_args[MAX_CMDLINE]; int res; BOOL use_args = FALSE; /* hack: we assume only need qualified names for these hardcoded apps * FIXME: read registry to see whether need qualification */ if (wcsicmp(pi->ProcessName, L"svchost.exe") == 0 || wcsicmp(pi->ProcessName, L"msiexec.exe") == 0 || wcsicmp(pi->ProcessName, L"tomcat.exe") == 0 || wcsicmp(pi->ProcessName, L"dllhost.exe") == 0) { /* use qualified names */ res = get_process_cmdline(pi->ProcessID, qual_name, BUFFER_SIZE_ELEMENTS(qual_name)); NULL_TERMINATE_BUFFER(qual_name); if (res == ERROR_SUCCESS) { if (get_commandline_qualifier(qual_name, qual_args, BUFFER_SIZE_ELEMENTS(qual_args), /* hack: we assume we only strip svchost, * and we also strip dllhost here to * fit more of the GUI in and avoid the * "Processid" string. * FIXME: read from registry. */ (wcsicmp(pi->ProcessName, L"svchost.exe") != 0 && wcsicmp(pi->ProcessName, L"dllhost.exe") != 0))) { NULL_TERMINATE_BUFFER(qual_args); use_args = TRUE; } /* else not an error, just no args. e.g.: plain sqlservr.exe */ } else { /* this is an error => notify user */ _snwprintf(qual_args, BUFFER_SIZE_ELEMENTS(qual_args), L"<error>"); NULL_TERMINATE_BUFFER(qual_args); use_args = TRUE; } } if (use_args) { _snwprintf(name_buf, name_buf_length, L"%s-%s", pi->ProcessName, qual_args); } else { _snwprintf(name_buf, name_buf_length, L"%s", pi->ProcessName); } NULL_TERMINATE_SIZED_BUFFER(name_buf, name_buf_length); }
static void event_exit() { #ifdef SHOW_RESULTS char msg[512]; int len; len = dr_snprintf(msg, sizeof(msg)/sizeof(msg[0]), "Instrumentation results:\n" " saw %llu memory references\n", num_refs); DR_ASSERT(len > 0); NULL_TERMINATE_BUFFER(msg); DISPLAY_STRING(msg); #endif /* SHOW_RESULTS */ code_cache_exit(); if (!drmgr_unregister_tls_field(tls_index) || !drmgr_unregister_thread_init_event(event_thread_init) || !drmgr_unregister_thread_exit_event(event_thread_exit) || !drmgr_unregister_bb_insertion_event(event_bb_insert) || drreg_exit() != DRREG_SUCCESS) DR_ASSERT(false); dr_mutex_destroy(mutex); drmgr_exit(); }
void symcache_init(const char *symcache_dir_in, size_t modsize_cache_threshold) { initialized = true; op_modsize_cache_threshold = modsize_cache_threshold; hashtable_init_ex(&symcache_table, SYMCACHE_MASTER_TABLE_HASH_BITS, IF_WINDOWS_ELSE(HASH_STRING_NOCASE, HASH_STRING), true/*strdup*/, false/*!synch*/, symcache_free_entry, NULL, NULL); symcache_lock = dr_mutex_create(); dr_snprintf(symcache_dir, BUFFER_SIZE_ELEMENTS(symcache_dir), "%s", symcache_dir_in); NULL_TERMINATE_BUFFER(symcache_dir); if (!dr_directory_exists(symcache_dir)) { if (!dr_create_dir(symcache_dir)) { /* check again in case of a race (i#616) */ if (!dr_directory_exists(symcache_dir)) { NOTIFY_ERROR("Unable to create symcache dir %s"NL, symcache_dir); ASSERT(false, "unable to create symcache dir"); dr_abort(); } } } }
static void wrap_pre_SSL_write(void *wrapcxt, OUT void **user_data) { /* int SSL_write(SSL *ssl, const void *buf, int num); * * ssize_t gnutls_record_send(gnutls_session_t session, * const void * data, size_t sizeofdata); */ void *ssl = (void *)drwrap_get_arg(wrapcxt, 0); unsigned char *buf = (unsigned char *)drwrap_get_arg(wrapcxt, 1); size_t sz = (size_t)drwrap_get_arg(wrapcxt, 2); /* By generating unique filenames (per SSL context), we are able to * simplify logging of SSL traffic (no file locking is required). */ char filename[MAXIMUM_PATH] = { 0 }; dr_snprintf(filename, BUFFER_SIZE_ELEMENTS(filename), "trace-%x.write", ssl); NULL_TERMINATE_BUFFER(filename); FILE *fp = fopen(filename, "ab+"); /* Error handling of logging operations isn't critical - in fact, we don't * even know what to do in such error conditions, so we simply return! */ if (!fp) { dr_fprintf(STDERR, "Couldn’t open the output file %s\n", filename); return; } /* We assume that SSL_write always succeeds and writes the whole buffer. */ fwrite(buf, 1, sz, fp); fclose(fp); }
drfront_status_t drfront_dir_try_writable(const char *path, OUT bool *is_writable) { /* It would be convenient to use O_TMPFILE but not all filesystems support it */ int fd; char tmpname[PATH_MAX]; /* We actually don't care about races w/ other threads or processes running * this same code: each syscall should succeed and truncate whatever is there. */ # define TMP_FILE_NAME ".__drfrontendlib_tmp" if (is_writable == NULL) return DRFRONT_ERROR_INVALID_PARAMETER; snprintf(tmpname, BUFFER_SIZE_ELEMENTS(tmpname), "%s/%s", path, TMP_FILE_NAME); NULL_TERMINATE_BUFFER(tmpname); fd = creat(tmpname, S_IRUSR | S_IWUSR); if (fd == -1) { drfront_status_t res; bool is_dir; *is_writable = false; res = drfront_dir_exists(path, &is_dir); if (res != DRFRONT_SUCCESS) return res; if (!is_dir) return DRFRONT_ERROR_INVALID_PATH; } else { *is_writable = true; close(fd); unlink(tmpname); } return DRFRONT_SUCCESS; }
void display_error(char *msg) { wchar_t buf[512]; _snwprintf(buf, BUFFER_SIZE_ELEMENTS(buf), L"%hs", msg); NULL_TERMINATE_BUFFER(buf); display_error_helper(buf); }
/* Implements a normal path search for fname on the paths in env_var. * Resolves symlinks, which is needed to get the right config filename (i#1062). */ drfront_status_t drfront_searchenv(const char *fname, const char *env_var, OUT char *full_path, const size_t full_path_size, OUT bool *ret) { const char *paths = getenv(env_var); const char *cur; const char *next; const char *end; char tmp[full_path_size]; char realpath_buf[PATH_MAX]; /* realpath hardcodes its buffer length */ drfront_status_t status_check = DRFRONT_ERROR; bool access_ret = false; if (ret == NULL) return DRFRONT_ERROR_INVALID_PARAMETER; /* Windows searches the current directory first. */ /* XXX: realpath resolves symlinks, which we may not want. */ if (realpath(fname, realpath_buf) != NULL) { status_check = drfront_access(realpath_buf, DRFRONT_EXIST, &access_ret); if (status_check != DRFRONT_SUCCESS) { *ret = false; return status_check; } else if (access_ret == true) { *ret = true; snprintf(full_path, full_path_size, "%s", realpath_buf); full_path[full_path_size - 1] = '\0'; return DRFRONT_SUCCESS; } } cur = paths; end = strchr(paths, '\0'); while (cur < end) { next = strchr(cur, ':'); next = (next == NULL ? end : next); snprintf(tmp, BUFFER_SIZE_ELEMENTS(tmp), "%.*s/%s", (int)(next - cur), cur, fname); NULL_TERMINATE_BUFFER(tmp); /* realpath checks for existence too. */ if (realpath(tmp, realpath_buf) != NULL) { status_check = drfront_access(realpath_buf, DRFRONT_EXIST, &access_ret); if (status_check != DRFRONT_SUCCESS) { *ret = false; return status_check; } else if (access_ret == true) { *ret = true; snprintf(full_path, full_path_size, "%s", realpath_buf); full_path[full_path_size - 1] = '\0'; return DRFRONT_SUCCESS; } } cur = next + 1; } full_path[0] = '\0'; *ret = false; return DRFRONT_ERROR; }
static void display_error_helper(wchar_t *msg) { wchar_t title_buf[MAX_PATH + 64]; _snwprintf(title_buf, BUFFER_SIZE_ELEMENTS(title_buf), L_PRODUCT_NAME L" Notice: %hs(%hs)", get_application_name(), get_application_pid()); NULL_TERMINATE_BUFFER(title_buf); nt_messagebox(msg, title_buf); }
/* XXX i#1440: share w/ Linux */ static bool handle_strarray_access(sysarg_iter_info_t *ii, const sysinfo_arg_t *arg_info, app_pc start, uint size) { char id[16]; dr_snprintf(id, BUFFER_SIZE_ELEMENTS(id), "%s%d", "parameter #", arg_info->param); NULL_TERMINATE_BUFFER(id); check_strarray(ii, (char **)start, arg_info->param, id); return true; /* check_strarray checks whole array */ }
int main(int argc, char *argv[]) { /* Test reading the symlink via readlink */ char proc[64]; char buf[512]; ssize_t res; snprintf(proc, BUFFER_SIZE_ELEMENTS(proc), "/proc/%d/exe", getpid()); NULL_TERMINATE_BUFFER(proc); res = readlink(proc, buf, BUFFER_SIZE_ELEMENTS(buf)); NULL_TERMINATE_BUFFER(buf); if (res > 0 && strrchr(buf, '/') != NULL) print("/proc/pid/exe points to %s\n", strrchr(buf, '/') + 1); else { perror("readlink failed"); return 1; } /* XXX: another good test would be to make a thread and use /proc/tid/exe */ /* Test executing the symlink via execve. * We invoked ourselves initially with an arg, to avoid repeated execs. */ if (argc > 1) { pid_t child = fork(); if (child < 0) { perror("fork failed"); } else if (child > 0) { pid_t result = waitpid(child, NULL, 0); assert(result == child); print("child has exited\n"); } else { const char *arg[2] = { proc, NULL }; /* Update for child's pid */ snprintf(proc, BUFFER_SIZE_ELEMENTS(proc), "/proc/%d/exe", getpid()); NULL_TERMINATE_BUFFER(proc); res = execve(proc, (char **)arg, NULL /*env*/); if (res < 0) perror("execve failed"); } } return 0; }
void display_error(char *msg) { # ifdef DISABLED /* going with msgbox always! */ fprintf(FP, msg); # else wchar_t buf[512]; _snwprintf(buf, BUFFER_SIZE_ELEMENTS(buf), L"%hs", msg); NULL_TERMINATE_BUFFER(buf); display_error_helper(buf); # endif }
drcovlib_status_t drcovlib_init(drcovlib_options_t *ops) { int count = dr_atomic_add32_return_sum(&drcovlib_init_count, 1); if (count > 1) return DRCOVLIB_SUCCESS; if (ops->struct_size != sizeof(options)) return DRCOVLIB_ERROR_INVALID_PARAMETER; if ((ops->flags & (~(DRCOVLIB_DUMP_AS_TEXT|DRCOVLIB_THREAD_PRIVATE))) != 0) return DRCOVLIB_ERROR_INVALID_PARAMETER; if (TEST(DRCOVLIB_THREAD_PRIVATE, ops->flags)) { if (!dr_using_all_private_caches()) return DRCOVLIB_ERROR_INVALID_SETUP; drcov_per_thread = true; } options = *ops; if (options.logdir != NULL) dr_snprintf(logdir, BUFFER_SIZE_ELEMENTS(logdir), "%s", ops->logdir); else /* default */ dr_snprintf(logdir, BUFFER_SIZE_ELEMENTS(logdir), "."); NULL_TERMINATE_BUFFER(logdir); options.logdir = logdir; if (options.native_until_thread > 0) go_native = true; drmgr_init(); drx_init(); /* We follow a simple model of the caller requesting the coverage dump, * either via calling the exit routine, using its own soft_kills nudge, or * an explicit dump call for unusual cases. This means that drx's * soft_kills remains inside the outer later, i.e., the drcov client. This * is the easiest approach for coordinating soft_kills among many libraries. * Thus, we do *not* register for an exit event here. */ drmgr_register_thread_init_event(event_thread_init); drmgr_register_thread_exit_event(event_thread_exit); drmgr_register_bb_instrumentation_event(event_basic_block_analysis, NULL, NULL); dr_register_filter_syscall_event(event_filter_syscall); drmgr_register_pre_syscall_event(event_pre_syscall); #ifdef UNIX dr_register_fork_init_event(event_fork); #endif tls_idx = drmgr_register_tls_field(); if (tls_idx == -1) return DRCOVLIB_ERROR; return event_init(); }
static void check_prctl_whitelist(byte *prctl_arg1) { /* disable instrumentation on seeing prctl(PR_SET_NAME) that does not * match any of the specified ,-separated names (PR 574018) */ char nm[PRCTL_NAME_SZ+1]; ASSERT(options.prctl_whitelist[0] != '\0', "caller should check for empty op"); if (safe_read(prctl_arg1, PRCTL_NAME_SZ, nm)) { bool on_whitelist = false; char *s, *next; char *list_end = options.prctl_whitelist + strlen(options.prctl_whitelist); size_t white_sz; NULL_TERMINATE_BUFFER(nm); LOG(1, "prctl set name %s\n", nm); s = options.prctl_whitelist; while (s < list_end) { next = strchr(s, ','); if (next == NULL) white_sz = (list_end - s); else white_sz = (next - s); LOG(2, "comparing \"%s\" with whitelist entry \"%.*s\" sz=%d\n", nm, white_sz, s, white_sz); if (strncmp(nm, s, white_sz) == 0) { LOG(0, "prctl name %s matches whitelist\n", nm); on_whitelist = true; break; } s += white_sz + 1 /* skip , itself */; } if (!on_whitelist) { /* ideally: suspend world, then set options, then flush * w/o resuming. * FIXME: just setting options is unsafe if another thread * hits an event and fails to restore state or sthg. * Fortunately we expect most uses of PR_SET_NAME to be * immediately after forking. * Ideally we'd call dr_suspend_all_other_threads() * and nest dr_flush_region() inside it but both want * the same master lock: should check whether easy to support * via internal vars indicating whether lock held. */ ELOGF(0, f_global, "\n*********\nDISABLING MEMORY CHECKING for %s\n", nm); options.shadowing = false; options.track_allocs = false; options.count_leaks = false; dr_flush_region(0, ~((ptr_uint_t)0)); } } }
void loader_init(void) { uint i; privmod_t *mod; acquire_recursive_lock(&privload_lock); VMVECTOR_ALLOC_VECTOR(modlist_areas, GLOBAL_DCONTEXT, VECTOR_SHARED | VECTOR_NEVER_MERGE /* protected by privload_lock */ | VECTOR_NO_LOCK, modlist_areas); /* os specific loader initialization prologue before finalize the load */ os_loader_init_prologue(); /* Process client libs we loaded early but did not finalize */ for (i = 0; i < privmod_static_idx; i++) { /* Transfer to real list so we can do normal processing */ char name_copy[MAXIMUM_PATH]; mod = privload_insert(NULL, privmod_static[i].base, privmod_static[i].size, privmod_static[i].name, privmod_static[i].path); LOG(GLOBAL, LOG_LOADER, 1, "%s: processing imports for %s\n", __FUNCTION__, mod->name); /* save a copy for error msg, b/c mod will be unloaded (i#643) */ snprintf(name_copy, BUFFER_SIZE_ELEMENTS(name_copy), "%s", mod->name); NULL_TERMINATE_BUFFER(name_copy); if (!privload_load_finalize(mod)) { mod = NULL; /* it's been unloaded! */ #ifdef CLIENT_INTERFACE SYSLOG(SYSLOG_ERROR, CLIENT_LIBRARY_UNLOADABLE, 5, get_application_name(), get_application_pid(), name_copy, "\n\tUnable to locate imports of client library"); #endif os_terminate(NULL, TERMINATE_PROCESS); ASSERT_NOT_REACHED(); } } /* os specific loader initialization epilogue after finalize the load */ os_loader_init_epilogue(); /* FIXME i#338: call loader_thread_init here once get * loader_init called after dynamo_thread_init but in a way that * works with Windows */ release_recursive_lock(&privload_lock); }
void internal_error(char *file, int line, char *expr) { #ifdef INTERNAL # define FILENAME_LENGTH L"" #else /* truncate file name to first character */ # define FILENAME_LENGTH L".1" #endif wchar_t buf[512]; _snwprintf(buf, BUFFER_SIZE_ELEMENTS(buf), L"Preinject Error %" FILENAME_LENGTH L"hs:%d %hs\n", file, line, expr); NULL_TERMINATE_BUFFER(buf); display_error_helper(buf); TerminateProcess(GetCurrentProcess(), (uint)-1); }
static bool inject_ld_preload(dr_inject_info_t *info, const char *library_path) { if (info->exec_self) { pre_execve_ld_preload(library_path); execv(info->exe, (char **) info->argv); return false; /* if execv returns, there was an error */ } else { /* Write the path to DR to the pipe. */ char cmd[MAXIMUM_PATH]; snprintf(cmd, BUFFER_SIZE_ELEMENTS(cmd), "ld_preload %s", library_path); NULL_TERMINATE_BUFFER(cmd); write_pipe_cmd(info->pipe_fd, cmd); } return true; }
/* Environment modifications before executing the child process for LD_PRELOAD * injection. */ static void pre_execve_ld_preload(const char *dr_path) { char ld_lib_path[MAX_OPTIONS_STRING]; const char *last_slash = NULL; const char *mode_slash = NULL; const char *lib_slash = NULL; const char *cur_path = getenv("LD_LIBRARY_PATH"); const char *cur = dr_path; /* Find last three occurrences of '/'. */ while (*cur != '\0') { if (*cur == '/') { lib_slash = mode_slash; mode_slash = last_slash; last_slash = cur; } cur++; } /* dr_path should be absolute and have at least three components. */ ASSERT(lib_slash != NULL && last_slash != NULL); ASSERT(strncmp(lib_slash, "/lib32", 5) == 0 || strncmp(lib_slash, "/lib64", 5) == 0); /* Put both DR's path and the extension path on LD_LIBRARY_PATH. We only * need the extension path if -no_private_loader is used. */ snprintf(ld_lib_path, BUFFER_SIZE_ELEMENTS(ld_lib_path), "%.*s:%.*s/ext%.*s%s%s", last_slash - dr_path, dr_path, /* DR path */ lib_slash - dr_path, dr_path, /* pre-ext path */ last_slash - lib_slash, lib_slash, /* libNN component */ cur_path == NULL ? "" : ":", cur_path == NULL ? "" : cur_path); NULL_TERMINATE_BUFFER(ld_lib_path); setenv("LD_LIBRARY_PATH", ld_lib_path, true/*overwrite*/); setenv("LD_PRELOAD", "libdynamorio.so libdrpreload.so", true/*overwrite*/); if (verbose) { printf("Setting LD_USE_LOAD_BIAS for PIEs so the loader will honor " "DR's preferred base. (i#719)\n" "Set LD_USE_LOAD_BIAS=0 prior to injecting if this is a " "problem.\n"); } setenv("LD_USE_LOAD_BIAS", "1", false/*!overwrite, let user set it*/); }
static void display_error_helper(wchar_t *msg) { wchar_t title_buf[MAX_PATH + 64]; _snwprintf(title_buf, BUFFER_SIZE_ELEMENTS(title_buf), L_PRODUCT_NAME L" Notice: %hs(%hs)", get_application_name(), get_application_pid()); NULL_TERMINATE_BUFFER(title_buf); /* for unit tests: assume that if a limit is set, we are in a * script so it's ok to just display to stderr. avoids hangs when * an error is encountered. */ if (limit <= 0) nt_messagebox(msg, title_buf); else { fprintf(FP, "\n\n%ls\n%ls\n\n", title_buf, msg); fflush(FP); } }
static bool inject_early(dr_inject_info_t *info, const char *library_path) { if (info->exec_self) { /* exec DR with the original command line and set an environment * variable pointing to the real exe. */ pre_execve_early(info->exe); execv(library_path, (char **) info->argv); return false; /* if execv returns, there was an error */ } else { /* Write the path to DR to the pipe. */ char cmd[MAXIMUM_PATH]; snprintf(cmd, BUFFER_SIZE_ELEMENTS(cmd), "exec_dr %s", library_path); NULL_TERMINATE_BUFFER(cmd); write_pipe_cmd(info->pipe_fd, cmd); } return true; }
drfront_status_t drfront_get_absolute_path(const char *src, OUT char *buf, size_t buflen/*# elements*/) { TCHAR wsrc[MAX_PATH]; TCHAR wdst[MAX_PATH]; drfront_status_t status_check = DRFRONT_ERROR; int res; status_check = drfront_char_to_tchar(src, wsrc, BUFFER_SIZE_ELEMENTS(wsrc)); if (status_check != DRFRONT_SUCCESS) return status_check; res = GetFullPathName(wsrc, BUFFER_SIZE_ELEMENTS(wdst), wdst, NULL); if (res <= 0) return DRFRONT_ERROR; NULL_TERMINATE_BUFFER(wdst); status_check = drfront_tchar_to_char(wdst, buf, buflen); return status_check; }
/* i#200/PR 459481: communicate child pid via file. * We don't need this on unix b/c we use exec. */ static void write_pid_to_file(const char *pidfile, process_id_t pid) { TCHAR wpidfile[MAXIMUM_PATH]; HANDLE f; char_to_tchar(pidfile, wpidfile, BUFFER_SIZE_ELEMENTS(wpidfile)); f = CreateFile(wpidfile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (f == INVALID_HANDLE_VALUE) { warn("cannot open %s: %d", pidfile, GetLastError()); } else { char pidbuf[16]; BOOL ok; DWORD written; _snprintf(pidbuf, BUFFER_SIZE_ELEMENTS(pidbuf), "%d\n", pid); NULL_TERMINATE_BUFFER(pidbuf); ok = WriteFile(f, pidbuf, (DWORD)strlen(pidbuf), &written, NULL); assert(ok && written == strlen(pidbuf)); CloseHandle(f); } }
static void handle_pre_execve(void *drcontext) { #ifndef USE_DRSYMS /* PR 453867: tell postprocess.pl to watch for new logdir and * fork a new copy. * FIXME: what if syscall fails? Punting on that for now. * Note that if it fails and then a later one succeeds, postprocess.pl * will replace the first with the last. */ char logdir[MAXIMUM_PATH]; /* one reason we're not inside os_post_syscall() */ size_t bytes_read = 0; /* Not using safe_read() since we want a partial read if hits page boundary */ dr_safe_read((void *) dr_syscall_get_param(drcontext, 0), BUFFER_SIZE_BYTES(logdir), logdir, &bytes_read); if (bytes_read < BUFFER_SIZE_BYTES(logdir)) logdir[bytes_read] = '\0'; NULL_TERMINATE_BUFFER(logdir); ELOGF(0, f_fork, "EXEC path=%s\n", logdir); #endif }
/* caller holds lock */ static const char * drsym_pecoff_symbol_name(pecoff_data_t *mod, IMAGE_SYMBOL *sym) { static char buf[sizeof(sym->N.ShortName)+1]; const char *name; size_t name_sz; if (sym->N.Name.Short == 0) { /* longer than 8 chars, so index into string table */ name = mod->string_table + sym->N.Name.Long; name_sz = strlen(name); } else { const char *c; name = (const char *) sym->N.ShortName; /* not null-terminated if 8 chars. caller holds lock so we can * use a static buffer to add a NULL, which caller requires. */ for (c = name; c < name + sizeof(sym->N.ShortName); c++) { if (*c == '\0') break; } if (c == name + sizeof(sym->N.ShortName)) { memcpy(buf, name, sizeof(sym->N.ShortName)); NULL_TERMINATE_BUFFER(buf); name = buf; } } #ifndef X64 /* XXX: all 32-bit pecoff symtables I've seen have leading underscores, * which we drop here. Is this always the case? It's true for Cygwin * gcc 3.4.4 and MinGW gcc 4.6.1. It's NOT true for MinGWx64 gcc 4.7.0 * but there's no 4.7 32-bit so I'm not sure whether the next MinGW * release is going to break us. We at least don't remove from "_Z" * so we'll work w/ mangled names (though there could be a C name that * starts w/ Z that was added by the linker and should start w/ _?) */ if (name[0] == '_' && name[1] != 'Z') name++; #endif return name; }
drfront_status_t drfront_set_client_symbol_search_path(const char *symdir, bool ignore_env, OUT char *symsrv_path, size_t symsrv_path_sz) { char app_symsrv_path[MAX_SYMSRV_PATH]; TCHAR wapp_symsrv_path[MAX_SYMSRV_PATH]; char tmp_srv_path[MAX_SYMSRV_PATH]; char tmp_symsrv_path[MAX_SYMSRV_PATH]; char *cur; char *end; size_t sofar; ssize_t len; bool has_srv; bool dir_exists; bool has_ms_symsrv; drfront_status_t sc; static const char ms_symsrv[] = "http://msdl.microsoft.com/download/symbols"; if (sym_set_path_func == NULL) return DRFRONT_ERROR_LIB_UNSUPPORTED; /* If the user set a non-empty _NT_SYMBOL_PATH, then we use that. * If not, we use symdir/symbols path and make sure it exists. */ if (ignore_env || drfront_get_env_var("_NT_SYMBOL_PATH", tmp_symsrv_path, BUFFER_SIZE_ELEMENTS(tmp_symsrv_path)) != DRFRONT_SUCCESS || strlen(tmp_symsrv_path) == 0) { char pdb_dir[MAXIMUM_PATH]; _snprintf(pdb_dir, BUFFER_SIZE_ELEMENTS(pdb_dir), "%s/symbols", symdir); NULL_TERMINATE_BUFFER(pdb_dir); drfront_string_replace_character(pdb_dir, '/', '\\'); /* canonicalize */ sc = drfront_create_dir(pdb_dir); if ((sc != DRFRONT_SUCCESS && sc != DRFRONT_ERROR_FILE_EXISTS) || drfront_access(pdb_dir, DRFRONT_READ, &dir_exists) != DRFRONT_SUCCESS || !dir_exists) { DO_DEBUG(DL_WARN, printf("Failed to create directory for symbols: %s\n", pdb_dir); );
LSTATUS WINAPI redirect_RegOpenKeyExA( __in HKEY hKey, __in_opt LPCSTR lpSubKey, __in_opt DWORD ulOptions, __in REGSAM samDesired, __out PHKEY phkResult ) { wchar_t *wkey; wchar_t wbuf[MAX_REG_KEY_NAME_LEN]; int len; if (lpSubKey != NULL) { len = _snwprintf(wbuf, BUFFER_SIZE_ELEMENTS(wbuf), L"%hs", lpSubKey); if (len < 0 || len >= BUFFER_SIZE_ELEMENTS(wbuf)) return ERROR_INVALID_PARAMETER; NULL_TERMINATE_BUFFER(wbuf); /* be paranoid */ wkey = wbuf; } else wkey = NULL; return redirect_RegOpenKeyExW(hKey, wkey, ulOptions, samDesired, phkResult); }
static void wrap_post_SSL_read(void *wrapcxt, void *user_data) { struct SSL_read_data *sd = (struct SSL_read_data *)user_data; int actual_read = (int)(ptr_int_t)drwrap_get_retval(wrapcxt); char filename[MAXIMUM_PATH] = { 0 }; dr_snprintf(filename, BUFFER_SIZE_ELEMENTS(filename), "trace-%x.read", sd->ssl); NULL_TERMINATE_BUFFER(filename); FILE *fp = fopen(filename, "ab+"); if (!fp) { dr_fprintf(STDERR, "Couldn’t open the output file %s\n", filename); dr_global_free(user_data, sizeof(struct SSL_read_data)); return; } if (actual_read > 0) { fwrite(sd->read_buffer, 1, actual_read, fp); } fclose(fp); dr_global_free(user_data, sizeof(struct SSL_read_data)); }
static bool load_dynamorio_lib(IF_NOT_X64(bool x64_in_wow64)) { HMODULE dll = NULL; char path[MAX_PATH]; #ifdef DEBUG char msg[3 * MAX_PATH]; #endif int retval = -1; /* failure */ #ifndef X64 bool wow64 = is_wow64_process(NT_CURRENT_PROCESS); if (x64_in_wow64) { ASSERT(wow64); retval = get_parameter_64(PARAM_STR(DYNAMORIO_VAR_AUTOINJECT), path, MAX_PATH); } else #endif retval = get_parameter(PARAM_STR(DYNAMORIO_VAR_AUTOINJECT), path, MAX_PATH); if (IS_GET_PARAMETER_SUCCESS(retval)) { dr_marker_t mark; VERBOSE_MESSAGE("Loading \"%hs\"", path); /* The read_and_verify_dr_marker is the canonical check for dr in a * process, we double check against GetModuleHandle here just to be * extra safe (in case dr failed to initialize before). Note that * GetModuleHandle won't find dr's dll if we implement certian -hide * or early_injection proposals. */ if (read_and_verify_dr_marker(GetCurrentProcess(), &mark) != DR_MARKER_FOUND && GetModuleHandle(DYNAMORIO_LIBRARY_NAME) == NULL #ifndef X64 /* these ifdefs are rather ugly: just export all routines in x64 builds? */ && /* check for 64-bit as well */ (!wow64 || read_and_verify_dr_marker_64(GetCurrentProcess(), &mark) != DR_MARKER_FOUND) /* FIXME PR 251677: need 64-bit early injection to fully test * read_and_verify_dr_marker_64 */ #endif ) { /* OK really going to load dr now, verify that we are injecting * early enough (i.e. user32.dll is statically linked). This * presumes preinject is only used with app_init injection which is * currently the case. FIXME - should we also check_sole_thread * here? We can't really handle more then one thread when dr is * loading, but this can happen with early remote injected threads * many of which (CTRL) are relatively harmless. */ LDR_MODULE *mod = get_ldr_module_by_name(L"user32.dll"); ASSERT(mod != NULL); if (ldr_module_statically_linked(mod)) { #ifndef X64 if (x64_in_wow64) dll = load_library_64(path); else #endif dll = LoadLibrary(path); } else { /* FIXME - would be really nice to communicate this back to * the controller. */ #ifdef DEBUG _snprintf(msg, BUFFER_SIZE_ELEMENTS(msg), PRODUCT_NAME " Error: improper injection - " PRODUCT_NAME " (%s) can't inject into process %s (%s) (user32.dll " "not statically linked)\n", path, get_application_name(), get_application_pid()); NULL_TERMINATE_BUFFER(msg); display_error(msg); #endif } } else { /* notify failure only in debug builds, otherwise just return */ #ifdef DEBUG /* with early injection this becomes even more likely */ if (read_and_verify_dr_marker(GetCurrentProcess(), &mark) == DR_MARKER_FOUND # ifndef X64 || (wow64 && read_and_verify_dr_marker_64(GetCurrentProcess(), &mark) == DR_MARKER_FOUND) # endif ) { /* ok, early injection should always beat this */ # if VERBOSE /* can't readily tell what was expected */ _snprintf(msg, BUFFER_SIZE_ELEMENTS(msg), PRODUCT_NAME " ok if early injection, otherwise ERROR: " "double injection, " PRODUCT_NAME " (%s) is already loaded " "in process %s (%s), continuing\n", path, get_application_name(), get_application_pid()); NULL_TERMINATE_BUFFER(msg); display_error(msg); # endif /* VERBOSE */ } else { /* if GetModuleHandle finds us but we don't have a marker * we may have failed somehow */ _snprintf(msg, BUFFER_SIZE_ELEMENTS(msg), PRODUCT_NAME " Error: failed injection, " PRODUCT_NAME " (%s) is " "loaded but not initialized in process %s (%s), continuing\n", path, get_application_name(), get_application_pid()); NULL_TERMINATE_BUFFER(msg); display_error(msg); } #endif /* DEBUG */ return false; } } else path[0] = 0; if (dll == NULL) { #ifdef DEBUG int err = GetLastError(); _snprintf(msg, BUFFER_SIZE_ELEMENTS(msg), PRODUCT_NAME " Error %d loading %s\n", err, path); NULL_TERMINATE_BUFFER(msg); display_error(msg); #endif return false; } else { int_func_t init_func; void_func_t take_over_func; int res; #ifndef X64 if (x64_in_wow64) { init_func = (int_func_t)(ptr_uint_t) /*we know <4GB*/ get_proc_address_64((uint64)dll, "dynamorio_app_init"); take_over_func = (void_func_t)(ptr_uint_t) /*we know <4GB*/ get_proc_address_64((uint64)dll, "dynamorio_app_take_over"); VERBOSE_MESSAGE("dynamorio_app_init: 0x%08x; dynamorio_app_take_over: " "0x%08x\n", init_func, take_over_func); } else { #endif init_func = (int_func_t)GetProcAddress(dll, "dynamorio_app_init"); take_over_func = (void_func_t)GetProcAddress(dll, "dynamorio_app_take_over"); #ifndef X64 } #endif if (init_func == NULL || take_over_func == NULL) { /* unload the library so that it's clear DR is not in control * (o/w the DR library is in the process and it's not clear * what's going on) */ #ifndef X64 if (x64_in_wow64) { # ifdef DEBUG bool ok = # endif free_library_64(dll); ASSERT(ok); } else #endif FreeLibrary(dll); #ifdef DEBUG display_error("Error getting " PRODUCT_NAME " functions\n"); #endif return false; } VERBOSE_MESSAGE("about to inject dynamorio"); #ifndef X64 if (x64_in_wow64) res = switch_modes_and_call(init_func, NULL, NULL, NULL); else #endif res = (*init_func)(); VERBOSE_MESSAGE("dynamorio_app_init() returned %d\n", res); #ifndef X64 if (x64_in_wow64) switch_modes_and_call(take_over_func, NULL, NULL, NULL); else #endif (*take_over_func)(); VERBOSE_MESSAGE("inside " PRODUCT_NAME " now\n"); } return true; }
int _tmain(int argc, TCHAR *targv[]) { char **argv; char *process = NULL; char *dr_root = NULL; char *drheapstat_root = NULL; char default_dr_root[MAXIMUM_PATH]; char default_drheapstat_root[MAXIMUM_PATH]; char client_path[MAXIMUM_PATH]; char client_ops[MAX_DR_CMDLINE]; size_t cliops_sofar = 0; /* for BUFPRINT to client_ops */ char dr_ops[MAX_DR_CMDLINE]; size_t drops_sofar = 0; /* for BUFPRINT to dr_ops */ ssize_t len; /* shared by all BUFPRINT */ char logdir[MAXIMUM_PATH]; bool have_logdir = false; bool use_root_for_logdir; #ifdef WINDOWS char *pidfile = NULL; #endif #ifndef MACOS /* XXX i#1286: implement nudge on MacOS */ process_id_t nudge_pid = 0; #endif bool use_dr_debug = false; bool use_drheapstat_debug = false; char *app_name; char full_app_name[MAXIMUM_PATH]; char **app_argv; int errcode; void *inject_data; int i; char *c; char buf[MAXIMUM_PATH]; process_id_t pid; bool exit0 = false; bool dr_logdir_specified = false; bool doubledash_present = false; drfront_status_t sc; bool res; #ifndef X64 bool is64, is32; #endif dr_standalone_init(); #if defined(WINDOWS) && !defined(_UNICODE) # error _UNICODE must be defined #else /* Convert to UTF-8 if necessary */ sc = drfront_convert_args((const TCHAR **)targv, &argv, argc); if (sc != DRFRONT_SUCCESS) fatal("failed to process args: %d\n", sc); #endif /* Default root: we assume this exe is <root>/bin/drheapstat.exe */ get_full_path(argv[0], buf, BUFFER_SIZE_ELEMENTS(buf)); c = buf + strlen(buf) - 1; while (*c != DIRSEP && *c != ALT_DIRSEP && c > buf) c--; _snprintf(c+1, BUFFER_SIZE_ELEMENTS(buf) - (c+1-buf), "../dynamorio"); NULL_TERMINATE_BUFFER(buf); get_absolute_path(buf, default_dr_root, BUFFER_SIZE_ELEMENTS(default_dr_root)); NULL_TERMINATE_BUFFER(default_dr_root); dr_root = default_dr_root; /* assuming we're in bin/ (mainly due to CPack NSIS limitations) */ _snprintf(c+1, BUFFER_SIZE_ELEMENTS(buf) - (c+1-buf), ".."); NULL_TERMINATE_BUFFER(buf); get_absolute_path(buf, default_drheapstat_root, BUFFER_SIZE_ELEMENTS(default_drheapstat_root)); NULL_TERMINATE_BUFFER(default_drheapstat_root); drheapstat_root = default_drheapstat_root; string_replace_character(drheapstat_root, ALT_DIRSEP, DIRSEP); /* canonicalize */ BUFPRINT(dr_ops, BUFFER_SIZE_ELEMENTS(dr_ops), drops_sofar, len, "%s ", DEFAULT_DR_OPS); client_ops[0] = '\0'; /* default logdir */ if (drfront_appdata_logdir(drheapstat_root, "Dr. Heapstat", &use_root_for_logdir, logdir, BUFFER_SIZE_ELEMENTS(logdir)) == DRFRONT_SUCCESS && !use_root_for_logdir) { if ((dr_create_dir(logdir) || dr_directory_exists(logdir)) && file_is_writable(logdir)) have_logdir = true; } if (!have_logdir) { _snprintf(logdir, BUFFER_SIZE_ELEMENTS(logdir), "%s%cdrheapstat%clogs", drheapstat_root, DIRSEP, DIRSEP); NULL_TERMINATE_BUFFER(logdir); if (!file_is_writable(logdir)) { _snprintf(logdir, BUFFER_SIZE_ELEMENTS(logdir), "%s%clogs", drheapstat_root, DIRSEP); NULL_TERMINATE_BUFFER(logdir); if (file_is_writable(logdir)) have_logdir = true; } else have_logdir = true; } else have_logdir = true; if (!have_logdir) { /* try logs in cur dir */ get_absolute_path("./logs", logdir, BUFFER_SIZE_ELEMENTS(logdir)); NULL_TERMINATE_BUFFER(logdir); if (!file_is_writable(logdir)) { /* try cur dir */ get_absolute_path(".", logdir, BUFFER_SIZE_ELEMENTS(logdir)); NULL_TERMINATE_BUFFER(logdir); } } /* parse command line */ /* FIXME PR 487993: use optionsx.h to construct this parsing code */ for (i=1; i<argc; i++) { if (strcmp(argv[i], "--") == 0) { doubledash_present = true; break; } } for (i=1; i<argc; i++) { /* note that we pass unknown args to client, until -- */ if (strcmp(argv[i], "--") == 0) { i++; break; } /* drag-and-drop does not include "--" so we try to identify the app. * we explicitly parse -logdir and -suppress, and all the other * client ops that take args take numbers so this should be safe. */ else if (argv[i][0] != '-' && !doubledash_present && ends_in_exe(argv[i])) { /* leave i alone: this is the app itself */ break; } else if (strcmp(argv[i], "-v") == 0) { verbose = true; continue; } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { print_usage(true/*full*/); exit(0); } else if (strcmp(argv[i], "-dr_debug") == 0) { use_dr_debug = true; continue; } else if (strcmp(argv[i], "-dr_release") == 0) { use_dr_debug = false; continue; } else if (strcmp(argv[i], "-debug") == 0) { use_drheapstat_debug = true; continue; } else if (strcmp(argv[i], "-release") == 0) { use_drheapstat_debug = false; continue; } else if (!strcmp(argv[i], "-version")) { #if defined(BUILD_NUMBER) && defined(VERSION_NUMBER) printf("Dr. Heapstat version %s -- build %d\n", VERSION_STRING, BUILD_NUMBER); #elif defined(BUILD_NUMBER) printf("Dr. Heapstat custom build %d -- %s\n", BUILD_NUMBER, __DATE__); #elif defined(VERSION_NUMBER) printf("Dr. Heapstat version %s -- custom build %s, %s\n", VERSION_STRING, __DATE__, __TIME__); #else printf("Dr. Heapstat custom build -- %s, %s\n", __DATE__, __TIME__); #endif exit(0); } else if (strcmp(argv[i], "-dr") == 0) { if (i >= argc - 1) usage("invalid arguments"); dr_root = argv[++i]; } else if (strcmp(argv[i], "-drheapstat") == 0) { if (i >= argc - 1) usage("invalid arguments"); drheapstat_root = argv[++i]; } else if (strcmp(argv[i], "-follow_children") == 0 || strcmp(argv[i], "-no_follow_children") == 0) { BUFPRINT(dr_ops, BUFFER_SIZE_ELEMENTS(dr_ops), drops_sofar, len, "%s ", argv[i]); } #ifndef MACOS /* XXX i#1286: implement nudge on MacOS */ else if (strcmp(argv[i], "-nudge") == 0) { if (i >= argc - 1) usage("invalid arguments"); nudge_pid = strtoul(argv[++i], NULL, 10); } #endif else if (strcmp(argv[i], "-dr_ops") == 0) { if (i >= argc - 1) usage("invalid arguments"); /* Slight risk of some other option containing "-logdir " but * -dr_ops is really only be used by Dr. Memory developers anyway. */ if (strstr(argv[i+1], "-logdir ") != NULL) dr_logdir_specified = true; BUFPRINT(dr_ops, BUFFER_SIZE_ELEMENTS(dr_ops), drops_sofar, len, "%s ", argv[++i]); } #ifdef WINDOWS else if (strcmp(argv[i], "-pid_file") == 0) { if (i >= argc - 1) usage("invalid arguments"); pidfile = argv[++i]; } #endif else if (strcmp(argv[i], "-logdir") == 0) { if (i >= argc - 1) usage("invalid arguments"); /* make absolute */ get_absolute_path(argv[++i], logdir, BUFFER_SIZE_ELEMENTS(logdir)); NULL_TERMINATE_BUFFER(logdir); /* added to client ops below */ } else if (strcmp(argv[i], "-exit0") == 0) { exit0 = true; } /* FIXME i#1653: add support for options the old perl frontend supported */ else { /* pass to client */ BUFPRINT(client_ops, BUFFER_SIZE_ELEMENTS(client_ops), cliops_sofar, len, "`%s` ", argv[i]); } } #ifndef MACOS /* XXX i#1286: implement nudge on MacOS */ if (nudge_pid != 0) { if (i < argc) usage("%s", "-nudge does not take an app to run"); /* could also complain about other client or app specific ops */ res = dr_nudge_pid(nudge_pid, CLIENT_ID, NUDGE_LEAK_SCAN, INFINITE); if (res != DR_SUCCESS) { fatal("error nudging %d%s", nudge_pid, (res == DR_NUDGE_PID_NOT_INJECTED) ? ": no such Dr. Heapstat process" : ""); assert(false); /* shouldn't get here */ } exit(0); } #endif if (i >= argc) usage("%s", "no app specified"); app_name = argv[i]; get_full_path(app_name, full_app_name, BUFFER_SIZE_ELEMENTS(full_app_name)); if (full_app_name[0] != '\0') app_name = full_app_name; info("targeting application: \"%s\"", app_name); /* XXX: See drmemory/frontend.c for notes about our argv processing. */ app_argv = &argv[i]; if (verbose) { int j; c = buf; for (j = 0; app_argv[j] != NULL; j++) { c += _snprintf(c, BUFFER_SIZE_ELEMENTS(buf) - (c - buf), "\"%s\" ", app_argv[j]); } NULL_TERMINATE_BUFFER(buf); assert(c - buf < BUFFER_SIZE_ELEMENTS(buf)); info("app cmdline: %s", buf); } #ifndef X64 /* XXX: See drmemory/frontend.c for notes about 64-bit support. */ if (drfront_is_64bit_app(app_name, &is64, &is32) == DRFRONT_SUCCESS && is64 && !is32) { fatal("This Dr. Heapstat release does not support 64-bit applications."); goto error; /* actually won't get here */ } #endif if (!file_is_readable(dr_root)) { fatal("invalid -dr %s", dr_root); goto error; /* actually won't get here */ } if (dr_root != default_dr_root) { /* Workaround for DRi#1082 where DR root path can't have ".." */ get_absolute_path(dr_root, default_dr_root, BUFFER_SIZE_ELEMENTS(default_dr_root)); NULL_TERMINATE_BUFFER(default_dr_root); dr_root = default_dr_root; } _snprintf(buf, BUFFER_SIZE_ELEMENTS(buf), "%s/"LIB_ARCH"/%s/%s", dr_root, use_dr_debug ? "debug" : "release", DR_LIB_NAME); NULL_TERMINATE_BUFFER(buf); if (!file_is_readable(buf)) { /* support debug build w/ integrated debug DR build and so no release */ if (!use_dr_debug) { _snprintf(buf, BUFFER_SIZE_ELEMENTS(buf), "%s/"LIB_ARCH"/%s/%s", dr_root, "debug", DR_LIB_NAME); NULL_TERMINATE_BUFFER(buf); if (!file_is_readable(buf)) { fatal("cannot find DynamoRIO library %s", buf); goto error; /* actually won't get here */ } warn("using debug DynamoRIO since release not found"); use_dr_debug = true; } } /* once we have 64-bit we'll need to address the NSIS "bin/" requirement */ _snprintf(client_path, BUFFER_SIZE_ELEMENTS(client_path), "%s%c"BIN_ARCH"%c%s%c%s", drheapstat_root, DIRSEP, DIRSEP, use_drheapstat_debug ? "debug" : "release", DIRSEP, DRHEAPSTAT_LIB_NAME); NULL_TERMINATE_BUFFER(client_path); if (!file_is_readable(client_path)) { if (!use_drheapstat_debug) { _snprintf(client_path, BUFFER_SIZE_ELEMENTS(client_path), "%s%c"BIN_ARCH"%c%s%c%s", drheapstat_root, DIRSEP, DIRSEP, "debug", DIRSEP, DRHEAPSTAT_LIB_NAME); NULL_TERMINATE_BUFFER(client_path); if (!file_is_readable(client_path)) { fatal("invalid -drheapstat_root: cannot find %s", client_path); goto error; /* actually won't get here */ } /* try to avoid warning for devs running from build dir */ _snprintf(buf, BUFFER_SIZE_ELEMENTS(client_path), "%s%cCMakeCache.txt", drheapstat_root, DIRSEP); NULL_TERMINATE_BUFFER(buf); if (!file_is_readable(buf)) warn("using debug Dr. Heapstat since release not found"); use_drheapstat_debug = true; } } drfront_string_replace_character(logdir, ALT_DIRSEP, DIRSEP); /* canonicalize */ if (!file_is_writable(logdir)) { fatal("invalid -logdir: cannot find/write %s", logdir); goto error; /* actually won't get here */ } info("logdir is \"%s\"", logdir); BUFPRINT(client_ops, BUFFER_SIZE_ELEMENTS(client_ops), cliops_sofar, len, "-logdir `%s` ", logdir); /* Put DR logs inside drheapstat logdir */ if (!dr_logdir_specified) { _snprintf(buf, BUFFER_SIZE_ELEMENTS(buf), "%s%cdynamorio", logdir, DIRSEP); NULL_TERMINATE_BUFFER(buf); if (!dr_directory_exists(buf)) { if (!dr_create_dir(buf)) { /* check again in case of a race */ if (!dr_directory_exists(buf)) { fatal("cannot create %s", buf); goto error; /* actually won't get here */ } } } BUFPRINT(dr_ops, BUFFER_SIZE_ELEMENTS(dr_ops), drops_sofar, len, "-logdir `%s` ", buf); } #ifdef UNIX errcode = dr_inject_prepare_to_exec(app_name, (const char **)app_argv, &inject_data); #else errcode = dr_inject_process_create(app_name, (const char **)app_argv, &inject_data); #endif /* Mismatch is just a warning */ if (errcode != 0 && errcode != WARN_IMAGE_MACHINE_TYPE_MISMATCH_EXE) { #ifdef WINDOWS int sofar = #endif _snprintf(buf, BUFFER_SIZE_ELEMENTS(buf), "failed to create process for \"%s\": ", app_name); #ifdef WINDOWS if (sofar > 0) { FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errcode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) buf + sofar, BUFFER_SIZE_ELEMENTS(buf) - sofar*sizeof(char), NULL); } NULL_TERMINATE_BUFFER(buf); #endif fatal("%s", buf); goto error; /* actually won't get here */ } pid = dr_inject_get_process_id(inject_data); #ifdef WINDOWS if (pidfile != NULL) write_pid_to_file(pidfile, pid); #endif process = dr_inject_get_image_name(inject_data); /* we don't care if this app is already registered for DR b/c our * this-pid config will override */ info("configuring %s pid=%d dr_ops=\"%s\"", process, pid, dr_ops); if (dr_register_process(process, pid, false/*local*/, dr_root, DR_MODE_CODE_MANIPULATION, use_dr_debug, DR_PLATFORM_DEFAULT, dr_ops) != DR_SUCCESS) { fatal("failed to register DynamoRIO configuration"); goto error; /* actually won't get here */ } info("configuring client \"%s\" ops=\"%s\"", client_path, client_ops); if (dr_register_client(process, pid, false/*local*/, DR_PLATFORM_DEFAULT, CLIENT_ID, 0, client_path, client_ops) != DR_SUCCESS) { fatal("failed to register DynamoRIO client configuration"); goto error; /* actually won't get here */ } if (!dr_inject_process_inject(inject_data, false/*!force*/, NULL)) { fatal("unable to inject"); goto error; /* actually won't get here */ } dr_inject_process_run(inject_data); #ifdef UNIX fatal("Failed to exec application"); #else info("waiting for app to exit..."); errcode = WaitForSingleObject(dr_inject_get_process_handle(inject_data), INFINITE); if (errcode != WAIT_OBJECT_0) info("failed to wait for app: %d\n", errcode); #endif errcode = dr_inject_process_exit(inject_data, false/*don't kill process*/); goto cleanup; error: dr_inject_process_exit(inject_data, false); errcode = 1; cleanup: sc = drfront_cleanup_args(argv, argc); if (sc != DRFRONT_SUCCESS) fatal("drfront_cleanup_args failed: %d\n", sc); return (exit0 ? 0 : errcode); }