[patch 3/3] Implement qXfer:libraries for Linux/gdbserver #3

Jan Kratochvil jan.kratochvil@redhat.com
Tue Oct 18 21:53:00 GMT 2011


On Tue, 18 Oct 2011 23:04:26 +0200, Eli Zaretskii wrote:
> Can we reword this not to say "For SVR4 systems" twice in a row?

done.


Thanks,
Jan


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

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

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

	* inferiors.c (main_lm): New variable.
	(loaded_dll): Rename to ...
	(loaded_svr4_so): ... here, add parameters lm_addr and l_ld, fill them
	in.
	(loaded_dll, clear_all_dlls): New function.
	(clear_inferiors): Move some code to clear_all_dlls, call it.
	* linux-low.c (get_phdr_phnum_from_proc_auxv, get_dynamic, get_r_debug)
	(struct map_entry, map_entries, map_entries_count, map_entries_free)
	(map_entries_init, map_entries_get_compar, map_entries_get_start)
	(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.
	Fill in optionally also main_lm and for each library lm_addr and l_ld.
	* server.h (struct dll_info): New fields lm_addr and l_ld.
	(main_lm, clear_all_dlls, loaded_svr4_so): New declarations.
	* target.h (struct target_ops): New member qxfer_libraries.

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

	* features/library-list.dtd: Add attributes library-list.main-lm,
	library.lm and library.l_ld.
	* solib-svr4.c: Include solib-target.h.
	(struct lm_info): New field l_addr_inferior_is_absolute.
	(svr4_file_displacement): New function.
	(lm_addr_check): Use it if L_ADDR_INFERIOR_IS_ABSOLUTE.  Extend the
	corruption message by addresses.
	(svr4_from_solib_target): New function.
	(svr4_current_sos): New variable solib_target_list, call
	solib_target_list_read and svr4_from_solib_target appropriately.
	* solib-target.c (library_list_start_library): New variables lm and
	l_ld, fill in the lib fields with them.
	(library_list_start_list): New variable list.  New variable main_lm,
	fill in the lib field with it.
	(library_attributes): New entries lm and l_ld.
	(library_list_attributes): New entry main-lm.
	* solib-target.h (struct solib_target_lib): New fields lm_addr and l_ld.
	(struct solib_target_list): New field main_lm.

gdb/testsuite/
2011-10-18  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
@@ -36989,6 +36989,19 @@ 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.
 
+For SVR4 systems, a single segment is reported and its parent
+@code{library-list} element should have @code{main-lm} attribute with address
+of @code{struct link_map} used for the main executable.  Additionally for each
+SVR4 @code{library} element the following parameters are reported:
+
+@itemize @minus
+@item
+@code{lm} with address of @code{struct link_map} used for TLS (Thread Local
+Storage) access
+@item
+@code{l_ld}, which is memory address of the @code{PT_DYNAMIC} segment
+@end itemize
+
 @value{GDBN} must be linked with the Expat library to support XML
 library lists.  @xref{Expat}.
 
@@ -37016,14 +37029,30 @@ allocated sections (.text, .data, .bss), looks like this:
 </library-list>
 @end smallexample
 
+SVR4 systems report:
+
+@smallexample
+<library-list main-lm="0x1328f8">
+  <library name="/lib/ld-linux.so.2" lm="0x13251c" l_ld="0x131efc">
+    <segment address="0x111000"/>
+  </library>
+  <library name="/lib/libc.so.6" lm="0xf7ffd6c0" l_ld="0x307d7c">
+    <segment address="0x15f000"/>
+  </library>
+</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_ld    CDATA   #IMPLIED>
 <!ELEMENT segment       EMPTY>
 <!ATTLIST segment       address CDATA   #REQUIRED>
 <!ELEMENT section       EMPTY>
@@ -37032,7 +37061,9 @@ The format of a library list is described by this DTD:
 
 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.
+section for each library.  For SVR4 systems each library must have one
+segment descriptor and both the @code{lm} and @code{l_ld} attributes
+must be present for each library; the @code{main-lm} attribute is optional.
 
 @node Memory Map Format
 @section Memory Map Format
--- a/gdb/features/library-list.dtd
+++ b/gdb/features/library-list.dtd
@@ -7,9 +7,12 @@
 <!-- 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_ld    CDATA   #IMPLIED>
 
 <!ELEMENT segment       EMPTY>
 <!ATTLIST segment       address CDATA   #REQUIRED>
--- a/gdb/gdbserver/inferiors.c
+++ b/gdb/gdbserver/inferiors.c
@@ -28,6 +28,10 @@ struct inferior_list all_threads;
 struct inferior_list all_dlls;
 int dlls_changed;
 
+/* Address of main executable's struct link_map for the current inferior.
+   It may be 0 if it is not known.  */
+CORE_ADDR main_lm;
+
 struct thread_info *current_inferior;
 
 #define get_thread(inf) ((struct thread_info *)(inf))
@@ -258,10 +262,13 @@ match_dll (struct inferior_list_entry *inf, void *arg)
   return 0;
 }
 
-/* Record a newly loaded DLL at BASE_ADDR.  */
+/* Record a newly loaded DLL at BASE_ADDR, struct link_map at LM_ADDR and
+   PT_DYNAMIC at L_LD.  LM_ADDR and L_LD can be 0 if not known.  BASE_ADDR is
+   not link_map->l_addr as l_addr is only bias, not the address.  */
 
 void
-loaded_dll (const char *name, CORE_ADDR base_addr)
+loaded_svr4_so (const char *name, CORE_ADDR base_addr, CORE_ADDR lm_addr,
+		CORE_ADDR l_ld)
 {
   struct dll_info *new_dll = xmalloc (sizeof (*new_dll));
   memset (new_dll, 0, sizeof (*new_dll));
@@ -270,11 +277,21 @@ loaded_dll (const char *name, CORE_ADDR base_addr)
 
   new_dll->name = xstrdup (name);
   new_dll->base_addr = base_addr;
+  new_dll->lm_addr = lm_addr;
+  new_dll->l_ld = l_ld;
 
   add_inferior_to_list (&all_dlls, &new_dll->entry);
   dlls_changed = 1;
 }
 
+/* Record a newly loaded DLL at BASE_ADDR.  */
+
+void
+loaded_dll (const char *name, CORE_ADDR base_addr)
+{
+  loaded_svr4_so (name, base_addr, 0, 0);
+}
+
 /* Record that the DLL with NAME and BASE_ADDR has been unloaded.  */
 
 void
@@ -312,14 +329,23 @@ unloaded_dll (const char *name, CORE_ADDR base_addr)
 #define clear_list(LIST) \
   do { (LIST)->head = (LIST)->tail = NULL; } while (0)
 
+/* Clear ALL_DLLS and MAIN_LM.  */
+
 void
-clear_inferiors (void)
+clear_all_dlls (void)
 {
-  for_each_inferior (&all_threads, free_one_thread);
   for_each_inferior (&all_dlls, free_one_dll);
+  clear_list (&all_dlls);
+  main_lm = 0;
+}
 
+void
+clear_inferiors (void)
+{
+  for_each_inferior (&all_threads, free_one_thread);
   clear_list (&all_threads);
-  clear_list (&all_dlls);
+
+  clear_all_dlls ();
 
   current_inferior = NULL;
 }
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -4953,6 +4953,444 @@ 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;
+}
+
+/* Parsed line from /proc/PID/maps - the 1st, 2nd and 4th column.  */
+struct map_entry
+{
+  CORE_ADDR start, end;
+
+  /* File offset where START is mapped from.  */
+  CORE_ADDR offset;
+};
+
+/* Cached array of MAP_ENTRIES_COUNT entries from /proc/PID/maps.  */
+static struct map_entry *map_entries;
+static unsigned map_entries_count;
+
+/* Free map_entries and map_entries_count.  */
+
+static void
+map_entries_free (void)
+{
+  free (map_entries);
+  map_entries = NULL;
+  map_entries_count = 0;
+}
+
+/* Load MAP_ENTRIES and MAP_ENTRIES_COUNT from /proc/PID/maps.  Keep them clear
+   if /proc/PID/maps cannot be read.  */
+
+static void
+map_entries_init (pid_t pid)
+{
+  unsigned allocated = 0;
+  char fname[50];
+  FILE *f;
+  unsigned long start, end, offset;
+  int line = 1;
+
+  map_entries_free ();
+  xsnprintf (fname, sizeof (fname), "/proc/%d/maps", (int) pid);
+  f = fopen (fname, "r");
+  if (f == NULL)
+    {
+      warning ("Opening \"%s\": %s", fname, strerror (errno));
+      return;
+    }
+  while (fscanf (f, "%lx-%lx%*s%lx", &start, &end, &offset) == 3)
+    {
+      if (map_entries_count && map_entries[map_entries_count - 1].end > start)
+	warning ("Error reading \"%s\" line %d: start < previous end", fname,
+		 line);
+      else if (start > end)
+	warning ("Error reading \"%s\" line %d: start > end", fname, line);
+      else
+	{
+	  struct map_entry *entry;
+
+	  if (map_entries_count == allocated)
+	    {
+	      allocated = allocated ? allocated * 2 : 0x40;
+	      map_entries = xrealloc (map_entries,
+				      allocated * sizeof (*map_entries));
+	    }
+	  entry = &map_entries[map_entries_count++];
+	  entry->start = start;
+	  entry->end = end;
+	  entry->offset = offset;
+	}
+      while (!ferror (f) && !feof (f) && fgetc (f) != '\n');
+      if (ferror (f) || feof (f))
+	break;
+      line++;
+    }
+  if (ferror (f))
+    warning ("Error reading \"%s\" line %d: %s", fname, line, strerror (errno));
+  if (fclose (f))
+    warning ("Error closing \"%s\": %s", fname, strerror (errno));
+}
+
+/* Find address stored in KEY, bsearch helper for map_entries_get_start.  */
+
+static int
+map_entries_get_compar (const void *key, const void *entry_voidp)
+{
+  CORE_ADDR l_ld = *(const CORE_ADDR *) key;
+  const struct map_entry *entry = entry_voidp;
+
+  if (l_ld < entry->start)
+    return -1;
+  else if (l_ld < entry->end)
+    return 0;
+  else
+    return +1;
+}
+
+/* Find address where start of a file is mapped to if L_LD is located somewhere
+   in the file.  Return zero otherwise.  */
+
+static CORE_ADDR
+map_entries_get_start (CORE_ADDR l_ld)
+{
+  struct map_entry *foundp;
+
+  foundp = bsearch (&l_ld, map_entries, map_entries_count,
+		    sizeof (*map_entries), map_entries_get_compar);
+
+  return foundp ? foundp->start - foundp->offset : 0;
+}
+
+/* Read one pointer from MEMADDR in the inferior.  */
+
+static int
+read_one_ptr (CORE_ADDR memaddr, CORE_ADDR *ptr, int ptr_size)
+{
+  *ptr = 0;
+  return linux_read_memory (memaddr, (unsigned char *) ptr, ptr_size);
+}
+
+struct link_map_offsets
+  {
+    /* Offset and size of r_debug.r_version.  */
+    int r_version_offset;
+
+    /* Offset and size of r_debug.r_map.  */
+    int r_map_offset;
+
+    /* Offset to l_addr field in struct link_map.  */
+    int l_addr_offset;
+
+    /* Offset to l_name field in struct link_map.  */
+    int l_name_offset;
+
+    /* Offset to l_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_refresh_libraries (void)
+{
+  struct process_info_private *const priv = current_process ()->private;
+  char filename[PATH_MAX];
+  int pid, is_elf64, ptr_size, r_version;
+  CORE_ADDR lm_addr, lm_prev, l_addr, l_name, l_ld, l_next, l_prev;
+
+  static const struct link_map_offsets lmo_32bit_offsets =
+    {
+      0,     /* r_version offset. */
+      4,     /* r_debug.r_map offset.  */
+      0,     /* l_addr offset in link_map.  */
+      4,     /* l_name offset in link_map.  */
+      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;
+
+  pid = lwpid_of (get_thread_lwp (current_inferior));
+  xsnprintf (filename, sizeof filename, "/proc/%d/exe", pid);
+  is_elf64 = elf_64_file_p (filename);
+  lmo = is_elf64 ? &lmo_64bit_offsets : &lmo_32bit_offsets;
+  ptr_size = is_elf64 ? 8 : 4;
+
+  clear_all_dlls ();
+
+  if (priv->r_debug == 0)
+    priv->r_debug = get_r_debug (pid, is_elf64);
+  if (priv->r_debug == (CORE_ADDR) -1)
+    return 0;
+  if (priv->r_debug == 0)
+    return 1;
+
+  r_version = 0;
+  if (linux_read_memory (priv->r_debug + lmo->r_version_offset,
+			 (unsigned char *) &r_version,
+			 sizeof (r_version)) != 0
+      || r_version != 1)
+    {
+      warning ("unexpected r_debug version %d", r_version);
+      return 0;
+    }
+
+  if (read_one_ptr (priv->r_debug + lmo->r_map_offset,
+		    &lm_addr, ptr_size) != 0)
+    {
+      warning ("unable to read r_map from 0x%lx",
+	       (long) priv->r_debug + lmo->r_map_offset);
+      return 0;
+    }
+
+  map_entries_init (pid);
+
+  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')
+	{
+	  CORE_ADDR start;
+
+	  start = map_entries_get_start (l_ld);
+	  if (start == 0)
+	    {
+	      /* Use at least L_ADDR if we failed to read /proc.  But L_ADDR is
+		 wrong for this purpose if the library file is prelinked.  */
+	      start = l_addr;
+	    }
+
+	  loaded_svr4_so ((const char *) libname, start, lm_addr, l_ld);
+	}
+      else if (lm_prev == 0)
+	main_lm = lm_addr;
+
+      if (l_next == 0)
+	break;
+
+      lm_prev = lm_addr;
+      lm_addr = l_next;
+    }
+  map_entries_free ();
+
+  /* The library notification response is not expected for GNU/Linux.  */
+  dlls_changed = 0;
+  return 1;
+}
+
 static struct target_ops linux_target_ops = {
   linux_create_inferior,
   linux_attach,
@@ -5014,6 +5452,7 @@ static struct target_ops linux_target_ops = {
   linux_install_fast_tracepoint_jump_pad,
   linux_emit_ops,
   linux_supports_disable_randomization,
+  linux_refresh_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
@@ -942,6 +942,10 @@ handle_qxfer_libraries (const char *annex,
   if (annex[0] != '\0' || !target_running ())
     return -1;
 
+  if (the_target->refresh_libraries != NULL
+      && !the_target->refresh_libraries ())
+    return -1;
+
   /* Over-estimate the necessary memory.  Assume that every character
      in the library name must be escaped.  */
   total_len = 64;
@@ -952,19 +956,30 @@ handle_qxfer_libraries (const char *annex,
   if (document == NULL)
     return -1;
 
-  strcpy (document, "<library-list>\n");
+  strcpy (document, "<library-list");
   p = document + strlen (document);
 
+  if (main_lm)
+    p += sprintf (p, " main-lm=\"0x%lx\"", (unsigned long) main_lm);
+
+  p += sprintf (p, ">\n");
+
   for (dll_ptr = all_dlls.head; dll_ptr != NULL; dll_ptr = dll_ptr->next)
     {
       struct dll_info *dll = (struct dll_info *) dll_ptr;
       char *name;
 
       name = xml_escape_text (dll->name);
-      p += sprintf (p, "  <library name=\"%s\"><segment address=\"0x%lx\"/>"
-		       "</library>\n",
-		    name, (unsigned long) dll->base_addr);
+      p += sprintf (p, "  <library name=\"%s\"", name);
       free (name);
+
+      if (dll->lm_addr)
+	p += sprintf (p, " lm=\"0x%lx\"", (unsigned long) dll->lm_addr);
+      if (dll->l_ld)
+	p += sprintf (p, " l_ld=\"0x%lx\"", (unsigned long) dll->l_ld);
+
+      p += sprintf (p, "><segment address=\"0x%lx\"/></library>\n",
+		    (unsigned long) dll->base_addr);
     }
 
   strcpy (p, "</library-list>\n");
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -183,6 +183,10 @@ struct dll_info
   struct inferior_list_entry entry;
   char *name;
   CORE_ADDR base_addr;
+
+  /* struct link_map address and the PT_DYNAMIC segment address for SVR4
+     systems, if known.  Otherwise these fields are zero.  */
+  CORE_ADDR lm_addr, l_ld;
 };
 
 struct sym_cache;
@@ -235,6 +239,7 @@ void initialize_low ();
 extern struct inferior_list all_processes;
 extern struct inferior_list all_threads;
 extern struct inferior_list all_dlls;
+extern CORE_ADDR main_lm;
 extern int dlls_changed;
 
 void add_inferior_to_list (struct inferior_list *list,
@@ -260,6 +265,7 @@ ptid_t thread_id_to_gdb_id (ptid_t);
 ptid_t thread_to_gdb_id (struct thread_info *);
 ptid_t gdb_id_to_thread_id (ptid_t);
 struct thread_info *gdb_id_to_thread (unsigned int);
+void clear_all_dlls (void);
 void clear_inferiors (void);
 struct inferior_list_entry *find_inferior
      (struct inferior_list *,
@@ -275,6 +281,8 @@ void set_inferior_regcache_data (struct thread_info *, void *);
 void add_pid_to_list (struct inferior_list *list, unsigned long pid);
 int pull_pid_from_list (struct inferior_list *list, unsigned long pid);
 
+void loaded_svr4_so (const char *name, CORE_ADDR base_addr, CORE_ADDR lm_addr,
+		     CORE_ADDR l_ld);
 void loaded_dll (const char *name, CORE_ADDR base_addr);
 void unloaded_dll (const char *name, CORE_ADDR base_addr);
 
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -380,6 +380,10 @@ struct target_ops
 
   /* Returns true if the target supports disabling randomization.  */
   int (*supports_disable_randomization) (void);
+
+  /* Refresh ALL_DLLS.  Return 1 if ALL_DLLS is valid, return 0 if some error
+     occured reading the inferior.  */
+  int (*refresh_libraries) (void);
 };
 
 extern struct target_ops *the_target;
--- a/gdb/solib-svr4.c
+++ b/gdb/solib-svr4.c
@@ -41,6 +41,7 @@
 #include "solist.h"
 #include "solib.h"
 #include "solib-svr4.h"
+#include "solib-target.h"
 
 #include "bfd-target.h"
 #include "elf-bfd.h"
@@ -66,6 +67,11 @@ struct lm_info
     CORE_ADDR l_addr, l_addr_inferior;
     unsigned int l_addr_p : 1;
 
+    /* If this flag is zero L_ADDR_INFERIOR is the bias from inferior
+       link_map->l_addr.  If this flag is set L_ADDR_INFERIOR is the absolute
+       address where the library mapping starts.  */
+    unsigned int l_addr_inferior_is_absolute : 1;
+
     /* The target location of lm.  */
     CORE_ADDR lm_addr;
 
@@ -189,6 +195,39 @@ has_lm_dynamic_from_link_map (void)
   return lmo->l_ld_offset >= 0;
 }
 
+/* Find where ABFD file is prelinked to and return the address.  Return
+   zero otherwise.  */
+
+static CORE_ADDR
+svr4_file_displacement (bfd *abfd)
+{
+  long phdr_size;
+  Elf_Internal_Phdr *phdrs;
+  int num_phdrs = -1, i;
+  
+  phdr_size = bfd_get_elf_phdr_upper_bound (abfd);
+  if (phdr_size != -1)
+    {
+      phdrs = alloca (phdr_size);
+
+      num_phdrs = bfd_get_elf_phdrs (abfd, phdrs);
+    }
+  if (num_phdrs == -1)
+    {
+      warning (_("Cannot find program headers in \"%s\": %s"),
+	       bfd_get_filename (abfd), bfd_errmsg (bfd_get_error ()));
+      return 0;
+    }
+
+  for (i = 0; i < num_phdrs; i++)
+    if (phdrs[i].p_type == PT_PHDR)
+      return phdrs[i].p_vaddr - phdrs[i].p_offset;
+
+  warning (_("Cannot find PT_PHDR in program headers of \"%s\""),
+	   bfd_get_filename (abfd));
+  return 0;
+}
+
 static CORE_ADDR
 lm_addr_check (struct so_list *so, bfd *abfd)
 {
@@ -210,6 +249,9 @@ lm_addr_check (struct so_list *so, bfd *abfd)
 
       dynaddr = bfd_section_vma (abfd, dyninfo_sect);
 
+      if (so->lm_info->l_addr_inferior_is_absolute)
+	l_addr -= svr4_file_displacement (abfd);
+
       if (dynaddr + l_addr != l_dynaddr)
 	{
 	  CORE_ADDR align = 0x1000;
@@ -1032,7 +1074,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;
 	}
@@ -1082,6 +1126,49 @@ svr4_read_so_list (CORE_ADDR lm, struct so_list ***link_ptr_ptr,
     }
 }
 
+/* Convert LIST into the so_list format and return it.  LIST lifetime is
+   handled by the caller.  This function may throw an exception if LIST is not
+   appropriate for SVR4.  */
+
+static struct so_list *
+svr4_from_solib_target (struct solib_target_list *list)
+{
+  struct solib_target_lib *lib;
+  int ix;
+  struct so_list *head = NULL, **tailp = &head;
+  struct cleanup *back_to = make_cleanup (svr4_free_library_list, &head);
+
+  for (ix = 0; VEC_iterate (solib_target_libp, list->vec, ix, lib); ix++)
+    {
+      struct so_list *so;
+
+      gdb_assert (lib->offsets == NULL);
+      if (!VEC_empty (CORE_ADDR, lib->section_bases))
+	error (_("gdbserver libraries with sections are unsupported by SVR4"));
+      if (VEC_length (CORE_ADDR, lib->segment_bases) != 1)
+	error (_("gdbserver libraries with segment count not 1 are unsupported "
+		 "by SVR4"));
+
+      so = xzalloc (sizeof (*so));
+      so->lm_info = xzalloc (sizeof (*so->lm_info));
+      so->lm_info->lm_addr = lib->lm_addr;
+      so->lm_info->l_addr_inferior = VEC_index (CORE_ADDR, lib->segment_bases,
+						0);
+      so->lm_info->l_addr_inferior_is_absolute = 1;
+      so->lm_info->l_ld = lib->l_ld;
+
+      strncpy (so->so_name, lib->name, sizeof (so->so_name) - 1);
+      so->so_name[sizeof (so->so_name) - 1] = 0;
+      strcpy (so->so_original_name, so->so_name);
+
+      *tailp = so;
+      tailp = &so->next;
+    }
+
+  discard_cleanups (back_to);
+  return head;
+}
+
 /* Implement the "current_sos" target_so_ops method.  */
 
 static struct so_list *
@@ -1093,6 +1180,32 @@ svr4_current_sos (void)
   struct svr4_info *info;
   struct cleanup *back_to;
   int ignore_first;
+  struct solib_target_list solib_target_list;
+
+  /* Optionally support gdbserver XML library list.  */
+  if (solib_target_list_read (&solib_target_list))
+    {
+      back_to = make_cleanup_solib_target_list_free (&solib_target_list);
+
+      if (solib_target_list.main_lm)
+	{
+	  info = get_svr4_info ();
+	  info->main_lm_addr = solib_target_list.main_lm;
+	}
+
+      if (VEC_empty (solib_target_libp, solib_target_list.vec))
+	{
+	  /* If the gdbserver packet succeeded and returned an empty list the
+	     inferior DT_DEBUG is known but currently no libraries are loaded
+	     in the inferior.  */
+	  head = svr4_default_sos ();
+	}
+      else
+	head = svr4_from_solib_target (&solib_target_list);
+
+      do_cleanups (back_to);
+      return head;
+    }
 
   info = get_svr4_info ();
 
--- a/gdb/solib-target.c
+++ b/gdb/solib-target.c
@@ -107,8 +107,14 @@ library_list_start_library (struct gdb_xml_parser *parser,
   struct solib_target_list *list = user_data;
   struct solib_target_lib *lib = xzalloc (sizeof (*lib));
   const char *name = xml_find_attribute (attributes, "name")->value;
+  struct gdb_xml_value *lm = xml_find_attribute (attributes, "lm");
+  struct gdb_xml_value *l_ld = xml_find_attribute (attributes, "l_ld");
 
   lib->name = xstrdup (name);
+  if (lm)
+    lib->lm_addr = *(ULONGEST *) lm->value;
+  if (l_ld)
+    lib->l_ld = *(ULONGEST *) l_ld->value;
   VEC_safe_push (solib_target_libp, list->vec, lib);
 }
 
@@ -133,12 +139,16 @@ 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 solib_target_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;
 }
 
 /* Discard the constructed library list.  Do not free its memory itself as it
@@ -185,6 +195,8 @@ static const struct gdb_xml_element library_children[] = {
 
 static const struct gdb_xml_attribute library_attributes[] = {
   { "name", GDB_XML_AF_NONE, NULL, NULL },
+  { "lm", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+  { "l_ld", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
   { NULL, GDB_XML_AF_NONE, NULL, NULL }
 };
 
@@ -197,6 +209,7 @@ static const struct gdb_xml_element library_list_children[] = {
 
 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 }
 };
 
--- a/gdb/solib-target.h
+++ b/gdb/solib-target.h
@@ -32,6 +32,10 @@ struct solib_target_lib
      so_list; it is only here during XML parsing.  */
   char *name;
 
+  /* struct link_map address and the PT_DYNAMIC segment address for SVR4
+     systems, if known.  Otherwise these fields are zero.  */
+  CORE_ADDR lm_addr, l_ld;
+
   /* The target can either specify segment bases or section bases, not
      both.  */
 
@@ -56,6 +60,10 @@ struct solib_target_list
 {
   /* All libraries.  The vector can be empty.  */
   VEC(solib_target_libp) *vec;
+
+  /* struct link_map address of the main executable, if known.  Otherwise this
+     field is zero.  */
+  CORE_ADDR main_lm;
 };
 
 extern int solib_target_list_read (struct solib_target_list *list);
--- 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"



More information about the Gdb-patches mailing list