This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[patch] Implement qXfer:libraries-svr4 for Linux/gdbserver #4


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"


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]