This is the mail archive of the cygwin-patches mailing list for the Cygwin 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]

Add cygwin_internal CW_GET_MODULE_PATH_FOR_ADDR


>From discussions with Bruno Haible about the slowness of full relocation
support in libintl and libiconv, he said:

>   - The Cygwin API only allows me to get _all_ file names behind all
>     addresses across the entire current process, and this is slow.

(talking about parsing /proc/self/maps)

>   - It would be useful to have a Cygwin API that gives me the file
>     file name behind one particular address in the current process.
>     This should not be that slow.

This patch is a proof of concept for the latter.  Naturally, it needs
additional work -- updating version.h, real changelog entries,
documentation somewhere, etc.  But...is it worth the effort?  Is
something like this likely to be accepted?

I've also attached a test program.  To compile it (using g++), you need
to ensure that the updated sys/cygwin.h is in the search path.  It
prints the contents of /proc/self/maps, and then you can type any (hex)
memory address and it should report the func's return value, and the
correct path to the associated module.  CTRL-D to exit.

61000020
0x61000020 (0) /usr/bin/cygwin1.dll
00020000
0x00020000 (1)

The call signature is:

	unsigned long
	cygwin_internal (CW_GET_MODULE_PATH_FOR_ADDR,
                         uintptr_t addr,
                         PWCHAR    buf,
                         size_t    buflen);

--
Chuck


Index: external.cc
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/external.cc,v
retrieving revision 1.124
diff -u -p -r1.124 external.cc
--- external.cc	5 Oct 2011 12:27:36 -0000	1.124
+++ external.cc	13 Oct 2011 06:50:43 -0000
@@ -197,6 +197,9 @@ exit_process (UINT status, bool useTermi
   ExitProcess (status);
 }
 
+/* Defined in fhandler_process.cc */
+extern int
+get_module_path_for_addr (uintptr_t addr, PWCHAR dest, size_t dlen);
 
 extern "C" unsigned long
 cygwin_internal (cygwin_getinfo_types t, ...)
@@ -528,6 +531,15 @@ cygwin_internal (cygwin_getinfo_types t,
 	}
 	break;
 
+      case CW_GET_MODULE_PATH_FOR_ADDR:
+	{
+	  uintptr_t addr = va_arg (arg, uintptr_t);
+	  PWCHAR dest = va_arg (arg, PWCHAR);
+	  size_t dlen = va_arg (arg, size_t);
+	  res = get_module_path_for_addr (addr, dest, dlen);
+	}
+	break;
+
       default:
 	set_errno (ENOSYS);
     }
Index: fhandler_process.cc
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/fhandler_process.cc,v
retrieving revision 1.110
diff -u -p -r1.110 fhandler_process.cc
--- fhandler_process.cc	10 Oct 2011 18:59:56 -0000	1.110
+++ fhandler_process.cc	13 Oct 2011 06:50:44 -0000
@@ -1441,3 +1441,97 @@ out:
   CloseHandle (hProcess);
   return res;
 }
+
+/* Helper function for cygwin_internal. Implemented here, rather
+ * than in external.cc, so as to reuse dos_drive_mappings,
+ * heap_info, and thread_info helper classes. Returns 0 on success,
+ * nonzero otherwise. Borrows heavily from format_process_maps().
+ */
+int
+get_module_path_for_addr (uintptr_t addr, PWCHAR dest, size_t dlen)
+{
+  int rval = -1;
+  DWORD wpid = GetCurrentProcessId ();
+  HANDLE proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
+                             FALSE, wpid);
+  if (!proc)
+    return rval;
+
+  NTSTATUS status;
+  PROCESS_BASIC_INFORMATION pbi;
+
+  memset (&pbi, 0, sizeof (pbi));
+  status = NtQueryInformationProcess (proc, ProcessBasicInformation,
+                                      &pbi, sizeof pbi, NULL);
+  if (!NT_SUCCESS (status))
+    {
+      CloseHandle (proc);
+      return rval;
+    }
+  PPEB peb = pbi.PebBaseAddress;
+
+  /* myself is in the same spot in every process, so is the pointer to the
+     procinfo.  But make sure the destructor doesn't try to release procinfo! */
+  pinfo proc_pinfo;
+  if (ReadProcessMemory (proc, &myself, &proc_pinfo, sizeof proc_pinfo, NULL))
+    proc_pinfo.preserve ();
+  /* The heap info on the cygheap is also in the same spot in each process
+     because the cygheap is located at the same address. */
+  user_heap_info user_heap;
+  ReadProcessMemory (proc, &cygheap->user_heap, &user_heap,
+                     sizeof user_heap, NULL);
+
+  MEMORY_BASIC_INFORMATION mb;
+  VirtualQueryEx (proc, (const void*)addr, &mb, sizeof mb);
+
+  dos_drive_mappings drive_maps;
+  heap_info heaps (wpid);
+  thread_info threads (wpid, proc);
+
+  tmp_pathbuf tp;
+  PMEMORY_SECTION_NAME msi = (PMEMORY_SECTION_NAME) tp.w_get ();
+  char *posix_modname = tp.c_get();
+  posix_modname[0] = '\0';
+  /* If the return length pointer is missing, NtQueryVirtualMemory
+   * returns with STATUS_ACCESS_VIOLATION on Windows 2000. */
+  ULONG ret_len = 0;
+
+  if ((mb.State != MEM_FREE)
+      && (mb.Type & (MEM_MAPPED | MEM_IMAGE))
+      && NT_SUCCESS (status = NtQueryVirtualMemory (proc, mb.AllocationBase,
+                                                    MemorySectionName,
+                                                    msi, 65536, &ret_len)))
+    {
+      PWCHAR dosname =
+        drive_maps.fixup_if_match (msi->SectionFileName.Buffer);
+      if (mount_table->conv_to_posix_path (dosname,
+                                           posix_modname, 0))
+        sys_wcstombs (posix_modname, NT_MAX_PATH, dosname);
+    }
+  else if (!threads.fill_if_match ((char*)mb.AllocationBase, mb.Type,
+                                   posix_modname)
+           && !heaps.fill_if_match ((char*)mb.AllocationBase, mb.Type,
+                                    posix_modname))
+    {
+      if (mb.AllocationBase == (char *) peb)
+        strcpy (posix_modname, "[peb]");
+      else if (mb.AllocationBase == (char *) &SharedUserData)
+        strcpy (posix_modname, "[shared-user-data]");
+      else if (mb.AllocationBase == (char *) cygwin_shared)
+        strcpy (posix_modname, "[cygwin-shared]");
+      else if (mb.AllocationBase == (char *) user_shared)
+        strcpy (posix_modname, "[cygwin-user-shared]");
+      else if (mb.AllocationBase == (char *) *proc_pinfo)
+        strcpy (posix_modname, "[procinfo]");
+      else if (mb.AllocationBase == user_heap.base)
+        strcpy (posix_modname, "[heap]");
+      else
+        posix_modname[0] = 0;
+    }
+
+  CloseHandle (proc);
+  rval = (posix_modname[0] == 0);
+  sys_mbstowcs (dest, dlen, posix_modname, NT_MAX_PATH);
+  return rval;
+}
+
Index: include/sys/cygwin.h
===================================================================
RCS file: /cvs/src/src/winsup/cygwin/include/sys/cygwin.h,v
retrieving revision 1.97
diff -u -p -r1.97 cygwin.h
--- include/sys/cygwin.h	7 Oct 2011 13:49:17 -0000	1.97
+++ include/sys/cygwin.h	13 Oct 2011 06:50:44 -0000
@@ -135,7 +135,8 @@ typedef enum
     CW_CVT_MNT_OPTS,
     CW_LST_MNT_OPTS,
     CW_STRERROR,
-    CW_CVT_ENV_TO_WINENV
+    CW_CVT_ENV_TO_WINENV,
+    CW_GET_MODULE_PATH_FOR_ADDR
   } cygwin_getinfo_types;
 
 #define CW_LOCK_PINFO CW_LOCK_PINFO
@@ -183,6 +184,7 @@ typedef enum
 #define CW_LST_MNT_OPTS CW_LST_MNT_OPTS
 #define CW_STRERROR CW_STRERROR
 #define CW_CVT_ENV_TO_WINENV CW_CVT_ENV_TO_WINENV
+#define CW_GET_MODULE_PATH_FOR_ADDR CW_GET_MODULE_PATH_FOR_ADDR
 
 /* Token type for CW_SET_EXTERNAL_TOKEN */
 enum

Attachment: test-get-module-path-for-addr.cc
Description: Text document


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