From 85d870a0fa7b4c527644a329b5b560eec6fcb4a9 Mon Sep 17 00:00:00 2001 From: drow Date: Tue, 17 Jul 2007 12:51:40 +0000 Subject: [PATCH] 2007-07-17 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. * 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. --- gdb/ChangeLog | 8 + gdb/NEWS | 3 + gdb/coff-pe-read.c | 3 - gdb/config/i386/cygwin.mt | 3 +- gdb/gdbserver/ChangeLog | 29 ++++ 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/ChangeLog | 8 + gdb/testsuite/gdb.base/unload.c | 14 +- gdb/testsuite/gdb.base/unload.exp | 23 +-- 13 files changed, 636 insertions(+), 43 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 0e2f5ff132..50e50edece 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,11 @@ +2007-07-17 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-17 Daniel Jacobowitz * dwarf2read.c (dwarf_decode_lines): Detect address size mismatches. diff --git a/gdb/NEWS b/gdb/NEWS index b3bd08a913..b003f63149 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -54,6 +54,9 @@ packet, this response allows GDB to debug shared libraries on targets 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 diff --git a/gdb/coff-pe-read.c b/gdb/coff-pe-read.c index bf92b4bf65..497c9513f7 100644 --- a/gdb/coff-pe-read.c +++ b/gdb/coff-pe-read.c @@ -309,9 +309,6 @@ read_pe_exported_syms (struct objfile *objfile) += 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); diff --git a/gdb/config/i386/cygwin.mt b/gdb/config/i386/cygwin.mt index 555b8bf043..47cfbaaacb 100644 --- a/gdb/config/i386/cygwin.mt +++ b/gdb/config/i386/cygwin.mt @@ -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 diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index ee41170240..7c58e030ea 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,32 @@ +2007-07-17 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. + 2007-07-12 Daniel Jacobowitz * configure.srv: Set srv_linux_regsets for sh*-*-linux*. diff --git a/gdb/gdbserver/inferiors.c b/gdb/gdbserver/inferiors.c index 6262d7e812..c73bf452e0 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 9c407aaac6..bd1b482ada 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, "'"); + 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; +} diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index bee1256403..9225f663e2 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, "\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 *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 573bde2893..ea7666e7a4 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 161cadf690..1382cb83a9 100644 --- a/gdb/gdbserver/win32-low.c +++ b/gdb/gdbserver/win32-low.c @@ -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 @@ -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 = ¤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; @@ -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); diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 79994ccea0..8ec0434e34 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2007-07-17 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-03 Markus Deuling * gdb.base/solib-symbol.exp: New file (testcase multiple symbol lookup). diff --git a/gdb/testsuite/gdb.base/unload.c b/gdb/testsuite/gdb.base/unload.c index 799bb55584..f8c8046e98 100644 --- a/gdb/testsuite/gdb.base/unload.c +++ b/gdb/testsuite/gdb.base/unload.c @@ -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); } diff --git a/gdb/testsuite/gdb.base/unload.exp b/gdb/testsuite/gdb.base/unload.exp index 1b731f0112..78090b9324 100644 --- a/gdb/testsuite/gdb.base/unload.exp +++ b/gdb/testsuite/gdb.base/unload.exp @@ -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." -- 2.11.0