gdbserver build-id attribute generator
authorJan Kratochvil <jan.kratochvil@redhat.com>
Wed, 15 Jul 2015 15:37:28 +0000 (17:37 +0200)
committerJan Kratochvil <jan.kratochvil@redhat.com>
Wed, 15 Jul 2015 15:41:18 +0000 (17:41 +0200)
Producer part of the new "build-id" XML attribute.

gdb/ChangeLog
2015-07-15  Aleksandar Ristovski  <aristovski@qnx.com
    Jan Kratochvil  <jan.kratochvil@redhat.com>

gdbserver build-id attribute generator.
* features/library-list-svr4.dtd (library-list-svr4): New
'build-id' attribute.

gdb/doc/ChangeLog
2015-07-15  Aleksandar Ristovski  <aristovski@qnx.com
    Jan Kratochvil  <jan.kratochvil@redhat.com>

gdbserver build-id attribute generator.
* gdb.texinfo (Library List Format for SVR4 Targets): Add
'build-id' in description, example, new attribute in dtd.

gdb/gdbserver/ChangeLog
2015-07-15  Aleksandar Ristovski  <aristovski@qnx.com
    Jan Kratochvil  <jan.kratochvil@redhat.com>

gdbserver build-id attribute generator.
* linux-low.c (nat/linux-maps.h, search.h, rsp-low.h): Include.
(ElfXX_Ehdr, ElfXX_Phdr, ElfXX_Nhdr): New.
(ELFXX_FLD, ELFXX_SIZEOF, ELFXX_ROUNDUP, BUILD_ID_INVALID): New.
(find_phdr): New.
(get_dynamic): Use find_pdhr to traverse program headers.
(struct mapping_entry, mapping_entry_s, free_mapping_entry_vec)
(compare_mapping_entry_range, struct find_memory_region_callback_data)
(read_build_id, find_memory_region_callback, lrfind_mapping_entry)
(get_hex_build_id): New.
(linux_qxfer_libraries_svr4): Add optional build-id attribute
to reply XML document.

gdb/ChangeLog
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/features/library-list-svr4.dtd
gdb/gdbserver/ChangeLog
gdb/gdbserver/linux-low.c

index 2f2ec28a619361f95fad86df8fcdfe2c69480d9f..b58b138c98b7b6c7950fce39975e988b25073804 100644 (file)
@@ -1,3 +1,10 @@
+2015-07-15  Aleksandar Ristovski  <aristovski@qnx.com
+           Jan Kratochvil  <jan.kratochvil@redhat.com>
+
+       gdbserver build-id attribute generator.
+       * features/library-list-svr4.dtd (library-list-svr4): New
+       'build-id' attribute.
+
 2015-07-15  Aleksandar Ristovski  <aristovski@qnx.com
            Jan Kratochvil  <jan.kratochvil@redhat.com>
 
index cf3748936e8951eb49f89f3b212ae04272f621de..5b90cb4364d30cbdba03c904954eb36580df9f47 100644 (file)
@@ -1,3 +1,10 @@
+2015-07-15  Aleksandar Ristovski  <aristovski@qnx.com
+           Jan Kratochvil  <jan.kratochvil@redhat.com>
+
+       gdbserver build-id attribute generator.
+       * gdb.texinfo (Library List Format for SVR4 Targets): Add
+       'build-id' in description, example, new attribute in dtd.
+
 2015-07-08  Andrew Burgess  <andrew.burgess@embecosm.com>
 
        * gdb.texinfo (Selection): Update documentation for 'frame'
index 9e2ecd150353bc07feedabe47d25f2b98a92057d..932c38d318a69ae8b540731acfc24806f667a57a 100644 (file)
@@ -39513,6 +39513,8 @@ 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
+@item
+@code{build-id}, hex encoded @code{NT_GNU_BUILD_ID} note, if it exists.
 @end itemize
 
 Additionally the single @code{main-lm} attribute specifies address of
@@ -39530,7 +39532,7 @@ looks like this:
   <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"/>
+           l_ld="0x152350" build-id="9afccf7cc41e6293476223fe72480854"/>
 </library-list-svr>
 @end smallexample
 
@@ -39539,13 +39541,14 @@ 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>
+<!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>
+<!ATTLIST library            name     CDATA   #REQUIRED>
+<!ATTLIST library            lm       CDATA   #REQUIRED>
+<!ATTLIST library            l_addr   CDATA   #REQUIRED>
+<!ATTLIST library            l_ld     CDATA   #REQUIRED>
+<!ATTLIST library            build-id CDATA   #IMPLIED>
 @end smallexample
 
 @node Memory Map Format
index 082945d1bbb169d1df4b923bafb8c2f4eabfc678..5741f031bb5ffba039f3b00e6f2219512c9df5ba 100644 (file)
@@ -6,11 +6,12 @@
 
 <!-- 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>
+<!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>
+<!ATTLIST library            name     CDATA   #REQUIRED>
+<!ATTLIST library            lm       CDATA   #REQUIRED>
+<!ATTLIST library            l_addr   CDATA   #REQUIRED>
+<!ATTLIST library            l_ld     CDATA   #REQUIRED>
+<!ATTLIST library            build-id CDATA   #IMPLIED>
index f4896ebe514e9aeb5ef23bf67660a28c61a30ad6..af8c8d5345f5f7e5372700734994068a83be7a45 100644 (file)
@@ -1,3 +1,19 @@
+2015-07-15  Aleksandar Ristovski  <aristovski@qnx.com
+           Jan Kratochvil  <jan.kratochvil@redhat.com>
+
+       gdbserver build-id attribute generator.
+       * linux-low.c (nat/linux-maps.h, search.h, rsp-low.h): Include.
+       (ElfXX_Ehdr, ElfXX_Phdr, ElfXX_Nhdr): New.
+       (ELFXX_FLD, ELFXX_SIZEOF, ELFXX_ROUNDUP, BUILD_ID_INVALID): New.
+       (find_phdr): New.
+       (get_dynamic): Use find_pdhr to traverse program headers.
+       (struct mapping_entry, mapping_entry_s, free_mapping_entry_vec)
+       (compare_mapping_entry_range, struct find_memory_region_callback_data)
+       (read_build_id, find_memory_region_callback, lrfind_mapping_entry)
+       (get_hex_build_id): New.
+       (linux_qxfer_libraries_svr4): Add optional build-id attribute
+       to reply XML document.
+
 2015-07-15  Aleksandar Ristovski  <aristovski@qnx.com
            Jan Kratochvil  <jan.kratochvil@redhat.com>
 
index 2dafb033bcec62b38c930f852788895932c95748..eedd652000ff2765e45a66e18af5f24276e9b7a1 100644 (file)
@@ -22,6 +22,7 @@
 #include "agent.h"
 #include "tdesc.h"
 #include "rsp-low.h"
+#include "nat/linux-maps.h"
 
 #include "nat/linux-nat.h"
 #include "nat/linux-waitpid.h"
 #include <sys/stat.h>
 #include <sys/vfs.h>
 #include <sys/uio.h>
+#include <search.h>
 #include "filestuff.h"
 #include "tracepoint.h"
 #include "hostio.h"
+#include "rsp-low.h"
 #ifndef ELFMAG0
 /* Don't include <linux/elf.h> here.  If it got included by gdb_proc_service.h
    then ELFMAG0 will have been defined.  If it didn't get included by
@@ -182,6 +185,31 @@ lwp_stop_reason (struct lwp_info *lwp)
   return lwp->stop_reason;
 }
 
+typedef union ElfXX_Ehdr
+{
+  Elf32_Ehdr _32;
+  Elf64_Ehdr _64;
+} ElfXX_Ehdr;
+
+typedef union ElfXX_Phdr
+{
+  Elf32_Phdr _32;
+  Elf64_Phdr _64;
+} ElfXX_Phdr;
+
+typedef union ElfXX_Nhdr
+{
+  Elf32_Nhdr _32;
+  Elf64_Nhdr _64;
+} ElfXX_Nhdr;
+
+#define ELFXX_FLD(elf64, hdr, fld) ((elf64) ? (hdr)._64.fld : (hdr)._32.fld)
+#define ELFXX_SIZEOF(elf64, hdr) ((elf64) ? sizeof ((hdr)._64) \
+                                         : sizeof ((hdr)._32))
+/* Round up to next 4 byte boundary.  */
+#define ELFXX_ROUNDUP_4(elf64, what) (((what) + 3) & ~(ULONGEST) 3)
+#define BUILD_ID_INVALID "?"
+
 /* A list of all unknown processes which receive stop signals.  Some
    other process will presumably claim each of these as forked
    children momentarily.  */
@@ -6010,15 +6038,38 @@ get_phdr_phnum_from_proc_auxv (const int pid, const int is_elf64,
   return 0;
 }
 
+/* Linearly traverse pheaders and look for P_TYPE pheader.  */
+
+static const void *
+find_phdr (const int is_elf64, const void *const phdr_begin,
+          const void *const phdr_end, const ULONGEST p_type)
+{
+#define PHDR_NEXT(hdrp) ((const void *) ((const gdb_byte *) (hdrp) + \
+                        ELFXX_SIZEOF (is_elf64, *hdrp)))
+
+  const ElfXX_Phdr *phdr = phdr_begin;
+
+  while (PHDR_NEXT (phdr) <= phdr_end)
+    {
+      if (ELFXX_FLD (is_elf64, *phdr, p_type) == p_type)
+       return phdr;
+      phdr = PHDR_NEXT (phdr);
+    }
+
+  return NULL;
+#undef PHDR_NEXT
+}
+
 /* 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;
+  int num_phdr;
   unsigned char *phdr_buf;
-  const int phdr_size = is_elf64 ? sizeof (Elf64_Phdr) : sizeof (Elf32_Phdr);
+  const ElfXX_Phdr *phdr;
+  const int phdr_size = ELFXX_SIZEOF (is_elf64, *phdr);
 
   if (get_phdr_phnum_from_proc_auxv (pid, is_elf64, &phdr_memaddr, &num_phdr))
     return 0;
@@ -6032,22 +6083,10 @@ get_dynamic (const int pid, const int is_elf64)
   /* 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;
-      }
-
+  phdr = find_phdr (is_elf64, phdr_buf, phdr_buf + num_phdr * phdr_size,
+                   PT_PHDR);
+  if (phdr != NULL)
+    relocation = phdr_memaddr - ELFXX_FLD (is_elf64, *phdr, p_vaddr);
   if (relocation == -1)
     {
       /* PT_PHDR is optional, but necessary for PIE in general.  Fortunately
@@ -6063,23 +6102,11 @@ get_dynamic (const int pid, const int is_elf64)
       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);
+  phdr = find_phdr (is_elf64, phdr_buf, phdr_buf + num_phdr * phdr_size,
+                   PT_DYNAMIC);
 
-         if (p->p_type == PT_DYNAMIC)
-           return p->p_vaddr + relocation;
-       }
-    }
+  if (phdr != NULL)
+    return ELFXX_FLD (is_elf64, *phdr, p_vaddr) + relocation;
 
   return 0;
 }
@@ -6219,6 +6246,278 @@ struct link_map_offsets
     int l_prev_offset;
   };
 
+
+/* Structure for holding a mapping.  Only mapping
+   containing l_ld can have hex_build_id set.  */
+
+struct mapping_entry
+{
+  /* Fields are populated from linux_find_memory_region parameters.  */
+
+  ULONGEST vaddr;
+  ULONGEST size;
+  ULONGEST offset;
+  ULONGEST inode;
+
+  /* Hex encoded string allocated using xmalloc, and
+     needs to be freed.  It can be NULL.  */
+
+  char *hex_build_id;
+};
+
+typedef struct mapping_entry mapping_entry_s;
+
+DEF_VEC_O(mapping_entry_s);
+
+/* Free vector of mapping_entry_s objects.  */
+
+static void
+free_mapping_entry_vec (VEC (mapping_entry_s) *lst)
+{
+  int ix;
+  mapping_entry_s *p;
+
+  for (ix = 0; VEC_iterate (mapping_entry_s, lst, ix, p); ++ix)
+    xfree (p->hex_build_id);
+
+  VEC_free (mapping_entry_s, lst);
+}
+
+/* Used for finding a mapping containing the given
+   l_ld passed in K.  */
+
+static int
+compare_mapping_entry_range (const void *const k, const void *const b)
+{
+  const ULONGEST key = *(const CORE_ADDR *) k;
+  const mapping_entry_s *const p = b;
+
+  if (key < p->vaddr)
+    return -1;
+
+  if (key < p->vaddr + p->size)
+    return 0;
+
+  return 1;
+}
+
+struct find_memory_region_callback_data
+{
+  unsigned is_elf64;
+
+  /* Return.  Must be freed with free_mapping_entry_vec.  */
+  VEC (mapping_entry_s) *list;
+};
+
+/* Read build-id from PT_NOTE.
+   Argument LOAD_ADDR represents run time virtual address corresponding to
+   the beginning of the first loadable segment.  L_ADDR is displacement
+   as supplied by the dynamic linker.  */
+
+static void
+read_build_id (struct find_memory_region_callback_data *const p,
+              mapping_entry_s *const bil, const CORE_ADDR load_addr,
+              const CORE_ADDR l_addr)
+{
+  const int is_elf64 = p->is_elf64;
+  ElfXX_Ehdr ehdr;
+
+  if (linux_read_memory (load_addr, (unsigned char *) &ehdr,
+                        ELFXX_SIZEOF (is_elf64, ehdr)) == 0
+      && ELFXX_FLD (is_elf64, ehdr, e_ident[EI_MAG0]) == ELFMAG0
+      && ELFXX_FLD (is_elf64, ehdr, e_ident[EI_MAG1]) == ELFMAG1
+      && ELFXX_FLD (is_elf64, ehdr, e_ident[EI_MAG2]) == ELFMAG2
+      && ELFXX_FLD (is_elf64, ehdr, e_ident[EI_MAG3]) == ELFMAG3)
+    {
+      const ElfXX_Phdr *phdr;
+      void *phdr_buf;
+      const unsigned e_phentsize = ELFXX_FLD (is_elf64, ehdr, e_phentsize);
+
+      if (ELFXX_FLD (is_elf64, ehdr, e_phnum) >= 100
+         || e_phentsize != ELFXX_SIZEOF (is_elf64, *phdr))
+       {
+         /* Basic sanity check failed.  */
+         warning (_("Could not identify program header at %s."),
+                  paddress (load_addr));
+         return;
+       }
+
+      phdr_buf = alloca (ELFXX_FLD (is_elf64, ehdr, e_phnum) * e_phentsize);
+
+      if (linux_read_memory (load_addr + ELFXX_FLD (is_elf64, ehdr, e_phoff),
+                            phdr_buf,
+                            ELFXX_FLD (is_elf64, ehdr, e_phnum) * e_phentsize)
+         != 0)
+       {
+         warning (_("Could not read program header at %s."),
+                  paddress (load_addr));
+         return;
+       }
+
+      phdr = phdr_buf;
+
+      for (;;)
+       {
+         gdb_byte *pt_note;
+         const gdb_byte *pt_end;
+         const ElfXX_Nhdr *nhdr;
+         CORE_ADDR note_addr;
+
+         phdr = find_phdr (p->is_elf64, phdr, (gdb_byte *) phdr_buf
+                           + ELFXX_FLD (is_elf64, ehdr, e_phnum) * e_phentsize,
+                           PT_NOTE);
+         if (phdr == NULL)
+           break;
+         pt_note = xmalloc (ELFXX_FLD (is_elf64, *phdr, p_memsz));
+         note_addr = ELFXX_FLD (is_elf64, *phdr, p_vaddr) + l_addr;
+         if (linux_read_memory (note_addr, pt_note,
+                                ELFXX_FLD (is_elf64, *phdr, p_memsz)) != 0)
+           {
+             xfree (pt_note);
+             warning (_("Could not read note at address 0x%s"),
+                      paddress (note_addr));
+             break;
+           }
+
+         pt_end = pt_note + ELFXX_FLD (is_elf64, *phdr, p_memsz);
+         nhdr = (void *) pt_note;
+         while ((const gdb_byte *) nhdr < pt_end)
+           {
+             const size_t namesz
+               = ELFXX_ROUNDUP_4 (is_elf64, ELFXX_FLD (is_elf64, *nhdr,
+                                                       n_namesz));
+             const size_t descsz
+               = ELFXX_ROUNDUP_4 (is_elf64, ELFXX_FLD (is_elf64, *nhdr,
+                                                       n_descsz));
+             const size_t note_sz = (ELFXX_SIZEOF (is_elf64, *nhdr) + namesz
+                                     + descsz);
+
+             if (((const gdb_byte *) nhdr + note_sz) > pt_end || note_sz == 0
+                 || descsz == 0)
+               {
+                 warning (_("Malformed PT_NOTE at address 0x%s\n"),
+                          paddress (note_addr + (gdb_byte *) nhdr - pt_note));
+                 break;
+               }
+             if (ELFXX_FLD (is_elf64, *nhdr, n_type) == NT_GNU_BUILD_ID
+                 && ELFXX_FLD (is_elf64, *nhdr, n_namesz) == 4)
+               {
+                 const char gnu[4] = "GNU\0";
+                 const char *const pname
+                   = (char *) nhdr + ELFXX_SIZEOF (is_elf64, *nhdr);
+
+                 if (memcmp (pname, gnu, 4) == 0)
+                   {
+                     const size_t n_descsz = ELFXX_FLD (is_elf64, *nhdr,
+                                                        n_descsz);
+
+                     bil->hex_build_id = xmalloc (n_descsz * 2 + 1);
+                     bin2hex ((const gdb_byte *) pname + namesz,
+                              bil->hex_build_id, n_descsz);
+                     xfree (pt_note);
+                     return;
+                   }
+               }
+             nhdr = (void *) ((gdb_byte *) nhdr + note_sz);
+           }
+         xfree (pt_note);
+       }
+    }
+}
+
+static linux_find_memory_region_ftype find_memory_region_callback;
+
+/* Add mapping_entry.  See linux_find_memory_ftype for the parameters
+   description.  */
+
+static int
+find_memory_region_callback (ULONGEST vaddr, ULONGEST size, ULONGEST offset,
+                            ULONGEST inode, int read, int write, int exec,
+                            int modified, const char *filename, void *data)
+{
+  if (inode != 0)
+    {
+      struct find_memory_region_callback_data *const p = data;
+      mapping_entry_s bil;
+
+      bil.vaddr = vaddr;
+      bil.size = size;
+      bil.offset = offset;
+      bil.inode = inode;
+      bil.hex_build_id = NULL;
+
+      VEC_safe_push (mapping_entry_s, p->list, &bil);
+    }
+
+  /* Continue the traversal.  */
+  return 0;
+}
+
+/* Linear reverse find starting from RBEGIN towards REND looking for
+   the lowest vaddr mapping of the same inode and zero offset.  */
+
+static mapping_entry_s *
+lrfind_mapping_entry (mapping_entry_s *const rbegin,
+                     const mapping_entry_s *const rend)
+{
+  mapping_entry_s *p;
+
+  for (p = rbegin - 1; p >= rend; --p)
+    if (p->offset == 0 && p->inode == rbegin->inode)
+      return p;
+
+  return NULL;
+}
+
+/* Get build-id for the given L_LD, where L_LD corresponds to
+   link_map.l_ld as specified by the dynamic linker.
+   DATA must point to already filled list of mapping_entry elements.
+
+   If build-id had not been read, read it and cache in corresponding
+   list element.
+
+   Return build_id as stored in the list element corresponding
+   to L_LD.
+
+   NULL may be returned if build-id could not be fetched.
+
+   Returned string must not be freed explicitly.  */
+
+static const char *
+get_hex_build_id (const CORE_ADDR l_addr, const CORE_ADDR l_ld,
+                 struct find_memory_region_callback_data *const data)
+{
+  mapping_entry_s *bil;
+
+  bil = bsearch (&l_ld, VEC_address (mapping_entry_s, data->list),
+                VEC_length (mapping_entry_s, data->list),
+                sizeof (mapping_entry_s), compare_mapping_entry_range);
+
+  if (bil == NULL)
+    return NULL;
+
+  if (bil->hex_build_id == NULL)
+    {
+      mapping_entry_s *bil_min;
+
+      bil_min = lrfind_mapping_entry (bil, VEC_address (mapping_entry_s,
+                                                       data->list));
+      if (bil_min != NULL)
+       read_build_id (data, bil, bil_min->vaddr, l_addr);
+      else
+       {
+         /* Do not try to find hex_build_id again.  */
+         bil->hex_build_id = xstrdup (BUILD_ID_INVALID);
+         warning (_("Could not determine load address; mapping entry with "
+                    "offset 0 corresponding to l_ld = 0x%s could not be "
+                    "found; build-id can not be used."),
+                  paddress (l_ld));
+       }
+    }
+
+  return bil->hex_build_id;
+}
+
 /* Construct qXfer:libraries-svr4:read reply.  */
 
 static int
@@ -6231,6 +6530,15 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf,
   struct process_info_private *const priv = current_process ()->priv;
   char filename[PATH_MAX];
   int pid, is_elf64;
+  struct find_memory_region_callback_data data;
+  
+  /* COREFILTER_ANON_PRIVATE and COREFILTER_ANON_SHARED do not have an
+     associated file so it is not expected it could have an ELF header.  */ 
+  const enum filterflags filterflags = (COREFILTER_MAPPED_PRIVATE
+                                       | COREFILTER_MAPPED_SHARED
+                                       | COREFILTER_ELF_HEADERS
+                                       | COREFILTER_HUGETLB_PRIVATE
+                                       | COREFILTER_HUGETLB_SHARED);
 
   static const struct link_map_offsets lmo_32bit_offsets =
     {
@@ -6273,6 +6581,14 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf,
   lmo = is_elf64 ? &lmo_64bit_offsets : &lmo_32bit_offsets;
   ptr_size = is_elf64 ? 8 : 4;
 
+  data.is_elf64 = is_elf64;
+  data.list = NULL;
+  VEC_reserve (mapping_entry_s, data.list, 16);
+  if (linux_find_memory_regions_full (pid, filterflags,
+                                     find_memory_region_callback, &data)
+      < 0)
+    warning (_("Finding memory regions failed"));
+
   while (annex[0] != '\0')
     {
       const char *sep;
@@ -6379,6 +6695,7 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf,
              /* 6x the size for xml_escape_text below.  */
              size_t len = 6 * strlen ((char *) libname);
              char *name;
+             const char *hex_enc_build_id = NULL;
 
              if (!header_done)
                {
@@ -6387,7 +6704,11 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf,
                  header_done = 1;
                }
 
-             while (allocated < p - document + len + 200)
+             hex_enc_build_id = get_hex_build_id (l_addr, l_ld, &data);
+
+             while (allocated < (p - document + len + 200
+                                 + (hex_enc_build_id != NULL
+                                    ? strlen (hex_enc_build_id) : 0)))
                {
                  /* Expand to guarantee sufficient storage.  */
                  uintptr_t document_len = p - document;
@@ -6399,9 +6720,13 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf,
 
              name = xml_escape_text ((char *) libname);
              p += sprintf (p, "<library name=\"%s\" lm=\"0x%lx\" "
-                           "l_addr=\"0x%lx\" l_ld=\"0x%lx\"/>",
+                           "l_addr=\"0x%lx\" l_ld=\"0x%lx\"",
                            name, (unsigned long) lm_addr,
                            (unsigned long) l_addr, (unsigned long) l_ld);
+             if (hex_enc_build_id != NULL
+                 && strcmp (hex_enc_build_id, BUILD_ID_INVALID) != 0)
+               p += sprintf (p, " build-id=\"%s\"", hex_enc_build_id);
+             p += sprintf (p, "/>");
              free (name);
            }
        }
@@ -6428,6 +6753,7 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf,
 
   memcpy (readbuf, document + offset, len);
   xfree (document);
+  free_mapping_entry_vec (data.list);
 
   return len;
 }