>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
#include <iostream>
#include <iomanip>
#include <sstream>
#include <fstream>
#include <string>
#include <unistd.h>
#include <sys/cygwin.h>
#include <limits.h>
#define NO_MINMAX
#include <windows.h>
int main(int argc, char* argv[])
{
int pid = getpid ();
std::ostringstream ostr;
ostr << "/proc/" << pid << "/maps";
std::ifstream ifs(ostr.str().c_str());
std::cout << ifs.rdbuf() << std::endl;
uintptr_t addr;
WCHAR wbuf[PATH_MAX];
char nbuf[4*PATH_MAX];
while (std::cin)
{
std::cin >> std::hex >> addr;
if (std::cin)
{
wbuf[0] = L'\0';
int rval = cygwin_internal(CW_GET_MODULE_PATH_FOR_ADDR,
addr, wbuf, PATH_MAX);
wcstombs (nbuf, wbuf, 4*PATH_MAX);
std::cout << "0x" << std::hex << std::setw(8) << std::setfill('0')
<< addr << " (" << std::dec << rval << ") " << nbuf << std::endl;
}
}
return 0;
}