[patch] Implement qXfer:libraries for Linux/gdbserver

Paul Pluzhnikov ppluzhnikov@google.com
Mon Aug 8 23:50:00 GMT 2011


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.
-------------- next part --------------
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 (&current_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;


More information about the Gdb-patches mailing list