From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 9192 invoked by alias); 28 Dec 2007 01:21:52 -0000 Received: (qmail 9144 invoked by uid 22791); 28 Dec 2007 01:21:51 -0000 X-Spam-Check-By: sourceware.org Received: from fk-out-0910.google.com (HELO fk-out-0910.google.com) (209.85.128.190) by sourceware.org (qpsmtpd/0.31) with ESMTP; Fri, 28 Dec 2007 01:21:42 +0000 Received: by fk-out-0910.google.com with SMTP id 26so4106086fkx.8 for ; Thu, 27 Dec 2007 17:21:39 -0800 (PST) Received: by 10.82.152.16 with SMTP id z16mr15635645bud.17.1198804898475; Thu, 27 Dec 2007 17:21:38 -0800 (PST) Received: from ?88.210.77.177? ( [88.210.77.177]) by mx.google.com with ESMTPS id i4sm6591839nfh.36.2007.12.27.17.21.35 (version=TLSv1/SSLv3 cipher=RC4-MD5); Thu, 27 Dec 2007 17:21:38 -0800 (PST) Message-ID: <47744F9C.8040604@portugalmail.pt> Date: Fri, 28 Dec 2007 01:35:00 -0000 From: Pedro Alves User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; pt-BR; rv:1.8.1.9) Gecko/20071031 Thunderbird/2.0.0.9 Mnenhy/0.7.5.0 MIME-Version: 1.0 To: gdb-patches@sourceware.org CC: Bogdan Slusarczyk Subject: PR/2386 [2/2]: MinGW attach to process without an exec file Content-Type: multipart/mixed; boundary="------------050701040603030200080805" X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2007-12/txt/msg00428.txt.bz2 This is a multi-part message in MIME format. --------------050701040603030200080805 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Content-length: 798 The target_pid_to_exec_file MinGW implementation win32-nat.c:win32_pid_to_exec_file is currently an empty stub, as I mentioned on http://sourceware.org/ml/gdb/2007-10/msg00104.html. This patch fills the gap. The patch covers 9x/NT, and tries to work even if psapi.dll isn't present, as it isn't part of the OS. To do that, it has to resort to non documented NT funcionality... If that doesn't work, it falls back into using psapi.dll. I'd be very happy if someone could point me at better ways to get at executable from a PID on Windows, or at a filename from a file handle. Note that de documented way to use a mapping relies on psapi.dll. This is also useful when debugging a native win32 app from a Cygwin gdb. Tested on XP Pro SP2, i686-pc-cygwin, no changes. OK ? -- Pedro Alves --------------050701040603030200080805 Content-Type: text/x-diff; name="win32_pid_to_exec_file.diff" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="win32_pid_to_exec_file.diff" Content-length: 13831 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) --------------050701040603030200080805--