This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

PR/2386 [2/2]: MinGW attach to process without an exec file


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




2007-12-28  Pedro Alves  <pedro_alves@portugalmail.pt>

	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 <stdlib.h>
 #include <windows.h>
 #include <imagehlp.h>
+#include <psapi.h>
+#include <tlhelp32.h>
+#include <ntdef.h>
 #ifdef __CYGWIN__
 #include <sys/cygwin.h>
 #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 <psapi.h>
 
 #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 (&current_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)



Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]