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]

[rfc] DLL support for gdbserver


Here's a final version of gdbserver support for Windows DLLs.
It works for me testing with Cygwin.  Pedro, would you like to test
this on Windows CE to see what I've broken?

-- 
Daniel Jacobowitz
CodeSourcery

2007-07-02  Pedro Alves  <pedro_alves@portugalmail.pt>
	    Daniel Jacobowitz  <dan@codesourcery.com>

	* config/i386/cygwin.mt (TDEPFILES): Add solib-target.o.
	* coff-pe-read.c (read_pe_exported_syms): Delete verbose
	printf.
	* NEWS: Mention gdbserver DLL support.

2007-07-02  Pedro Alves  <pedro_alves@portugalmail.pt>
	    Daniel Jacobowitz  <dan@codesourcery.com>

	* gdb.base/unload.c (dlopen, dlsym, dlclose, dlerror): Define
	for __WIN32__.
	(SHLIB_NAME): Delete definition.  Always pass dlerror to fprintf.
	* gdb.base/unload.exp: Use shared library test routines.

2007-07-02  Pedro Alves  <pedro_alves@portugalmail.pt>
	    Daniel Jacobowitz  <dan@codesourcery.com>

	* inferiors.c (all_dlls, dlls_changed, get_dll): New.
	(add_thread): Minor cleanups.
	(clear_inferiors): Move lower in the file.  Clear the DLL
	list.
	(free_one_dll, match_dll, loaded_dll, unloaded_dll, clear_list): New.
	* remote-utils.c (prepare_resume_reply): Check dlls_changed.
	(xml_escape_text): New.
	* server.c (handle_query): Handle qXfer:libraries:read.  Report it
	for qSupported.
	(handle_v_cont): Report errors.
	(gdbserver_version): Update.
	(main): Correct size of own_buf.  Do not report initial DLL events.
	* server.h (struct dll_info, all_dlls, dlls_changed, loaded_dll)
	(unloaded_dll, xml_escape_text): New.
	* win32-low.c (enum target_waitkind): Update comments.
	(win32_add_one_solib, get_image_name, winapi_EnumProcessModules)
	(winapi_GetModuleInformation, winapi_GetModuleFileNameExA)
	(win32_EnumProcessModules, win32_GetModuleInformation)
	(win32_GetModuleFileNameExA, load_psapi, psapi_get_dll_name)
	(winapi_CreateToolhelp32Snapshot, winapi_Module32First)
	(winapi_Module32Next, win32_CreateToolhelp32Snapshot)
	(win32_Module32First, win32_Module32Next, load_toolhelp)
	(toolhelp_get_dll_name, handle_load_dll, handle_unload_dll): New.
	(get_child_debug_event): Handle DLL events.
	(win32_wait): Likewise.

---
 gdb/NEWS                          |    3 
 gdb/coff-pe-read.c                |    3 
 gdb/config/i386/cygwin.mt         |    3 
 gdb/gdbserver/inferiors.c         |   93 +++++++++-
 gdb/gdbserver/remote-utils.c      |   69 +++++++
 gdb/gdbserver/server.c            |   79 ++++++++-
 gdb/gdbserver/server.h            |   15 +
 gdb/gdbserver/win32-low.c         |  332 +++++++++++++++++++++++++++++++++++++-
 gdb/testsuite/gdb.base/unload.c   |   14 +
 gdb/testsuite/gdb.base/unload.exp |   23 --
 10 files changed, 591 insertions(+), 43 deletions(-)

Index: src/gdb/config/i386/cygwin.mt
===================================================================
--- src.orig/gdb/config/i386/cygwin.mt	2007-07-02 15:28:31.000000000 -0400
+++ src/gdb/config/i386/cygwin.mt	2007-07-02 15:28:55.000000000 -0400
@@ -1,2 +1,3 @@
 # Target: Intel 386 run win32
-TDEPFILES= i386-tdep.o i386-cygwin-tdep.o i387-tdep.o
+TDEPFILES= i386-tdep.o i386-cygwin-tdep.o i387-tdep.o \
+	   solib-target.o
Index: src/gdb/gdbserver/inferiors.c
===================================================================
--- src.orig/gdb/gdbserver/inferiors.c	2007-07-02 15:28:31.000000000 -0400
+++ src/gdb/gdbserver/inferiors.c	2007-07-02 16:15:10.000000000 -0400
@@ -33,10 +33,13 @@ struct thread_info
 };
 
 struct inferior_list all_threads;
+struct inferior_list all_dlls;
+int dlls_changed;
 
 struct thread_info *current_inferior;
 
 #define get_thread(inf) ((struct thread_info *)(inf))
+#define get_dll(inf) ((struct dll_info *)(inf))
 
 void
 add_inferior_to_list (struct inferior_list *list,
@@ -109,15 +112,14 @@ remove_inferior (struct inferior_list *l
 void
 add_thread (unsigned long thread_id, void *target_data, unsigned int gdb_id)
 {
-  struct thread_info *new_thread
-    = (struct thread_info *) malloc (sizeof (*new_thread));
+  struct thread_info *new_thread = malloc (sizeof (*new_thread));
 
   memset (new_thread, 0, sizeof (*new_thread));
 
   new_thread->entry.id = thread_id;
 
   add_inferior_to_list (&all_threads, & new_thread->entry);
-  
+
   if (current_inferior == NULL)
     current_inferior = new_thread;
 
@@ -187,14 +189,6 @@ remove_thread (struct thread_info *threa
   free_one_thread (&thread->entry);
 }
 
-void
-clear_inferiors (void)
-{
-  for_each_inferior (&all_threads, free_one_thread);
-
-  all_threads.head = all_threads.tail = NULL;
-}
-
 struct inferior_list_entry *
 find_inferior (struct inferior_list *list,
 	       int (*func) (struct inferior_list_entry *, void *), void *arg)
@@ -249,3 +243,80 @@ set_inferior_regcache_data (struct threa
 {
   inferior->regcache_data = data;
 }
+
+static void
+free_one_dll (struct inferior_list_entry *inf)
+{
+  struct dll_info *dll = get_dll (inf);
+  if (dll->name != NULL)
+    free (dll->name);
+  free (dll);
+}
+
+/* Find a DLL with the same name and/or base address.  A NULL name in
+   the key is ignored; so is an all-ones base address.  */
+
+static int
+match_dll (struct inferior_list_entry *inf, void *arg)
+{
+  struct dll_info *iter = (void *) inf;
+  struct dll_info *key = arg;
+
+  if (key->base_addr != ~(CORE_ADDR) 0
+      && iter->base_addr == key->base_addr)
+    return 1;
+  else if (key->name != NULL
+	   && iter->name != NULL
+	   && strcmp (key->name, iter->name) == 0)
+    return 1;
+
+  return 0;
+}
+
+/* Record a newly loaded DLL at BASE_ADDR.  */
+
+void
+loaded_dll (const char *name, CORE_ADDR base_addr)
+{
+  struct dll_info *new_dll = malloc (sizeof (*new_dll));
+  memset (new_dll, 0, sizeof (*new_dll));
+
+  new_dll->entry.id = -1;
+
+  new_dll->name = strdup (name);
+  new_dll->base_addr = base_addr;
+
+  add_inferior_to_list (&all_dlls, &new_dll->entry);
+  dlls_changed = 1;
+}
+
+/* Record that the DLL with NAME and BASE_ADDR has been unloaded.  */
+
+void
+unloaded_dll (const char *name, CORE_ADDR base_addr)
+{
+  struct dll_info *dll;
+  struct dll_info key_dll;
+
+  /* Be careful not to put the key DLL in any list.  */
+  key_dll.name = (char *) name;
+  key_dll.base_addr = base_addr;
+
+  dll = (void *) find_inferior (&all_dlls, match_dll, &key_dll);
+  remove_inferior (&all_dlls, &dll->entry);
+  free_one_dll (&dll->entry);
+  dlls_changed = 1;
+}
+
+#define clear_list(LIST) \
+  do { (LIST)->head = (LIST)->tail = NULL; } while (0)
+
+void
+clear_inferiors (void)
+{
+  for_each_inferior (&all_threads, free_one_thread);
+  for_each_inferior (&all_dlls, free_one_dll);
+
+  clear_list (&all_threads);
+  clear_list (&all_dlls);
+}
Index: src/gdb/gdbserver/remote-utils.c
===================================================================
--- src.orig/gdb/gdbserver/remote-utils.c	2007-07-02 15:28:31.000000000 -0400
+++ src/gdb/gdbserver/remote-utils.c	2007-07-02 16:03:27.000000000 -0400
@@ -965,6 +965,13 @@ prepare_resume_reply (char *buf, char st
 	      old_thread_from_wait = thread_from_wait;
 	    }
 	}
+
+      if (dlls_changed)
+	{
+	  strcpy (buf, "library:;");
+	  buf += strlen (buf);
+	  dlls_changed = 0;
+	}
     }
   /* For W and X, we're done.  */
   *buf++ = 0;
@@ -1153,3 +1160,65 @@ monitor_output (const char *msg)
   putpkt (buf);
   free (buf);
 }
+
+/* Return a malloc allocated string with special characters from TEXT
+   replaced by entity references.  */
+
+char *
+xml_escape_text (const char *text)
+{
+  char *result;
+  int i, special;
+
+  /* Compute the length of the result.  */
+  for (i = 0, special = 0; text[i] != '\0'; i++)
+    switch (text[i])
+      {
+      case '\'':
+      case '\"':
+	special += 5;
+	break;
+      case '&':
+	special += 4;
+	break;
+      case '<':
+      case '>':
+	special += 3;
+	break;
+      default:
+	break;
+      }
+
+  /* Expand the result.  */
+  result = malloc (i + special + 1);
+  for (i = 0, special = 0; text[i] != '\0'; i++)
+    switch (text[i])
+      {
+      case '\'':
+	strcpy (result + i + special, "&apos;");
+	special += 5;
+	break;
+      case '\"':
+	strcpy (result + i + special, "&quot;");
+	special += 5;
+	break;
+      case '&':
+	strcpy (result + i + special, "&amp;");
+	special += 4;
+	break;
+      case '<':
+	strcpy (result + i + special, "&lt;");
+	special += 3;
+	break;
+      case '>':
+	strcpy (result + i + special, "&gt;");
+	special += 3;
+	break;
+      default:
+	result[i + special] = text[i];
+	break;
+      }
+  result[i + special] = '\0';
+
+  return result;
+}
Index: src/gdb/gdbserver/server.c
===================================================================
--- src.orig/gdb/gdbserver/server.c	2007-07-02 15:28:31.000000000 -0400
+++ src/gdb/gdbserver/server.c	2007-07-02 16:10:31.000000000 -0400
@@ -458,12 +458,80 @@ handle_query (char *own_buf, int packet_
       return;
     }
 
+  if (strncmp ("qXfer:libraries:read:", own_buf, 21) == 0)
+    {
+      CORE_ADDR ofs;
+      unsigned int len, total_len;
+      char *document, *p;
+      struct inferior_list_entry *dll_ptr;
+      char *annex;
+
+      /* Reject any annex; grab the offset and length.  */
+      if (decode_xfer_read (own_buf + 21, &annex, &ofs, &len) < 0
+	  || annex[0] != '\0')
+	{
+	  strcpy (own_buf, "E00");
+	  return;
+	}
+
+      /* Over-estimate the necessary memory.  Assume that every character
+	 in the library name must be escaped.  */
+      total_len = 64;
+      for (dll_ptr = all_dlls.head; dll_ptr != NULL; dll_ptr = dll_ptr->next)
+	total_len += 128 + 6 * strlen (((struct dll_info *) dll_ptr)->name);
+
+      document = malloc (total_len);
+      strcpy (document, "<library-list>\n");
+      p = document + strlen (document);
+
+      for (dll_ptr = all_dlls.head; dll_ptr != NULL; dll_ptr = dll_ptr->next)
+	{
+	  struct dll_info *dll = (struct dll_info *) dll_ptr;
+	  char *name;
+
+	  strcpy (p, "  <library name=\"");
+	  p = p + strlen (p);
+	  name = xml_escape_text (dll->name);
+	  strcpy (p, name);
+	  free (name);
+	  p = p + strlen (p);
+	  strcpy (p, "\"><segment address=\"");
+	  p = p + strlen (p);
+	  sprintf (p, "0x%lx", (long) dll->base_addr);
+	  p = p + strlen (p);
+	  strcpy (p, "\"/></library>\n");
+	  p = p + strlen (p);
+	}
+
+      strcpy (p, "</library-list>\n");
+
+      total_len = strlen (document);
+      if (len > PBUFSIZ - 2)
+	len = PBUFSIZ - 2;
+
+      if (ofs > total_len)
+	write_enn (own_buf);
+      else if (len < total_len - ofs)
+	*new_packet_len_p = write_qxfer_response (own_buf, document + ofs,
+						  len, 1);
+      else
+	*new_packet_len_p = write_qxfer_response (own_buf, document + ofs,
+						  total_len - ofs, 0);
+
+      free (document);
+      return;
+    }
+
   /* Protocol features query.  */
   if (strncmp ("qSupported", own_buf, 10) == 0
       && (own_buf[10] == ':' || own_buf[10] == '\0'))
     {
       sprintf (own_buf, "PacketSize=%x;QPassSignals+", PBUFSIZ - 1);
 
+      /* We do not have any hook to indicate whether the target backend
+	 supports qXfer:libraries:read, so always report it.  */
+      strcat (own_buf, ";qXfer:libraries:read+");
+
       if (the_target->read_auxv != NULL)
 	strcat (own_buf, ";qXfer:auxv:read+");
      
@@ -696,8 +764,7 @@ handle_v_cont (char *own_buf, char *stat
   return;
 
 err:
-  /* No other way to report an error... */
-  strcpy (own_buf, "");
+  write_enn (own_buf);
   free (resume_info);
   return;
 }
@@ -753,7 +820,7 @@ static void
 gdbserver_version (void)
 {
   printf ("GNU gdbserver %s\n"
-	  "Copyright (C) 2006 Free Software Foundation, Inc.\n"
+	  "Copyright (C) 2007 Free Software Foundation, Inc.\n"
 	  "gdbserver is free software, covered by the GNU General Public License.\n"
 	  "This gdbserver was configured as \"%s\"\n",
 	  version, host_name);
@@ -824,7 +891,7 @@ main (int argc, char *argv[])
 
   initialize_low ();
 
-  own_buf = malloc (PBUFSIZ);
+  own_buf = malloc (PBUFSIZ + 1);
   mem_buf = malloc (PBUFSIZ);
 
   if (pid == 0)
@@ -833,6 +900,10 @@ main (int argc, char *argv[])
       signal = start_inferior (&argv[2], &status);
 
       /* We are now stopped at the first instruction of the target process */
+
+      /* Don't report shared library events on the initial connection,
+	 even if some libraries are preloaded.  */
+      dlls_changed = 0;
     }
   else
     {
Index: src/gdb/gdbserver/server.h
===================================================================
--- src.orig/gdb/gdbserver/server.h	2007-07-02 15:28:31.000000000 -0400
+++ src/gdb/gdbserver/server.h	2007-07-02 16:15:02.000000000 -0400
@@ -91,6 +91,13 @@ struct inferior_list_entry
 /* Opaque type for user-visible threads.  */
 struct thread_info;
 
+struct dll_info
+{
+  struct inferior_list_entry entry;
+  char *name;
+  CORE_ADDR base_addr;
+};
+
 #include "regcache.h"
 #include "gdb/signals.h"
 
@@ -104,6 +111,9 @@ void initialize_low ();
 /* From inferiors.c.  */
 
 extern struct inferior_list all_threads;
+extern struct inferior_list all_dlls;
+extern int dlls_changed;
+
 void add_inferior_to_list (struct inferior_list *list,
 			   struct inferior_list_entry *new_inferior);
 void for_each_inferior (struct inferior_list *list,
@@ -132,6 +142,9 @@ void set_inferior_regcache_data (struct 
 void change_inferior_id (struct inferior_list *list,
 			 unsigned long new_id);
 
+void loaded_dll (const char *name, CORE_ADDR base_addr);
+void unloaded_dll (const char *name, CORE_ADDR base_addr);
+
 /* Public variables in server.c */
 
 extern unsigned long cont_thread;
@@ -190,6 +203,8 @@ int look_up_one_symbol (const char *name
 
 void monitor_output (const char *msg);
 
+char *xml_escape_text (const char *text);
+
 /* Functions from ``signals.c''.  */
 enum target_signal target_signal_from_host (int hostsig);
 int target_signal_to_host_p (enum target_signal oursig);
Index: src/gdb/gdbserver/win32-low.c
===================================================================
--- src.orig/gdb/gdbserver/win32-low.c	2007-07-02 15:28:31.000000000 -0400
+++ src/gdb/gdbserver/win32-low.c	2007-07-02 15:28:39.000000000 -0400
@@ -29,6 +29,7 @@
 #include <windows.h>
 #include <winnt.h>
 #include <imagehlp.h>
+#include <tlhelp32.h>
 #include <psapi.h>
 #include <sys/param.h>
 #include <malloc.h>
@@ -202,8 +203,8 @@ enum target_waitkind
      value.sig.  */
   TARGET_WAITKIND_STOPPED,
 
-  /* The program is letting us know that it dynamically loaded something
-     (e.g. it called load(2) on AIX).  */
+  /* The program is letting us know that it dynamically loaded
+     or unloaded something.  */
   TARGET_WAITKIND_LOADED,
 
   /* The program has exec'ed a new executable file.  The new file's
@@ -765,6 +766,316 @@ win32_resume (struct thread_resume *resu
 }
 
 static void
+win32_add_one_solib (const char *name, CORE_ADDR load_addr)
+{
+  char buf[MAX_PATH + 1];
+  char buf2[MAX_PATH + 1];
+
+#ifdef _WIN32_WCE
+  WIN32_FIND_DATA w32_fd;
+  WCHAR wname[MAX_PATH + 1];
+  mbstowcs (wname, name, MAX_PATH);
+  HANDLE h = FindFirstFile (wname, &w32_fd);
+#else
+  WIN32_FIND_DATAA w32_fd;
+  HANDLE h = FindFirstFileA (name, &w32_fd);
+#endif
+
+  if (h == INVALID_HANDLE_VALUE)
+    strcpy (buf, name);
+  else
+    {
+      FindClose (h);
+      strcpy (buf, name);
+#ifndef _WIN32_WCE
+      {
+	char cwd[MAX_PATH + 1];
+	char *p;
+	if (GetCurrentDirectoryA (MAX_PATH + 1, cwd))
+	  {
+	    p = strrchr (buf, '\\');
+	    if (p)
+	      p[1] = '\0';
+	    SetCurrentDirectoryA (buf);
+	    GetFullPathNameA (w32_fd.cFileName, MAX_PATH, buf, &p);
+	    SetCurrentDirectoryA (cwd);
+	  }
+      }
+#endif
+    }
+
+#ifdef __CYGWIN__
+  cygwin_conv_to_posix_path (buf, buf2);
+#else
+  strcpy (buf2, buf);
+#endif
+
+  loaded_dll (buf2, load_addr);
+}
+
+static char *
+get_image_name (HANDLE h, void *address, int unicode)
+{
+  static char buf[(2 * MAX_PATH) + 1];
+  DWORD size = unicode ? sizeof (WCHAR) : sizeof (char);
+  char *address_ptr;
+  int len = 0;
+  char b[2];
+  DWORD done;
+
+  /* Attempt to read the name of the dll that was detected.
+     This is documented to work only when actively debugging
+     a program.  It will not work for attached processes. */
+  if (address == NULL)
+    return NULL;
+
+#ifdef _WIN32_WCE
+  /* Windows CE reports the address of the image name,
+     instead of an address of a pointer into the image name.  */
+  address_ptr = address;
+#else
+  /* See if we could read the address of a string, and that the
+     address isn't null. */
+  if (!ReadProcessMemory (h, address,  &address_ptr,
+			  sizeof (address_ptr), &done)
+      || done != sizeof (address_ptr)
+      || !address_ptr)
+    return NULL;
+#endif
+
+  /* Find the length of the string */
+  while (ReadProcessMemory (h, address_ptr + len++ * size, &b, size, &done)
+	 && (b[0] != 0 || b[size - 1] != 0) && done == size)
+    continue;
+
+  if (!unicode)
+    ReadProcessMemory (h, address_ptr, buf, len, &done);
+  else
+    {
+      WCHAR *unicode_address = (WCHAR *) alloca (len * sizeof (WCHAR));
+      ReadProcessMemory (h, address_ptr, unicode_address, len * sizeof (WCHAR),
+			 &done);
+
+      WideCharToMultiByte (CP_ACP, 0, unicode_address, len, buf, len, 0, 0);
+    }
+
+  return buf;
+}
+
+typedef BOOL (WINAPI *winapi_EnumProcessModules) (HANDLE, HMODULE *,
+						  DWORD, LPDWORD);
+typedef BOOL (WINAPI *winapi_GetModuleInformation) (HANDLE, HMODULE,
+						    LPMODULEINFO, DWORD);
+typedef DWORD (WINAPI *winapi_GetModuleFileNameExA) (HANDLE, HMODULE,
+						     LPSTR, DWORD);
+
+static winapi_EnumProcessModules win32_EnumProcessModules;
+static winapi_GetModuleInformation win32_GetModuleInformation;
+static winapi_GetModuleFileNameExA win32_GetModuleFileNameExA;
+
+static BOOL
+load_psapi (void)
+{
+  static int psapi_loaded = 0;
+  static HMODULE dll = NULL;
+
+  if (!psapi_loaded)
+    {
+      psapi_loaded = 1;
+      dll = LoadLibrary (TEXT("psapi.dll"));
+      if (!dll)
+	return FALSE;
+      win32_EnumProcessModules =
+	      GETPROCADDRESS (dll, EnumProcessModules);
+      win32_GetModuleInformation =
+	      GETPROCADDRESS (dll, GetModuleInformation);
+      win32_GetModuleFileNameExA =
+	      GETPROCADDRESS (dll, GetModuleFileNameExA);
+    }
+
+  return (win32_EnumProcessModules != NULL
+	  && win32_GetModuleInformation != NULL
+	  && win32_GetModuleFileNameExA != NULL);
+}
+
+static int
+psapi_get_dll_name (DWORD BaseAddress, char *dll_name_ret)
+{
+  DWORD len;
+  MODULEINFO mi;
+  size_t i;
+  HMODULE dh_buf[1];
+  HMODULE *DllHandle = dh_buf;
+  DWORD cbNeeded;
+  BOOL ok;
+
+  if (!load_psapi ())
+    goto failed;
+
+  cbNeeded = 0;
+  ok = (*win32_EnumProcessModules) (current_process_handle,
+				    DllHandle,
+				    sizeof (HMODULE),
+				    &cbNeeded);
+
+  if (!ok || !cbNeeded)
+    goto failed;
+
+  DllHandle = (HMODULE *) alloca (cbNeeded);
+  if (!DllHandle)
+    goto failed;
+
+  ok = (*win32_EnumProcessModules) (current_process_handle,
+				    DllHandle,
+				    cbNeeded,
+				    &cbNeeded);
+  if (!ok)
+    goto failed;
+
+  for (i = 0; i < ((size_t) cbNeeded / sizeof (HMODULE)); i++)
+    {
+      if (!(*win32_GetModuleInformation) (current_process_handle,
+					  DllHandle[i],
+					  &mi,
+					  sizeof (mi)))
+	{
+	  DWORD err = GetLastError ();
+	  error ("Can't get module info: (error %d): %s\n",
+		 (int) err, strwinerror (err));
+	}
+
+      if ((DWORD) (mi.lpBaseOfDll) == BaseAddress)
+	{
+	  len = (*win32_GetModuleFileNameExA) (current_process_handle,
+					       DllHandle[i],
+					       dll_name_ret,
+					       MAX_PATH);
+	  if (len == 0)
+	    {
+	      DWORD err = GetLastError ();
+	      error ("Error getting dll name: (error %d): %s\n",
+		     (int) err, strwinerror (err));
+	    }
+	  return 1;
+	}
+    }
+
+failed:
+  dll_name_ret[0] = '\0';
+  return 0;
+}
+
+typedef HANDLE (WINAPI *winapi_CreateToolhelp32Snapshot) (DWORD, DWORD);
+typedef BOOL (WINAPI *winapi_Module32First) (HANDLE, LPMODULEENTRY32);
+typedef BOOL (WINAPI *winapi_Module32Next) (HANDLE, LPMODULEENTRY32);
+
+static winapi_CreateToolhelp32Snapshot win32_CreateToolhelp32Snapshot;
+static winapi_Module32First win32_Module32First;
+static winapi_Module32Next win32_Module32Next;
+
+static BOOL
+load_toolhelp (void)
+{
+  static int toolhelp_loaded = 0;
+  static HMODULE dll = NULL;
+
+  if (!toolhelp_loaded)
+    {
+      toolhelp_loaded = 1;
+#ifndef _WIN32_WCE
+      dll = GetModuleHandle (_T("KERNEL32.DLL"));
+#else
+      dll = GetModuleHandle (_T("COREDLL.DLL"));
+#endif
+      if (!dll)
+	return FALSE;
+
+      win32_CreateToolhelp32Snapshot =
+	GETPROCADDRESS (dll, CreateToolhelp32Snapshot);
+      win32_Module32First = GETPROCADDRESS (dll, Module32First);
+      win32_Module32Next = GETPROCADDRESS (dll, Module32Next);
+    }
+
+  return (win32_CreateToolhelp32Snapshot != NULL
+	  && win32_Module32First != NULL
+	  && win32_Module32Next != NULL);
+}
+
+static int
+toolhelp_get_dll_name (DWORD BaseAddress, char *dll_name_ret)
+{
+  HANDLE snapshot_module;
+  MODULEENTRY32 modEntry = { sizeof (MODULEENTRY32) };
+
+  if (!load_toolhelp ())
+    return 0;
+
+  snapshot_module = win32_CreateToolhelp32Snapshot (TH32CS_SNAPMODULE,
+						    current_event.dwProcessId);
+  if (snapshot_module == INVALID_HANDLE_VALUE)
+    return 0;
+
+  /* Ignore the first module, which is the exe.  */
+  if (!win32_Module32First (snapshot_module, &modEntry))
+    goto failed;
+
+  while (win32_Module32Next (snapshot_module, &modEntry))
+    if ((DWORD) modEntry.modBaseAddr == BaseAddress)
+      {
+#ifdef UNICODE
+	wcstombs (dll_name_ret, modEntry.szExePath, MAX_PATH + 1);
+#else
+	strcpy (dll_name_ret, modEntry.szExePath);
+#endif
+	CloseHandle (snapshot_module);
+	return 1;
+      }
+
+failed:
+  CloseHandle (snapshot_module);
+  return 0;
+}
+
+static void
+handle_load_dll (void)
+{
+  LOAD_DLL_DEBUG_INFO *event = &current_event.u.LoadDll;
+  char dll_buf[MAX_PATH + 1];
+  char *dll_name = NULL;
+  DWORD load_addr;
+
+  dll_buf[0] = dll_buf[sizeof (dll_buf) - 1] = '\0';
+
+  if (!psapi_get_dll_name ((DWORD) (event->lpBaseOfDll), dll_buf)
+      && !toolhelp_get_dll_name ((DWORD) (event->lpBaseOfDll), dll_buf))
+    dll_buf[0] = dll_buf[sizeof (dll_buf) - 1] = '\0';
+
+  dll_name = dll_buf;
+
+  if (*dll_name == '\0')
+    dll_name = get_image_name (current_process_handle,
+			       event->lpImageName, event->fUnicode);
+  if (!dll_name)
+    return;
+
+  /* The symbols in a dll are offset by 0x1000, which is the
+     the offset from 0 of the first byte in an image - because
+     of the file header and the section alignment. */
+
+  load_addr = (DWORD) event->lpBaseOfDll + 0x1000;
+  win32_add_one_solib (dll_name, load_addr);
+}
+
+static void
+handle_unload_dll (void)
+{
+  CORE_ADDR load_addr =
+	  (CORE_ADDR) (DWORD) current_event.u.UnloadDll.lpBaseOfDll;
+  load_addr += 0x1000;
+  unloaded_dll (NULL, load_addr);
+}
+
+static void
 handle_exception (struct target_waitstatus *ourstatus)
 {
   DWORD code = current_event.u.Exception.ExceptionRecord.ExceptionCode;
@@ -955,9 +1266,10 @@ get_child_debug_event (struct target_wai
 		(unsigned) current_event.dwProcessId,
 		(unsigned) current_event.dwThreadId));
       CloseHandle (current_event.u.LoadDll.hFile);
+      handle_load_dll ();
 
       ourstatus->kind = TARGET_WAITKIND_LOADED;
-      ourstatus->value.integer = 0;
+      ourstatus->value.sig = TARGET_SIGNAL_TRAP;
       break;
 
     case UNLOAD_DLL_DEBUG_EVENT:
@@ -965,6 +1277,9 @@ get_child_debug_event (struct target_wai
 		"for pid=%d tid=%x\n",
 		(unsigned) current_event.dwProcessId,
 		(unsigned) current_event.dwThreadId));
+      handle_unload_dll ();
+      ourstatus->kind = TARGET_WAITKIND_LOADED;
+      ourstatus->value.sig = TARGET_SIGNAL_TRAP;
       break;
 
     case EXCEPTION_DEBUG_EVENT:
@@ -1027,6 +1342,7 @@ win32_wait (char *status)
 
 	  return our_status.value.integer;
 	case TARGET_WAITKIND_STOPPED:
+ 	case TARGET_WAITKIND_LOADED:
 	  OUTMSG2 (("Child Stopped with signal = %d \n",
 		    our_status.value.sig));
 
@@ -1034,12 +1350,20 @@ win32_wait (char *status)
 
 	  child_fetch_inferior_registers (-1);
 
+	  if (our_status.kind == TARGET_WAITKIND_LOADED
+	      && !server_waiting)
+	    {
+	      /* When gdb connects, we want to be stopped at the
+		 initial breakpoint, not in some dll load event.  */
+	      child_continue (DBG_CONTINUE, -1);
+	      break;
+	    }
+
 	  return our_status.value.sig;
  	default:
 	  OUTMSG (("Ignoring unknown internal event, %d\n", our_status.kind));
  	  /* fall-through */
  	case TARGET_WAITKIND_SPURIOUS:
- 	case TARGET_WAITKIND_LOADED:
  	case TARGET_WAITKIND_EXECD:
 	  /* do nothing, just continue */
 	  child_continue (DBG_CONTINUE, -1);
Index: src/gdb/coff-pe-read.c
===================================================================
--- src.orig/gdb/coff-pe-read.c	2007-07-02 15:28:31.000000000 -0400
+++ src/gdb/coff-pe-read.c	2007-07-02 15:28:39.000000000 -0400
@@ -309,9 +309,6 @@ read_pe_exported_syms (struct objfile *o
 	+= ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile));
     }
 
-  printf_filtered (_("Minimal symbols from %s..."), dll_name);
-  wrap_here ("");
-
   /* Truncate name at first dot. Should maybe also convert to all
      lower case for convenience on Windows. */
   read_pe_truncate_name (dll_name);
Index: src/gdb/testsuite/gdb.base/unload.c
===================================================================
--- src.orig/gdb/testsuite/gdb.base/unload.c	2007-07-02 15:28:31.000000000 -0400
+++ src/gdb/testsuite/gdb.base/unload.c	2007-07-02 15:28:39.000000000 -0400
@@ -18,12 +18,19 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+
+#ifdef __WIN32__
+#include <windows.h>
+#define dlopen(name, mode) LoadLibrary (name)
+#define dlsym(handle, func) GetProcAddress (handle, func)
+#define dlclose(handle) FreeLibrary (handle)
+#define dlerror() "error %d occurred", GetLastError ()
+#else
 #include <dlfcn.h>
+#endif
 
 int k = 0;
 
-#define SHLIB_NAME SHLIB_DIR "/unloadshr.sl"
-
 int main()
 {
   void *handle;
@@ -32,11 +39,10 @@ int main()
   const char *msg;
 
   handle = dlopen (SHLIB_NAME, RTLD_LAZY);
-  msg = dlerror ();
   
   if (!handle)
     {
-      fprintf (stderr, msg);
+      fprintf (stderr, dlerror ());
       exit (1);
     }
 
Index: src/gdb/testsuite/gdb.base/unload.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/unload.exp	2007-07-02 15:28:31.000000000 -0400
+++ src/gdb/testsuite/gdb.base/unload.exp	2007-07-02 15:28:39.000000000 -0400
@@ -30,37 +30,28 @@ if {[skip_shlib_tests]} {
     return 0
 }
 
-# TODO: Use LoadLibrary on these targets instead of dlopen.
-if {([istarget arm*-*-symbianelf*]
-     || [istarget *-*-mingw*]
-     || [istarget *-*-cygwin*]
-     || [istarget *-*-pe*])} {
+# TODO: Use LoadLibrary on this target instead of dlopen.
+if {[istarget arm*-*-symbianelf*]} {
     return 0
 }
 
 set testfile "unload"
 set libfile "unloadshr"
+set libname "${libfile}.sl"
 set libsrcfile ${libfile}.c
 set srcfile $srcdir/$subdir/$testfile.c
 set binfile $objdir/$subdir/$testfile
 set shlibdir ${objdir}/${subdir}
 set libsrc  $srcdir/$subdir/$libfile.c
-set lib_sl  $objdir/$subdir/$libfile.sl
-
-set lib_opts debug
-set exec_opts [list debug additional_flags=-DSHLIB_DIR\=\"${shlibdir}\"]
-
-switch -glob [istarget] {
-    "hppa*-hp-hpux*" { }
-    "*-*-linux*"     { lappend exec_opts "libs=-ldl" }
-    "*-*-solaris*"   { lappend exec_opts "libs=-ldl" }
-    default          { }
-}
+set lib_sl  $objdir/$subdir/$libname
 
 if [get_compiler_info ${binfile}] {
     return -1
 }
 
+set lib_opts debug
+set exec_opts [list debug shlib_load additional_flags=-DSHLIB_NAME\=\"${libname}\"]
+
 if { [gdb_compile_shlib $libsrc $lib_sl $lib_opts] != ""
      || [gdb_compile $srcfile $binfile executable $exec_opts] != ""} {
     untested "Couldn't compile $libsrc or $srcfile."
Index: src/gdb/NEWS
===================================================================
--- src.orig/gdb/NEWS	2007-07-02 16:18:41.000000000 -0400
+++ src/gdb/NEWS	2007-07-02 16:22:41.000000000 -0400
@@ -50,6 +50,9 @@ packet, this response allows GDB to debu
 where the operating system manages the list of loaded libraries (e.g.
 Windows and SymbianOS).
 
+* The GDB remote stub, gdbserver, now supports dynamic link libraries
+(DLLs) on Windows and Windows CE targets.
+
 * New commands
 
 set remoteflow


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