Blame SOURCES/gdb-rhbz1842691-corefile-mem-access-8of15.patch

0c1cd1
From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
0c1cd1
From: Keith Seitz <keiths@redhat.com>
0c1cd1
Date: Mon, 27 Jul 2020 18:51:07 -0400
0c1cd1
Subject: gdb-rhbz1842691-corefile-mem-access-8of15.patch
0c1cd1
0c1cd1
;; Use NT_FILE note section for reading core target memory
0c1cd1
;; Kevin Buettner, RH BZ 1842961
0c1cd1
0c1cd1
   Author: Kevin Buettner <kevinb@redhat.com>
0c1cd1
   Date:   Thu Jun 11 19:20:03 2020 -0700
0c1cd1
0c1cd1
    Use NT_FILE note section for reading core target memory
0c1cd1
0c1cd1
    In his reviews of my v1 and v2 corefile related patches, Pedro
0c1cd1
    identified two cases which weren't handled by those patches.
0c1cd1
0c1cd1
    In https://sourceware.org/pipermail/gdb-patches/2020-May/168826.html,
0c1cd1
    Pedro showed that debugging a core file in which mmap() is used to
0c1cd1
    create a new mapping over an existing file-backed mapping will
0c1cd1
    produce incorrect results.  I.e, for his example, GDB would
0c1cd1
    show:
0c1cd1
0c1cd1
    (gdb) disassemble main
0c1cd1
    Dump of assembler code for function main:
0c1cd1
       0x00000000004004e6 <+0>:	push   %rbp
0c1cd1
       0x00000000004004e7 <+1>:	mov    %rsp,%rbp
0c1cd1
    => 0x00000000004004ea <+4>:	callq  0x4003f0 <abort@plt>
0c1cd1
    End of assembler dump.
0c1cd1
0c1cd1
    This sort of looks like it might be correct, but is not due to the
0c1cd1
    fact that mmap(...MAP_FIXED...) was used to create a mapping (of all
0c1cd1
    zeros) on top of the .text section.  So, the correct result should be:
0c1cd1
0c1cd1
    (gdb) disassemble main
0c1cd1
    Dump of assembler code for function main:
0c1cd1
       0x00000000004004e6 <+0>:	add    %al,(%rax)
0c1cd1
       0x00000000004004e8 <+2>:	add    %al,(%rax)
0c1cd1
    => 0x00000000004004ea <+4>:	add    %al,(%rax)
0c1cd1
       0x00000000004004ec <+6>:	add    %al,(%rax)
0c1cd1
       0x00000000004004ee <+8>:	add    %al,(%rax)
0c1cd1
    End of assembler dump.
0c1cd1
0c1cd1
    The other case that Pedro found involved an attempted examination of a
0c1cd1
    particular section in the test case from gdb.base/corefile.exp.  On
0c1cd1
    Fedora 27 or 28, the following behavior may be observed:
0c1cd1
0c1cd1
    (gdb) info proc mappings
0c1cd1
    Mapped address spaces:
0c1cd1
0c1cd1
              Start Addr           End Addr       Size     Offset objfile
0c1cd1
    ...
0c1cd1
          0x7ffff7839000     0x7ffff7a38000   0x1ff000   0x1b5000 /usr/lib64/libc-2.27.so
0c1cd1
    ...
0c1cd1
    (gdb) x/4x 0x7ffff7839000
0c1cd1
    0x7ffff7839000:	Cannot access memory at address 0x7ffff7839000
0c1cd1
0c1cd1
    FYI, this section appears to be unrelocated vtable data.  See
0c1cd1
    https://sourceware.org/pipermail/gdb-patches/2020-May/168331.html for
0c1cd1
    a detailed analysis.
0c1cd1
0c1cd1
    The important thing here is that GDB should be able to access this
0c1cd1
    address since it should be backed by the shared library.  I.e. it
0c1cd1
    should do this:
0c1cd1
0c1cd1
    (gdb) x/4x 0x7ffff7839000
0c1cd1
    0x7ffff7839000:	0x0007ddf0	0x00000000	0x0007dba0	0x00000000
0c1cd1
0c1cd1
    Both of these cases are fixed with this commit.
0c1cd1
0c1cd1
    In a nutshell, this commit opens a "binary" target BFD for each of the
0c1cd1
    files that are mentioned in an NT_FILE / .note.linuxcore.file note
0c1cd1
    section.  It then uses these mappings instead of the file stratum
0c1cd1
    mappings that GDB has used in the past.
0c1cd1
0c1cd1
    If this note section doesn't exist or is mangled for some reason, then
0c1cd1
    GDB will use the file stratum as before.  Should this happen, then
0c1cd1
    we can expect both of the above problems to again be present.
0c1cd1
0c1cd1
    See the code comments in the commit for other details.
0c1cd1
0c1cd1
    gdb/ChangeLog:
0c1cd1
0c1cd1
    	* corelow.c (solist.h, unordered_map): Include.
0c1cd1
    	(class core_target): Add field m_core_file_mappings and
0c1cd1
    	method build_file_mappings.
0c1cd1
    	(core_target::core_target): Call build_file_mappings.
0c1cd1
    	(core_target::~core_target): Free memory associated with
0c1cd1
    	m_core_file_mappings.
0c1cd1
    	(core_target::build_file_mappings): New method.
0c1cd1
    	(core_target::xfer_partial): Use m_core_file_mappings
0c1cd1
    	for memory transfers.
0c1cd1
    	* linux-tdep.c (linux_read_core_file_mappings): New
0c1cd1
    	function.
0c1cd1
    	(linux_core_info_proc_mappings): Rewrite to use
0c1cd1
    	linux_read_core_file_mappings.
0c1cd1
    	(linux_init_abi): Register linux_read_core_file_mappings.
0c1cd1
0c1cd1
diff --git a/gdb/corelow.c b/gdb/corelow.c
0c1cd1
--- a/gdb/corelow.c
0c1cd1
+++ b/gdb/corelow.c
0c1cd1
@@ -39,6 +39,7 @@
0c1cd1
 #include "exec.h"
0c1cd1
 #include "readline/readline.h"
0c1cd1
 #include "solib.h"
0c1cd1
+#include "solist.h"
0c1cd1
 #include "filenames.h"
0c1cd1
 #include "progspace.h"
0c1cd1
 #include "objfiles.h"
0c1cd1
@@ -49,6 +50,8 @@
0c1cd1
 #include "elf/common.h"
0c1cd1
 #include "gdbcmd.h"
0c1cd1
 #include "build-id.h"
0c1cd1
+#include "common/pathstuff.h"
0c1cd1
+#include <unordered_map>
0c1cd1
 
0c1cd1
 #ifndef O_LARGEFILE
0c1cd1
 #define O_LARGEFILE 0
0c1cd1
@@ -129,6 +132,13 @@ private: /* per-core data */
0c1cd1
      core file currently open on core_bfd.  */
0c1cd1
   core_fns *m_core_vec = NULL;
0c1cd1
 
0c1cd1
+  /* File-backed address space mappings: some core files include
0c1cd1
+     information about memory mapped files.  */
0c1cd1
+  target_section_table m_core_file_mappings {};
0c1cd1
+
0c1cd1
+  /* Build m_core_file_mappings.  Called from the constructor.  */
0c1cd1
+  void build_file_mappings ();
0c1cd1
+
0c1cd1
   /* FIXME: kettenis/20031023: Eventually this field should
0c1cd1
      disappear.  */
0c1cd1
   struct gdbarch *m_core_gdbarch = NULL;
0c1cd1
@@ -149,11 +159,120 @@ core_target::core_target ()
0c1cd1
 			   &m_core_section_table.sections_end))
0c1cd1
     error (_("\"%s\": Can't find sections: %s"),
0c1cd1
 	   bfd_get_filename (core_bfd), bfd_errmsg (bfd_get_error ()));
0c1cd1
+
0c1cd1
+  build_file_mappings ();
0c1cd1
 }
0c1cd1
 
0c1cd1
 core_target::~core_target ()
0c1cd1
 {
0c1cd1
   xfree (m_core_section_table.sections);
0c1cd1
+  xfree (m_core_file_mappings.sections);
0c1cd1
+}
0c1cd1
+
0c1cd1
+/* Construct the target_section_table for file-backed mappings if
0c1cd1
+   they exist.
0c1cd1
+
0c1cd1
+   For each unique path in the note, we'll open a BFD with a bfd
0c1cd1
+   target of "binary".  This is an unstructured bfd target upon which
0c1cd1
+   we'll impose a structure from the mappings in the architecture-specific
0c1cd1
+   mappings note.  A BFD section is allocated and initialized for each
0c1cd1
+   file-backed mapping.
0c1cd1
+
0c1cd1
+   We take care to not share already open bfds with other parts of
0c1cd1
+   GDB; in particular, we don't want to add new sections to existing
0c1cd1
+   BFDs.  We do, however, ensure that the BFDs that we allocate here
0c1cd1
+   will go away (be deallocated) when the core target is detached.  */
0c1cd1
+
0c1cd1
+void
0c1cd1
+core_target::build_file_mappings ()
0c1cd1
+{
0c1cd1
+  std::unordered_map<std::string, struct bfd *> bfd_map;
0c1cd1
+
0c1cd1
+  /* See linux_read_core_file_mappings() in linux-tdep.c for an example
0c1cd1
+     read_core_file_mappings method.  */
0c1cd1
+  gdbarch_read_core_file_mappings (m_core_gdbarch, core_bfd,
0c1cd1
+
0c1cd1
+    /* After determining the number of mappings, read_core_file_mappings
0c1cd1
+       will invoke this lambda which allocates target_section storage for
0c1cd1
+       the mappings.  */
0c1cd1
+    [&] (ULONGEST count)
0c1cd1
+      {
0c1cd1
+	m_core_file_mappings.sections = XNEWVEC (struct target_section, count);
0c1cd1
+	m_core_file_mappings.sections_end = m_core_file_mappings.sections;
0c1cd1
+      },
0c1cd1
+
0c1cd1
+    /* read_core_file_mappings will invoke this lambda for each mapping
0c1cd1
+       that it finds.  */
0c1cd1
+    [&] (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs,
0c1cd1
+         const char *filename, const void *other)
0c1cd1
+      {
0c1cd1
+	/* Architecture-specific read_core_mapping methods are expected to
0c1cd1
+	   weed out non-file-backed mappings.  */
0c1cd1
+	gdb_assert (filename != nullptr);
0c1cd1
+
0c1cd1
+	struct bfd *bfd = bfd_map[filename];
0c1cd1
+	if (bfd == nullptr)
0c1cd1
+	  {
0c1cd1
+	    /* Use exec_file_find() to do sysroot expansion.  It'll
0c1cd1
+	       also strip the potential sysroot "target:" prefix.  If
0c1cd1
+	       there is no sysroot, an equivalent (possibly more
0c1cd1
+	       canonical) pathname will be provided.  */
0c1cd1
+	    gdb::unique_xmalloc_ptr<char> expanded_fname
0c1cd1
+	      = exec_file_find (filename, NULL);
0c1cd1
+	    if (expanded_fname == nullptr)
0c1cd1
+	      {
0c1cd1
+		warning (_("Can't open file %s during file-backed mapping "
0c1cd1
+			   "note processing"),
0c1cd1
+			 expanded_fname.get ());
0c1cd1
+		return;
0c1cd1
+	      }
0c1cd1
+
0c1cd1
+	    bfd = bfd_map[filename] = bfd_openr (expanded_fname.get (),
0c1cd1
+	                                         "binary");
0c1cd1
+
0c1cd1
+	    if (bfd == nullptr || !bfd_check_format (bfd, bfd_object))
0c1cd1
+	      {
0c1cd1
+		/* If we get here, there's a good chance that it's due to
0c1cd1
+		   an internal error.  We issue a warning instead of an
0c1cd1
+		   internal error because of the possibility that the
0c1cd1
+		   file was removed in between checking for its
0c1cd1
+		   existence during the expansion in exec_file_find()
0c1cd1
+		   and the calls to bfd_openr() / bfd_check_format(). 
0c1cd1
+		   Output both the path from the core file note along
0c1cd1
+		   with its expansion to make debugging this problem
0c1cd1
+		   easier.  */
0c1cd1
+		warning (_("Can't open file %s which was expanded to %s "
0c1cd1
+			   "during file-backed mapping note processing"),
0c1cd1
+			 filename, expanded_fname.get ());
0c1cd1
+		if (bfd != nullptr)
0c1cd1
+		  bfd_close (bfd);
0c1cd1
+		return;
0c1cd1
+	      }
0c1cd1
+	    /* Ensure that the bfd will be closed when core_bfd is closed. 
0c1cd1
+	       This can be checked before/after a core file detach via
0c1cd1
+	       "maint info bfds".  */
0c1cd1
+	    gdb_bfd_record_inclusion (core_bfd, bfd);
0c1cd1
+	  }
0c1cd1
+
0c1cd1
+	/* Make new BFD section.  All sections have the same name,
0c1cd1
+	   which is permitted by bfd_make_section_anyway().  */
0c1cd1
+	asection *sec = bfd_make_section_anyway (bfd, "load");
0c1cd1
+	if (sec == nullptr)
0c1cd1
+	  error (_("Can't make section"));
0c1cd1
+	sec->filepos = file_ofs;
0c1cd1
+	bfd_set_section_flags (sec->owner, sec, SEC_READONLY | SEC_HAS_CONTENTS);
0c1cd1
+	bfd_set_section_size (sec->owner, sec, end - start);
0c1cd1
+	bfd_set_section_vma (sec->owner, sec, start);
0c1cd1
+	sec->lma = start;
0c1cd1
+	bfd_set_section_alignment (sec->owner, sec, 2);
0c1cd1
+
0c1cd1
+	/* Set target_section fields.  */
0c1cd1
+	struct target_section *ts = m_core_file_mappings.sections_end++;
0c1cd1
+	ts->addr = start;
0c1cd1
+	ts->endaddr = end;
0c1cd1
+	ts->owner = nullptr;
0c1cd1
+	ts->the_bfd_section = sec;
0c1cd1
+      });
0c1cd1
 }
0c1cd1
 
0c1cd1
 /* List of all available core_fns.  On gdb startup, each core file
0c1cd1
@@ -835,10 +954,21 @@ core_target::xfer_partial (enum target_object object, const char *annex,
0c1cd1
 	if (xfer_status == TARGET_XFER_OK)
0c1cd1
 	  return TARGET_XFER_OK;
0c1cd1
 
0c1cd1
-	/* Now check the stratum beneath us; this should be file_stratum.  */
0c1cd1
-	xfer_status = this->beneath ()->xfer_partial (object, annex, readbuf,
0c1cd1
-						      writebuf, offset, len,
0c1cd1
-						      xfered_len);
0c1cd1
+	/* Check file backed mappings.  If they're available, use
0c1cd1
+	   core file provided mappings (e.g. from .note.linuxcore.file
0c1cd1
+	   or the like) as this should provide a more accurate
0c1cd1
+	   result.  If not, check the stratum beneath us, which should
0c1cd1
+	   be the file stratum.  */
0c1cd1
+	if (m_core_file_mappings.sections != nullptr)
0c1cd1
+	  xfer_status = section_table_xfer_memory_partial
0c1cd1
+			  (readbuf, writebuf,
0c1cd1
+			   offset, len, xfered_len,
0c1cd1
+			   m_core_file_mappings.sections,
0c1cd1
+			   m_core_file_mappings.sections_end);
0c1cd1
+	else
0c1cd1
+	  xfer_status = this->beneath ()->xfer_partial (object, annex, readbuf,
0c1cd1
+							writebuf, offset, len,
0c1cd1
+							xfered_len);
0c1cd1
 	if (xfer_status == TARGET_XFER_OK)
0c1cd1
 	  return TARGET_XFER_OK;
0c1cd1
 
0c1cd1
diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
0c1cd1
--- a/gdb/linux-tdep.c
0c1cd1
+++ b/gdb/linux-tdep.c
0c1cd1
@@ -1002,106 +1002,174 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args,
0c1cd1
     }
0c1cd1
 }
0c1cd1
 
0c1cd1
-/* Implement "info proc mappings" for a corefile.  */
0c1cd1
+/* Implementation of `gdbarch_read_core_file_mappings', as defined in
0c1cd1
+   gdbarch.h.
0c1cd1
+   
0c1cd1
+   This function reads the NT_FILE note (which BFD turns into the
0c1cd1
+   section ".note.linuxcore.file").  The format of this note / section
0c1cd1
+   is described as follows in the Linux kernel sources in
0c1cd1
+   fs/binfmt_elf.c:
0c1cd1
+   
0c1cd1
+      long count     -- how many files are mapped
0c1cd1
+      long page_size -- units for file_ofs
0c1cd1
+      array of [COUNT] elements of
0c1cd1
+        long start
0c1cd1
+        long end
0c1cd1
+        long file_ofs
0c1cd1
+      followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL...
0c1cd1
+      
0c1cd1
+   CBFD is the BFD of the core file.
0c1cd1
+
0c1cd1
+   PRE_LOOP_CB is the callback function to invoke prior to starting
0c1cd1
+   the loop which processes individual entries.  This callback will
0c1cd1
+   only be executed after the note has been examined in enough
0c1cd1
+   detail to verify that it's not malformed in some way.
0c1cd1
+   
0c1cd1
+   LOOP_CB is the callback function that will be executed once
0c1cd1
+   for each mapping.  */
0c1cd1
 
0c1cd1
 static void
0c1cd1
-linux_core_info_proc_mappings (struct gdbarch *gdbarch, const char *args)
0c1cd1
+linux_read_core_file_mappings (struct gdbarch *gdbarch,
0c1cd1
+                               struct bfd *cbfd,
0c1cd1
+                               gdb::function_view<void (ULONGEST count)>
0c1cd1
+                                 pre_loop_cb,
0c1cd1
+                               gdb::function_view
0c1cd1
+                                                        ULONGEST start,
0c1cd1
+                                                        ULONGEST end,
0c1cd1
+                                                        ULONGEST file_ofs,
0c1cd1
+                                                        const char *filename,
0c1cd1
+                                                        const void *other)>
0c1cd1
+                                 loop_cb)
0c1cd1
 {
0c1cd1
-  asection *section;
0c1cd1
-  ULONGEST count, page_size;
0c1cd1
-  unsigned char *descdata, *filenames, *descend;
0c1cd1
-  size_t note_size;
0c1cd1
-  unsigned int addr_size_bits, addr_size;
0c1cd1
-  struct gdbarch *core_gdbarch = gdbarch_from_bfd (core_bfd);
0c1cd1
-  /* We assume this for reading 64-bit core files.  */
0c1cd1
+  /* Ensure that ULONGEST is big enough for reading 64-bit core files.  */
0c1cd1
   gdb_static_assert (sizeof (ULONGEST) >= 8);
0c1cd1
 
0c1cd1
-  section = bfd_get_section_by_name (core_bfd, ".note.linuxcore.file");
0c1cd1
-  if (section == NULL)
0c1cd1
-    {
0c1cd1
-      warning (_("unable to find mappings in core file"));
0c1cd1
-      return;
0c1cd1
-    }
0c1cd1
+  /* It's not required that the NT_FILE note exists, so return silently
0c1cd1
+     if it's not found.  Beyond this point though, we'll complain
0c1cd1
+     if problems are found.  */
0c1cd1
+  asection *section = bfd_get_section_by_name (cbfd, ".note.linuxcore.file");
0c1cd1
+  if (section == nullptr)
0c1cd1
+    return;
0c1cd1
 
0c1cd1
-  addr_size_bits = gdbarch_addr_bit (core_gdbarch);
0c1cd1
-  addr_size = addr_size_bits / 8;
0c1cd1
-  note_size = bfd_get_section_size (section);
0c1cd1
+  unsigned int addr_size_bits = gdbarch_addr_bit (gdbarch);
0c1cd1
+  unsigned int addr_size = addr_size_bits / 8;
0c1cd1
+  size_t note_size = bfd_section_size (section->abfd, section);
0c1cd1
 
0c1cd1
   if (note_size < 2 * addr_size)
0c1cd1
-    error (_("malformed core note - too short for header"));
0c1cd1
+    {
0c1cd1
+      warning (_("malformed core note - too short for header"));
0c1cd1
+      return;
0c1cd1
+    }
0c1cd1
 
0c1cd1
-  gdb::def_vector<unsigned char> contents (note_size);
0c1cd1
+  gdb::def_vector<gdb_byte> contents (note_size);
0c1cd1
   if (!bfd_get_section_contents (core_bfd, section, contents.data (),
0c1cd1
-				 0, note_size))
0c1cd1
-    error (_("could not get core note contents"));
0c1cd1
+                                 0, note_size))
0c1cd1
+    {
0c1cd1
+      warning (_("could not get core note contents"));
0c1cd1
+      return;
0c1cd1
+    }
0c1cd1
 
0c1cd1
-  descdata = contents.data ();
0c1cd1
-  descend = descdata + note_size;
0c1cd1
+  gdb_byte *descdata = contents.data ();
0c1cd1
+  char *descend = (char *) descdata + note_size;
0c1cd1
 
0c1cd1
   if (descdata[note_size - 1] != '\0')
0c1cd1
-    error (_("malformed note - does not end with \\0"));
0c1cd1
+    {
0c1cd1
+      warning (_("malformed note - does not end with \\0"));
0c1cd1
+      return;
0c1cd1
+    }
0c1cd1
 
0c1cd1
-  count = bfd_get (addr_size_bits, core_bfd, descdata);
0c1cd1
+  ULONGEST count = bfd_get (addr_size_bits, core_bfd, descdata);
0c1cd1
   descdata += addr_size;
0c1cd1
 
0c1cd1
-  page_size = bfd_get (addr_size_bits, core_bfd, descdata);
0c1cd1
+  ULONGEST page_size = bfd_get (addr_size_bits, core_bfd, descdata);
0c1cd1
   descdata += addr_size;
0c1cd1
 
0c1cd1
   if (note_size < 2 * addr_size + count * 3 * addr_size)
0c1cd1
-    error (_("malformed note - too short for supplied file count"));
0c1cd1
-
0c1cd1
-  printf_filtered (_("Mapped address spaces:\n\n"));
0c1cd1
-  if (gdbarch_addr_bit (gdbarch) == 32)
0c1cd1
     {
0c1cd1
-      printf_filtered ("\t%10s %10s %10s %10s %s\n",
0c1cd1
-		       "Start Addr",
0c1cd1
-		       "  End Addr",
0c1cd1
-		       "      Size", "    Offset", "objfile");
0c1cd1
-    }
0c1cd1
-  else
0c1cd1
-    {
0c1cd1
-      printf_filtered ("  %18s %18s %10s %10s %s\n",
0c1cd1
-		       "Start Addr",
0c1cd1
-		       "  End Addr",
0c1cd1
-		       "      Size", "    Offset", "objfile");
0c1cd1
+      warning (_("malformed note - too short for supplied file count"));
0c1cd1
+      return;
0c1cd1
     }
0c1cd1
 
0c1cd1
-  filenames = descdata + count * 3 * addr_size;
0c1cd1
-  while (--count > 0)
0c1cd1
+  char *filenames = (char *) descdata + count * 3 * addr_size;
0c1cd1
+
0c1cd1
+  /* Make sure that the correct number of filenames exist.  Complain
0c1cd1
+     if there aren't enough or are too many.  */
0c1cd1
+  char *f = filenames;
0c1cd1
+  for (int i = 0; i < count; i++)
0c1cd1
     {
0c1cd1
-      ULONGEST start, end, file_ofs;
0c1cd1
+      if (f >= descend)
0c1cd1
+        {
0c1cd1
+          warning (_("malformed note - filename area is too small"));
0c1cd1
+          return;
0c1cd1
+        }
0c1cd1
+      f += strnlen (f, descend - f) + 1;
0c1cd1
+    }
0c1cd1
+  /* Complain, but don't return early if the filename area is too big.  */
0c1cd1
+  if (f != descend)
0c1cd1
+    warning (_("malformed note - filename area is too big"));
0c1cd1
 
0c1cd1
-      if (filenames == descend)
0c1cd1
-	error (_("malformed note - filenames end too early"));
0c1cd1
+  pre_loop_cb (count);
0c1cd1
 
0c1cd1
-      start = bfd_get (addr_size_bits, core_bfd, descdata);
0c1cd1
+  for (int i = 0; i < count; i++)
0c1cd1
+    {
0c1cd1
+      ULONGEST start = bfd_get (addr_size_bits, core_bfd, descdata);
0c1cd1
       descdata += addr_size;
0c1cd1
-      end = bfd_get (addr_size_bits, core_bfd, descdata);
0c1cd1
+      ULONGEST end = bfd_get (addr_size_bits, core_bfd, descdata);
0c1cd1
       descdata += addr_size;
0c1cd1
-      file_ofs = bfd_get (addr_size_bits, core_bfd, descdata);
0c1cd1
+      ULONGEST file_ofs
0c1cd1
+        = bfd_get (addr_size_bits, core_bfd, descdata) * page_size;
0c1cd1
       descdata += addr_size;
0c1cd1
+      char * filename = filenames;
0c1cd1
+      filenames += strlen ((char *) filenames) + 1;
0c1cd1
 
0c1cd1
-      file_ofs *= page_size;
0c1cd1
-
0c1cd1
-      if (gdbarch_addr_bit (gdbarch) == 32)
0c1cd1
-	printf_filtered ("\t%10s %10s %10s %10s %s\n",
0c1cd1
-			 paddress (gdbarch, start),
0c1cd1
-			 paddress (gdbarch, end),
0c1cd1
-			 hex_string (end - start),
0c1cd1
-			 hex_string (file_ofs),
0c1cd1
-			 filenames);
0c1cd1
-      else
0c1cd1
-	printf_filtered ("  %18s %18s %10s %10s %s\n",
0c1cd1
-			 paddress (gdbarch, start),
0c1cd1
-			 paddress (gdbarch, end),
0c1cd1
-			 hex_string (end - start),
0c1cd1
-			 hex_string (file_ofs),
0c1cd1
-			 filenames);
0c1cd1
-
0c1cd1
-      filenames += 1 + strlen ((char *) filenames);
0c1cd1
+      loop_cb (i, start, end, file_ofs, filename, nullptr);
0c1cd1
     }
0c1cd1
 }
0c1cd1
 
0c1cd1
+/* Implement "info proc mappings" for a corefile.  */
0c1cd1
+
0c1cd1
+static void
0c1cd1
+linux_core_info_proc_mappings (struct gdbarch *gdbarch, const char *args)
0c1cd1
+{
0c1cd1
+  linux_read_core_file_mappings (gdbarch, core_bfd,
0c1cd1
+    [=] (ULONGEST count)
0c1cd1
+      {
0c1cd1
+        printf_filtered (_("Mapped address spaces:\n\n"));
0c1cd1
+        if (gdbarch_addr_bit (gdbarch) == 32)
0c1cd1
+          {
0c1cd1
+            printf_filtered ("\t%10s %10s %10s %10s %s\n",
0c1cd1
+                             "Start Addr",
0c1cd1
+                             "  End Addr",
0c1cd1
+                             "      Size", "    Offset", "objfile");
0c1cd1
+          }
0c1cd1
+        else
0c1cd1
+          {
0c1cd1
+            printf_filtered ("  %18s %18s %10s %10s %s\n",
0c1cd1
+                             "Start Addr",
0c1cd1
+                             "  End Addr",
0c1cd1
+                             "      Size", "    Offset", "objfile");
0c1cd1
+          }
0c1cd1
+      },
0c1cd1
+    [=] (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs,
0c1cd1
+         const char *filename, const void *other)
0c1cd1
+      {
0c1cd1
+        if (gdbarch_addr_bit (gdbarch) == 32)
0c1cd1
+          printf_filtered ("\t%10s %10s %10s %10s %s\n",
0c1cd1
+                           paddress (gdbarch, start),
0c1cd1
+                           paddress (gdbarch, end),
0c1cd1
+                           hex_string (end - start),
0c1cd1
+                           hex_string (file_ofs),
0c1cd1
+                           filename);
0c1cd1
+        else
0c1cd1
+          printf_filtered ("  %18s %18s %10s %10s %s\n",
0c1cd1
+                           paddress (gdbarch, start),
0c1cd1
+                           paddress (gdbarch, end),
0c1cd1
+                           hex_string (end - start),
0c1cd1
+                           hex_string (file_ofs),
0c1cd1
+                           filename);
0c1cd1
+      });
0c1cd1
+}
0c1cd1
+
0c1cd1
 /* Implement "info proc" for a corefile.  */
0c1cd1
 
0c1cd1
 static void
0c1cd1
@@ -2501,6 +2569,7 @@ linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
0c1cd1
   set_gdbarch_info_proc (gdbarch, linux_info_proc);
0c1cd1
   set_gdbarch_core_info_proc (gdbarch, linux_core_info_proc);
0c1cd1
   set_gdbarch_core_xfer_siginfo (gdbarch, linux_core_xfer_siginfo);
0c1cd1
+  set_gdbarch_read_core_file_mappings (gdbarch, linux_read_core_file_mappings);
0c1cd1
   set_gdbarch_find_memory_regions (gdbarch, linux_find_memory_regions);
0c1cd1
   set_gdbarch_make_corefile_notes (gdbarch, linux_make_corefile_notes);
0c1cd1
   set_gdbarch_has_shared_address_space (gdbarch,