2007-12-28 Pedro Alves PR gdb/2386 * win32-nat.c: Include "tlhelp32.h", "ntdef.h" and "safe-ctype.h". (current_process_file_handle): New variable. (load_psapi): New function. (psapi_get_dll_name): Use load_psapi. (get_win32_debug_event): Don't close the process file handle. Store it in current_process_file_handle. (do_initial_win32_stuff): Clear current_process_file_handle. (pid_to_exec_file_psapi): New function. (toolhelp_loaded, toolhelp_module_handle) (toolhelp_CreateToolhelp32Snapshot, toolhelp_Process32First) (toolhelp_Process32Next): New variables. (load_toolhelp, pid_to_exec_file_toolhelp_9x) (device_filename_to_dos_filename): New functions. (OBJECT_INFO_CLASS, OBJECT_NAME_INFO): New structs. (NTQUERYOBJECT): New variable. (get_nt_object_name, get_file_name_from_handle_objname) (win32_pid_to_exec_file_1): New functions. (win32_pid_to_exec_file): Use win32_pid_to_exec_file_1. (win32_mourn_inferior): Close current_process_file_handle. * Makefile.in (win32-nat.o): Add dependency on $(safe_ctype_h). --- gdb/Makefile.in | 2 gdb/win32-nat.c | 333 +++++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 284 insertions(+), 51 deletions(-) Index: src/gdb/win32-nat.c =================================================================== --- src.orig/gdb/win32-nat.c 2007-12-27 00:45:50.000000000 +0000 +++ src/gdb/win32-nat.c 2007-12-27 23:55:02.000000000 +0000 @@ -38,6 +38,9 @@ #include #include #include +#include +#include +#include #ifdef __CYGWIN__ #include #endif @@ -57,6 +60,7 @@ #include "solist.h" #include "solib.h" #include "xml-support.h" +#include "safe-ctype.h" #include "i386-tdep.h" #include "i387-tdep.h" @@ -84,7 +88,6 @@ enum CONTEXT_DEBUGGER = (CONTEXT_FULL | CONTEXT_FLOATING_POINT) }; #endif -#include #define CONTEXT_DEBUGGER_DR CONTEXT_DEBUGGER | CONTEXT_DEBUG_REGISTERS \ | CONTEXT_EXTENDED_REGISTERS @@ -135,6 +138,8 @@ static thread_info thread_head; static DEBUG_EVENT current_event; /* The current debug event from WaitForDebugEvent */ static HANDLE current_process_handle; /* Currently executing process */ +static HANDLE current_process_file_handle; /* Currently executing + process' file handle */ static thread_info *current_thread; /* Info on currently selected thread */ static DWORD main_thread_id; /* Thread ID of the main thread */ @@ -463,6 +468,28 @@ static BOOL WINAPI (*psapi_GetModuleInfo static DWORD WINAPI (*psapi_GetModuleFileNameExA) (HANDLE, HMODULE, LPSTR, DWORD) = NULL; static int +load_psapi (void) +{ + if (!psapi_loaded) + { + psapi_loaded = 1; + psapi_module_handle = LoadLibrary ("psapi.dll"); + if (psapi_module_handle != NULL) + { + psapi_EnumProcessModules + = GetProcAddress (psapi_module_handle, "EnumProcessModules"); + psapi_GetModuleInformation + = GetProcAddress (psapi_module_handle, "GetModuleInformation"); + psapi_GetModuleFileNameExA = (void *) + GetProcAddress (psapi_module_handle, "GetModuleFileNameExA"); + } + } + + return psapi_module_handle != NULL; +} + + +static int psapi_get_dll_name (DWORD BaseAddress, char *dll_name_ret) { DWORD len; @@ -473,29 +500,13 @@ psapi_get_dll_name (DWORD BaseAddress, c DWORD cbNeeded; BOOL ok; - if (!psapi_loaded || - psapi_EnumProcessModules == NULL || - psapi_GetModuleInformation == NULL || - psapi_GetModuleFileNameExA == NULL) - { - if (psapi_loaded) - goto failed; - psapi_loaded = 1; - psapi_module_handle = LoadLibrary ("psapi.dll"); - if (!psapi_module_handle) - { - /* printf_unfiltered ("error loading psapi.dll: %u", GetLastError ()); */ - goto failed; - } - psapi_EnumProcessModules = GetProcAddress (psapi_module_handle, "EnumProcessModules"); - psapi_GetModuleInformation = GetProcAddress (psapi_module_handle, "GetModuleInformation"); - psapi_GetModuleFileNameExA = (void *) GetProcAddress (psapi_module_handle, - "GetModuleFileNameExA"); - if (psapi_EnumProcessModules == NULL || - psapi_GetModuleInformation == NULL || - psapi_GetModuleFileNameExA == NULL) - goto failed; - } + if (!load_psapi ()) + goto failed; + + if (psapi_EnumProcessModules == NULL + || psapi_GetModuleInformation == NULL + || psapi_GetModuleFileNameExA == NULL) + goto failed; cbNeeded = 0; ok = (*psapi_EnumProcessModules) (current_process_handle, @@ -1277,7 +1288,7 @@ get_win32_debug_event (int pid, struct t (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId, "CREATE_PROCESS_DEBUG_EVENT")); - CloseHandle (current_event.u.CreateProcessInfo.hFile); + current_process_file_handle = current_event.u.CreateProcessInfo.hFile; if (++saw_create != 1) break; @@ -1439,8 +1450,9 @@ do_initial_win32_stuff (DWORD pid) #ifdef __CYGWIN__ cygwin_load_start = cygwin_load_end = 0; #endif - current_event.dwProcessId = pid; memset (¤t_event, 0, sizeof (current_event)); + current_event.dwProcessId = pid; + current_process_file_handle = INVALID_HANDLE_VALUE; push_target (&win32_ops); disable_breakpoints_in_shlibs (); win32_clear_solib (); @@ -1659,38 +1671,253 @@ win32_detach (char *args, int from_tty) unpush_target (&win32_ops); } +static BOOL +pid_to_exec_file_psapi (DWORD pid, char *pathbuf) +{ + BOOL ok = FALSE; + + load_psapi (); + + if (psapi_GetModuleFileNameExA != NULL) + { + HANDLE h; + h = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, pid); + if (h != NULL) + { + if (psapi_GetModuleFileNameExA (h, 0, pathbuf, MAX_PATH) > 0) + ok = TRUE; + CloseHandle (h); + } + } + + return ok; +} + +static int toolhelp_loaded = 0; +static HMODULE toolhelp_module_handle = NULL; +static HANDLE WINAPI (*toolhelp_CreateToolhelp32Snapshot) (DWORD, DWORD); +static BOOL WINAPI (*toolhelp_Process32First) (HANDLE, LPPROCESSENTRY32); +static BOOL WINAPI (*toolhelp_Process32Next) (HANDLE, LPPROCESSENTRY32); + +static int +load_toolhelp (void) +{ + if (!toolhelp_loaded) + { + toolhelp_loaded = 1; + toolhelp_module_handle = GetModuleHandle ("kernel32.dll"); + if (toolhelp_module_handle != NULL) + { + toolhelp_CreateToolhelp32Snapshot = (void *) + GetProcAddress (toolhelp_module_handle, "CreateToolhelp32Snapshot"); + toolhelp_Process32First + = GetProcAddress (toolhelp_module_handle, "Process32First"); + toolhelp_Process32Next + = GetProcAddress (toolhelp_module_handle, "Process32Next"); + } + } + + return toolhelp_module_handle != NULL; +} + +static BOOL +pid_to_exec_file_toolhelp_9x (DWORD pid, char *pathbuf) +{ + BOOL ok = 0; + HANDLE h; + + if (!load_toolhelp () + || toolhelp_CreateToolhelp32Snapshot == NULL + || toolhelp_Process32First == NULL + || toolhelp_Process32Next == NULL) + return FALSE; + + h = toolhelp_CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); + if (h != INVALID_HANDLE_VALUE) + { + PROCESSENTRY32 e = { sizeof (PROCESSENTRY32) }; + int n; + for (n = toolhelp_Process32First (h, &e); + n; + n = toolhelp_Process32Next (h, &e)) + if (e.th32ProcessID == pid) + { + strcpy (pathbuf, e.szExeFile); + ok = TRUE; + break; + } + + CloseHandle (h); + } + + return ok; +} + +/* Translate a Windows path with device name to drive letters. */ +static BOOL +device_filename_to_dos_filename (char *out, const char *in, size_t nSize) +{ + char drive[] = "_:"; + char all_drives[0x1000]; + char devices[MAX_PATH + 1]; + char *p; + + /* A NULL terminated list of NULL terminated strings. */ + if (!GetLogicalDriveStrings (sizeof all_drives - 1, all_drives)) + return FALSE; + + p = all_drives; + while (*p) + { + /* Copy the drive letter to the template string. */ + *drive = *p; + + /* Look up each device name. DEVICES is a NULL terminated list + of NULL terminated strings. */ + if (QueryDosDevice (drive, devices, sizeof devices - 1)) + { + size_t len = strlen (devices); + if (len < MAX_PATH + && (in[len] == '\\' || in[len] == '\0') + && strncasecmp (in, devices, len) == 0) + { + /* Reconstruct the filename by replacing the device path + with a DOS drive path. */ + snprintf (out, MAX_PATH, "%s%s", drive, in + len); + return TRUE; + } + } + + /* Next drive letter. */ + while (*p++) + ; + } + + return FALSE; +} + +typedef enum tagOBJECT_INFO_CLASS +{ + ObjectNameInfo = 1, +} OBJECT_INFO_CLASS; + +typedef struct tagOBJECT_NAME_INFO +{ + UNICODE_STRING ObjectName; + WCHAR ObjectNameBuffer[1]; +} OBJECT_NAME_INFO; + +typedef NTSTATUS (NTAPI * NTQUERYOBJECT)(HANDLE, OBJECT_INFO_CLASS, + PVOID, ULONG, PULONG); +static BOOL +get_nt_object_name (HANDLE h, char *namebuf) +{ + BOOL ok = FALSE; + HMODULE ntdll = GetModuleHandle ("ntdll.dll"); + NTQUERYOBJECT NtQueryObject + = (NTQUERYOBJECT) GetProcAddress (ntdll, "NtQueryObject"); + + size_t size = sizeof (OBJECT_NAME_INFO) + (MAX_PATH + 1) * sizeof(WCHAR); + OBJECT_NAME_INFO *nameinfo = xmalloc (size); + + NTSTATUS rc = NtQueryObject (h, ObjectNameInfo, nameinfo, size, NULL); + if (rc == STATUS_SUCCESS) + { + wchar_t *wname = nameinfo->ObjectName.Buffer; + wcstombs (namebuf, wname, size - wcslen (wname) + 1); + ok = TRUE; + } + + xfree (nameinfo); + return ok; +} + +static BOOL +get_file_name_from_handle_objname (HANDLE file, char *pathbuf) +{ + char buf[MAX_PATH + 1]; + return (file != INVALID_HANDLE_VALUE + && get_nt_object_name (file, buf) + && device_filename_to_dos_filename (pathbuf, buf, MAX_PATH) + && ISALPHA (pathbuf[0]) && pathbuf[1] == ':'); +} + +static int +win32_pid_to_exec_file_1 (int pid, char *pathbuf) +{ + OSVERSIONINFO osvi = { 0 }; + +#ifdef __CYGWIN__ + { + /* Try to find the process path using the Cygwin internal + process list. + + TODO: Also find native Windows processes using + CW_GETPINFO_FULL. */ + int cpid; + struct external_pinfo *pinfo; + + cygwin_internal (CW_LOCK_PINFO, 1000); + for (cpid = 0; + (pinfo = (struct external_pinfo *) + cygwin_internal (CW_GETPINFO, cpid | CW_NEXTPID)); + cpid = pinfo->pid) + { + if (pinfo->dwProcessId == pid) /* Got it */ + { + strcpy (pathbuf, pinfo->progname); + return 1; + } + } + cygwin_internal (CW_UNLOCK_PINFO); + } +#endif + + osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); + GetVersionEx (&osvi); + if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) + { + if (pid == current_event.dwProcessId) + { + /* Try with native NT functionality. */ + HANDLE f = current_process_file_handle; + if (get_file_name_from_handle_objname (f, pathbuf)) + return 1; + } + + /* Fall back to using psapi.dll, which may not be present. */ + if (pid_to_exec_file_psapi (pid, pathbuf)) + return 1; + + return 0; + } + else + /* Toolhelp functionality on 9x is always available in + kernel32.dll. Unlike the NT version, it returns a full + path. */ + return pid_to_exec_file_toolhelp_9x (pid, pathbuf); +} + static char * win32_pid_to_exec_file (int pid) { - /* Try to find the process path using the Cygwin internal process list - pid isn't a valid pid, unfortunately. Use current_event.dwProcessId - instead. */ + static char pathbuf[MAX_PATH + 1]; - static char path[MAX_PATH + 1]; - char *path_ptr = NULL; + /* PID isn't a valid pid, unfortunately. Use current_event.dwProcessId + instead. */ + pid = current_event.dwProcessId; + if (win32_pid_to_exec_file_1 (pid, pathbuf)) + { #ifdef __CYGWIN__ - /* TODO: Also find native Windows processes using CW_GETPINFO_FULL. */ - int cpid; - struct external_pinfo *pinfo; - - cygwin_internal (CW_LOCK_PINFO, 1000); - for (cpid = 0; - (pinfo = (struct external_pinfo *) - cygwin_internal (CW_GETPINFO, cpid | CW_NEXTPID)); - cpid = pinfo->pid) - { - if (pinfo->dwProcessId == current_event.dwProcessId) /* Got it */ - { - cygwin_conv_to_full_posix_path (pinfo->progname, path); - path_ptr = path; - break; - } - } - cygwin_internal (CW_UNLOCK_PINFO); + char buf[sizeof pathbuf]; + strcpy (buf, pathbuf); + cygwin_conv_to_full_posix_path (buf, pathbuf); #endif + return pathbuf; + } - return path_ptr; + return NULL; } /* Print status information about what we're accessing. */ @@ -1848,8 +2075,14 @@ win32_mourn_inferior (void) if (open_process_used) { CHECK (CloseHandle (current_process_handle)); + current_process_handle = NULL; open_process_used = 0; } + if (current_process_file_handle != INVALID_HANDLE_VALUE) + { + CHECK (CloseHandle (current_process_file_handle)); + current_process_file_handle = INVALID_HANDLE_VALUE; + } unpush_target (&win32_ops); generic_mourn_inferior (); } Index: src/gdb/Makefile.in =================================================================== --- src.orig/gdb/Makefile.in 2007-12-27 00:45:50.000000000 +0000 +++ src/gdb/Makefile.in 2007-12-27 23:50:04.000000000 +0000 @@ -2946,7 +2946,7 @@ win32-nat.o: win32-nat.c $(defs_h) $(fra $(regcache_h) $(top_h) $(buildsym_h) $(symfile_h) $(objfiles_h) \ $(gdb_string_h) $(gdbthread_h) $(gdbcmd_h) $(exec_h) $(solist_h) \ $(solib_h) $(i386_tdep_h) $(i387_tdep_h) $(gdb_obstack_h) \ - $(xml_support_h) $(i386_cygwin_tdep_h) $(gdb_stdint_h) + $(xml_support_h) $(safe_ctype_h) $(i386_cygwin_tdep_h) $(gdb_stdint_h) win32-termcap.o: win32-termcap.c wrapper.o: wrapper.c $(defs_h) $(value_h) $(exceptions_h) $(wrapper_h) \ $(ui_out_h)