aboutsummaryrefslogtreecommitdiff
path: root/gdb/gdbserver
diff options
context:
space:
mode:
authorDaniel Jacobowitz <drow@false.org>2007-07-17 12:51:41 +0000
committerDaniel Jacobowitz <drow@false.org>2007-07-17 12:51:41 +0000
commit255e7678a93693bd4d16cc3246442a1b8e11064e (patch)
treee30bf1ce55c5475d65bb16a04e840be2d61b8ab7 /gdb/gdbserver
parenta8c50c1f557c99062f17be4a7b9d1c596771e4c1 (diff)
downloadgdb-255e7678a93693bd4d16cc3246442a1b8e11064e.zip
gdb-255e7678a93693bd4d16cc3246442a1b8e11064e.tar.gz
gdb-255e7678a93693bd4d16cc3246442a1b8e11064e.tar.bz2
2007-07-17 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. * 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. * 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.
Diffstat (limited to 'gdb/gdbserver')
-rw-r--r--gdb/gdbserver/ChangeLog29
-rw-r--r--gdb/gdbserver/inferiors.c93
-rw-r--r--gdb/gdbserver/remote-utils.c69
-rw-r--r--gdb/gdbserver/server.c79
-rw-r--r--gdb/gdbserver/server.h15
-rw-r--r--gdb/gdbserver/win32-low.c332
6 files changed, 598 insertions, 19 deletions
diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog
index ee41170..7c58e03 100644
--- a/gdb/gdbserver/ChangeLog
+++ b/gdb/gdbserver/ChangeLog
@@ -1,3 +1,32 @@
+2007-07-17 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.
+
2007-07-12 Daniel Jacobowitz <dan@codesourcery.com>
* configure.srv: Set srv_linux_regsets for sh*-*-linux*.
diff --git a/gdb/gdbserver/inferiors.c b/gdb/gdbserver/inferiors.c
index 6262d7e..c73bf45 100644
--- a/gdb/gdbserver/inferiors.c
+++ b/gdb/gdbserver/inferiors.c
@@ -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 *list,
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 *thread)
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 thread_info *inferior, void *data)
{
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);
+}
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 9c407aa..bd1b482 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -965,6 +965,13 @@ prepare_resume_reply (char *buf, char status, unsigned char sig)
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;
@@ -1172,3 +1179,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;
+}
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index bee1256..9225f66 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -458,12 +458,80 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
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 *status, int *signal)
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
{
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index 573bde2..ea7666e 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -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 thread_info *, void *);
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, CORE_ADDR *addrp);
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);
diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
index 161cadf..1382cb8 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -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
@@ -773,6 +774,316 @@ win32_resume (struct thread_resume *resume_info)
}
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;
@@ -963,9 +1274,10 @@ get_child_debug_event (struct target_waitstatus *ourstatus)
(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:
@@ -973,6 +1285,9 @@ get_child_debug_event (struct target_waitstatus *ourstatus)
"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:
@@ -1035,6 +1350,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));
@@ -1042,12 +1358,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);