From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 21899 invoked by alias); 2 Jul 2007 22:12:10 -0000 Received: (qmail 21881 invoked by uid 22791); 2 Jul 2007 22:12:07 -0000 X-Spam-Check-By: sourceware.org Received: from NaN.false.org (HELO nan.false.org) (208.75.86.248) by sourceware.org (qpsmtpd/0.31) with ESMTP; Mon, 02 Jul 2007 22:12:02 +0000 Received: from nan.false.org (localhost [127.0.0.1]) by nan.false.org (Postfix) with ESMTP id 99C04982C3; Mon, 2 Jul 2007 22:12:00 +0000 (GMT) Received: from caradoc.them.org (22.svnf5.xdsl.nauticom.net [209.195.183.55]) by nan.false.org (Postfix) with ESMTP id 9D5CD9817B; Mon, 2 Jul 2007 22:11:59 +0000 (GMT) Received: from drow by caradoc.them.org with local (Exim 4.67) (envelope-from ) id 1I5U7F-0004z2-80; Mon, 02 Jul 2007 18:11:29 -0400 Date: Mon, 02 Jul 2007 22:12:00 -0000 From: Daniel Jacobowitz To: Pedro Alves , gdb-patches@sourceware.org Subject: [rfc] DLL support for gdbserver Message-ID: <20070702221129.GD32495@caradoc.them.org> Mail-Followup-To: Pedro Alves , gdb-patches@sourceware.org MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.5.15 (2007-04-09) X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2007-07/txt/msg00041.txt.bz2 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 Daniel Jacobowitz * 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 Daniel Jacobowitz * 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 Daniel Jacobowitz * 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, "'"); + special += 5; + break; + case '\"': + strcpy (result + i + special, """); + special += 5; + break; + case '&': + strcpy (result + i + special, "&"); + special += 4; + break; + case '<': + strcpy (result + i + special, "<"); + special += 3; + break; + case '>': + strcpy (result + i + special, ">"); + 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, "\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, " name); + strcpy (p, name); + free (name); + p = p + strlen (p); + strcpy (p, "\">base_addr); + p = p + strlen (p); + strcpy (p, "\"/>\n"); + p = p + strlen (p); + } + + strcpy (p, "\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 #include #include +#include #include #include #include @@ -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 = ¤t_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 #include + +#ifdef __WIN32__ +#include +#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 +#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