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]

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


Christopher Faylor wrote:
I'm not going to comment on the MinGW aspects of this other than to note
that I think it is rather intrusive and I don't worrying about ancient
Windows versions is a good idea.


Then we should document it somewhere, that we don't support Windows 9x anymore. Maybe in the NEWS file ?

Can we reach a compromise here ?  I've removed the 9x support from this
patch.  With the changes inplace, win32_pid_to_exec_file

  - looks in the Cygwin processes using cygwin_internal (CW_GETPINFO, ...).
    This is what's done currently, so Cygwin processes will be detected
    like before.
  - If that fails, tries to get at the filename associated with the file
    handle that the debug api gives us in the CREATE_PROCESS_DEBUG_EVENT.
    Previously, it was just closed; we now store it in a global variable.
    This relies on the internal NT name of the HANDLE, and it may change
    in future releases, hence,
  - If that fails, GetModuleFileNameEx from psapi.dll is used.

The rest of the patch is just refactoring the psapi.dll loading
into its own function, not closing the file handle immediatelly, and
auxilary static functions to keep the code readable.

The enabling of the extra methods on Cygwin removes the need to
implement this TODO:
   "TODO: Also find native Windows processes using CW_GETPINFO_FULL."

... which is useful for attaching to apps built with
'gcc -mno-cygwin' with a Cygwin gdb.

If you want to get the process name from a cygwin pid you just use the
/proc interface.


Not useful in this case, as the pid gdb holds is the native Windows pid. Hence current usage of cygwin_internal (CW_GETPINFO, ...).

(I have a patch to clean up the ptid_t usage in win32-nat.c which
removes that "PID isn't a valid pid, unfortunately" limitation ... )

cgf


-- Pedro Alves

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

	PR gdb/2386
	* win32-nat.c: Include "ntdef.h", "wchar.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, 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): Also find native Windows processes.
	(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 |  262 +++++++++++++++++++++++++++++++++++++++++++++-----------
 2 files changed, 214 insertions(+), 50 deletions(-)

Index: src/gdb/win32-nat.c
===================================================================
--- src.orig/gdb/win32-nat.c	2007-12-28 22:29:52.000000000 +0000
+++ src/gdb/win32-nat.c	2007-12-28 22:30:48.000000000 +0000
@@ -38,6 +38,9 @@
 #include <stdlib.h>
 #include <windows.h>
 #include <imagehlp.h>
+#include <psapi.h>
+#include <ntdef.h>
+#include <wchar.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,
@@ -1345,7 +1356,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;
 
@@ -1507,8 +1518,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 ();
@@ -1727,38 +1739,184 @@ 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;
+}
+
+/* Translate a Windows path with device name to a dos style path with
+   drive letters.
+   E.g.:
+   "\\Device\\HarddiskVolume1\\cygwin\\bin\\cat.exe"
+     -> "c:\\cygwin\\bin\\cat.exe"
+*/
+static BOOL
+device_filename_to_dos_filename (char *out, const char *in, size_t outsize)
+{
+  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, outsize, "%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, 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] == ':');
+}
+
+/* Accepts an integer PID; Returns a string representing a file that
+   can be opened to get the symbols for the child process.  */
+
 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
+  static char path[MAX_PATH + 1];
+  BOOL ok = FALSE;
+
+  /* PID isn't a valid pid, unfortunately.  Use current_event.dwProcessId
      instead.  */
+  pid = current_event.dwProcessId;
 
-  static char path[MAX_PATH + 1];
-  char *path_ptr = NULL;
+#ifdef __CYGWIN__
+  {
+    /* Try to find the process path using the Cygwin internal process
+       list.  */
+    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);
+	    ok = TRUE;
+	    break;
+	  }
+      }
+    cygwin_internal (CW_UNLOCK_PINFO);
+    if (ok)
+      return path;
+  }
+#endif
 
+  /* Try with native NT functionality.  This only works if
+     pid == current_event.dwProcessId.  */
+  if (!ok
+      && pid == current_event.dwProcessId
+      && get_file_name_from_handle_objname (current_process_file_handle,
+					    path))
+    ok = TRUE;
+
+  /* Fallback to trying with PSAPI.  */
+  if (!ok && pid_to_exec_file_psapi (pid, path))
+    ok = TRUE;
+
+  if (ok)
+    {
 #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 path];
+      strcpy (buf, path);
+      cygwin_conv_to_full_posix_path (buf, path);
 #endif
+      return path;
+    }
 
-  return path_ptr;
+  return NULL;
 }
 
 /* Print status information about what we're accessing.  */
@@ -1916,8 +2074,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-28 22:29:46.000000000 +0000
+++ src/gdb/Makefile.in	2007-12-28 22:30:48.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]