* [patch] Implement qXfer:libraries for Linux/gdbserver [Was: Re: [RFC] Make target_read_string faster over high-latency links.]
@ 2011-08-05 18:11 Paul Pluzhnikov
2011-08-08 18:36 ` Jan Kratochvil
2011-08-08 21:10 ` Jan Kratochvil
0 siblings, 2 replies; 13+ messages in thread
From: Paul Pluzhnikov @ 2011-08-05 18:11 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 1929 bytes --]
On Tue, Jul 26, 2011 at 1:36 AM, Jan Kratochvil
<jan.kratochvil@redhat.com> wrote:
> On Fri, 15 Jul 2011 20:07:48 +0200, Paul Pluzhnikov wrote:
>> This is GDB walking through the shared library list, and reading shared
>> library names from gdbserver via target_read_string 4 bytes at a time ;-(
>
> Just that you do not mention supporting qXfer:libraries:read also on GNU/Linux
> platforms (currently used only on MS-Windows)
That sounds like an excellent idea: it reduces many many packets with
just one.
> that is to move solib-svr4.c to gdb/common/ etc.
Moving solib-svr4.c to gdb/common doesn't appear necessary: the parts
that I would have used in gdbserver/linux-low.c are quite small, and not
easily re-usable.
Tested on Linux/x86_64 native, and with gdbserver (x86_64) against x86_64
and i686 inferiors. No regressions.
Thanks,
--
Paul Pluzhnikov
2011-08-05 Paul Pluzhnikov <ppluzhnikov@google.com>
* solib-svr4.c (library_list_start_segment): New function.
(library_list_start_library, library_list_end_library): Likewise.
(library_list_start_list): Likewise.
(segment_attributes, library_children): New variables.
(library_attributes, library_list_children): Likewise.
(library_list_attributes, library_list_elements): Likewise.
(svr4_free_so): Moved to here.
(svr4_free_library_list, svr4_parse_libraries): New functions.
(svr4_current_sos_via_xfer_libraries): Likewise.
(svr4_current_sos): Adjust.
gdbserver/ChangeLog:
2011-08-05 Paul Pluzhnikov <ppluzhnikov@google.com>
* linux-low.c (linux_add_process): Adjust.
(get_phdr_phnum_from_proc_auxv, get_dynamic): New functions.
(get_r_debug, read_one_ptr): Likewise.
(struct link_map_offsets): New struct decl.
(linux_qxfer_libraries): New function.
(struct linux_target_ops): Adjust.
* linux-low.h (struct process_info_private): New member.
* server.c (handle_qxfer_libraries): Adjust.
* target.h (struct target_ops): New member.
[-- Attachment #2: gdb-solib-list-20110805-2.txt --]
[-- Type: text/plain, Size: 17973 bytes --]
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 5 Aug 2011 17:56:24 -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 <segment> 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 <library> 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 <library-list> 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 <library-list>. */
+
+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/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 5 Aug 2011 17:56:24 -0000
@@ -264,6 +264,7 @@ linux_add_process (int pid, int attached
proc = add_process (pid, attached);
proc->private = xcalloc (1, sizeof (*proc->private));
+ proc->private->r_debug = (CORE_ADDR) -1;
if (the_low_target.new_process != NULL)
proc->private->arch_private = the_low_target.new_process ();
@@ -4755,6 +4756,343 @@ 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 0 if not present. */
+
+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 0;
+
+ 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 0;
+}
+
+/* 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;
+
+ };
+
+/* Construct qXfer:libraries:read reply. */
+
+static int
+linux_qxfer_libraries (const char *annex, unsigned char *readbuf,
+ unsigned const char *writebuf,
+ CORE_ADDR offset, int len)
+{
+ char *document;
+ unsigned document_len;
+ struct process_info_private *const priv = current_process ()->private;
+ char filename[PATH_MAX];
+ int pid, is_elf64;
+
+ 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;
+
+ if (writebuf != NULL)
+ return -2;
+ if (readbuf == NULL)
+ return -1;
+
+ 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;
+
+ if (priv->r_debug == (CORE_ADDR) -1)
+ priv->r_debug = get_r_debug (pid, is_elf64);
+
+ gdb_assert (priv->r_debug != (CORE_ADDR) -1);
+
+ if (priv->r_debug == 0)
+ {
+ document = xstrdup ("<library-list/>\n");
+ }
+ else
+ {
+ int allocated = 1024;
+ char *p;
+ const int ptr_size = is_elf64 ? 8 : 4;
+ CORE_ADDR lm_addr, lm_prev, l_name, l_addr, l_next, l_prev;
+ int r_version;
+
+ document = xmalloc (allocated);
+ strcpy (document, "<library-list>");
+ p = document + strlen (document);
+
+ 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);
+ goto done;
+ }
+
+ 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);
+ goto done;
+ }
+
+ 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')
+ {
+ size_t len = strlen ((char *) libname);
+ char *name;
+
+ while (allocated < p - document + len + 100)
+ {
+ /* Expand to guarantee sufficient storage. */
+ uintptr_t document_len = p - document;
+
+ document = xrealloc (document, 2 * allocated);
+ allocated *= 2;
+ p = document + document_len;
+ }
+
+ strcpy (p, "<library name=\"");
+ p = p + strlen (p);
+ name = xml_escape_text ((char *) libname);
+ strcpy (p, name);
+ free (name);
+ p = p + strlen (p);
+ strcpy (p, "\"><segment address=\"");
+ p = p + strlen (p);
+ sprintf (p, "0x%lx", (long) l_addr);
+ p = p + strlen (p);
+ strcpy (p, "\"/></library>");
+ p = p + strlen (p);
+ }
+
+ if (l_next == 0)
+ break;
+
+ lm_prev = lm_addr;
+ lm_addr = l_next;
+ }
+ done:
+ strcpy (p, "</library-list>");
+ }
+
+ document_len = strlen (document + offset);
+ if (len > document_len)
+ len = document_len;
+
+ memcpy (readbuf, document + offset, len);
+ xfree (document);
+
+ return len;
+}
+
+
static struct target_ops linux_target_ops = {
linux_create_inferior,
linux_attach,
@@ -4813,7 +5151,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_qxfer_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 5 Aug 2011 17:56:24 -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 5 Aug 2011 17:56:24 -0000
@@ -918,6 +918,9 @@ handle_qxfer_libraries (const char *anne
if (annex[0] != '\0' || !target_running ())
return -1;
+ if (the_target->qxfer_libraries != NULL)
+ return the_target->qxfer_libraries (annex, readbuf, writebuf, offset, len);
+
/* Over-estimate the necessary memory. Assume that every character
in the library name must be escaped. */
total_len = 64;
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 5 Aug 2011 17:56:24 -0000
@@ -373,6 +373,11 @@ 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);
+
+ /* Read solib info. */
+ int (*qxfer_libraries) (const char *annex, unsigned char *readbuf,
+ unsigned const char *writebuf,
+ CORE_ADDR offset, int len);
};
extern struct target_ops *the_target;
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [patch] Implement qXfer:libraries for Linux/gdbserver [Was: Re: [RFC] Make target_read_string faster over high-latency links.]
2011-08-05 18:11 [patch] Implement qXfer:libraries for Linux/gdbserver [Was: Re: [RFC] Make target_read_string faster over high-latency links.] Paul Pluzhnikov
@ 2011-08-08 18:36 ` Jan Kratochvil
2011-08-08 18:56 ` Paul Pluzhnikov
2011-08-08 21:10 ` Jan Kratochvil
1 sibling, 1 reply; 13+ messages in thread
From: Jan Kratochvil @ 2011-08-08 18:36 UTC (permalink / raw)
To: Paul Pluzhnikov; +Cc: gdb-patches
On Fri, 05 Aug 2011 20:10:09 +0200, Paul Pluzhnikov wrote:
> Moving solib-svr4.c to gdb/common doesn't appear necessary: the parts
> that I would have used in gdbserver/linux-low.c are quite small, and not
> easily re-usable.
GDB could reuse solib-target.c and split solib-svr4.c to the small part
getting the library list - providing it for UNIX *-nat.c files as
TARGET_OBJECT_LIBRARIES - and the other UNIX code useful in both local and
remote cases there.
It would make libexpat mandatory even for the linux-nat.c usage so libexpat
could be bundler into the sourceware tree.
The same way all_dlls->dll_info could be renamed and reused on the server side
even for UNIX, that would be easier.
I do not say it would say LoC but it would be more clean, currently there
would be two isolated producer + consumer pairs of the same protocol.
Sure it may not be so easy as it sounds, I have not tried it.
Thanks,
Jan
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [patch] Implement qXfer:libraries for Linux/gdbserver [Was: Re: [RFC] Make target_read_string faster over high-latency links.]
2011-08-08 18:36 ` Jan Kratochvil
@ 2011-08-08 18:56 ` Paul Pluzhnikov
2011-08-08 21:24 ` [patch] Implement qXfer:libraries for Linux/gdbserver Jan Kratochvil
0 siblings, 1 reply; 13+ messages in thread
From: Paul Pluzhnikov @ 2011-08-08 18:56 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: gdb-patches
On Mon, Aug 8, 2011 at 11:36 AM, Jan Kratochvil
<jan.kratochvil@redhat.com> wrote:
> GDB could reuse solib-target.c
That is in fact where I started.
The trouble is that solib-target.c's notion of what info is in solib->lm_info
is radically different from solib-svr4.c's notion, each having a private
and distinct 'struct lm_info'. Un-tangling that seemed troublesome.
> It would make libexpat mandatory even for the linux-nat.c usage so libexpat
> could be bundler into the sourceware tree.
I think it's highly desirable to keep the current solib-srv4 code working
(e.g. in case you have a gdbserver that answers with empty list; either
because it is old, or because it's for a platform that hasn't been updated).
Given above, you can always fall back to it when libexpat is not detected.
> I do not say it would say LoC but it would be more clean, currently there
> would be two isolated producer + consumer pairs of the same protocol.
Yes, the two isolated consumers are a bit bothersome.
The producers would have to stay separate anyway, as the details of getting
the list differ greatly.
Thanks,
--
Paul Pluzhnikov
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [patch] Implement qXfer:libraries for Linux/gdbserver
2011-08-05 18:11 [patch] Implement qXfer:libraries for Linux/gdbserver [Was: Re: [RFC] Make target_read_string faster over high-latency links.] Paul Pluzhnikov
2011-08-08 18:36 ` Jan Kratochvil
@ 2011-08-08 21:10 ` Jan Kratochvil
2011-08-08 21:31 ` Paul Pluzhnikov
1 sibling, 1 reply; 13+ messages in thread
From: Jan Kratochvil @ 2011-08-08 21:10 UTC (permalink / raw)
To: Paul Pluzhnikov; +Cc: gdb-patches
Hi Paul,
with:
./gdbserver :1234 ~/t/pause64
../gdb ~/t/pause64 -ex 'target remote localhost:1234' -ex 'b main' -ex c
it falls back back memory-transfers as DT_DEBUG is zero initially at _start.
just to get the patch integrated on some final patches assembly.
Thanks,
Jan
gdb/gdbserver/
2011-08-08 Jan Kratochvil <jan.kratochvil@redhat.com>
Fix initial zero DT_DEBUG.
* linux-low.c (linux_add_process): Remove r_debug initialization.
(get_r_debug): Update function comment. Return -1 when DT_DEBUG is not
found.
(linux_qxfer_libraries): Call get_r_debug on -1 (not 0). Return on
both -1 or 0.
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -264,7 +264,6 @@ linux_add_process (int pid, int attached)
proc = add_process (pid, attached);
proc->private = xcalloc (1, sizeof (*proc->private));
- proc->private->r_debug = (CORE_ADDR) -1;
if (the_low_target.new_process != NULL)
proc->private->arch_private = the_low_target.new_process ();
@@ -4863,7 +4862,8 @@ get_dynamic (const int pid, const int is_elf64)
return 0;
}
-/* Return &_r_debug in the inferior, or 0 if not present. */
+/* 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)
@@ -4874,7 +4874,7 @@ get_r_debug (const int pid, const int is_elf64)
dynamic_memaddr = get_dynamic (pid, is_elf64);
if (dynamic_memaddr == 0)
- return 0;
+ return (CORE_ADDR) -1;
while (linux_read_memory (dynamic_memaddr, buf, dyn_size) == 0)
{
@@ -4981,12 +4981,10 @@ linux_qxfer_libraries (const char *annex, unsigned char *readbuf,
is_elf64 = elf_64_file_p (filename);
lmo = is_elf64 ? &lmo_64bit_offsets : &lmo_32bit_offsets;
- if (priv->r_debug == (CORE_ADDR) -1)
+ if (priv->r_debug == 0)
priv->r_debug = get_r_debug (pid, is_elf64);
- gdb_assert (priv->r_debug != (CORE_ADDR) -1);
-
- if (priv->r_debug == 0)
+ if (priv->r_debug == (CORE_ADDR) -1 || priv->r_debug == 0)
{
document = xstrdup ("<library-list/>\n");
}
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [patch] Implement qXfer:libraries for Linux/gdbserver
2011-08-08 18:56 ` Paul Pluzhnikov
@ 2011-08-08 21:24 ` Jan Kratochvil
2011-08-09 18:25 ` Daniel Jacobowitz
0 siblings, 1 reply; 13+ messages in thread
From: Jan Kratochvil @ 2011-08-08 21:24 UTC (permalink / raw)
To: Paul Pluzhnikov; +Cc: gdb-patches
On Mon, 08 Aug 2011 20:55:40 +0200, Paul Pluzhnikov wrote:
> On Mon, Aug 8, 2011 at 11:36 AM, Jan Kratochvil > <jan.kratochvil@redhat.com> wrote:
[...]
> The trouble is that solib-target.c's notion of what info is in solib->lm_info
> is radically different from solib-svr4.c's notion, each having a private
> and distinct 'struct lm_info'. Un-tangling that seemed troublesome.
I agree the solib-svr4.c split looks a bit complex to me.
> > It would make libexpat mandatory even for the linux-nat.c usage so libexpat
> > could be bundler into the sourceware tree.
>
> I think it's highly desirable to keep the current solib-srv4 code working
> (e.g. in case you have a gdbserver that answers with empty list; either
> because it is old, or because it's for a platform that hasn't been updated).
>
> Given above, you can always fall back to it when libexpat is not detected.
I tried to copy how windows-nat.c works, which even in local mode produces and
consumes the XML document, exchanged internally as TARGET_OBJECT_LIBRARIES.
Therefore the core svr4_current_sos function would also produce
TARGET_OBJECT_LIBRARIES XML document, even in local run.
Sure other solutions exist but this unifies the remote and local codepath the
most.
I do not think it is any problem to require + bundle libexpat, there was even
some FAQ of people with broken GDB for ARM remote targets before some proper
error messages were added indicating libexpat is missing for GDB.
> The producers would have to stay separate anyway, as the details of getting
> the list differ greatly.
While it is a detail it saves 46 LoC (Lines of Code) and it IMO also makes the
code more readable. Getting the list and producing the XML are two different
parts of code.
> Yes, the two isolated consumers are a bit bothersome.
Unfortunately the solib-svr4.c part is not so trivial as this one but IMO it
should be also done, I could get to it only later.
Thanks,
Jan
gdb/gdbserver/
2011-08-08 Jan Kratochvil <jan.kratochvil@redhat.com>
* inferiors.c (clear_all_dlls): New function.
(clear_inferiors): Move there the code from here, call it here.
* linux-low.c (linux_qxfer_libraries): Rename to ...
(linux_refresh_libraries): ... here. Drop all the XML handling. Call
clear_all_dlls and loaded_dll instead. Clear DLLS_CHANGED.
(linux_target_ops): Rename linux_qxfer_libraries to
linux_refresh_libraries.
* server.c (handle_qxfer_libraries): Likewise. Continue execution even
if REFRESH_LIBRARIES exists.
* server.h (clear_all_dlls): New prototype.
* target.h (struct target_ops): Rename linux_qxfer_libraries to
linux_refresh_libraries. Drop its parameters and return type.
--- a/gdb/gdbserver/inferiors.c
+++ b/gdb/gdbserver/inferiors.c
@@ -312,14 +312,22 @@ unloaded_dll (const char *name, CORE_ADDR base_addr)
#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;
}
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -4939,16 +4939,13 @@ struct link_map_offsets
/* Construct qXfer:libraries:read reply. */
-static int
-linux_qxfer_libraries (const char *annex, unsigned char *readbuf,
- unsigned const char *writebuf,
- CORE_ADDR offset, int len)
+static void
+linux_refresh_libraries (void)
{
- char *document;
- unsigned document_len;
struct process_info_private *const priv = current_process ()->private;
char filename[PATH_MAX];
- int pid, is_elf64;
+ 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 =
{
@@ -4971,34 +4968,17 @@ linux_qxfer_libraries (const char *annex, unsigned char *readbuf,
};
const struct link_map_offsets *lmo;
- if (writebuf != NULL)
- return -2;
- if (readbuf == NULL)
- return -1;
-
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)
- {
- document = xstrdup ("<library-list/>\n");
- }
- else
- {
- int allocated = 1024;
- char *p;
- const int ptr_size = is_elf64 ? 8 : 4;
- CORE_ADDR lm_addr, lm_prev, l_name, l_addr, l_next, l_prev;
- int r_version;
-
- document = xmalloc (allocated);
- strcpy (document, "<library-list>");
- p = document + strlen (document);
+ return;
r_version = 0;
if (linux_read_memory (priv->r_debug + lmo->r_version_offset,
@@ -5007,7 +4987,7 @@ linux_qxfer_libraries (const char *annex, unsigned char *readbuf,
|| r_version != 1)
{
warning ("unexpected r_debug version %d", r_version);
- goto done;
+ return;
}
if (read_one_ptr (priv->r_debug + lmo->r_map_offset,
@@ -5015,9 +4995,11 @@ linux_qxfer_libraries (const char *annex, unsigned char *readbuf,
{
warning ("unable to read r_map from 0x%lx",
(long) priv->r_debug + lmo->r_map_offset);
- goto done;
+ return;
}
+ clear_all_dlls ();
+
lm_prev = 0;
while (read_one_ptr (lm_addr + lmo->l_name_offset,
&l_name, ptr_size) == 0
@@ -5042,33 +5024,7 @@ linux_qxfer_libraries (const char *annex, unsigned char *readbuf,
libname[0] = '\0';
linux_read_memory (l_name, libname, sizeof (libname));
if (libname[0] != '\0')
- {
- size_t len = strlen ((char *) libname);
- char *name;
-
- while (allocated < p - document + len + 100)
- {
- /* Expand to guarantee sufficient storage. */
- uintptr_t document_len = p - document;
-
- document = xrealloc (document, 2 * allocated);
- allocated *= 2;
- p = document + document_len;
- }
-
- strcpy (p, "<library name=\"");
- p = p + strlen (p);
- name = xml_escape_text ((char *) libname);
- strcpy (p, name);
- free (name);
- p = p + strlen (p);
- strcpy (p, "\"><segment address=\"");
- p = p + strlen (p);
- sprintf (p, "0x%lx", (long) l_addr);
- p = p + strlen (p);
- strcpy (p, "\"/></library>");
- p = p + strlen (p);
- }
+ loaded_dll ((const char *) libname, l_addr);
if (l_next == 0)
break;
@@ -5076,18 +5032,9 @@ linux_qxfer_libraries (const char *annex, unsigned char *readbuf,
lm_prev = lm_addr;
lm_addr = l_next;
}
- done:
- strcpy (p, "</library-list>");
- }
- document_len = strlen (document + offset);
- if (len > document_len)
- len = document_len;
-
- memcpy (readbuf, document + offset, len);
- xfree (document);
-
- return len;
+ /* The library notification response is not expected for GNU/Linux. */
+ dlls_changed = 0;
}
@@ -5150,7 +5097,7 @@ static struct target_ops linux_target_ops = {
linux_stabilize_threads,
linux_install_fast_tracepoint_jump_pad,
linux_emit_ops,
- linux_qxfer_libraries
+ linux_refresh_libraries
};
static void
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -918,8 +918,8 @@ handle_qxfer_libraries (const char *annex,
if (annex[0] != '\0' || !target_running ())
return -1;
- if (the_target->qxfer_libraries != NULL)
- return the_target->qxfer_libraries (annex, readbuf, writebuf, offset, len);
+ 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. */
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -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 *,
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -374,10 +374,8 @@ struct target_ops
Returns NULL if bytecode compilation is not supported. */
struct emit_ops *(*emit_ops) (void);
- /* Read solib info. */
- int (*qxfer_libraries) (const char *annex, unsigned char *readbuf,
- unsigned const char *writebuf,
- CORE_ADDR offset, int len);
+ /* Refresh ALL_DLLS. */
+ void (*refresh_libraries) (void);
};
extern struct target_ops *the_target;
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [patch] Implement qXfer:libraries for Linux/gdbserver
2011-08-08 21:10 ` Jan Kratochvil
@ 2011-08-08 21:31 ` Paul Pluzhnikov
2011-08-08 21:38 ` Jan Kratochvil
0 siblings, 1 reply; 13+ messages in thread
From: Paul Pluzhnikov @ 2011-08-08 21:31 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: gdb-patches
On Mon, Aug 8, 2011 at 2:09 PM, Jan Kratochvil
<jan.kratochvil@redhat.com> wrote:
> with:
> ./gdbserver :1234 ~/t/pause64
> ../gdb ~/t/pause64 -ex 'target remote localhost:1234' -ex 'b main' -ex c
>
> it falls back back memory-transfers as DT_DEBUG is zero initially at _start.
>
> just to get the patch integrated on some final patches assembly.
Thanks.
A patch incorporating that change is attached. Or not.
I see you've reworked some of the gdbserver code as well. I'll update
the original thread with a unified patch.
> gdb/gdbserver/
> 2011-08-08 Jan Kratochvil <jan.kratochvil@redhat.com>
>
> Fix initial zero DT_DEBUG.
> * linux-low.c (linux_add_process): Remove r_debug initialization.
> (get_r_debug): Update function comment. Return -1 when DT_DEBUG is not
> found.
> (linux_qxfer_libraries): Call get_r_debug on -1 (not 0). Return on
> both -1 or 0.
>
> --- a/gdb/gdbserver/linux-low.c
> +++ b/gdb/gdbserver/linux-low.c
> @@ -264,7 +264,6 @@ linux_add_process (int pid, int attached)
>
> proc = add_process (pid, attached);
> proc->private = xcalloc (1, sizeof (*proc->private));
> - proc->private->r_debug = (CORE_ADDR) -1;
>
> if (the_low_target.new_process != NULL)
> proc->private->arch_private = the_low_target.new_process ();
> @@ -4863,7 +4862,8 @@ get_dynamic (const int pid, const int is_elf64)
> return 0;
> }
>
> -/* Return &_r_debug in the inferior, or 0 if not present. */
> +/* 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)
> @@ -4874,7 +4874,7 @@ get_r_debug (const int pid, const int is_elf64)
>
> dynamic_memaddr = get_dynamic (pid, is_elf64);
> if (dynamic_memaddr == 0)
> - return 0;
> + return (CORE_ADDR) -1;
I've updated the other return from get_r_debug() (if no DT_DEBUG
found) to also return -1.
Thanks,
--
Paul Pluzhnikov
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [patch] Implement qXfer:libraries for Linux/gdbserver
2011-08-08 21:31 ` Paul Pluzhnikov
@ 2011-08-08 21:38 ` Jan Kratochvil
2011-08-08 23:50 ` Paul Pluzhnikov
0 siblings, 1 reply; 13+ messages in thread
From: Jan Kratochvil @ 2011-08-08 21:38 UTC (permalink / raw)
To: Paul Pluzhnikov; +Cc: gdb-patches
On Mon, 08 Aug 2011 23:30:58 +0200, Paul Pluzhnikov wrote:
> I've updated the other return from get_r_debug() (if no DT_DEBUG
> found) to also return -1.
I apparently forgot, yes.
Thanks,
Jan
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [patch] Implement qXfer:libraries for Linux/gdbserver
2011-08-08 21:38 ` Jan Kratochvil
@ 2011-08-08 23:50 ` Paul Pluzhnikov
2011-08-09 9:07 ` Jan Kratochvil
0 siblings, 1 reply; 13+ messages in thread
From: Paul Pluzhnikov @ 2011-08-08 23:50 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 1796 bytes --]
On Mon, Aug 8, 2011 at 2:38 PM, Jan Kratochvil
<jan.kratochvil@redhat.com> wrote:
> On Mon, 08 Aug 2011 23:30:58 +0200, Paul Pluzhnikov wrote:
>> I've updated the other return from get_r_debug() (if no DT_DEBUG
>> found) to also return -1.
>
> I apparently forgot, yes.
There were some other missing updates (e.g. comment on
linux_refresh_libraries() was now wrong).
Updated unified patch re-tested on Linux/x86_64 and i686 and attached.
Thanks,
--
Paul Pluzhnikov
2011-08-08 Paul Pluzhnikov <ppluzhnikov@google.com>
* solib-svr4.c (library_list_start_segment): New function.
(library_list_start_library, library_list_end_library): Likewise.
(library_list_start_list): Likewise.
(segment_attributes, library_children): New variables.
(library_attributes, library_list_children): Likewise.
(library_list_attributes, library_list_elements): Likewise.
(svr4_free_so): Moved to here.
(svr4_free_library_list, svr4_parse_libraries): New functions.
(svr4_current_sos_via_xfer_libraries): Likewise.
(svr4_current_sos): Adjust.
gdbserver/ChangeLog:
2011-08-08 Paul Pluzhnikov <ppluzhnikov@google.com>
Jan Kratochvil <jan.kratochvil@redhat.com>
* inferiors.c (clear_all_dlls): New function.
(clear_inferiors): Move there the code from here, call it here.
* linux-low.c (linux_add_process): Adjust.
(get_phdr_phnum_from_proc_auxv, get_dynamic): New functions.
(get_r_debug, read_one_ptr): Likewise.
(struct link_map_offsets): New struct decl.
(linux_refresh_libraries): New function.
(struct linux_target_ops): Adjust.
(struct process_info_private): New member.
(handle_qxfer_libraries): Adjust.
(struct target_ops): New member.
* server.c (handle_qxfer_libraries): Adjust.
* server.h (clear_all_dlls): New prototype.
* target.h (struct target_ops): New member.
[-- Attachment #2: gdb-solib-list-20110808.txt --]
[-- Type: text/plain, Size: 17762 bytes --]
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 <segment> 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 <library> 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 <library-list> 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 <library-list>. */
+
+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;
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [patch] Implement qXfer:libraries for Linux/gdbserver
2011-08-08 23:50 ` Paul Pluzhnikov
@ 2011-08-09 9:07 ` Jan Kratochvil
2011-08-09 16:57 ` Paul Pluzhnikov
0 siblings, 1 reply; 13+ messages in thread
From: Jan Kratochvil @ 2011-08-09 9:07 UTC (permalink / raw)
To: Paul Pluzhnikov; +Cc: gdb-patches
On Tue, 09 Aug 2011 01:49:49 +0200, Paul Pluzhnikov wrote:
> --- 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)
[...]
> +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);
> +
> +
Two empty lines, should be one.
> + 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;
This does not work for -fPIE -pie executables such as those on Chromebook.
One needs to relocate A_VAL as it is the in-file (0-based) address while
in-memory address is shifted by X. X is calculated by solib-svr4.c
svr4_exec_displacement.
One can find here X probably most easily by subtracting PT_PHDR->P_VADDR from
PHDR_MEMADDR.
There is also a bug in it in solib-svr4.c scan_dyntag_auxv
[rfc] Use auxillary vector to retrieve .dynamic/.interp sections
http://sourceware.org/ml/gdb-patches/2008-08/msg00360.html
but one would need to patch the code around to get the fix applicable first.
> + }
> + else
> + {
> + Elf32_Phdr *const p = (Elf32_Phdr *) (phdr_buf + i * phdr_size);
> +
> + if (p->p_type == PT_DYNAMIC)
> + return p->p_vaddr;
Here too.
> + }
> + }
> +
> + 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);
> +}
> +
> +
Two empty lines, should be one.
> +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;
> +
Excessive empty line.
> + };
> +
> +/* 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);
Unprotected against inferior string longer than PATH_MAX, this is a regression
against solib-svr4.c.
> +
> + 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. */
+ /* &_r_debug. 0 if not yet determined. -1 if no PT_DYNAMIC in Phdrs. */
My mistake.
> + CORE_ADDR r_debug;
> };
>
> struct lwp_info;
But I still believe the GDB part should be more unified, I will try it.
Thanks,
Jan
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [patch] Implement qXfer:libraries for Linux/gdbserver
2011-08-09 9:07 ` Jan Kratochvil
@ 2011-08-09 16:57 ` Paul Pluzhnikov
2011-08-14 16:36 ` Jan Kratochvil
0 siblings, 1 reply; 13+ messages in thread
From: Paul Pluzhnikov @ 2011-08-09 16:57 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 4115 bytes --]
On Tue, Aug 9, 2011 at 2:06 AM, Jan Kratochvil
<jan.kratochvil@redhat.com> wrote:
> On Tue, 09 Aug 2011 01:49:49 +0200, Paul Pluzhnikov wrote:
>> --- 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)
> [...]
>> +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);
>> +
>> +
>
> Two empty lines, should be one.
Fixed here and elsewhere ...
>> + 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;
>
> This does not work for -fPIE -pie executables such as those on Chromebook.
> One needs to relocate A_VAL as it is the in-file (0-based) address while
> in-memory address is shifted by X. X is calculated by solib-svr4.c
> svr4_exec_displacement.
>
> One can find here X probably most easily by subtracting PT_PHDR->P_VADDR from
> PHDR_MEMADDR.
Done.
>
> There is also a bug in it in solib-svr4.c scan_dyntag_auxv
> [rfc] Use auxillary vector to retrieve .dynamic/.interp sections
> http://sourceware.org/ml/gdb-patches/2008-08/msg00360.html
> but one would need to patch the code around to get the fix applicable first.
>
>
>> + }
>> + else
>> + {
>> + Elf32_Phdr *const p = (Elf32_Phdr *) (phdr_buf + i * phdr_size);
>> +
>> + if (p->p_type == PT_DYNAMIC)
>> + return p->p_vaddr;
>
> Here too.
Ditto.
>> + /* 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);
>
> Unprotected against inferior string longer than PATH_MAX, this is a regression
> against solib-svr4.c.
Fixed.
Side note: SO_NAME_MAX_PATH_SIZE == 512 in solist.h is wrong for Linux;
I am surprised this hasn't bitten anyone yet. Why doesn't it use PATH_MAX ?
Thanks,
--
Paul Pluzhnikov
2011-08-09 Paul Pluzhnikov <ppluzhnikov@google.com>
* solib-svr4.c (library_list_start_segment): New function.
(library_list_start_library, library_list_end_library): Likewise.
(library_list_start_list): Likewise.
(segment_attributes, library_children): New variables.
(library_attributes, library_list_children): Likewise.
(library_list_attributes, library_list_elements): Likewise.
(svr4_free_so): Moved to here.
(svr4_free_library_list, svr4_parse_libraries): New functions.
(svr4_current_sos_via_xfer_libraries): Likewise.
(svr4_current_sos): Adjust.
gdbserver/ChangeLog:
2011-08-09 Paul Pluzhnikov <ppluzhnikov@google.com>
Jan Kratochvil <jan.kratochvil@redhat.com>
* inferiors.c (clear_all_dlls): New function.
(clear_inferiors): Move there the code from here, call it here.
* linux-low.c (linux_add_process): Adjust.
(get_phdr_phnum_from_proc_auxv, get_dynamic): New functions.
(get_r_debug, read_one_ptr): Likewise.
(struct link_map_offsets): New struct decl.
(linux_refresh_libraries): New function.
(struct linux_target_ops): Adjust.
(struct process_info_private): New member.
(handle_qxfer_libraries): Adjust.
(struct target_ops): New member.
* server.c (handle_qxfer_libraries): Adjust.
* server.h (clear_all_dlls): New prototype.
* target.h (struct target_ops): New member.
[-- Attachment #2: gdb-solib-list-20110809.txt --]
[-- Type: text/plain, Size: 18476 bytes --]
Index: solib-svr4.c
===================================================================
RCS file: /cvs/src/src/gdb/solib-svr4.c,v
retrieving revision 1.153
diff -u -p -r1.153 solib-svr4.c
--- solib-svr4.c 9 Aug 2011 12:51:47 -0000 1.153
+++ solib-svr4.c 9 Aug 2011 16:51:37 -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 <segment> 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 <library> 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 <library-list> 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 <library-list>. */
+
+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. */
@@ -1068,6 +1263,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. */
@@ -2125,13 +2324,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 9 Aug 2011 16:51:37 -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 9 Aug 2011 16:51:37 -0000
@@ -4755,6 +4755,312 @@ 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, relocation;
+ 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;
+
+ /* Compute relocation: it is expected to be 0 for "regular" executables,
+ non-zero for PIE ones. */
+ relocation = -1;
+ for (i = 0; relocation == -1 && i < num_phdr; i++)
+ if (is_elf64)
+ {
+ Elf64_Phdr *const p = (Elf64_Phdr *) (phdr_buf + i * phdr_size);
+
+ if (p->p_type == PT_PHDR)
+ relocation = phdr_memaddr - p->p_vaddr;
+ }
+ else
+ {
+ Elf32_Phdr *const p = (Elf32_Phdr *) (phdr_buf + i * phdr_size);
+
+ if (p->p_type == PT_PHDR)
+ relocation = phdr_memaddr - p->p_vaddr;
+ }
+
+ if (relocation == -1)
+ {
+ warning ("Unexpected missing PT_PHDR");
+ 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 + relocation;
+ }
+ else
+ {
+ Elf32_Phdr *const p = (Elf32_Phdr *) (phdr_buf + i * phdr_size);
+
+ if (p->p_type == PT_DYNAMIC)
+ return p->p_vaddr + relocation;
+ }
+ }
+
+ 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));
+ libname[sizeof (libname) - 1] = '\0';
+ 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 +5119,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 9 Aug 2011 16:51:37 -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. 0 if not yet determined. -1 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 9 Aug 2011 16:51:37 -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 9 Aug 2011 16:51:37 -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 9 Aug 2011 16:51:37 -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;
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [patch] Implement qXfer:libraries for Linux/gdbserver
2011-08-08 21:24 ` [patch] Implement qXfer:libraries for Linux/gdbserver Jan Kratochvil
@ 2011-08-09 18:25 ` Daniel Jacobowitz
0 siblings, 0 replies; 13+ messages in thread
From: Daniel Jacobowitz @ 2011-08-09 18:25 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: Paul Pluzhnikov, gdb-patches
On Mon, Aug 8, 2011 at 5:24 PM, Jan Kratochvil
<jan.kratochvil@redhat.com> wrote:
> I do not think it is any problem to require + bundle libexpat, there was even
> some FAQ of people with broken GDB for ARM remote targets before some proper
> error messages were added indicating libexpat is missing for GDB.
You may want to go through the archives on this topic; there were
considerable reasons not to bundle libexpat when I introduced the
dependency.
--
Thanks,
Daniel
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [patch] Implement qXfer:libraries for Linux/gdbserver
2011-08-09 16:57 ` Paul Pluzhnikov
@ 2011-08-14 16:36 ` Jan Kratochvil
2011-10-03 21:57 ` IMO-obsolste: " Jan Kratochvil
0 siblings, 1 reply; 13+ messages in thread
From: Jan Kratochvil @ 2011-08-14 16:36 UTC (permalink / raw)
To: Paul Pluzhnikov; +Cc: gdb-patches
On Tue, 09 Aug 2011 18:56:15 +0200, Paul Pluzhnikov wrote:
> Side note: SO_NAME_MAX_PATH_SIZE == 512 in solist.h is wrong for Linux;
> I am surprised this hasn't bitten anyone yet. Why doesn't it use PATH_MAX ?
I agree, that static array is not nice anyway.
Regarding design merge of solib-svr4.c XML with solib-target.c XML I have
read the threads:
[RFC] Add expat to the GDB sources
http://sourceware.org/ml/gdb-patches/2006-07/msg00231.html
[RFC, take 2] Optional expat
http://sourceware.org/ml/gdb-patches/2006-08/msg00048.html
and there is opposition by Mark and Eli (and IIRC only by Mark and Eli)
against any additional pre-requisites for some minimal native GDB build.
I have to agree just because of some minor internal design clean up it may not
make sense to break this pre-requisite rule.
Sidenote: readline needs to continue being bundled as even latest (=bundled)
readline release needs to stay forked at least by:
[patch 2/3] readline-6.2: Workaround "ask" regression
http://sourceware.org/ml/gdb-patches/2011-05/msg00005.html
But work is continuing in general to make the possibility of dropping bundled
readline, where it turns from current technical reasons to political reasons.
solib-svr4.c should be split etc. but I find it now outside of the scope of
this patch.
I would therefore find OK to just technically merge the XML parsing in
solib-svr4.c and solib-target.c into some solib.c for example to be called
from both points (if it still makes sense after the changes requested below).
Besides that there are some bugs in the current solib-svr4.c implementation.
If you:
prelink -R /lib64/libc-2.14.90.so
Then it will:
Sending packet: $qXfer:libraries:read::0,fff#d5...Packet received: l<library-list>\n <library name="/lib64/libc.so.6"><segment address="0x0"/></library>\n <library name="/lib64/ld-linux-x86-64.so.2"><segment address="0x0"/></library>\n</library-list>\n
warning: while parsing target library list (at line 2): No segment defined for /lib64/libc.so.6
Which is correct, because `l_addr' being sent as `segment address' is just
in-memory vs. in-file-at-program-start-time displacement, on prelinked system
l_addr is normally always 0.
This also violates the current protocol description:
gdb.texinfo
@node Library List Format
The segment or section bases are start addresses, not relocation offsets; they
do not depend on the library's link-time base addresses.
So either gdbserver needs to find the real base address of the library (which
is IMO possible only by searching /proc/PID/maps for l_ld mapped object and
taking its base address). Or a different XML attribute needs to be used.
It also needs to pass l_ld address as it is being used by lm_addr_check to
match up the host in-file bfd addresses to the target in-memory addresses
across various prelink situations. There are 4 different addresses present at
the same time during debugging:
(1) in-memory target base address where library has been loaded
(2) in-file base address the time the library has been loaded,
which is contained in ELF data structures in the target memory
(3) in-file host base address of library the time debugger has been started
(4) in-file host separate .debug file address (which is always 0)
gdb.base/break-interp.exp should PASS with it, with working <library-list/>,
currently it prints a lot of:
warning: while parsing target library list: no element found
lm_addr_check currently expects it can read so->lm_info->lm being now NULL
with <library-list/>. But lm_addr_check will start to be needed with
gdb.base/break-interp.exp so one may hit this problem afterwards.
Sorry for the delay, I can return to it in 1-2 weeks (need to push entryval).
Thanks,
Jan
^ permalink raw reply [flat|nested] 13+ messages in thread
* IMO-obsolste: Re: [patch] Implement qXfer:libraries for Linux/gdbserver
2011-08-14 16:36 ` Jan Kratochvil
@ 2011-10-03 21:57 ` Jan Kratochvil
0 siblings, 0 replies; 13+ messages in thread
From: Jan Kratochvil @ 2011-10-03 21:57 UTC (permalink / raw)
To: Paul Pluzhnikov; +Cc: gdb-patches
IMO-obsoleted by:
[patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2
http://sourceware.org/ml/gdb-patches/2011-10/msg00059.html
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2011-10-03 21:57 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-08-05 18:11 [patch] Implement qXfer:libraries for Linux/gdbserver [Was: Re: [RFC] Make target_read_string faster over high-latency links.] Paul Pluzhnikov
2011-08-08 18:36 ` Jan Kratochvil
2011-08-08 18:56 ` Paul Pluzhnikov
2011-08-08 21:24 ` [patch] Implement qXfer:libraries for Linux/gdbserver Jan Kratochvil
2011-08-09 18:25 ` Daniel Jacobowitz
2011-08-08 21:10 ` Jan Kratochvil
2011-08-08 21:31 ` Paul Pluzhnikov
2011-08-08 21:38 ` Jan Kratochvil
2011-08-08 23:50 ` Paul Pluzhnikov
2011-08-09 9:07 ` Jan Kratochvil
2011-08-09 16:57 ` Paul Pluzhnikov
2011-08-14 16:36 ` Jan Kratochvil
2011-10-03 21:57 ` IMO-obsolste: " Jan Kratochvil
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox