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 3/3] Implement qXfer:libraries for Linux/gdbserver #3


Hi,

this follows the advice in
	Re: [patch 3/3] Implement qXfer:libraries for Linux/gdbserver #2
	http://sourceware.org/ml/gdb-patches/2011-10/msg00250.html

but I do not see anything more useful making the protocol compatible.
The reader can be shared then but both the server and client have to convert
the addresses between absolute and relative forms.  I thought there is some
advantage for PIC but there isn't any.

There is about 50 more LoC (Lines of Code); there are the new map_entries* and
svr4_file_displacement conversions but solib-target.c reader can be shared.

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


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.  For SVR4 system the
+@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;
@@ -999,7 +1041,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;
 	}
@@ -1049,6 +1093,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 *
@@ -1060,6 +1147,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"


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