public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2
@ 2011-10-03 21:55 Jan Kratochvil
  2011-10-04  6:11 ` Eli Zaretskii
                   ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Jan Kratochvil @ 2011-10-03 21:55 UTC (permalink / raw)
  To: gdb-patches; +Cc: Paul Pluzhnikov

Hi Paul,

this is updated:
	Re: [patch] Implement qXfer:libraries for Linux/gdbserver
	http://sourceware.org/ml/gdb-patches/2011-08/msg00204.html

I gave up on the unification way.  solib-svr4.c just needs too much more
information than the former <library-list/> format to: (a) Make the format
compatible, (b) make the gdbserver producer meaningful to unify, (c) gdb
consumer likewise.

It could for example use absolute base address instead of the l_addr bias to
be compatible with established <library-list/>.  But it would need to pass
additionally l_ld for dealing with PIE+PIC displacement and also `struct
link_map' addresses for TLS.  And it would be complicated for both producer
and consumer side to construct and use the absolute library base addresses.

The testcase would benefit from some gdbserver -> gdb in-protocol channel used
for the `warning' calls in gdbserver.

No regressions on {x86_64,x86_64-m32,i686}-fedora16pre-linux-gnu and with
gdbserver and with gdbserver+PIE.


Thanks,
Jan


gdb/
2011-10-03  Paul Pluzhnikov  <ppluzhnikov@google.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>

	* features/library-list.dtd: Add attributes library-list.main-lm,
	library.lm, library.l_addr and library.l_ld.
	* solib-svr4.c (struct svr4_library_list): New.
	[HAVE_LIBEXPAT]: Include xml-support.h.
	[HAVE_LIBEXPAT] (library_list_start_library, library_list_start_list)
	(library_attributes, library_list_children, library_list_attributes)
	(library_list_elements, svr4_parse_libraries)
	(svr4_current_sos_via_xfer_libraries): New.
	[!HAVE_LIBEXPAT] (svr4_current_sos_via_xfer_libraries): New.
	(svr4_read_so_list): Extend the corruption message by addresses.
	(svr4_current_sos): New variable library_list, call
	svr4_current_sos_via_xfer_libraries.

gdb/gdbserver/
2011-10-03  Paul Pluzhnikov  <ppluzhnikov@google.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>

	* linux-low.c (get_phdr_phnum_from_proc_auxv, get_dynamic, get_r_debug)
	(read_one_ptr, struct link_map_offsets, linux_refresh_libraries): New.
	(struct linux_target_ops): Install linux_qxfer_libraries.
	* linux-low.h (struct process_info_private): New member r_debug.
	* server.c (handle_qxfer_libraries): Call the_target->qxfer_libraries.
	* target.h (struct target_ops): New member qxfer_libraries.

gdb/doc/
2011-10-03  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* gdb.texinfo (Library List Format): Add SVR4 format description.

gdb/testsuite/
2011-10-03  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* gdb.base/solib-corrupted.exp: Suppress test on is_remote target.
	(corrupted list): Adjust the expectation.

--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -36412,15 +36412,21 @@ are loaded.
 
 The @samp{qXfer:libraries:read} packet returns an XML document which
 lists loaded libraries and their offsets.  Each library has an
-associated name and one or more segment or section base addresses,
-which report where the library was loaded in memory.
+associated name and one or more segment, section base addresses or SVR4 linker
+parameters, which report where the library was loaded in memory.
 
 For the common case of libraries that are fully linked binaries, the
 library should have a list of segments.  If the target supports
 dynamic linking of a relocatable object file, its library XML element
 should instead include a list of allocated sections.  The segment or
 section bases are start addresses, not relocation offsets; they do not
-depend on the library's link-time base addresses.
+depend on the library's link-time base addresses.  For SVR4 systems
+are reported parameters @{lm} with address of @code{struct link_map}
+used for TLS (Thread Local Storage) access, @code{l_addr} specifying
+bias of the mapped memory address minus the prelinked base address
+and also @code{l_ld} which is memory address of the @code{PT_DYNAMIC}
+segment.  SVR4 systems additionally specify @code{struct link_map}
+address of the main executable in the @code{<library-list>} element.
 
 @value{GDBN} must be linked with the Expat library to support XML
 library lists.  @xref{Expat}.
@@ -36449,23 +36455,38 @@ allocated sections (.text, .data, .bss), looks like this:
 </library-list>
 @end smallexample
 
+SVR4 systems report:
+
+@smallexample
+<library-list main-lm="0xe4f8f8">
+  <library name="/lib/ld-linux.so.2" lm="0xe4f51c" l_addr="0xe2d000"
+           l_ld="0xe4eefc"/>
+  <library name="/lib/libc.so.6" lm="0xe4fbe8" l_addr="0x154000"
+           l_ld="0x152350"/>
+</library-list>
+@end smallexample
+
 The format of a library list is described by this DTD:
 
 @smallexample
 <!-- library-list: Root element with versioning -->
 <!ELEMENT library-list  (library)*>
 <!ATTLIST library-list  version CDATA   #FIXED  "1.0">
+<!ATTLIST library-list  main-lm CDATA   #IMPLIED>
 <!ELEMENT library       (segment*, section*)>
 <!ATTLIST library       name    CDATA   #REQUIRED>
+<!ATTLIST library       lm      CDATA   #IMPLIED>
+<!ATTLIST library       l_addr  CDATA   #IMPLIED>
+<!ATTLIST library       l_ld    CDATA   #IMPLIED>
 <!ELEMENT segment       EMPTY>
 <!ATTLIST segment       address CDATA   #REQUIRED>
 <!ELEMENT section       EMPTY>
 <!ATTLIST section       address CDATA   #REQUIRED>
 @end smallexample
 
-In addition, segments and section descriptors cannot be mixed within a
-single library element, and you must supply at least one segment or
-section for each library.
+In addition, segments, section and SVR4 descriptors cannot be mixed
+within a single library element, and you must supply at least one
+segment or section or both @code{l_addr} and @code{l_ld} for each library.
 
 @node Memory Map Format
 @section Memory Map Format
--- a/gdb/features/library-list.dtd
+++ b/gdb/features/library-list.dtd
@@ -7,9 +7,13 @@
 <!-- library-list: Root element with versioning -->
 <!ELEMENT library-list  (library)*>
 <!ATTLIST library-list  version CDATA   #FIXED  "1.0">
+<!ATTLIST library-list  main-lm CDATA   #IMPLIED>
 
 <!ELEMENT library       (segment*, section*)>
 <!ATTLIST library       name    CDATA   #REQUIRED>
+<!ATTLIST library       lm      CDATA   #IMPLIED>
+<!ATTLIST library       l_addr  CDATA   #IMPLIED>
+<!ATTLIST library       l_ld    CDATA   #IMPLIED>
 
 <!ELEMENT segment       EMPTY>
 <!ATTLIST segment       address CDATA   #REQUIRED>
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -4906,6 +4906,377 @@ 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_ld field in struct link_map.  */
+    int l_ld_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.  */
+      8,     /* l_ld 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.  */
+      16,    /* l_ld 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 == 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_ld, l_next, l_prev;
+      int r_version, header_done = 0;
+
+      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_ld_offset,
+			      &l_ld, 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 ("Corrupted shared library list: 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) - 1);
+	  libname[sizeof (libname) - 1] = '\0';
+	  if (libname[0] != '\0')
+	    {
+	      size_t len = strlen ((char *) libname);
+	      char *name;
+
+	      if (!header_done)
+		{
+		  /* Terminate `<library-list'.  */
+		  *p++ = '>';
+		  header_done = 1;
+		}
+
+	      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;
+		}
+
+	      name = xml_escape_text ((char *) libname);
+	      p += sprintf (p, "<library name=\"%s\" lm=\"0x%lx\" "
+			       "l_addr=\"0x%lx\" l_ld=\"0x%lx\"/>",
+			    name, (unsigned long) lm_addr,
+			    (unsigned long) l_addr, (unsigned long) l_ld);
+	      free (name);
+	    }
+	  else if (lm_prev == 0)
+	    {
+	      sprintf (p, " main-lm=\"0x%lx\"", (unsigned long) lm_addr);
+	      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,
@@ -4965,7 +5336,8 @@ static struct target_ops linux_target_ops = {
   linux_cancel_breakpoints,
   linux_stabilize_threads,
   linux_install_fast_tracepoint_jump_pad,
-  linux_emit_ops
+  linux_emit_ops,
+  linux_qxfer_libraries
 };
 
 static void
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -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;
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -917,6 +917,9 @@ 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);
+
   /* Over-estimate the necessary memory.  Assume that every character
      in the library name must be escaped.  */
   total_len = 64;
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -377,6 +377,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;
--- a/gdb/solib-svr4.c
+++ b/gdb/solib-svr4.c
@@ -916,6 +916,104 @@ open_symbol_file_object (void *from_ttyp)
   return 1;
 }
 
+/* Data exchange structure for the XML parser as returned by
+   svr4_current_sos_via_xfer_libraries.  */
+
+struct svr4_library_list
+{
+  struct so_list *head, **tailp;
+  CORE_ADDR main_lm;
+};
+
+#ifdef HAVE_LIBEXPAT
+
+#include "xml-support.h"
+
+/* Handle the start of a <library> element.  Note: new elements are added
+   at the tail of the list, keeping the list in 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)
+{
+  struct svr4_library_list *list = user_data;
+  const char *name = xml_find_attribute (attributes, "name")->value;
+  ULONGEST *lmp = xml_find_attribute (attributes, "lm")->value;
+  ULONGEST *l_addrp = xml_find_attribute (attributes, "l_addr")->value;
+  ULONGEST *l_ldp = xml_find_attribute (attributes, "l_ld")->value;
+  struct so_list *new_elem;
+
+  new_elem = XZALLOC (struct so_list);
+  new_elem->lm_info = XZALLOC (struct lm_info);
+  new_elem->lm_info->lm_addr = *lmp;
+  new_elem->lm_info->l_addr_inferior = *l_addrp;
+  new_elem->lm_info->l_ld = *l_ldp;
+
+  strncpy (new_elem->so_name, name, sizeof (new_elem->so_name) - 1);
+  new_elem->so_name[sizeof (new_elem->so_name) - 1] = 0;
+  strcpy (new_elem->so_original_name, new_elem->so_name);
+
+  *list->tailp = new_elem;
+  list->tailp = &new_elem->next;
+}
+
+/* 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)
+{
+  struct svr4_library_list *list = user_data;
+  char *version = xml_find_attribute (attributes, "version")->value;
+  struct gdb_xml_value *main_lm = xml_find_attribute (attributes, "main-lm");
+
+  if (strcmp (version, "1.0") != 0)
+    gdb_xml_error (parser,
+		   _("Library list has unsupported version \"%s\""),
+		   version);
+
+  if (main_lm)
+    list->main_lm = *(ULONGEST *) main_lm->value;
+}
+
+/* The allowed elements and attributes for an XML library list.
+   The root element is a <library-list>.  */
+
+static const struct gdb_xml_attribute library_attributes[] =
+{
+  { "name", GDB_XML_AF_NONE, NULL, NULL },
+  { "lm", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "l_addr", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "l_ld", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_element library_list_children[] =
+{
+  {
+    "library", library_attributes, NULL,
+    GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
+    library_list_start_library, NULL
+  },
+  { 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 },
+  { "main-lm", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, 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 }
+};
+
 /* Implementation for target_so_ops.free_so.  */
 
 static void
@@ -940,6 +1038,69 @@ svr4_free_library_list (void *p_list)
     }
 }
 
+/* Parse qXfer:libraries:read packet into *SO_LIST_RETURN.  Return 1 if
+
+   Return 0 if packet not supported, *SO_LIST_RETURN is not modified in such
+   case.  Return 1 if *SO_LIST_RETURN contains the library list, it may be
+   empty, caller is responsible for freeing all its entries.  */
+
+static int
+svr4_parse_libraries (const char *document, struct svr4_library_list *list)
+{
+  struct cleanup *back_to = make_cleanup (svr4_free_library_list,
+					  &list->head);
+
+  memset (list, 0, sizeof (*list));
+  list->tailp = &list->head;
+  if (gdb_xml_parse_quick (_("target library list"), "library-list.dtd",
+			   library_list_elements, document, list) == 0)
+    {
+      /* Parsed successfully, keep the result.  */
+      discard_cleanups (back_to);
+      return 1;
+    }
+
+  do_cleanups (back_to);
+  return 0;
+}
+
+/* Attempt to get so_list from target via qXfer:libraries:read packet.
+
+   Return 0 if packet not supported, *SO_LIST_RETURN is not modified in such
+   case.  Return 1 if *SO_LIST_RETURN contains the library list, it may be
+   empty, caller is responsible for freeing all its entries.  */
+
+static int
+svr4_current_sos_via_xfer_libraries (struct svr4_library_list *list)
+{
+  char *library_document;
+  int 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 0;
+
+  back_to = make_cleanup (xfree, library_document);
+  result = svr4_parse_libraries (library_document, list);
+  do_cleanups (back_to);
+
+  return result;
+}
+
+#else
+
+static int
+svr4_current_sos_via_xfer_libraries (struct svr4_library_list *list)
+{
+  return 0;
+}
+
+#endif
+
 /* If no shared library information is available from the dynamic
    linker, build a fallback list from other sources.  */
 
@@ -999,7 +1160,9 @@ svr4_read_so_list (CORE_ADDR lm, struct so_list ***link_ptr_ptr,
 
       if (new->lm_info->l_prev != prev_lm)
 	{
-	  warning (_("Corrupted shared library list"));
+	  warning (_("Corrupted shared library list: %s != %s"),
+		   paddress (target_gdbarch, prev_lm),
+		   paddress (target_gdbarch, new->lm_info->l_prev));
 	  do_cleanups (old_chain);
 	  break;
 	}
@@ -1060,6 +1223,18 @@ svr4_current_sos (void)
   struct svr4_info *info;
   struct cleanup *back_to;
   int ignore_first;
+  struct svr4_library_list library_list;
+
+  if (svr4_current_sos_via_xfer_libraries (&library_list))
+    {
+      if (library_list.main_lm)
+	{
+	  info = get_svr4_info ();
+	  info->main_lm_addr = library_list.main_lm;
+	}
+
+      return library_list.head ? library_list.head : svr4_default_sos ();
+    }
 
   info = get_svr4_info ();
 
--- a/gdb/testsuite/gdb.base/solib-corrupted.exp
+++ b/gdb/testsuite/gdb.base/solib-corrupted.exp
@@ -17,6 +17,12 @@ if {[skip_shlib_tests]} {
     return 0
 }
 
+if {[is_remote target]} {
+    # gdbserver prints the warning message but expect is parsing only the GDB
+    # output, not the gdbserver output.
+    return 0
+}
+
 set testfile "solib-corrupted"
 set srcfile start.c
 
@@ -47,4 +53,4 @@ gdb_test_multiple "p/x _r_debug->r_map->l_next = _r_debug->r_map" $test {
 	pass $test
     }
 }
-gdb_test "info sharedlibrary" "warning: Corrupted shared library list\r\n.*" "corrupted list"
+gdb_test "info sharedlibrary" "warning: Corrupted shared library list: .*" "corrupted list"

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2
  2011-10-03 21:55 [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2 Jan Kratochvil
@ 2011-10-04  6:11 ` Eli Zaretskii
  2011-10-18 17:49   ` Messages localization in gdbserver [Re: [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2] Jan Kratochvil
  2011-10-06 19:09 ` [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2 Pedro Alves
  2011-10-10  2:44 ` [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2 Daniel Jacobowitz
  2 siblings, 1 reply; 12+ messages in thread
From: Eli Zaretskii @ 2011-10-04  6:11 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches, ppluzhnikov

> Date: Mon, 3 Oct 2011 23:55:30 +0200
> From: Jan Kratochvil <jan.kratochvil@redhat.com>
> Cc: Paul Pluzhnikov <ppluzhnikov@google.com>
> 
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -36412,15 +36412,21 @@ are loaded.
>  
>  The @samp{qXfer:libraries:read} packet returns an XML document which
>  lists loaded libraries and their offsets.  Each library has an
> -associated name and one or more segment or section base addresses,
> -which report where the library was loaded in memory.
> +associated name and one or more segment, section base addresses or SVR4 linker
> +parameters, which report where the library was loaded in memory.

Please reword:

 ... one or more segment addresses, section base addresses, or SVR4
 linker parameters.  These describe where the library was loaded into
 memory.

> +depend on the library's link-time base addresses.  For SVR4 systems
> +are reported parameters @{lm} with address of @code{struct link_map}
> +used for TLS (Thread Local Storage) access, @code{l_addr} specifying
> +bias of the mapped memory address minus the prelinked base address
> +and also @code{l_ld} which is memory address of the @code{PT_DYNAMIC}
> +segment.  SVR4 systems additionally specify @code{struct link_map}
> +address of the main executable in the @code{<library-list>} element.

This is too much for a single sentence.  I suggest an itemized list
instead:

 For SVR4 systems, the following parameters are reported:

 @itemize @minus
 @item
 @code{lm} with address of @code{struct link_map} used ...
 @item
 @code{l_ld}, which is memory address of ...
 ...
 @end itemize

And btw, are we sure that sayin "SVR4 systems" is enough to
unambiguously identify those systems?  How about at least a partial
list in parentheses?

> +  if (*phdr_memaddr == 0 || *num_phdr == 0)
> +    {
> +      warning ("Unexpected missing AT_PHDR and/or AT_PHNUM: "
> +	       "phdr_memaddr = %ld, phdr_num = %d",

No _() ?  Doesn't gdbserver support translations of messages?

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2
  2011-10-03 21:55 [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2 Jan Kratochvil
  2011-10-04  6:11 ` Eli Zaretskii
@ 2011-10-06 19:09 ` Pedro Alves
  2011-10-21 11:05   ` Jan Kratochvil
  2011-10-10  2:44 ` [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2 Daniel Jacobowitz
  2 siblings, 1 reply; 12+ messages in thread
From: Pedro Alves @ 2011-10-06 19:09 UTC (permalink / raw)
  To: gdb-patches; +Cc: Jan Kratochvil, Paul Pluzhnikov

I think this is great,

On Monday 03 October 2011 22:55:30, Jan Kratochvil wrote:
>  should instead include a list of allocated sections.  The segment or
>  section bases are start addresses, not relocation offsets; they do not
> -depend on the library's link-time base addresses.
> +depend on the library's link-time base addresses.  For SVR4 systems
> +are reported parameters @{lm} with address of @code{struct link_map}
> +used for TLS (Thread Local Storage) access, @code{l_addr} specifying
> +bias of the mapped memory address minus the prelinked base address
> +and also @code{l_ld} which is memory address of the @code{PT_DYNAMIC}
> +segment.  SVR4 systems additionally specify @code{struct link_map}
> +address of the main executable in the @code{<library-list>} element.

but this reuse of <library-list> is a mistake.  If we're not going to use
an solib-target.c at all, and the semantics of what goes over the
wire is not the same as <library-list>/TARGET_OBJECT_LIBRARIES, then
let's come up with a completely independent new target object + dtd
instead.  Let's call it for example TARGET_OBJECT_SVR4_LIBRARIES.
We should not prevent the possibility of _both_ using
solib-svr4.c and solib-target.c at the same time, or the
possibility of having a completely target-side implementation
of svr4 libraries in the future, using <library-list> as is
(and having gdb be aware that support is present from
qXfer:read:libraries+ in qSupported).

-- 
Pedro Alves

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2
  2011-10-03 21:55 [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2 Jan Kratochvil
  2011-10-04  6:11 ` Eli Zaretskii
  2011-10-06 19:09 ` [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2 Pedro Alves
@ 2011-10-10  2:44 ` Daniel Jacobowitz
  2011-10-12 20:22   ` Jan Kratochvil
  2 siblings, 1 reply; 12+ messages in thread
From: Daniel Jacobowitz @ 2011-10-10  2:44 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches, Paul Pluzhnikov

On Mon, Oct 3, 2011 at 5:55 PM, Jan Kratochvil
<jan.kratochvil@redhat.com> wrote:
> Hi Paul,
>
> this is updated:
>        Re: [patch] Implement qXfer:libraries for Linux/gdbserver
>        http://sourceware.org/ml/gdb-patches/2011-08/msg00204.html
>
> I gave up on the unification way.  solib-svr4.c just needs too much more
> information than the former <library-list/> format to: (a) Make the format
> compatible, (b) make the gdbserver producer meaningful to unify, (c) gdb
> consumer likewise.
>
> It could for example use absolute base address instead of the l_addr bias to
> be compatible with established <library-list/>.  But it would need to pass
> additionally l_ld for dealing with PIE+PIC displacement and also `struct
> link_map' addresses for TLS.  And it would be complicated for both producer
> and consumer side to construct and use the absolute library base addresses.

I would be a whole lot happier if we could use the same <library-list>
elements for SVR4.  There may be some creative interpretations we can
use to make it work... yes, we'd likely have to pass the link map
addresses as additional information; fortunately that's easy.  I'm not
sure I understand the issue with dealing with absolute library base
addresses.  But in short, the way to think about this is to move all
the complicated bits of library processing to the target agent, and
give GDB a simplified view of what's really going on.

If that's not practical, then +1 to Pedro's suggestion; if you're
bulk-reading the link maps, or doing some svr4-specific semantics, we
should express it that way.

>
> The testcase would benefit from some gdbserver -> gdb in-protocol channel used
> for the `warning' calls in gdbserver.
>
> No regressions on {x86_64,x86_64-m32,i686}-fedora16pre-linux-gnu and with
> gdbserver and with gdbserver+PIE.
>
>
> Thanks,
> Jan
>
>
> gdb/
> 2011-10-03  Paul Pluzhnikov  <ppluzhnikov@google.com>
>            Jan Kratochvil  <jan.kratochvil@redhat.com>
>
>        * features/library-list.dtd: Add attributes library-list.main-lm,
>        library.lm, library.l_addr and library.l_ld.
>        * solib-svr4.c (struct svr4_library_list): New.
>        [HAVE_LIBEXPAT]: Include xml-support.h.
>        [HAVE_LIBEXPAT] (library_list_start_library, library_list_start_list)
>        (library_attributes, library_list_children, library_list_attributes)
>        (library_list_elements, svr4_parse_libraries)
>        (svr4_current_sos_via_xfer_libraries): New.
>        [!HAVE_LIBEXPAT] (svr4_current_sos_via_xfer_libraries): New.
>        (svr4_read_so_list): Extend the corruption message by addresses.
>        (svr4_current_sos): New variable library_list, call
>        svr4_current_sos_via_xfer_libraries.
>
> gdb/gdbserver/
> 2011-10-03  Paul Pluzhnikov  <ppluzhnikov@google.com>
>            Jan Kratochvil  <jan.kratochvil@redhat.com>
>
>        * linux-low.c (get_phdr_phnum_from_proc_auxv, get_dynamic, get_r_debug)
>        (read_one_ptr, struct link_map_offsets, linux_refresh_libraries): New.
>        (struct linux_target_ops): Install linux_qxfer_libraries.
>        * linux-low.h (struct process_info_private): New member r_debug.
>        * server.c (handle_qxfer_libraries): Call the_target->qxfer_libraries.
>        * target.h (struct target_ops): New member qxfer_libraries.
>
> gdb/doc/
> 2011-10-03  Jan Kratochvil  <jan.kratochvil@redhat.com>
>
>        * gdb.texinfo (Library List Format): Add SVR4 format description.
>
> gdb/testsuite/
> 2011-10-03  Jan Kratochvil  <jan.kratochvil@redhat.com>
>
>        * gdb.base/solib-corrupted.exp: Suppress test on is_remote target.
>        (corrupted list): Adjust the expectation.
>
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -36412,15 +36412,21 @@ are loaded.
>
>  The @samp{qXfer:libraries:read} packet returns an XML document which
>  lists loaded libraries and their offsets.  Each library has an
> -associated name and one or more segment or section base addresses,
> -which report where the library was loaded in memory.
> +associated name and one or more segment, section base addresses or SVR4 linker
> +parameters, which report where the library was loaded in memory.
>
>  For the common case of libraries that are fully linked binaries, the
>  library should have a list of segments.  If the target supports
>  dynamic linking of a relocatable object file, its library XML element
>  should instead include a list of allocated sections.  The segment or
>  section bases are start addresses, not relocation offsets; they do not
> -depend on the library's link-time base addresses.
> +depend on the library's link-time base addresses.  For SVR4 systems
> +are reported parameters @{lm} with address of @code{struct link_map}
> +used for TLS (Thread Local Storage) access, @code{l_addr} specifying
> +bias of the mapped memory address minus the prelinked base address
> +and also @code{l_ld} which is memory address of the @code{PT_DYNAMIC}
> +segment.  SVR4 systems additionally specify @code{struct link_map}
> +address of the main executable in the @code{<library-list>} element.
>
>  @value{GDBN} must be linked with the Expat library to support XML
>  library lists.  @xref{Expat}.
> @@ -36449,23 +36455,38 @@ allocated sections (.text, .data, .bss), looks like this:
>  </library-list>
>  @end smallexample
>
> +SVR4 systems report:
> +
> +@smallexample
> +<library-list main-lm="0xe4f8f8">
> +  <library name="/lib/ld-linux.so.2" lm="0xe4f51c" l_addr="0xe2d000"
> +           l_ld="0xe4eefc"/>
> +  <library name="/lib/libc.so.6" lm="0xe4fbe8" l_addr="0x154000"
> +           l_ld="0x152350"/>
> +</library-list>
> +@end smallexample
> +
>  The format of a library list is described by this DTD:
>
>  @smallexample
>  <!-- library-list: Root element with versioning -->
>  <!ELEMENT library-list  (library)*>
>  <!ATTLIST library-list  version CDATA   #FIXED  "1.0">
> +<!ATTLIST library-list  main-lm CDATA   #IMPLIED>
>  <!ELEMENT library       (segment*, section*)>
>  <!ATTLIST library       name    CDATA   #REQUIRED>
> +<!ATTLIST library       lm      CDATA   #IMPLIED>
> +<!ATTLIST library       l_addr  CDATA   #IMPLIED>
> +<!ATTLIST library       l_ld    CDATA   #IMPLIED>
>  <!ELEMENT segment       EMPTY>
>  <!ATTLIST segment       address CDATA   #REQUIRED>
>  <!ELEMENT section       EMPTY>
>  <!ATTLIST section       address CDATA   #REQUIRED>
>  @end smallexample
>
> -In addition, segments and section descriptors cannot be mixed within a
> -single library element, and you must supply at least one segment or
> -section for each library.
> +In addition, segments, section and SVR4 descriptors cannot be mixed
> +within a single library element, and you must supply at least one
> +segment or section or both @code{l_addr} and @code{l_ld} for each library.
>
>  @node Memory Map Format
>  @section Memory Map Format
> --- a/gdb/features/library-list.dtd
> +++ b/gdb/features/library-list.dtd
> @@ -7,9 +7,13 @@
>  <!-- library-list: Root element with versioning -->
>  <!ELEMENT library-list  (library)*>
>  <!ATTLIST library-list  version CDATA   #FIXED  "1.0">
> +<!ATTLIST library-list  main-lm CDATA   #IMPLIED>
>
>  <!ELEMENT library       (segment*, section*)>
>  <!ATTLIST library       name    CDATA   #REQUIRED>
> +<!ATTLIST library       lm      CDATA   #IMPLIED>
> +<!ATTLIST library       l_addr  CDATA   #IMPLIED>
> +<!ATTLIST library       l_ld    CDATA   #IMPLIED>
>
>  <!ELEMENT segment       EMPTY>
>  <!ATTLIST segment       address CDATA   #REQUIRED>
> --- a/gdb/gdbserver/linux-low.c
> +++ b/gdb/gdbserver/linux-low.c
> @@ -4906,6 +4906,377 @@ 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_ld field in struct link_map.  */
> +    int l_ld_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.  */
> +      8,     /* l_ld 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.  */
> +      16,    /* l_ld 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 == 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_ld, l_next, l_prev;
> +      int r_version, header_done = 0;
> +
> +      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_ld_offset,
> +                             &l_ld, 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 ("Corrupted shared library list: 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) - 1);
> +         libname[sizeof (libname) - 1] = '\0';
> +         if (libname[0] != '\0')
> +           {
> +             size_t len = strlen ((char *) libname);
> +             char *name;
> +
> +             if (!header_done)
> +               {
> +                 /* Terminate `<library-list'.  */
> +                 *p++ = '>';
> +                 header_done = 1;
> +               }
> +
> +             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;
> +               }
> +
> +             name = xml_escape_text ((char *) libname);
> +             p += sprintf (p, "<library name=\"%s\" lm=\"0x%lx\" "
> +                              "l_addr=\"0x%lx\" l_ld=\"0x%lx\"/>",
> +                           name, (unsigned long) lm_addr,
> +                           (unsigned long) l_addr, (unsigned long) l_ld);
> +             free (name);
> +           }
> +         else if (lm_prev == 0)
> +           {
> +             sprintf (p, " main-lm=\"0x%lx\"", (unsigned long) lm_addr);
> +             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,
> @@ -4965,7 +5336,8 @@ static struct target_ops linux_target_ops = {
>   linux_cancel_breakpoints,
>   linux_stabilize_threads,
>   linux_install_fast_tracepoint_jump_pad,
> -  linux_emit_ops
> +  linux_emit_ops,
> +  linux_qxfer_libraries
>  };
>
>  static void
> --- a/gdb/gdbserver/linux-low.h
> +++ b/gdb/gdbserver/linux-low.h
> @@ -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;
> --- a/gdb/gdbserver/server.c
> +++ b/gdb/gdbserver/server.c
> @@ -917,6 +917,9 @@ 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);
> +
>   /* Over-estimate the necessary memory.  Assume that every character
>      in the library name must be escaped.  */
>   total_len = 64;
> --- a/gdb/gdbserver/target.h
> +++ b/gdb/gdbserver/target.h
> @@ -377,6 +377,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;
> --- a/gdb/solib-svr4.c
> +++ b/gdb/solib-svr4.c
> @@ -916,6 +916,104 @@ open_symbol_file_object (void *from_ttyp)
>   return 1;
>  }
>
> +/* Data exchange structure for the XML parser as returned by
> +   svr4_current_sos_via_xfer_libraries.  */
> +
> +struct svr4_library_list
> +{
> +  struct so_list *head, **tailp;
> +  CORE_ADDR main_lm;
> +};
> +
> +#ifdef HAVE_LIBEXPAT
> +
> +#include "xml-support.h"
> +
> +/* Handle the start of a <library> element.  Note: new elements are added
> +   at the tail of the list, keeping the list in 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)
> +{
> +  struct svr4_library_list *list = user_data;
> +  const char *name = xml_find_attribute (attributes, "name")->value;
> +  ULONGEST *lmp = xml_find_attribute (attributes, "lm")->value;
> +  ULONGEST *l_addrp = xml_find_attribute (attributes, "l_addr")->value;
> +  ULONGEST *l_ldp = xml_find_attribute (attributes, "l_ld")->value;
> +  struct so_list *new_elem;
> +
> +  new_elem = XZALLOC (struct so_list);
> +  new_elem->lm_info = XZALLOC (struct lm_info);
> +  new_elem->lm_info->lm_addr = *lmp;
> +  new_elem->lm_info->l_addr_inferior = *l_addrp;
> +  new_elem->lm_info->l_ld = *l_ldp;
> +
> +  strncpy (new_elem->so_name, name, sizeof (new_elem->so_name) - 1);
> +  new_elem->so_name[sizeof (new_elem->so_name) - 1] = 0;
> +  strcpy (new_elem->so_original_name, new_elem->so_name);
> +
> +  *list->tailp = new_elem;
> +  list->tailp = &new_elem->next;
> +}
> +
> +/* 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)
> +{
> +  struct svr4_library_list *list = user_data;
> +  char *version = xml_find_attribute (attributes, "version")->value;
> +  struct gdb_xml_value *main_lm = xml_find_attribute (attributes, "main-lm");
> +
> +  if (strcmp (version, "1.0") != 0)
> +    gdb_xml_error (parser,
> +                  _("Library list has unsupported version \"%s\""),
> +                  version);
> +
> +  if (main_lm)
> +    list->main_lm = *(ULONGEST *) main_lm->value;
> +}
> +
> +/* The allowed elements and attributes for an XML library list.
> +   The root element is a <library-list>.  */
> +
> +static const struct gdb_xml_attribute library_attributes[] =
> +{
> +  { "name", GDB_XML_AF_NONE, NULL, NULL },
> +  { "lm", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
> +  { "l_addr", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
> +  { "l_ld", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
> +  { NULL, GDB_XML_AF_NONE, NULL, NULL }
> +};
> +
> +static const struct gdb_xml_element library_list_children[] =
> +{
> +  {
> +    "library", library_attributes, NULL,
> +    GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
> +    library_list_start_library, NULL
> +  },
> +  { 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 },
> +  { "main-lm", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, 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 }
> +};
> +
>  /* Implementation for target_so_ops.free_so.  */
>
>  static void
> @@ -940,6 +1038,69 @@ svr4_free_library_list (void *p_list)
>     }
>  }
>
> +/* Parse qXfer:libraries:read packet into *SO_LIST_RETURN.  Return 1 if
> +
> +   Return 0 if packet not supported, *SO_LIST_RETURN is not modified in such
> +   case.  Return 1 if *SO_LIST_RETURN contains the library list, it may be
> +   empty, caller is responsible for freeing all its entries.  */
> +
> +static int
> +svr4_parse_libraries (const char *document, struct svr4_library_list *list)
> +{
> +  struct cleanup *back_to = make_cleanup (svr4_free_library_list,
> +                                         &list->head);
> +
> +  memset (list, 0, sizeof (*list));
> +  list->tailp = &list->head;
> +  if (gdb_xml_parse_quick (_("target library list"), "library-list.dtd",
> +                          library_list_elements, document, list) == 0)
> +    {
> +      /* Parsed successfully, keep the result.  */
> +      discard_cleanups (back_to);
> +      return 1;
> +    }
> +
> +  do_cleanups (back_to);
> +  return 0;
> +}
> +
> +/* Attempt to get so_list from target via qXfer:libraries:read packet.
> +
> +   Return 0 if packet not supported, *SO_LIST_RETURN is not modified in such
> +   case.  Return 1 if *SO_LIST_RETURN contains the library list, it may be
> +   empty, caller is responsible for freeing all its entries.  */
> +
> +static int
> +svr4_current_sos_via_xfer_libraries (struct svr4_library_list *list)
> +{
> +  char *library_document;
> +  int 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 0;
> +
> +  back_to = make_cleanup (xfree, library_document);
> +  result = svr4_parse_libraries (library_document, list);
> +  do_cleanups (back_to);
> +
> +  return result;
> +}
> +
> +#else
> +
> +static int
> +svr4_current_sos_via_xfer_libraries (struct svr4_library_list *list)
> +{
> +  return 0;
> +}
> +
> +#endif
> +
>  /* If no shared library information is available from the dynamic
>    linker, build a fallback list from other sources.  */
>
> @@ -999,7 +1160,9 @@ svr4_read_so_list (CORE_ADDR lm, struct so_list ***link_ptr_ptr,
>
>       if (new->lm_info->l_prev != prev_lm)
>        {
> -         warning (_("Corrupted shared library list"));
> +         warning (_("Corrupted shared library list: %s != %s"),
> +                  paddress (target_gdbarch, prev_lm),
> +                  paddress (target_gdbarch, new->lm_info->l_prev));
>          do_cleanups (old_chain);
>          break;
>        }
> @@ -1060,6 +1223,18 @@ svr4_current_sos (void)
>   struct svr4_info *info;
>   struct cleanup *back_to;
>   int ignore_first;
> +  struct svr4_library_list library_list;
> +
> +  if (svr4_current_sos_via_xfer_libraries (&library_list))
> +    {
> +      if (library_list.main_lm)
> +       {
> +         info = get_svr4_info ();
> +         info->main_lm_addr = library_list.main_lm;
> +       }
> +
> +      return library_list.head ? library_list.head : svr4_default_sos ();
> +    }
>
>   info = get_svr4_info ();
>
> --- a/gdb/testsuite/gdb.base/solib-corrupted.exp
> +++ b/gdb/testsuite/gdb.base/solib-corrupted.exp
> @@ -17,6 +17,12 @@ if {[skip_shlib_tests]} {
>     return 0
>  }
>
> +if {[is_remote target]} {
> +    # gdbserver prints the warning message but expect is parsing only the GDB
> +    # output, not the gdbserver output.
> +    return 0
> +}
> +
>  set testfile "solib-corrupted"
>  set srcfile start.c
>
> @@ -47,4 +53,4 @@ gdb_test_multiple "p/x _r_debug->r_map->l_next = _r_debug->r_map" $test {
>        pass $test
>     }
>  }
> -gdb_test "info sharedlibrary" "warning: Corrupted shared library list\r\n.*" "corrupted list"
> +gdb_test "info sharedlibrary" "warning: Corrupted shared library list: .*" "corrupted list"
>



-- 
Thanks,
Daniel

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2
  2011-10-10  2:44 ` [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2 Daniel Jacobowitz
@ 2011-10-12 20:22   ` Jan Kratochvil
  0 siblings, 0 replies; 12+ messages in thread
From: Jan Kratochvil @ 2011-10-12 20:22 UTC (permalink / raw)
  To: Daniel Jacobowitz; +Cc: gdb-patches, Paul Pluzhnikov, Pedro Alves

On Mon, 10 Oct 2011 04:43:36 +0200, Daniel Jacobowitz wrote:
> I would be a whole lot happier if we could use the same <library-list>
> elements for SVR4.  There may be some creative interpretations we can
> use to make it work... yes, we'd likely have to pass the link map
> addresses as additional information; fortunately that's easy.  I'm not
> sure I understand the issue with dealing with absolute library base
> addresses.

> But in short, the way to think about this is to move all
> the complicated bits of library processing to the target agent, and
> give GDB a simplified view of what's really going on.

The problem is the host symbol file may be also prelinked, therefore there
still needs to be the relocation on gdb side, whether the single number covers
just the host displacement or a sum of both the host and target displacement
does not matter much.

Just this case:
	[patch] [i386] Fix {,un}prelinked libraries for attach/core-load
	http://sourceware.org/ml/gdb-patches/2011-02/msg00630.html

had to be made in a heuristic way while with using /proc/PID/maps on the
gdbserver Linux side for absolute addresses normalization it could be made
reliable.  Going to try it.


> If that's not practical, then +1 to Pedro's suggestion; if you're
> bulk-reading the link maps, or doing some svr4-specific semantics, we
> should express it that way.

If the addresses are absolute then we can just add new attributes to the
current <library-list/> format and I assume the Pedro's suggestion for an
incompatible format is no longer valid then.



Thanks,
Jan

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Messages localization in gdbserver  [Re: [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2]
  2011-10-04  6:11 ` Eli Zaretskii
@ 2011-10-18 17:49   ` Jan Kratochvil
  0 siblings, 0 replies; 12+ messages in thread
From: Jan Kratochvil @ 2011-10-18 17:49 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches, ppluzhnikov

On Tue, 04 Oct 2011 08:11:37 +0200, Eli Zaretskii wrote:
> > +  if (*phdr_memaddr == 0 || *num_phdr == 0)
> > +    {
> > +      warning ("Unexpected missing AT_PHDR and/or AT_PHNUM: "
> > +	       "phdr_memaddr = %ld, phdr_num = %d",
> 
> No _() ?  Doesn't gdbserver support translations of messages?

The _() macro is defined for gdbserver but it is used only for a single
message there.  So I also did not use it.
thread-db.c:      warning (_("libthread-db-search-path component too long,"


[ The doc update to be posted in a new patch.  ]


Thanks,
Jan

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2
  2011-10-06 19:09 ` [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2 Pedro Alves
@ 2011-10-21 11:05   ` Jan Kratochvil
  2011-10-21 13:32     ` Pedro Alves
  0 siblings, 1 reply; 12+ messages in thread
From: Jan Kratochvil @ 2011-10-21 11:05 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches, Paul Pluzhnikov

On Thu, 06 Oct 2011 21:09:24 +0200, Pedro Alves wrote:
> but this reuse of <library-list> is a mistake.

<library-list/> already supports the "segment" format and "section" format.
SVR4 attributes are just another format of <library-list/>.
Or do you agree there should rather have been <library-list-segments/> and
<library-list-sections/> and it is kept this way for backward compatibility?


> If we're not going to use
> an solib-target.c at all, and the semantics of what goes over the
> wire is not the same as <library-list>/TARGET_OBJECT_LIBRARIES, then
> let's come up with a completely independent new target object + dtd
> instead.  Let's call it for example TARGET_OBJECT_SVR4_LIBRARIES.


> We should not prevent the possibility of _both_ using solib-svr4.c and
> solib-target.c at the same time, or the possibility of having a completely
> target-side implementation of svr4 libraries in the future,

I find this implementation as the fully target-side one.  SVR4 needs some
information to match symbol files <-> target memory wrt prelinked files and to
aid libthread_db.

Maybe DYNAMIC segment offset could be sent the other way (host->target) to
match the prelink displacement.  And also struct link_map address for
libthread_db may not be transferred and TLS variables could be read by special
packets instead.  I do not find any of the cases to have advantage, though.


> using <library-list> as is (and having gdb be aware that support is present
> from qXfer:read:libraries+ in qSupported).

That is you suggest to have also new qXfer:read:svr4libraries+?


I do not follow how differently / more target-side could be the libraries
implemented on SVR4 systems, I already tried to implement it that way.


Thanks,
Jan

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2
  2011-10-21 11:05   ` Jan Kratochvil
@ 2011-10-21 13:32     ` Pedro Alves
  2011-11-03 21:30       ` [patch] Implement qXfer:libraries-svr4 for Linux/gdbserver #4 Jan Kratochvil
  0 siblings, 1 reply; 12+ messages in thread
From: Pedro Alves @ 2011-10-21 13:32 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches, Paul Pluzhnikov

On Friday 21 October 2011 10:42:58, Jan Kratochvil wrote:
> On Thu, 06 Oct 2011 21:09:24 +0200, Pedro Alves wrote:
> > but this reuse of <library-list> is a mistake.
> 
> <library-list/> already supports the "segment" format and "section" format.
> SVR4 attributes are just another format of <library-list/>.
> Or do you agree there should rather have been <library-list-segments/> and
> <library-list-sections/> and it is kept this way for backward compatibility?

No, it's not the same.  The <library-list/> support, in either the segment or
section variants is completely self-described.  solib-target.c knows nothing
about the target --- the <library-list/> reported tells GDB which unit of
relocation the target uses, either a section or a segment.  Everything is abstracted,
and gdb is given a simplified view.  Your proposal violates that -- gdb still
knows upfront that the target is svr4-ish, and you still go through
solib-svr4.c.  The assumption that there's only one segment is
really nasty too.

The correct solutions are:

 - Report the library list with <library-list>, _and_ make gdb use solib-target.c.
   What minimal extensions would solib-target.c need so that
   we could make that work, without hardcoding "svr4" semantics?  E.g.,
   <library-list> is defined to _not_ return an element for the main executable.
   Would we need an extension (a new element, say?) for that?  Or is the
   qOffsets mechanism sufficient for relocating the main executable?

 - Still us solib-svr4.c, but then don't abuse <library-list>, but instead
   add a new packet and target object to accelerate getting at the link map.
   TARGET_OBJECT_SVR4_LIBRARIES or TARGET_OBJECT_SVR4_LINKMAP or something along
   those lines.

> > If we're not going to use
> > an solib-target.c at all, and the semantics of what goes over the
> > wire is not the same as <library-list>/TARGET_OBJECT_LIBRARIES, then
> > let's come up with a completely independent new target object + dtd
> > instead.  Let's call it for example TARGET_OBJECT_SVR4_LIBRARIES.
> 
> 
> > We should not prevent the possibility of _both_ using solib-svr4.c and
> > solib-target.c at the same time, or the possibility of having a completely
> > target-side implementation of svr4 libraries in the future,
> 
> I find this implementation as the fully target-side one.  SVR4 needs some
> information to match symbol files <-> target memory wrt prelinked files and to
> aid libthread_db.

If you're still using solib-svr4.c for other things than fetching the
link map, then it's not a fully target-side one.  If the implementation
ends up with svr4_so_ops installed in the inferior instead of
solib_target_so_ops, then it's not a fully target-side one.

> Maybe DYNAMIC segment offset could be sent the other way (host->target) to
> match the prelink displacement.  And also struct link_map address for
> libthread_db may not be transferred 

> and TLS variables could be read by special packets instead.  

We already have "qGetTLSAddr", and that's used over
gdbarch_fetch_tls_load_module_address -> svr4_lpXX_fetch_link_map_offsets.
Are you thinking of something else?

> I do not find any of the cases to have advantage, though.

One advantage I was considering would be that making the
target manage (and hide from gdb) the shared library event
breakpoint itself, we'd cut the roundtrips of hitting and
stepping over the breakpoint in half (gdb wouldn't see the
step), and we'd make the target always keep that breakpoint
inserted too, avoiding extra plant/unplant roundtrips for
that breakpoint too.  But since with a fully target-managed
DSO list, we'd end up reporting dso loads/unloads
anyway with TARGET_WAITKIND_LOADED (`T05:...:library' in the RSP),
it may not be much of a gain, and, most importantly, we can get
the same benefits with other generic changes, such as teaching
the server side to step over breakpoints itself, and making gdb
be smart to not remove/insert that breakpoint constantly
(`set breakpoint always-inserted on' by default or maybe just
apply that to some breakpoints).  So the benefits indeed
aren't clear.  But that is not to say we won't find benefits
in the future, and we're free to mess up the design.

And that's why I'm personally okay with still using solib-svr4.c,
_provided_ we come up with a new target object (and packets and dtd)
to fetch the DSO list from the link map efficiently.

> > using <library-list> as is (and having gdb be aware that support is present
> > from qXfer:read:libraries+ in qSupported).
> 
> That is you suggest to have also new qXfer:read:svr4libraries+?

Yes.  We need a new packet for a each new target object:

TARGET_OBJECT_AUXV -> qXfer:read:auxv
TARGET_OBJECT_LIBRARIES -> qXfer:read:libraries
TARGET_OBJECT_SIGNAL_INFO -> qXfer:read:siginfo
TARGET_OBJECT_FDPIC -> qXfer:read:fdpid
TARGET_OBJECT_SVR4_LIBRARIES -> qXfer:read:svr4libraries
TARGET_OBJECT_SVR4_LINKMAP -> qXfer:read:svr4linkmap
....

> I do not follow how differently / more target-side could be the libraries
> implemented on SVR4 systems, I already tried to implement it that way.

-- 
Pedro Alves

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [patch] Implement qXfer:libraries-svr4 for Linux/gdbserver #4
  2011-10-21 13:32     ` Pedro Alves
@ 2011-11-03 21:30       ` Jan Kratochvil
  2011-12-02 22:33         ` [commit] " Jan Kratochvil
  0 siblings, 1 reply; 12+ messages in thread
From: Jan Kratochvil @ 2011-11-03 21:30 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches, Paul Pluzhnikov

On Fri, 21 Oct 2011 13:05:06 +0200, Pedro Alves wrote:
> The correct solutions are:
> 
>  - Report the library list with <library-list>, _and_ make gdb use solib-target.c.
>    What minimal extensions would solib-target.c need so that
>    we could make that work, without hardcoding "svr4" semantics?  E.g.,
>    <library-list> is defined to _not_ return an element for the main executable.
>    Would we need an extension (a new element, say?) for that?  Or is the
>    qOffsets mechanism sufficient for relocating the main executable?

The main executable is relocated (for PIE) via AUXV (AT_ENTRY).  But the
"main-lm" attribute is needed for libthread_db access as `struct link_map *',
it needs to be read out some way from the target link_map list.

Thanks for the detailed description what I in fact thought about before as
"solib-svr4.c split".  I tried to, I do not think it would be too complicated,
but I haven't found enough justification for the work when (a) you accept the
solib-svr4.c specific protocol and (b) it does bring neither more
functionality (c) nor protocol efficiency (as you analysed below).
I understand it would be a nice code + protocol cleanup, just I have too many
other serious Bugs to do to find the energy for such solib-svr4.c rework.


>  - Still us solib-svr4.c, but then don't abuse <library-list>, but instead
>    add a new packet and target object to accelerate getting at the link map.
>    TARGET_OBJECT_SVR4_LIBRARIES or TARGET_OBJECT_SVR4_LINKMAP or something along
>    those lines.

I used TARGET_OBJECT_LIBRARIES_SVR4 for it, it has merged link_map inside.
The separate link_map XML content would be nice for the solib-target.c reuse
as you designed above, I do not think it matters when a special protocol for
solib-svr4.c is there by this patch anyway.


> TARGET_OBJECT_SVR4_LIBRARIES -> qXfer:read:svr4libraries
> TARGET_OBJECT_SVR4_LINKMAP -> qXfer:read:svr4linkmap

I just chose single:

TARGET_OBJECT_LIBRARIES_SVR4 -> qXfer:libraries-svr4:read

If you have some reasons for the split and/or differently named objects I have
no problem updating the patch.

No regressions on {x86_64,x86_64-m32,i686}-fedora16pre-linux-gnu and with
gdbserver.

The patch uses the gdb/ change without the gdb/gdbserver/ change as in:
	[patch] gdbserver <library-list> and its #FIXED version="1.0"
	http://sourceware.org/ml/gdb-patches/2011-11/msg00099.html


Thanks,
Jan


gdb/
2011-11-03  Paul Pluzhnikov  <ppluzhnikov@google.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>

	* Makefile.in (XMLFILES): Add library-list-svr4.dtd.
	* features/library-list-svr4.dtd: New file.
	* remote.c (PACKET_qXfer_libraries_svr4): New.
	(remote_protocol_features): new entry for PACKET_qXfer_libraries_svr4.
	(remote_xfer_partial): Handle TARGET_OBJECT_LIBRARIES_SVR4.
	* solib-svr4.c (struct svr4_library_list): New.
	[HAVE_LIBEXPAT]: Include xml-support.h.
	[HAVE_LIBEXPAT] (svr4_library_list_start_library)
	[HAVE_LIBEXPAT] (svr4_library_list_start_list, svr4_library_attributes)
	[HAVE_LIBEXPAT] (svr4_library_list_children)
	[HAVE_LIBEXPAT] (svr4_library_list_attributes)
	[HAVE_LIBEXPAT] (svr4_library_list_elements, svr4_parse_libraries)
	[HAVE_LIBEXPAT] (svr4_current_sos_via_xfer_libraries)
	[!HAVE_LIBEXPAT] (svr4_current_sos_via_xfer_libraries): New.
	(svr4_read_so_list): Extend the corruption message by addresses.
	(svr4_current_sos): New variable library_list, call
	svr4_current_sos_via_xfer_libraries.
	* target.h (enum target_object): New TARGET_OBJECT_LIBRARIES_SVR4.

gdb/gdbserver/
2011-10-03  Paul Pluzhnikov  <ppluzhnikov@google.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>

	* linux-low.c (get_phdr_phnum_from_proc_auxv, get_dynamic, get_r_debug)
	(read_one_ptr, struct link_map_offsets, linux_qxfer_libraries_svr4):
	New.
	(struct linux_target_ops): Install linux_qxfer_libraries_svr4.
	* linux-low.h (struct process_info_private): New member r_debug.
	* server.c (handle_qxfer_libraries): Call
	the_target->qxfer_libraries_svr4.
	(handle_qxfer_libraries_svr4): New function.
	(qxfer_packets): New entry "libraries-svr4".
	(handle_query): Check QXFER_LIBRARIES_SVR4 and report libraries-svr4.
	* target.h (struct target_ops): New member qxfer_libraries_svr4.
	* remote.c (remote_xfer_partial): Call add_packet_config_cmd for
	PACKET_qXfer_libraries_svr4.

gdb/doc/
2011-11-03  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* gdb.texinfo (Requirements, Remote Protocol): Reference also `Library
	List Format for SVR4 Targets'.
	(General Query Packets): New item qXfer:libraries-svr4:read.
	(Library List Format for SVR4 Targets): New node.

gdb/testsuite/
2011-10-03  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* gdb.base/solib-corrupted.exp: Suppress test on is_remote target.
	(corrupted list): Adjust the expectation.

--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -495,7 +495,8 @@ RUNTESTFLAGS=
 
 # XML files to build in to GDB.
 XMLFILES = $(srcdir)/features/gdb-target.dtd $(srcdir)/features/xinclude.dtd \
-	$(srcdir)/features/library-list.dtd $(srcdir)/features/osdata.dtd \
+	$(srcdir)/features/library-list.dtd \
+	$(srcdir)/features/library-list-svr4.dtd $(srcdir)/features/osdata.dtd \
 	$(srcdir)/features/threads.dtd $(srcdir)/features/traceframe-info.dtd
 
 # This is ser-unix.o for any system which supports a v7/BSD/SYSV/POSIX
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -32216,7 +32216,8 @@ Remote protocol memory maps (@pxref{Memory Map Format})
 @item
 Target descriptions (@pxref{Target Descriptions})
 @item
-Remote shared library lists (@pxref{Library List Format})
+Remote shared library lists (@xref{Library List Format},
+or alternatively @pxref{Library List Format for SVR4 Targets})
 @item
 MS-Windows shared libraries (@pxref{Shared Libraries})
 @item
@@ -33050,6 +33051,7 @@ Show the current setting of the target wait timeout.
 * Examples::
 * File-I/O Remote Protocol Extension::
 * Library List Format::
+* Library List Format for SVR4 Targets::
 * Memory Map Format::
 * Thread List Format::
 * Traceframe Info Format::
@@ -34751,6 +34753,10 @@ The remote stub understands the @samp{qXfer:features:read} packet
 The remote stub understands the @samp{qXfer:libraries:read} packet
 (@pxref{qXfer library list read}).
 
+@item qXfer:libraries-svr4:read
+The remote stub understands the @samp{qXfer:libraries-svr4:read} packet
+(@pxref{qXfer svr4 library list read}).
+
 @item qXfer:memory-map:read
 The remote stub understands the @samp{qXfer:memory-map:read} packet
 (@pxref{qXfer memory map read}).
@@ -34988,6 +34994,18 @@ the operating system manages the list of loaded libraries.
 This packet is not probed by default; the remote stub must request it,
 by supplying an appropriate @samp{qSupported} response (@pxref{qSupported}).
 
+@item qXfer:libraries-svr4:read:@var{annex}:@var{offset},@var{length}
+@anchor{qXfer svr4 library list read}
+Access the target's list of loaded libraries when the target is an SVR4
+platform.  @xref{Library List Format for SVR4 Targets}.  The annex part
+of the generic @samp{qXfer} packet must be empty (@pxref{qXfer read}).
+
+This packet is optional for better performance on SVR4 targets.  
+@value{GDBN} uses memory read packets to read the SVR4 library list otherwise.
+
+This packet is not probed by default; the remote stub must request it,
+by supplying an appropriate @samp{qSupported} response (@pxref{qSupported}).
+
 @item qXfer:memory-map:read::@var{offset},@var{length}
 @anchor{qXfer memory map read}
 Access the target's @dfn{memory-map}.  @xref{Memory Map Format}.  The
@@ -37251,6 +37269,68 @@ In addition, segments and section descriptors cannot be mixed within a
 single library element, and you must supply at least one segment or
 section for each library.
 
+@node Library List Format for SVR4 Targets
+@section Library List Format for SVR4 Targets
+@cindex library list format, remote protocol
+
+On SVR4 platforms @value{GDBN} can use the symbol table of a dynamic loader
+(e.g.@: @file{ld.so}) and normal memory operations to maintain a list of
+shared libraries.  Still a special library list provided by this packet is
+more efficient for the @value{GDBN} remote protocol.
+
+The @samp{qXfer:libraries-svr4:read} packet returns an XML document which lists
+loaded libraries and their SVR4 linker parameters.  For each library on SVR4
+target, the following parameters are reported:
+
+@itemize @minus
+@item
+@code{name}, the absolute file name from the @code{l_name} field of
+@code{struct link_map}.
+@item
+@code{lm} with address of @code{struct link_map} used for TLS
+(Thread Local Storage) access.
+@item
+@code{l_addr}, the displacement as read from the field @code{l_addr} of
+@code{struct link_map}.  For prelinked libraries this is not an absolute
+memory address.  It is a displacement of absolute memory address against
+address the file was prelinked to during the library load.
+@item
+@code{l_ld}, which is memory address of the @code{PT_DYNAMIC} segment
+@end itemize
+
+Additionally the single @code{main-lm} attribute specifies address of
+@code{struct link_map} used for the main executable.  This parameter is used
+for TLS access and its presence is optional.
+
+@value{GDBN} must be linked with the Expat library to support XML
+SVR4 library lists.  @xref{Expat}.
+
+A simple memory map, with two loaded libraries (which do not use prelink),
+looks like this:
+
+@smallexample
+<library-list-svr4 main-lm="0xe4f8f8">
+  <library name="/lib/ld-linux.so.2" lm="0xe4f51c" l_addr="0xe2d000"
+           l_ld="0xe4eefc"/>
+  <library name="/lib/libc.so.6" lm="0xe4fbe8" l_addr="0x154000"
+           l_ld="0x152350"/>
+</library-list-svr>
+@end smallexample
+
+The format of an SVR4 library list is described by this DTD:
+
+@smallexample
+<!-- library-list-svr4: Root element with versioning -->
+<!ELEMENT library-list-svr4  (library)*>
+<!ATTLIST library-list-svr4  version CDATA   #FIXED  "1.0">
+<!ATTLIST library-list-svr4  main-lm CDATA   #IMPLIED>
+<!ELEMENT library            EMPTY>
+<!ATTLIST library            name    CDATA   #REQUIRED>
+<!ATTLIST library            lm      CDATA   #REQUIRED>
+<!ATTLIST library            l_addr  CDATA   #REQUIRED>
+<!ATTLIST library            l_ld    CDATA   #REQUIRED>
+@end smallexample
+
 @node Memory Map Format
 @section Memory Map Format
 @cindex memory map format
--- /dev/null
+++ b/gdb/features/library-list-svr4.dtd
@@ -0,0 +1,16 @@
+<!-- Copyright (C) 2011 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!-- library-list-svr4: Root element with versioning -->
+<!ELEMENT library-list-svr4  (library)*>
+<!ATTLIST library-list-svr4  version CDATA   #FIXED  "1.0">
+<!ATTLIST library-list-svr4  main-lm CDATA   #IMPLIED>
+
+<!ELEMENT library            EMPTY>
+<!ATTLIST library            name    CDATA   #REQUIRED>
+<!ATTLIST library            lm      CDATA   #REQUIRED>
+<!ATTLIST library            l_addr  CDATA   #REQUIRED>
+<!ATTLIST library            l_ld    CDATA   #REQUIRED>
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -4953,6 +4953,377 @@ 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_ld field in struct link_map.  */
+    int l_ld_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_svr4 (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.  */
+      8,     /* l_ld 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.  */
+      16,    /* l_ld 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 == 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-svr4/>\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_ld, l_next, l_prev;
+      int r_version, header_done = 0;
+
+      document = xmalloc (allocated);
+      strcpy (document, "<library-list-svr4");
+      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_ld_offset,
+			      &l_ld, 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 ("Corrupted shared library list: 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) - 1);
+	  libname[sizeof (libname) - 1] = '\0';
+	  if (libname[0] != '\0')
+	    {
+	      size_t len = strlen ((char *) libname);
+	      char *name;
+
+	      if (!header_done)
+		{
+		  /* Terminate `<library-list-svr4'.  */
+		  *p++ = '>';
+		  header_done = 1;
+		}
+
+	      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;
+		}
+
+	      name = xml_escape_text ((char *) libname);
+	      p += sprintf (p, "<library name=\"%s\" lm=\"0x%lx\" "
+			       "l_addr=\"0x%lx\" l_ld=\"0x%lx\"/>",
+			    name, (unsigned long) lm_addr,
+			    (unsigned long) l_addr, (unsigned long) l_ld);
+	      free (name);
+	    }
+	  else if (lm_prev == 0)
+	    {
+	      sprintf (p, " main-lm=\"0x%lx\"", (unsigned long) lm_addr);
+	      p = p + strlen (p);
+	    }
+
+	  if (l_next == 0)
+	    break;
+
+	  lm_prev = lm_addr;
+	  lm_addr = l_next;
+	}
+    done:
+      strcpy (p, "</library-list-svr4>");
+    }
+
+  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,
@@ -5014,6 +5385,7 @@ static struct target_ops linux_target_ops = {
   linux_install_fast_tracepoint_jump_pad,
   linux_emit_ops,
   linux_supports_disable_randomization,
+  linux_qxfer_libraries_svr4
 };
 
 static void
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -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;
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -942,6 +942,10 @@ handle_qxfer_libraries (const char *annex,
   if (annex[0] != '\0' || !target_running ())
     return -1;
 
+  /* Do not confuse this packet with qXfer:libraries-svr4:read.  */
+  if (the_target->qxfer_libraries_svr4 != NULL)
+    return 0;
+
   /* Over-estimate the necessary memory.  Assume that every character
      in the library name must be escaped.  */
   total_len = 64;
@@ -992,6 +996,23 @@ handle_qxfer_libraries (const char *annex,
   return len;
 }
 
+/* Handle qXfer:libraries-svr4:read.  */
+
+static int
+handle_qxfer_libraries_svr4 (const char *annex,
+			     gdb_byte *readbuf, const gdb_byte *writebuf,
+			     ULONGEST offset, LONGEST len)
+{
+  if (writebuf != NULL)
+    return -2;
+
+  if (annex[0] != '\0' || !target_running ()
+      || the_target->qxfer_libraries_svr4 == NULL)
+    return -1;
+
+  return the_target->qxfer_libraries_svr4 (annex, readbuf, writebuf, offset, len);
+}
+
 /* Handle qXfer:osadata:read.  */
 
 static int
@@ -1216,6 +1237,7 @@ static const struct qxfer qxfer_packets[] =
     { "fdpic", handle_qxfer_fdpic},
     { "features", handle_qxfer_features },
     { "libraries", handle_qxfer_libraries },
+    { "libraries-svr4", handle_qxfer_libraries_svr4 },
     { "osdata", handle_qxfer_osdata },
     { "siginfo", handle_qxfer_siginfo },
     { "spu", handle_qxfer_spu },
@@ -1536,9 +1558,14 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 
       sprintf (own_buf, "PacketSize=%x;QPassSignals+", PBUFSIZ - 1);
 
-      /* We do not have any hook to indicate whether the target backend
-	 supports qXfer:libraries:read, so always report it.  */
-      strcat (own_buf, ";qXfer:libraries:read+");
+      if (the_target->qxfer_libraries_svr4 != NULL)
+	strcat (own_buf, ";qXfer:libraries-svr4:read+");
+      else
+	{
+	  /* We do not have any hook to indicate whether the non-SVR4 target
+	     backend supports qXfer:libraries:read, so always report it.  */
+	  strcat (own_buf, ";qXfer:libraries:read+");
+	}
 
       if (the_target->read_auxv != NULL)
 	strcat (own_buf, ";qXfer:auxv:read+");
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -380,6 +380,11 @@ struct target_ops
 
   /* Returns true if the target supports disabling randomization.  */
   int (*supports_disable_randomization) (void);
+
+  /* Read solib info on SVR4 platforms.  */
+  int (*qxfer_libraries_svr4) (const char *annex, unsigned char *readbuf,
+			       unsigned const char *writebuf,
+			       CORE_ADDR offset, int len);
 };
 
 extern struct target_ops *the_target;
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1239,6 +1239,7 @@ enum {
   PACKET_qXfer_auxv,
   PACKET_qXfer_features,
   PACKET_qXfer_libraries,
+  PACKET_qXfer_libraries_svr4,
   PACKET_qXfer_memory_map,
   PACKET_qXfer_spu_read,
   PACKET_qXfer_spu_write,
@@ -3733,6 +3734,8 @@ static struct protocol_feature remote_protocol_features[] = {
     PACKET_qXfer_features },
   { "qXfer:libraries:read", PACKET_DISABLE, remote_supported_packet,
     PACKET_qXfer_libraries },
+  { "qXfer:libraries-svr4:read", PACKET_DISABLE, remote_supported_packet,
+    PACKET_qXfer_libraries_svr4 },
   { "qXfer:memory-map:read", PACKET_DISABLE, remote_supported_packet,
     PACKET_qXfer_memory_map },
   { "qXfer:spu:read", PACKET_DISABLE, remote_supported_packet,
@@ -8331,6 +8334,11 @@ remote_xfer_partial (struct target_ops *ops, enum target_object object,
 	(ops, "libraries", annex, readbuf, offset, len,
 	 &remote_protocol_packets[PACKET_qXfer_libraries]);
 
+    case TARGET_OBJECT_LIBRARIES_SVR4:
+      return remote_read_qxfer
+	(ops, "libraries-svr4", annex, readbuf, offset, len,
+	 &remote_protocol_packets[PACKET_qXfer_libraries_svr4]);
+
     case TARGET_OBJECT_MEMORY_MAP:
       gdb_assert (annex == NULL);
       return remote_read_qxfer (ops, "memory-map", annex, readbuf, offset, len,
@@ -10912,6 +10920,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
   add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_libraries],
 			 "qXfer:libraries:read", "library-info", 0);
 
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_libraries_svr4],
+			 "qXfer:libraries-svr4:read", "library-info-svr4", 0);
+
   add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_memory_map],
 			 "qXfer:memory-map:read", "memory-map", 0);
 
--- a/gdb/solib-svr4.c
+++ b/gdb/solib-svr4.c
@@ -949,6 +949,110 @@ open_symbol_file_object (void *from_ttyp)
   return 1;
 }
 
+/* Data exchange structure for the XML parser as returned by
+   svr4_current_sos_via_xfer_libraries.  */
+
+struct svr4_library_list
+{
+  struct so_list *head, **tailp;
+  CORE_ADDR main_lm;
+};
+
+#ifdef HAVE_LIBEXPAT
+
+#include "xml-support.h"
+
+/* Handle the start of a <library> element.  Note: new elements are added
+   at the tail of the list, keeping the list in 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)
+{
+  struct svr4_library_list *list = user_data;
+  const char *name = xml_find_attribute (attributes, "name")->value;
+  ULONGEST *lmp = xml_find_attribute (attributes, "lm")->value;
+  ULONGEST *l_addrp = xml_find_attribute (attributes, "l_addr")->value;
+  ULONGEST *l_ldp = xml_find_attribute (attributes, "l_ld")->value;
+  struct so_list *new_elem;
+
+  new_elem = XZALLOC (struct so_list);
+  new_elem->lm_info = XZALLOC (struct lm_info);
+  new_elem->lm_info->lm_addr = *lmp;
+  new_elem->lm_info->l_addr_inferior = *l_addrp;
+  new_elem->lm_info->l_ld = *l_ldp;
+
+  strncpy (new_elem->so_name, name, sizeof (new_elem->so_name) - 1);
+  new_elem->so_name[sizeof (new_elem->so_name) - 1] = 0;
+  strcpy (new_elem->so_original_name, new_elem->so_name);
+
+  *list->tailp = new_elem;
+  list->tailp = &new_elem->next;
+}
+
+/* Handle the start of a <library-list-svr4> element.  */
+
+static void
+svr4_library_list_start_list (struct gdb_xml_parser *parser,
+			      const struct gdb_xml_element *element,
+			      void *user_data, VEC(gdb_xml_value_s) *attributes)
+{
+  struct svr4_library_list *list = user_data;
+  struct gdb_xml_value *version = xml_find_attribute (attributes, "version");
+  struct gdb_xml_value *main_lm = xml_find_attribute (attributes, "main-lm");
+
+  /* #FIXED attribute may be omitted, Expat returns NULL in such case.  */
+  if (version)
+    {
+      const char *string = version->value;
+
+      if (strcmp (string, "1.0") != 0)
+	gdb_xml_error (parser,
+		       _("SVR4 Library list has unsupported version \"%s\""),
+		       version);
+    }
+
+  if (main_lm)
+    list->main_lm = *(ULONGEST *) main_lm->value;
+}
+
+/* The allowed elements and attributes for an XML library list.
+   The root element is a <library-list>.  */
+
+static const struct gdb_xml_attribute svr4_library_attributes[] =
+{
+  { "name", GDB_XML_AF_NONE, NULL, NULL },
+  { "lm", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "l_addr", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "l_ld", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_element svr4_library_list_children[] =
+{
+  {
+    "library", svr4_library_attributes, NULL,
+    GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
+    library_list_start_library, NULL
+  },
+  { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_attribute svr4_library_list_attributes[] =
+{
+  { "version", GDB_XML_AF_OPTIONAL, NULL, NULL },
+  { "main-lm", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+  { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_element svr4_library_list_elements[] =
+{
+  { "library-list-svr4", svr4_library_list_attributes, svr4_library_list_children,
+    GDB_XML_EF_NONE, svr4_library_list_start_list, NULL },
+  { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
 /* Implementation for target_so_ops.free_so.  */
 
 static void
@@ -973,6 +1077,69 @@ svr4_free_library_list (void *p_list)
     }
 }
 
+/* Parse qXfer:libraries:read packet into *SO_LIST_RETURN.  Return 1 if
+
+   Return 0 if packet not supported, *SO_LIST_RETURN is not modified in such
+   case.  Return 1 if *SO_LIST_RETURN contains the library list, it may be
+   empty, caller is responsible for freeing all its entries.  */
+
+static int
+svr4_parse_libraries (const char *document, struct svr4_library_list *list)
+{
+  struct cleanup *back_to = make_cleanup (svr4_free_library_list,
+					  &list->head);
+
+  memset (list, 0, sizeof (*list));
+  list->tailp = &list->head;
+  if (gdb_xml_parse_quick (_("target library list"), "library-list.dtd",
+			   svr4_library_list_elements, document, list) == 0)
+    {
+      /* Parsed successfully, keep the result.  */
+      discard_cleanups (back_to);
+      return 1;
+    }
+
+  do_cleanups (back_to);
+  return 0;
+}
+
+/* Attempt to get so_list from target via qXfer:libraries:read packet.
+
+   Return 0 if packet not supported, *SO_LIST_RETURN is not modified in such
+   case.  Return 1 if *SO_LIST_RETURN contains the library list, it may be
+   empty, caller is responsible for freeing all its entries.  */
+
+static int
+svr4_current_sos_via_xfer_libraries (struct svr4_library_list *list)
+{
+  char *svr4_library_document;
+  int result;
+  struct cleanup *back_to;
+
+  /* Fetch the list of shared libraries.  */
+  svr4_library_document = target_read_stralloc (&current_target,
+						TARGET_OBJECT_LIBRARIES_SVR4,
+						NULL);
+  if (svr4_library_document == NULL)
+    return 0;
+
+  back_to = make_cleanup (xfree, svr4_library_document);
+  result = svr4_parse_libraries (svr4_library_document, list);
+  do_cleanups (back_to);
+
+  return result;
+}
+
+#else
+
+static int
+svr4_current_sos_via_xfer_libraries (struct svr4_library_list *list)
+{
+  return 0;
+}
+
+#endif
+
 /* If no shared library information is available from the dynamic
    linker, build a fallback list from other sources.  */
 
@@ -1032,7 +1199,9 @@ svr4_read_so_list (CORE_ADDR lm, struct so_list ***link_ptr_ptr,
 
       if (new->lm_info->l_prev != prev_lm)
 	{
-	  warning (_("Corrupted shared library list"));
+	  warning (_("Corrupted shared library list: %s != %s"),
+		   paddress (target_gdbarch, prev_lm),
+		   paddress (target_gdbarch, new->lm_info->l_prev));
 	  do_cleanups (old_chain);
 	  break;
 	}
@@ -1093,6 +1262,18 @@ svr4_current_sos (void)
   struct svr4_info *info;
   struct cleanup *back_to;
   int ignore_first;
+  struct svr4_library_list library_list;
+
+  if (svr4_current_sos_via_xfer_libraries (&library_list))
+    {
+      if (library_list.main_lm)
+	{
+	  info = get_svr4_info ();
+	  info->main_lm_addr = library_list.main_lm;
+	}
+
+      return library_list.head ? library_list.head : svr4_default_sos ();
+    }
 
   info = get_svr4_info ();
 
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -255,6 +255,8 @@ enum target_object
   TARGET_OBJECT_AVAILABLE_FEATURES,
   /* Currently loaded libraries, in XML format.  */
   TARGET_OBJECT_LIBRARIES,
+  /* Currently loaded libraries specific for SVR4 systems, in XML format.  */
+  TARGET_OBJECT_LIBRARIES_SVR4,
   /* Get OS specific data.  The ANNEX specifies the type (running
      processes, etc.).  The data being transfered is expected to follow
      the DTD specified in features/osdata.dtd.  */
--- a/gdb/testsuite/gdb.base/solib-corrupted.exp
+++ b/gdb/testsuite/gdb.base/solib-corrupted.exp
@@ -17,6 +17,12 @@ if {[skip_shlib_tests]} {
     return 0
 }
 
+if {[is_remote target]} {
+    # gdbserver prints the warning message but expect is parsing only the GDB
+    # output, not the gdbserver output.
+    return 0
+}
+
 set testfile "solib-corrupted"
 set srcfile start.c
 
@@ -47,4 +53,4 @@ gdb_test_multiple "p/x _r_debug->r_map->l_next = _r_debug->r_map" $test {
 	pass $test
     }
 }
-gdb_test "info sharedlibrary" "warning: Corrupted shared library list\r\n.*" "corrupted list"
+gdb_test "info sharedlibrary" "warning: Corrupted shared library list: .*" "corrupted list"

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [commit] [patch] Implement qXfer:libraries-svr4 for Linux/gdbserver #4
  2011-11-03 21:30       ` [patch] Implement qXfer:libraries-svr4 for Linux/gdbserver #4 Jan Kratochvil
@ 2011-12-02 22:33         ` Jan Kratochvil
  2011-12-03 17:03           ` Doug Evans
  0 siblings, 1 reply; 12+ messages in thread
From: Jan Kratochvil @ 2011-12-02 22:33 UTC (permalink / raw)
  To: gdb-patches; +Cc: Paul Pluzhnikov, Pedro Alves

Hi,

checked in, therefore introducing the new <library-list-svr4/> and
TARGET_OBJECT_LIBRARIES_SVR4 protocol (not reusing <library-list/>).

Some gdbserver memory corruption has been fixed.

The patch uses now always the version="1.0" attribute as #FIXED, provided by
gdbserver and required by gdb.  A similar change I suggested for existing
<library-list/> in:
	[patch] gdbserver <library-list> and its #FIXED version="1.0"
	http://sourceware.org/ml/gdb-patches/2011-11/msg00099.html

No regressions on {x86_64,x86_64-m32,i686}-fedora16-linux-gnu in both nat and
gdbserver mode.


Thanks,
Jan


http://sourceware.org/ml/gdb-cvs/2011-12/msg00012.html

--- src/gdb/ChangeLog	2011/12/02 17:01:17	1.13568
+++ src/gdb/ChangeLog	2011/12/02 22:26:50	1.13569
@@ -1,3 +1,25 @@
+2011-12-02  Paul Pluzhnikov  <ppluzhnikov@google.com>
+	    Jan Kratochvil  <jan.kratochvil@redhat.com>
+
+	* Makefile.in (XMLFILES): Add library-list-svr4.dtd.
+	* features/library-list-svr4.dtd: New file.
+	* remote.c (PACKET_qXfer_libraries_svr4): New.
+	(remote_protocol_features): new entry for PACKET_qXfer_libraries_svr4.
+	(remote_xfer_partial): Handle TARGET_OBJECT_LIBRARIES_SVR4.
+	* solib-svr4.c (struct svr4_library_list): New.
+	[HAVE_LIBEXPAT]: Include xml-support.h.
+	[HAVE_LIBEXPAT] (svr4_library_list_start_library)
+	[HAVE_LIBEXPAT] (svr4_library_list_start_list, svr4_library_attributes)
+	[HAVE_LIBEXPAT] (svr4_library_list_children)
+	[HAVE_LIBEXPAT] (svr4_library_list_attributes)
+	[HAVE_LIBEXPAT] (svr4_library_list_elements, svr4_parse_libraries)
+	[HAVE_LIBEXPAT] (svr4_current_sos_via_xfer_libraries)
+	[!HAVE_LIBEXPAT] (svr4_current_sos_via_xfer_libraries): New.
+	(svr4_read_so_list): Extend the corruption message by addresses.
+	(svr4_current_sos): New variable library_list, call
+	svr4_current_sos_via_xfer_libraries.
+	* target.h (enum target_object): New TARGET_OBJECT_LIBRARIES_SVR4.
+
 2011-12-02  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
 	PR threads/13448
--- src/gdb/doc/ChangeLog	2011/12/01 20:27:27	1.1252
+++ src/gdb/doc/ChangeLog	2011/12/02 22:26:52	1.1253
@@ -1,3 +1,10 @@
+2011-12-02  Jan Kratochvil  <jan.kratochvil@redhat.com>
+
+	* gdb.texinfo (Requirements, Remote Protocol): Reference also `Library
+	List Format for SVR4 Targets'.
+	(General Query Packets): New item qXfer:libraries-svr4:read.
+	(Library List Format for SVR4 Targets): New node.
+
 2011-12-01  Tom Tromey  <tromey@redhat.com>
 
 	* gdb.texinfo (Writing a Pretty-Printer): Use append method, not
--- src/gdb/gdbserver/ChangeLog	2011/11/30 16:06:55	1.514
+++ src/gdb/gdbserver/ChangeLog	2011/12/02 22:26:52	1.515
@@ -1,3 +1,20 @@
+2011-12-02  Paul Pluzhnikov  <ppluzhnikov@google.com>
+	    Jan Kratochvil  <jan.kratochvil@redhat.com>
+
+	* linux-low.c (get_phdr_phnum_from_proc_auxv, get_dynamic, get_r_debug)
+	(read_one_ptr, struct link_map_offsets, linux_qxfer_libraries_svr4):
+	New.
+	(struct linux_target_ops): Install linux_qxfer_libraries_svr4.
+	* linux-low.h (struct process_info_private): New member r_debug.
+	* server.c (handle_qxfer_libraries): Call
+	the_target->qxfer_libraries_svr4.
+	(handle_qxfer_libraries_svr4): New function.
+	(qxfer_packets): New entry "libraries-svr4".
+	(handle_query): Check QXFER_LIBRARIES_SVR4 and report libraries-svr4.
+	* target.h (struct target_ops): New member qxfer_libraries_svr4.
+	* remote.c (remote_xfer_partial): Call add_packet_config_cmd for
+	PACKET_qXfer_libraries_svr4.
+
 2011-11-30  Ulrich Weigand  <uweigand@de.ibm.com>
 
 	* linux-s390-low.c (s390_collect_ptrace_register): Fully convert
--- src/gdb/testsuite/ChangeLog	2011/12/02 17:01:20	1.2957
+++ src/gdb/testsuite/ChangeLog	2011/12/02 22:26:53	1.2958
@@ -1,5 +1,10 @@
 2011-12-02  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
+	* gdb.base/solib-corrupted.exp: Suppress test on is_remote target.
+	(corrupted list): Adjust the expectation.
+
+2011-12-02  Jan Kratochvil  <jan.kratochvil@redhat.com>
+
 	PR threads/13448
 	* gdb.dwarf2/dw2-var-zero-addr.S: New file.
 	* gdb.dwarf2/dw2-var-zero-addr.exp: New file.
--- src/gdb/Makefile.in	2011/11/20 08:59:56	1.1178
+++ src/gdb/Makefile.in	2011/12/02 22:26:51	1.1179
@@ -495,7 +495,8 @@
 
 # XML files to build in to GDB.
 XMLFILES = $(srcdir)/features/gdb-target.dtd $(srcdir)/features/xinclude.dtd \
-	$(srcdir)/features/library-list.dtd $(srcdir)/features/osdata.dtd \
+	$(srcdir)/features/library-list.dtd \
+	$(srcdir)/features/library-list-svr4.dtd $(srcdir)/features/osdata.dtd \
 	$(srcdir)/features/threads.dtd $(srcdir)/features/traceframe-info.dtd
 
 # This is ser-unix.o for any system which supports a v7/BSD/SYSV/POSIX
--- src/gdb/remote.c	2011/11/27 17:39:30	1.472
+++ src/gdb/remote.c	2011/12/02 22:26:51	1.473
@@ -1243,6 +1243,7 @@
   PACKET_qXfer_auxv,
   PACKET_qXfer_features,
   PACKET_qXfer_libraries,
+  PACKET_qXfer_libraries_svr4,
   PACKET_qXfer_memory_map,
   PACKET_qXfer_spu_read,
   PACKET_qXfer_spu_write,
@@ -3748,6 +3749,8 @@
     PACKET_qXfer_features },
   { "qXfer:libraries:read", PACKET_DISABLE, remote_supported_packet,
     PACKET_qXfer_libraries },
+  { "qXfer:libraries-svr4:read", PACKET_DISABLE, remote_supported_packet,
+    PACKET_qXfer_libraries_svr4 },
   { "qXfer:memory-map:read", PACKET_DISABLE, remote_supported_packet,
     PACKET_qXfer_memory_map },
   { "qXfer:spu:read", PACKET_DISABLE, remote_supported_packet,
@@ -8356,6 +8359,11 @@
 	(ops, "libraries", annex, readbuf, offset, len,
 	 &remote_protocol_packets[PACKET_qXfer_libraries]);
 
+    case TARGET_OBJECT_LIBRARIES_SVR4:
+      return remote_read_qxfer
+	(ops, "libraries-svr4", annex, readbuf, offset, len,
+	 &remote_protocol_packets[PACKET_qXfer_libraries_svr4]);
+
     case TARGET_OBJECT_MEMORY_MAP:
       gdb_assert (annex == NULL);
       return remote_read_qxfer (ops, "memory-map", annex, readbuf, offset, len,
@@ -11078,6 +11086,9 @@
   add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_libraries],
 			 "qXfer:libraries:read", "library-info", 0);
 
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_libraries_svr4],
+			 "qXfer:libraries-svr4:read", "library-info-svr4", 0);
+
   add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_memory_map],
 			 "qXfer:memory-map:read", "memory-map", 0);
 
--- src/gdb/solib-svr4.c	2011/10/18 14:00:41	1.158
+++ src/gdb/solib-svr4.c	2011/12/02 22:26:51	1.159
@@ -949,6 +949,107 @@
   return 1;
 }
 
+/* Data exchange structure for the XML parser as returned by
+   svr4_current_sos_via_xfer_libraries.  */
+
+struct svr4_library_list
+{
+  struct so_list *head, **tailp;
+
+  /* Inferior address of struct link_map used for the main executable.  It is
+     NULL if not known.  */
+  CORE_ADDR main_lm;
+};
+
+#ifdef HAVE_LIBEXPAT
+
+#include "xml-support.h"
+
+/* Handle the start of a <library> element.  Note: new elements are added
+   at the tail of the list, keeping the list in 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)
+{
+  struct svr4_library_list *list = user_data;
+  const char *name = xml_find_attribute (attributes, "name")->value;
+  ULONGEST *lmp = xml_find_attribute (attributes, "lm")->value;
+  ULONGEST *l_addrp = xml_find_attribute (attributes, "l_addr")->value;
+  ULONGEST *l_ldp = xml_find_attribute (attributes, "l_ld")->value;
+  struct so_list *new_elem;
+
+  new_elem = XZALLOC (struct so_list);
+  new_elem->lm_info = XZALLOC (struct lm_info);
+  new_elem->lm_info->lm_addr = *lmp;
+  new_elem->lm_info->l_addr_inferior = *l_addrp;
+  new_elem->lm_info->l_ld = *l_ldp;
+
+  strncpy (new_elem->so_name, name, sizeof (new_elem->so_name) - 1);
+  new_elem->so_name[sizeof (new_elem->so_name) - 1] = 0;
+  strcpy (new_elem->so_original_name, new_elem->so_name);
+
+  *list->tailp = new_elem;
+  list->tailp = &new_elem->next;
+}
+
+/* Handle the start of a <library-list-svr4> element.  */
+
+static void
+svr4_library_list_start_list (struct gdb_xml_parser *parser,
+			      const struct gdb_xml_element *element,
+			      void *user_data, VEC(gdb_xml_value_s) *attributes)
+{
+  struct svr4_library_list *list = user_data;
+  const char *version = xml_find_attribute (attributes, "version")->value;
+  struct gdb_xml_value *main_lm = xml_find_attribute (attributes, "main-lm");
+
+  if (strcmp (version, "1.0") != 0)
+    gdb_xml_error (parser,
+		   _("SVR4 Library list has unsupported version \"%s\""),
+		   version);
+
+  if (main_lm)
+    list->main_lm = *(ULONGEST *) main_lm->value;
+}
+
+/* The allowed elements and attributes for an XML library list.
+   The root element is a <library-list>.  */
+
+static const struct gdb_xml_attribute svr4_library_attributes[] =
+{
+  { "name", GDB_XML_AF_NONE, NULL, NULL },
+  { "lm", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "l_addr", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "l_ld", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_element svr4_library_list_children[] =
+{
+  {
+    "library", svr4_library_attributes, NULL,
+    GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
+    library_list_start_library, NULL
+  },
+  { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_attribute svr4_library_list_attributes[] =
+{
+  { "version", GDB_XML_AF_NONE, NULL, NULL },
+  { "main-lm", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+  { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_element svr4_library_list_elements[] =
+{
+  { "library-list-svr4", svr4_library_list_attributes, svr4_library_list_children,
+    GDB_XML_EF_NONE, svr4_library_list_start_list, NULL },
+  { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
 /* Implementation for target_so_ops.free_so.  */
 
 static void
@@ -973,6 +1074,69 @@
     }
 }
 
+/* Parse qXfer:libraries:read packet into *SO_LIST_RETURN.  Return 1 if
+
+   Return 0 if packet not supported, *SO_LIST_RETURN is not modified in such
+   case.  Return 1 if *SO_LIST_RETURN contains the library list, it may be
+   empty, caller is responsible for freeing all its entries.  */
+
+static int
+svr4_parse_libraries (const char *document, struct svr4_library_list *list)
+{
+  struct cleanup *back_to = make_cleanup (svr4_free_library_list,
+					  &list->head);
+
+  memset (list, 0, sizeof (*list));
+  list->tailp = &list->head;
+  if (gdb_xml_parse_quick (_("target library list"), "library-list.dtd",
+			   svr4_library_list_elements, document, list) == 0)
+    {
+      /* Parsed successfully, keep the result.  */
+      discard_cleanups (back_to);
+      return 1;
+    }
+
+  do_cleanups (back_to);
+  return 0;
+}
+
+/* Attempt to get so_list from target via qXfer:libraries:read packet.
+
+   Return 0 if packet not supported, *SO_LIST_RETURN is not modified in such
+   case.  Return 1 if *SO_LIST_RETURN contains the library list, it may be
+   empty, caller is responsible for freeing all its entries.  */
+
+static int
+svr4_current_sos_via_xfer_libraries (struct svr4_library_list *list)
+{
+  char *svr4_library_document;
+  int result;
+  struct cleanup *back_to;
+
+  /* Fetch the list of shared libraries.  */
+  svr4_library_document = target_read_stralloc (&current_target,
+						TARGET_OBJECT_LIBRARIES_SVR4,
+						NULL);
+  if (svr4_library_document == NULL)
+    return 0;
+
+  back_to = make_cleanup (xfree, svr4_library_document);
+  result = svr4_parse_libraries (svr4_library_document, list);
+  do_cleanups (back_to);
+
+  return result;
+}
+
+#else
+
+static int
+svr4_current_sos_via_xfer_libraries (struct svr4_library_list *list)
+{
+  return 0;
+}
+
+#endif
+
 /* If no shared library information is available from the dynamic
    linker, build a fallback list from other sources.  */
 
@@ -1032,7 +1196,9 @@
 
       if (new->lm_info->l_prev != prev_lm)
 	{
-	  warning (_("Corrupted shared library list"));
+	  warning (_("Corrupted shared library list: %s != %s"),
+		   paddress (target_gdbarch, prev_lm),
+		   paddress (target_gdbarch, new->lm_info->l_prev));
 	  do_cleanups (old_chain);
 	  break;
 	}
@@ -1093,6 +1259,18 @@
   struct svr4_info *info;
   struct cleanup *back_to;
   int ignore_first;
+  struct svr4_library_list library_list;
+
+  if (svr4_current_sos_via_xfer_libraries (&library_list))
+    {
+      if (library_list.main_lm)
+	{
+	  info = get_svr4_info ();
+	  info->main_lm_addr = library_list.main_lm;
+	}
+
+      return library_list.head ? library_list.head : svr4_default_sos ();
+    }
 
   info = get_svr4_info ();
 
--- src/gdb/target.h	2011/11/20 23:59:47	1.219
+++ src/gdb/target.h	2011/12/02 22:26:51	1.220
@@ -255,6 +255,8 @@
   TARGET_OBJECT_AVAILABLE_FEATURES,
   /* Currently loaded libraries, in XML format.  */
   TARGET_OBJECT_LIBRARIES,
+  /* Currently loaded libraries specific for SVR4 systems, in XML format.  */
+  TARGET_OBJECT_LIBRARIES_SVR4,
   /* Get OS specific data.  The ANNEX specifies the type (running
      processes, etc.).  The data being transfered is expected to follow
      the DTD specified in features/osdata.dtd.  */
--- src/gdb/doc/gdb.texinfo	2011/12/01 20:27:28	1.902
+++ src/gdb/doc/gdb.texinfo	2011/12/02 22:26:52	1.903
@@ -32448,7 +32448,8 @@
 @item
 Target descriptions (@pxref{Target Descriptions})
 @item
-Remote shared library lists (@pxref{Library List Format})
+Remote shared library lists (@xref{Library List Format},
+or alternatively @pxref{Library List Format for SVR4 Targets})
 @item
 MS-Windows shared libraries (@pxref{Shared Libraries})
 @item
@@ -33285,6 +33286,7 @@
 * Examples::
 * File-I/O Remote Protocol Extension::
 * Library List Format::
+* Library List Format for SVR4 Targets::
 * Memory Map Format::
 * Thread List Format::
 * Traceframe Info Format::
@@ -34986,6 +34988,10 @@
 The remote stub understands the @samp{qXfer:libraries:read} packet
 (@pxref{qXfer library list read}).
 
+@item qXfer:libraries-svr4:read
+The remote stub understands the @samp{qXfer:libraries-svr4:read} packet
+(@pxref{qXfer svr4 library list read}).
+
 @item qXfer:memory-map:read
 The remote stub understands the @samp{qXfer:memory-map:read} packet
 (@pxref{qXfer memory map read}).
@@ -35231,6 +35237,18 @@
 This packet is not probed by default; the remote stub must request it,
 by supplying an appropriate @samp{qSupported} response (@pxref{qSupported}).
 
+@item qXfer:libraries-svr4:read:@var{annex}:@var{offset},@var{length}
+@anchor{qXfer svr4 library list read}
+Access the target's list of loaded libraries when the target is an SVR4
+platform.  @xref{Library List Format for SVR4 Targets}.  The annex part
+of the generic @samp{qXfer} packet must be empty (@pxref{qXfer read}).
+
+This packet is optional for better performance on SVR4 targets.  
+@value{GDBN} uses memory read packets to read the SVR4 library list otherwise.
+
+This packet is not probed by default; the remote stub must request it,
+by supplying an appropriate @samp{qSupported} response (@pxref{qSupported}).
+
 @item qXfer:memory-map:read::@var{offset},@var{length}
 @anchor{qXfer memory map read}
 Access the target's @dfn{memory-map}.  @xref{Memory Map Format}.  The
@@ -37542,6 +37560,68 @@
 single library element, and you must supply at least one segment or
 section for each library.
 
+@node Library List Format for SVR4 Targets
+@section Library List Format for SVR4 Targets
+@cindex library list format, remote protocol
+
+On SVR4 platforms @value{GDBN} can use the symbol table of a dynamic loader
+(e.g.@: @file{ld.so}) and normal memory operations to maintain a list of
+shared libraries.  Still a special library list provided by this packet is
+more efficient for the @value{GDBN} remote protocol.
+
+The @samp{qXfer:libraries-svr4:read} packet returns an XML document which lists
+loaded libraries and their SVR4 linker parameters.  For each library on SVR4
+target, the following parameters are reported:
+
+@itemize @minus
+@item
+@code{name}, the absolute file name from the @code{l_name} field of
+@code{struct link_map}.
+@item
+@code{lm} with address of @code{struct link_map} used for TLS
+(Thread Local Storage) access.
+@item
+@code{l_addr}, the displacement as read from the field @code{l_addr} of
+@code{struct link_map}.  For prelinked libraries this is not an absolute
+memory address.  It is a displacement of absolute memory address against
+address the file was prelinked to during the library load.
+@item
+@code{l_ld}, which is memory address of the @code{PT_DYNAMIC} segment
+@end itemize
+
+Additionally the single @code{main-lm} attribute specifies address of
+@code{struct link_map} used for the main executable.  This parameter is used
+for TLS access and its presence is optional.
+
+@value{GDBN} must be linked with the Expat library to support XML
+SVR4 library lists.  @xref{Expat}.
+
+A simple memory map, with two loaded libraries (which do not use prelink),
+looks like this:
+
+@smallexample
+<library-list-svr4 version="1.0" main-lm="0xe4f8f8">
+  <library name="/lib/ld-linux.so.2" lm="0xe4f51c" l_addr="0xe2d000"
+           l_ld="0xe4eefc"/>
+  <library name="/lib/libc.so.6" lm="0xe4fbe8" l_addr="0x154000"
+           l_ld="0x152350"/>
+</library-list-svr>
+@end smallexample
+
+The format of an SVR4 library list is described by this DTD:
+
+@smallexample
+<!-- library-list-svr4: Root element with versioning -->
+<!ELEMENT library-list-svr4  (library)*>
+<!ATTLIST library-list-svr4  version CDATA   #FIXED  "1.0">
+<!ATTLIST library-list-svr4  main-lm CDATA   #IMPLIED>
+<!ELEMENT library            EMPTY>
+<!ATTLIST library            name    CDATA   #REQUIRED>
+<!ATTLIST library            lm      CDATA   #REQUIRED>
+<!ATTLIST library            l_addr  CDATA   #REQUIRED>
+<!ATTLIST library            l_ld    CDATA   #REQUIRED>
+@end smallexample
+
 @node Memory Map Format
 @section Memory Map Format
 @cindex memory map format
--- src/gdb/features/library-list-svr4.dtd
+++ src/gdb/features/library-list-svr4.dtd	2011-12-02 22:27:46.927012000 +0000
@@ -0,0 +1,16 @@
+<!-- Copyright (C) 2011 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!-- library-list-svr4: Root element with versioning -->
+<!ELEMENT library-list-svr4  (library)*>
+<!ATTLIST library-list-svr4  version CDATA   #FIXED  "1.0">
+<!ATTLIST library-list-svr4  main-lm CDATA   #IMPLIED>
+
+<!ELEMENT library            EMPTY>
+<!ATTLIST library            name    CDATA   #REQUIRED>
+<!ATTLIST library            lm      CDATA   #REQUIRED>
+<!ATTLIST library            l_addr  CDATA   #REQUIRED>
+<!ATTLIST library            l_ld    CDATA   #REQUIRED>
--- src/gdb/gdbserver/linux-low.c	2011/11/14 20:07:24	1.179
+++ src/gdb/gdbserver/linux-low.c	2011/12/02 22:26:52	1.180
@@ -4964,6 +4964,382 @@
   return (*the_low_target.get_min_fast_tracepoint_insn_len) ();
 }
 
+/* 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_ld field in struct link_map.  */
+    int l_ld_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_svr4 (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.  */
+      8,     /* l_ld 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.  */
+      16,    /* l_ld 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 == 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-svr4 version=\"1.0\"/>\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_ld, l_next, l_prev;
+      int r_version, header_done = 0;
+
+      document = xmalloc (allocated);
+      strcpy (document, "<library-list-svr4 version=\"1.0\"");
+      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_ld_offset,
+			      &l_ld, 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 ("Corrupted shared library list: 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) - 1);
+	  libname[sizeof (libname) - 1] = '\0';
+	  if (libname[0] != '\0')
+	    {
+	      /* 6x the size for xml_escape_text below.  */
+	      size_t len = 6 * strlen ((char *) libname);
+	      char *name;
+
+	      if (!header_done)
+		{
+		  /* Terminate `<library-list-svr4'.  */
+		  *p++ = '>';
+		  header_done = 1;
+		}
+
+	      while (allocated < p - document + len + 200)
+		{
+		  /* Expand to guarantee sufficient storage.  */
+		  uintptr_t document_len = p - document;
+
+		  document = xrealloc (document, 2 * allocated);
+		  allocated *= 2;
+		  p = document + document_len;
+		}
+
+	      name = xml_escape_text ((char *) libname);
+	      p += sprintf (p, "<library name=\"%s\" lm=\"0x%lx\" "
+			       "l_addr=\"0x%lx\" l_ld=\"0x%lx\"/>",
+			    name, (unsigned long) lm_addr,
+			    (unsigned long) l_addr, (unsigned long) l_ld);
+	      free (name);
+	    }
+	  else if (lm_prev == 0)
+	    {
+	      sprintf (p, " main-lm=\"0x%lx\"", (unsigned long) lm_addr);
+	      p = p + strlen (p);
+	    }
+
+	  if (l_next == 0)
+	    break;
+
+	  lm_prev = lm_addr;
+	  lm_addr = l_next;
+	}
+    done:
+      strcpy (p, "</library-list-svr4>");
+    }
+
+  document_len = strlen (document);
+  if (offset < document_len)
+    document_len -= offset;
+  else
+    document_len = 0;
+  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,
@@ -5026,6 +5402,7 @@
   linux_emit_ops,
   linux_supports_disable_randomization,
   linux_get_min_fast_tracepoint_insn_len,
+  linux_qxfer_libraries_svr4,
 };
 
 static void
--- src/gdb/gdbserver/linux-low.h	2011/11/14 20:07:24	1.48
+++ src/gdb/gdbserver/linux-low.h	2011/12/02 22:26:53	1.49
@@ -56,6 +56,9 @@
   /* 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;
--- src/gdb/gdbserver/server.c	2011/11/14 15:18:51	1.151
+++ src/gdb/gdbserver/server.c	2011/12/02 22:26:53	1.152
@@ -942,6 +942,10 @@
   if (annex[0] != '\0' || !target_running ())
     return -1;
 
+  /* Do not confuse this packet with qXfer:libraries-svr4:read.  */
+  if (the_target->qxfer_libraries_svr4 != NULL)
+    return 0;
+
   /* Over-estimate the necessary memory.  Assume that every character
      in the library name must be escaped.  */
   total_len = 64;
@@ -992,6 +996,23 @@
   return len;
 }
 
+/* Handle qXfer:libraries-svr4:read.  */
+
+static int
+handle_qxfer_libraries_svr4 (const char *annex,
+			     gdb_byte *readbuf, const gdb_byte *writebuf,
+			     ULONGEST offset, LONGEST len)
+{
+  if (writebuf != NULL)
+    return -2;
+
+  if (annex[0] != '\0' || !target_running ()
+      || the_target->qxfer_libraries_svr4 == NULL)
+    return -1;
+
+  return the_target->qxfer_libraries_svr4 (annex, readbuf, writebuf, offset, len);
+}
+
 /* Handle qXfer:osadata:read.  */
 
 static int
@@ -1216,6 +1237,7 @@
     { "fdpic", handle_qxfer_fdpic},
     { "features", handle_qxfer_features },
     { "libraries", handle_qxfer_libraries },
+    { "libraries-svr4", handle_qxfer_libraries_svr4 },
     { "osdata", handle_qxfer_osdata },
     { "siginfo", handle_qxfer_siginfo },
     { "spu", handle_qxfer_spu },
@@ -1536,9 +1558,14 @@
 
       sprintf (own_buf, "PacketSize=%x;QPassSignals+", PBUFSIZ - 1);
 
-      /* We do not have any hook to indicate whether the target backend
-	 supports qXfer:libraries:read, so always report it.  */
-      strcat (own_buf, ";qXfer:libraries:read+");
+      if (the_target->qxfer_libraries_svr4 != NULL)
+	strcat (own_buf, ";qXfer:libraries-svr4:read+");
+      else
+	{
+	  /* We do not have any hook to indicate whether the non-SVR4 target
+	     backend supports qXfer:libraries:read, so always report it.  */
+	  strcat (own_buf, ";qXfer:libraries:read+");
+	}
 
       if (the_target->read_auxv != NULL)
 	strcat (own_buf, ";qXfer:auxv:read+");
--- src/gdb/gdbserver/target.h	2011/11/14 20:07:24	1.59
+++ src/gdb/gdbserver/target.h	2011/12/02 22:26:53	1.60
@@ -390,6 +390,11 @@
   /* Return the minimum length of an instruction that can be safely overwritten
      for use as a fast tracepoint.  */
   int (*get_min_fast_tracepoint_insn_len) (void);
+
+  /* Read solib info on SVR4 platforms.  */
+  int (*qxfer_libraries_svr4) (const char *annex, unsigned char *readbuf,
+			       unsigned const char *writebuf,
+			       CORE_ADDR offset, int len);
 };
 
 extern struct target_ops *the_target;
--- src/gdb/testsuite/gdb.base/solib-corrupted.exp	2011/01/01 15:33:42	1.4
+++ src/gdb/testsuite/gdb.base/solib-corrupted.exp	2011/12/02 22:26:54	1.5
@@ -17,6 +17,12 @@
     return 0
 }
 
+if {[is_remote target]} {
+    # gdbserver prints the warning message but expect is parsing only the GDB
+    # output, not the gdbserver output.
+    return 0
+}
+
 set testfile "solib-corrupted"
 set srcfile start.c
 
@@ -47,4 +53,4 @@
 	pass $test
     }
 }
-gdb_test "info sharedlibrary" "warning: Corrupted shared library list\r\n.*" "corrupted list"
+gdb_test "info sharedlibrary" "warning: Corrupted shared library list: .*" "corrupted list"

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [commit] [patch] Implement qXfer:libraries-svr4 for Linux/gdbserver #4
  2011-12-02 22:33         ` [commit] " Jan Kratochvil
@ 2011-12-03 17:03           ` Doug Evans
  2011-12-03 18:34             ` [commit] Fix compilation --without-expat [Re: [commit] [patch] Implement qXfer:libraries-svr4 for Linux/gdbserver #4] Jan Kratochvil
  0 siblings, 1 reply; 12+ messages in thread
From: Doug Evans @ 2011-12-03 17:03 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

On Fri, Dec 2, 2011 at 2:32 PM, Jan Kratochvil
<jan.kratochvil@redhat.com> wrote:
> Hi,
>
> checked in, therefore introducing the new <library-list-svr4/> and
> TARGET_OBJECT_LIBRARIES_SVR4 protocol (not reusing <library-list/>).
>
> Some gdbserver memory corruption has been fixed.
>
> The patch uses now always the version="1.0" attribute as #FIXED, provided by
> gdbserver and required by gdb.  A similar change I suggested for existing
> <library-list/> in:
>        [patch] gdbserver <library-list> and its #FIXED version="1.0"
>        http://sourceware.org/ml/gdb-patches/2011-11/msg00099.html
>
> No regressions on {x86_64,x86_64-m32,i686}-fedora16-linux-gnu in both nat and
> gdbserver mode.

I happened to do a non-expat build and got a build failure.


../../../src/gdb/solib-svr4.c: In function 'svr4_current_sos':
../../../src/gdb/solib-svr4.c:1293: error: 'svr4_free_library_list'
undeclared (first use in this function)
../../../src/gdb/solib-svr4.c:1293: error: (Each undeclared identifier
is reported only once
../../../src/gdb/solib-svr4.c:1293: error: for each function it appears in.)
../../../src/gdb/solib-svr4.c: In function '_initialize_svr4_solib':
../../../src/gdb/solib-svr4.c:2479: error: 'svr4_free_so' undeclared
(first use in this function)
make: *** [solib-svr4.o] Error 1

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [commit] Fix compilation --without-expat  [Re: [commit] [patch] Implement qXfer:libraries-svr4 for Linux/gdbserver #4]
  2011-12-03 17:03           ` Doug Evans
@ 2011-12-03 18:34             ` Jan Kratochvil
  0 siblings, 0 replies; 12+ messages in thread
From: Jan Kratochvil @ 2011-12-03 18:34 UTC (permalink / raw)
  To: Doug Evans; +Cc: gdb-patches

On Sat, 03 Dec 2011 18:03:26 +0100, Doug Evans wrote:
> I happened to do a non-expat build and got a build failure.
> 
> ../../../src/gdb/solib-svr4.c:1293: error: 'svr4_free_library_list' undeclared (first use in this function)

Checked in.


Sorry,
Jan


http://sourceware.org/ml/gdb-cvs/2011-12/msg00019.html

--- src/gdb/ChangeLog	2011/12/02 22:26:50	1.13569
+++ src/gdb/ChangeLog	2011/12/03 18:32:28	1.13570
@@ -1,3 +1,10 @@
+2011-12-03  Jan Kratochvil  <jan.kratochvil@redhat.com>
+
+	Fix compilation --without-expat.
+	* solib-svr4.c (svr4_free_so, svr4_free_library_list): Move them here
+	from ...
+	[HAVE_LIBEXPAT] (svr4_free_so, svr4_free_library_list): ... here.
+
 2011-12-02  Paul Pluzhnikov  <ppluzhnikov@google.com>
 	    Jan Kratochvil  <jan.kratochvil@redhat.com>
 
--- src/gdb/solib-svr4.c	2011/12/02 22:26:51	1.159
+++ src/gdb/solib-svr4.c	2011/12/03 18:32:29	1.160
@@ -961,6 +961,30 @@
   CORE_ADDR main_lm;
 };
 
+/* Implementation for target_so_ops.free_so.  */
+
+static void
+svr4_free_so (struct so_list *so)
+{
+  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;
+    }
+}
+
 #ifdef HAVE_LIBEXPAT
 
 #include "xml-support.h"
@@ -1050,30 +1074,6 @@
   { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
 };
 
-/* Implementation for target_so_ops.free_so.  */
-
-static void
-svr4_free_so (struct so_list *so)
-{
-  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_RETURN.  Return 1 if
 
    Return 0 if packet not supported, *SO_LIST_RETURN is not modified in such

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2011-12-03 18:34 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-10-03 21:55 [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2 Jan Kratochvil
2011-10-04  6:11 ` Eli Zaretskii
2011-10-18 17:49   ` Messages localization in gdbserver [Re: [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2] Jan Kratochvil
2011-10-06 19:09 ` [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2 Pedro Alves
2011-10-21 11:05   ` Jan Kratochvil
2011-10-21 13:32     ` Pedro Alves
2011-11-03 21:30       ` [patch] Implement qXfer:libraries-svr4 for Linux/gdbserver #4 Jan Kratochvil
2011-12-02 22:33         ` [commit] " Jan Kratochvil
2011-12-03 17:03           ` Doug Evans
2011-12-03 18:34             ` [commit] Fix compilation --without-expat [Re: [commit] [patch] Implement qXfer:libraries-svr4 for Linux/gdbserver #4] Jan Kratochvil
2011-10-10  2:44 ` [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2 Daniel Jacobowitz
2011-10-12 20:22   ` Jan Kratochvil

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).