Index: solib-svr4.c =================================================================== RCS file: /cvs/src/src/gdb/solib-svr4.c,v retrieving revision 1.152 diff -u -p -r1.152 solib-svr4.c --- solib-svr4.c 5 Aug 2011 15:17:23 -0000 1.152 +++ solib-svr4.c 8 Aug 2011 23:34:10 -0000 @@ -106,6 +106,201 @@ static const char * const main_name_lis NULL }; + +#if defined(HAVE_LIBEXPAT) + +#include "xml-support.h" + +/* Handle the start of a element. */ + +static void +library_list_start_segment (struct gdb_xml_parser *parser, + const struct gdb_xml_element *element, + void *user_data, VEC(gdb_xml_value_s) *attributes) +{ + ULONGEST *address_p = xml_find_attribute (attributes, "address")->value; + CORE_ADDR address = (CORE_ADDR) *address_p; + struct so_list **so_list = user_data; + + (*so_list)->lm_info->l_addr = address; +} + +/* Handle the start of a element. Note: new elements are added + at the head of the list (i.e. the list is built in reverse order). */ + +static void +library_list_start_library (struct gdb_xml_parser *parser, + const struct gdb_xml_element *element, + void *user_data, VEC(gdb_xml_value_s) *attributes) +{ + const char *name = xml_find_attribute (attributes, "name")->value; + struct so_list *new_elem, **so_list = user_data; + + new_elem = XZALLOC (struct so_list); + new_elem->next = *so_list; + new_elem->lm_info = XZALLOC (struct lm_info); + strcpy (new_elem->so_original_name, name); + strcpy (new_elem->so_name, name); + + *so_list = new_elem; +} + +static void +library_list_end_library (struct gdb_xml_parser *parser, + const struct gdb_xml_element *element, + void *user_data, const char *body_text) +{ + struct so_list **so_list = user_data; + + if ((*so_list)->lm_info->l_addr == 0) + gdb_xml_error (parser, _("No segment defined for %s"), + (*so_list)->so_name); +} + + +/* Handle the start of a element. */ + +static void +library_list_start_list (struct gdb_xml_parser *parser, + const struct gdb_xml_element *element, + void *user_data, VEC(gdb_xml_value_s) *attributes) +{ + char *version = xml_find_attribute (attributes, "version")->value; + + if (strcmp (version, "1.0") != 0) + gdb_xml_error (parser, + _("Library list has unsupported version \"%s\""), + version); +} + + +/* The allowed elements and attributes for an XML library list. + The root element is a . */ + +static const struct gdb_xml_attribute segment_attributes[] = { + { "address", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL }, + { NULL, GDB_XML_AF_NONE, NULL, NULL } +}; + +static const struct gdb_xml_element library_children[] = { + { "segment", segment_attributes, NULL, + GDB_XML_EF_NONE, library_list_start_segment, NULL }, + { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } +}; + +static const struct gdb_xml_attribute library_attributes[] = { + { "name", GDB_XML_AF_NONE, NULL, NULL }, + { NULL, GDB_XML_AF_NONE, NULL, NULL } +}; + +static const struct gdb_xml_element library_list_children[] = { + { "library", library_attributes, library_children, + GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL, + library_list_start_library, library_list_end_library }, + { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } +}; + +static const struct gdb_xml_attribute library_list_attributes[] = { + { "version", GDB_XML_AF_NONE, NULL, NULL }, + { NULL, GDB_XML_AF_NONE, NULL, NULL } +}; + +static const struct gdb_xml_element library_list_elements[] = { + { "library-list", library_list_attributes, library_list_children, + GDB_XML_EF_NONE, library_list_start_list, NULL }, + { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } +}; + +static void +svr4_free_so (struct so_list *so) +{ + xfree (so->lm_info->lm); + xfree (so->lm_info); +} + +/* Free so_list built so far (called via cleanup). */ + +static void +svr4_free_library_list (void *p_list) +{ + struct so_list *list = *(struct so_list **) p_list; + while (list != NULL) + { + struct so_list *next = list->next; + + svr4_free_so (list); + list = next; + } +} + +/* Parse qXfer:libraries:read packet into so_list. */ + +static struct so_list * +svr4_parse_libraries (const char *document) +{ + struct so_list *result = NULL; + struct cleanup *back_to = make_cleanup (svr4_free_library_list, + &result); + + if (gdb_xml_parse_quick (_("target library list"), "library-list.dtd", + library_list_elements, document, &result) == 0) + { + struct so_list *prev; + + /* Parsed successfully, keep the result. */ + discard_cleanups (back_to); + + /* Reverse the list -- it was built in reverse order. */ + prev = NULL; + while (result) + { + struct so_list *next = result->next; + + result->next = prev; + prev = result; + result = next; + } + return prev; + } + + do_cleanups (back_to); + return NULL; +} + +/* Attempt to get so_list from target via qXfer:libraries:read packet. + Return NULL if packet not supported, or contains no libraries. */ + +static struct so_list * +svr4_current_sos_via_xfer_libraries () +{ + char *library_document; + struct so_list *result; + struct cleanup *back_to; + + /* Fetch the list of shared libraries. */ + library_document = target_read_stralloc (¤t_target, + TARGET_OBJECT_LIBRARIES, + NULL); + if (library_document == NULL) + return NULL; + + back_to = make_cleanup (xfree, library_document); + result = svr4_parse_libraries (library_document); + do_cleanups (back_to); + + return result; +} + +#else + +static struct so_list * +svr4_current_sos_via_xfer_libraries () +{ + return NULL; +} + +#endif + /* Return non-zero if GDB_SO_NAME and INFERIOR_SO_NAME represent the same shared library. */ @@ -1136,6 +1331,10 @@ svr4_current_sos (void) CORE_ADDR ldsomap = 0; struct svr4_info *info; + head = svr4_current_sos_via_xfer_libraries (); + if (head != NULL) + return head; + info = get_svr4_info (); /* Always locate the debug struct, in case it has moved. */ @@ -2249,13 +2448,6 @@ svr4_clear_solib (void) info->debug_loader_name = NULL; } -static void -svr4_free_so (struct so_list *so) -{ - xfree (so->lm_info->lm); - xfree (so->lm_info); -} - /* Clear any bits of ADDR that wouldn't fit in a target-format data pointer. "Data pointer" here refers to whatever sort of Index: gdbserver/inferiors.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/inferiors.c,v retrieving revision 1.30 diff -u -p -r1.30 inferiors.c --- gdbserver/inferiors.c 21 Jul 2011 23:46:12 -0000 1.30 +++ gdbserver/inferiors.c 8 Aug 2011 23:34:10 -0000 @@ -312,14 +312,22 @@ unloaded_dll (const char *name, CORE_ADD #define clear_list(LIST) \ do { (LIST)->head = (LIST)->tail = NULL; } while (0) +/* Clear ALL_DLLS. */ + void -clear_inferiors (void) +clear_all_dlls (void) { - for_each_inferior (&all_threads, free_one_thread); for_each_inferior (&all_dlls, free_one_dll); + clear_list (&all_dlls); +} +void +clear_inferiors (void) +{ + for_each_inferior (&all_threads, free_one_thread); clear_list (&all_threads); - clear_list (&all_dlls); + + clear_all_dlls (); current_inferior = NULL; } Index: gdbserver/linux-low.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/linux-low.c,v retrieving revision 1.173 diff -u -p -r1.173 linux-low.c --- gdbserver/linux-low.c 21 Jul 2011 23:46:12 -0000 1.173 +++ gdbserver/linux-low.c 8 Aug 2011 23:34:10 -0000 @@ -4755,6 +4755,289 @@ linux_emit_ops (void) return NULL; } +/* Extract &phdr and num_phdr in the inferior. Return 0 on success. */ + +static int +get_phdr_phnum_from_proc_auxv (const int pid, const int is_elf64, + CORE_ADDR *phdr_memaddr, int *num_phdr) +{ + char filename[PATH_MAX]; + int fd; + const int auxv_size = is_elf64 + ? sizeof (Elf64_auxv_t) : sizeof (Elf32_auxv_t); + char buf[sizeof (Elf64_auxv_t)]; /* The larger of the two. */ + + xsnprintf (filename, sizeof filename, "/proc/%d/auxv", pid); + + fd = open (filename, O_RDONLY); + if (fd < 0) + return 1; + + *phdr_memaddr = 0; + *num_phdr = 0; + while (read (fd, buf, auxv_size) == auxv_size + && (*phdr_memaddr == 0 || *num_phdr == 0)) + { + if (is_elf64) + { + Elf64_auxv_t *const aux = (Elf64_auxv_t *) buf; + + switch (aux->a_type) + { + case AT_PHDR: + *phdr_memaddr = aux->a_un.a_val; + break; + case AT_PHNUM: + *num_phdr = aux->a_un.a_val; + break; + } + } + else + { + Elf32_auxv_t *const aux = (Elf32_auxv_t *) buf; + + switch (aux->a_type) + { + case AT_PHDR: + *phdr_memaddr = aux->a_un.a_val; + break; + case AT_PHNUM: + *num_phdr = aux->a_un.a_val; + break; + } + } + } + + close (fd); + + if (*phdr_memaddr == 0 || *num_phdr == 0) + { + warning ("Unexpected missing AT_PHDR and/or AT_PHNUM: " + "phdr_memaddr = %ld, phdr_num = %d", + (long) *phdr_memaddr, *num_phdr); + return 2; + } + + return 0; +} + +/* Return &_DYNAMIC (via PT_DYNAMIC) in the inferior, or 0 if not present. */ + +static CORE_ADDR +get_dynamic (const int pid, const int is_elf64) +{ + CORE_ADDR phdr_memaddr; + int num_phdr, i; + unsigned char *phdr_buf; + const int phdr_size = is_elf64 ? sizeof (Elf64_Phdr) : sizeof (Elf32_Phdr); + + + if (get_phdr_phnum_from_proc_auxv (pid, is_elf64, &phdr_memaddr, &num_phdr)) + return 0; + + gdb_assert (num_phdr < 100); /* Basic sanity check. */ + phdr_buf = alloca (num_phdr * phdr_size); + + if (linux_read_memory (phdr_memaddr, phdr_buf, num_phdr * phdr_size)) + return 0; + + for (i = 0; i < num_phdr; i++) + { + if (is_elf64) + { + Elf64_Phdr *const p = (Elf64_Phdr *) (phdr_buf + i * phdr_size); + + if (p->p_type == PT_DYNAMIC) + return p->p_vaddr; + } + else + { + Elf32_Phdr *const p = (Elf32_Phdr *) (phdr_buf + i * phdr_size); + + if (p->p_type == PT_DYNAMIC) + return p->p_vaddr; + } + } + + return 0; +} + +/* Return &_r_debug in the inferior, or -1 if not present. Return value + can be 0 if the inferior does not yet have the library list initialized. */ + +static CORE_ADDR +get_r_debug (const int pid, const int is_elf64) +{ + CORE_ADDR dynamic_memaddr; + const int dyn_size = is_elf64 ? sizeof (Elf64_Dyn) : sizeof (Elf32_Dyn); + unsigned char buf[sizeof (Elf64_Dyn)]; /* The larger of the two. */ + + dynamic_memaddr = get_dynamic (pid, is_elf64); + if (dynamic_memaddr == 0) + return (CORE_ADDR) -1; + + while (linux_read_memory (dynamic_memaddr, buf, dyn_size) == 0) + { + if (is_elf64) + { + Elf64_Dyn *const dyn = (Elf64_Dyn *) buf; + + if (dyn->d_tag == DT_DEBUG) + return dyn->d_un.d_val; + + if (dyn->d_tag == DT_NULL) + break; + } + else + { + Elf32_Dyn *const dyn = (Elf32_Dyn *) buf; + + if (dyn->d_tag == DT_DEBUG) + return dyn->d_un.d_val; + + if (dyn->d_tag == DT_NULL) + break; + } + + dynamic_memaddr += dyn_size; + } + + return (CORE_ADDR) -1; +} + +/* Read one pointer from MEMADDR in the inferior. */ + +static int +read_one_ptr (CORE_ADDR memaddr, CORE_ADDR *ptr, int ptr_size) +{ + *ptr = 0; + return linux_read_memory (memaddr, (unsigned char *) ptr, ptr_size); +} + + +struct link_map_offsets + { + /* Offset and size of r_debug.r_version. */ + int r_version_offset; + + /* Offset and size of r_debug.r_map. */ + int r_map_offset; + + /* Offset to l_addr field in struct link_map. */ + int l_addr_offset; + + /* Offset to l_name field in struct link_map. */ + int l_name_offset; + + /* Offset to l_next field in struct link_map. */ + int l_next_offset; + + /* Offset to l_prev field in struct link_map. */ + int l_prev_offset; + + }; + +/* Clear and refresh ALL_DLLS list. */ + +static void +linux_refresh_libraries (void) +{ + struct process_info_private *const priv = current_process ()->private; + char filename[PATH_MAX]; + int pid, is_elf64, ptr_size, r_version; + CORE_ADDR lm_addr, lm_prev, l_name, l_addr, l_next, l_prev; + + static const struct link_map_offsets lmo_32bit_offsets = + { + 0, /* r_version offset. */ + 4, /* r_debug.r_map offset. */ + 0, /* l_addr offset in link_map. */ + 4, /* l_name offset in link_map. */ + 12, /* l_next offset in link_map. */ + 16 /* l_prev offset in link_map. */ + }; + + static const struct link_map_offsets lmo_64bit_offsets = + { + 0, /* r_version offset. */ + 8, /* r_debug.r_map offset. */ + 0, /* l_addr offset in link_map. */ + 8, /* l_name offset in link_map. */ + 24, /* l_next offset in link_map. */ + 32 /* l_prev offset in link_map. */ + }; + const struct link_map_offsets *lmo; + + pid = lwpid_of (get_thread_lwp (current_inferior)); + xsnprintf (filename, sizeof filename, "/proc/%d/exe", pid); + is_elf64 = elf_64_file_p (filename); + lmo = is_elf64 ? &lmo_64bit_offsets : &lmo_32bit_offsets; + ptr_size = is_elf64 ? 8 : 4; + + if (priv->r_debug == 0) + priv->r_debug = get_r_debug (pid, is_elf64); + + if (priv->r_debug == (CORE_ADDR) -1 || priv->r_debug == 0) + return; + + r_version = 0; + if (linux_read_memory (priv->r_debug + lmo->r_version_offset, + (unsigned char *) &r_version, + sizeof (r_version)) != 0 + || r_version != 1) + { + warning ("unexpected r_debug version %d", r_version); + return; + } + + if (read_one_ptr (priv->r_debug + lmo->r_map_offset, + &lm_addr, ptr_size) != 0) + { + warning ("unable to read r_map from 0x%lx", + (long) priv->r_debug + lmo->r_map_offset); + return; + } + + clear_all_dlls (); + + lm_prev = 0; + while (read_one_ptr (lm_addr + lmo->l_name_offset, + &l_name, ptr_size) == 0 + && read_one_ptr (lm_addr + lmo->l_addr_offset, + &l_addr, ptr_size) == 0 + && read_one_ptr (lm_addr + lmo->l_prev_offset, + &l_prev, ptr_size) == 0 + && read_one_ptr (lm_addr + lmo->l_next_offset, + &l_next, ptr_size) == 0) + { + unsigned char libname[PATH_MAX]; + + if (lm_prev != l_prev) + { + warning ("corrupt solib chain: 0x%lx != 0x%lx", + (long) lm_prev, (long) l_prev); + break; + } + + /* Not checking for error because reading may stop before + we've got PATH_MAX worth of characters. */ + libname[0] = '\0'; + linux_read_memory (l_name, libname, sizeof (libname)); + if (libname[0] != '\0') + loaded_dll ((const char *) libname, l_addr); + + if (l_next == 0) + break; + + lm_prev = lm_addr; + lm_addr = l_next; + } + + /* The library notification response is not expected for GNU/Linux. */ + dlls_changed = 0; +} + + static struct target_ops linux_target_ops = { linux_create_inferior, linux_attach, @@ -4813,7 +5096,8 @@ static struct target_ops linux_target_op linux_cancel_breakpoints, linux_stabilize_threads, linux_install_fast_tracepoint_jump_pad, - linux_emit_ops + linux_emit_ops, + linux_refresh_libraries }; static void Index: gdbserver/linux-low.h =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/linux-low.h,v retrieving revision 1.47 diff -u -p -r1.47 linux-low.h --- gdbserver/linux-low.h 1 Jan 2011 15:33:24 -0000 1.47 +++ gdbserver/linux-low.h 8 Aug 2011 23:34:10 -0000 @@ -56,6 +56,9 @@ struct process_info_private /* libthread_db-specific additions. Not NULL if this process has loaded thread_db, and it is active. */ struct thread_db *thread_db; + + /* &_r_debug. -1 if not yet determined. 0 if no PT_DYNAMIC in Phdrs. */ + CORE_ADDR r_debug; }; struct lwp_info; Index: gdbserver/server.c =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/server.c,v retrieving revision 1.146 diff -u -p -r1.146 server.c --- gdbserver/server.c 21 Jul 2011 23:46:12 -0000 1.146 +++ gdbserver/server.c 8 Aug 2011 23:34:10 -0000 @@ -918,6 +918,9 @@ handle_qxfer_libraries (const char *anne if (annex[0] != '\0' || !target_running ()) return -1; + if (the_target->refresh_libraries != NULL) + the_target->refresh_libraries (); + /* Over-estimate the necessary memory. Assume that every character in the library name must be escaped. */ total_len = 64; Index: gdbserver/server.h =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/server.h,v retrieving revision 1.84 diff -u -p -r1.84 server.h --- gdbserver/server.h 21 Jul 2011 23:46:12 -0000 1.84 +++ gdbserver/server.h 8 Aug 2011 23:34:10 -0000 @@ -260,6 +260,7 @@ ptid_t thread_id_to_gdb_id (ptid_t); ptid_t thread_to_gdb_id (struct thread_info *); ptid_t gdb_id_to_thread_id (ptid_t); struct thread_info *gdb_id_to_thread (unsigned int); +void clear_all_dlls (void); void clear_inferiors (void); struct inferior_list_entry *find_inferior (struct inferior_list *, Index: gdbserver/target.h =================================================================== RCS file: /cvs/src/src/gdb/gdbserver/target.h,v retrieving revision 1.54 diff -u -p -r1.54 target.h --- gdbserver/target.h 1 Jan 2011 15:33:24 -0000 1.54 +++ gdbserver/target.h 8 Aug 2011 23:34:10 -0000 @@ -373,6 +373,9 @@ struct target_ops /* Return the bytecode operations vector for the current inferior. Returns NULL if bytecode compilation is not supported. */ struct emit_ops *(*emit_ops) (void); + + /* Refresh ALL_DLLS. */ + void (*refresh_libraries) (void); }; extern struct target_ops *the_target;