Automatic date update in version.in
[deliverable/binutils-gdb.git] / gdb / linux-tdep.c
index 8ce2f34d4d2d82dc5307ff61463eb3c015da2c56..927e69bf1e1e2032fc1758e2b4bef54c7219a254 100644 (file)
@@ -1,6 +1,6 @@
 /* Target-dependent code for GNU/Linux, architecture independent.
 
-   Copyright (C) 2009-2015 Free Software Foundation, Inc.
+   Copyright (C) 2009-2021 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
 #include "cli/cli-utils.h"
 #include "arch-utils.h"
 #include "gdb_obstack.h"
-#include "observer.h"
+#include "observable.h"
 #include "objfiles.h"
 #include "infcall.h"
 #include "gdbcmd.h"
 #include "gdb_regex.h"
+#include "gdbsupport/enum-flags.h"
+#include "gdbsupport/gdb_optional.h"
+#include "gcore.h"
+#include "gcore-elf.h"
 
 #include <ctype.h>
 
@@ -46,7 +50,7 @@
    Documentation/filesystems/proc.txt, inside the Linux kernel
    tree.  */
 
-enum
+enum filter_flag
   {
     COREFILTER_ANON_PRIVATE = 1 << 0,
     COREFILTER_ANON_SHARED = 1 << 1,
@@ -56,6 +60,7 @@ enum
     COREFILTER_HUGETLB_PRIVATE = 1 << 5,
     COREFILTER_HUGETLB_SHARED = 1 << 6,
   };
+DEF_ENUM_FLAGS_TYPE (enum filter_flag, filter_flags);
 
 /* This struct is used to map flags found in the "VmFlags:" field (in
    the /proc/<PID>/smaps file).  */
@@ -83,12 +88,42 @@ struct smaps_vmflags
     /* Is this a MAP_SHARED mapping (VM_SHARED, "sh").  */
 
     unsigned int shared_mapping : 1;
+
+    /* Memory map has memory tagging enabled.  */
+
+    unsigned int memory_tagging : 1;
   };
 
+/* Data structure that holds the information contained in the
+   /proc/<pid>/smaps file.  */
+
+struct smaps_data
+{
+  ULONGEST start_address;
+  ULONGEST end_address;
+  std::string filename;
+  struct smaps_vmflags vmflags;
+  bool read;
+  bool write;
+  bool exec;
+  bool priv;
+  bool has_anonymous;
+  bool mapping_anon_p;
+  bool mapping_file_p;
+
+  ULONGEST inode;
+  ULONGEST offset;
+};
+
 /* Whether to take the /proc/PID/coredump_filter into account when
    generating a corefile.  */
 
-static int use_coredump_filter = 1;
+static bool use_coredump_filter = true;
+
+/* Whether the value of smaps_vmflags->exclude_coredump should be
+   ignored, including mappings marked with the VM_DONTDUMP flag in
+   the dump.  */
+static bool dump_excluded_mappings = false;
 
 /* This enum represents the signals' numbers on a generic architecture
    running the Linux kernel.  The definition of "generic" comes from
@@ -158,25 +193,24 @@ enum
 static struct gdbarch_data *linux_gdbarch_data_handle;
 
 struct linux_gdbarch_data
-  {
-    struct type *siginfo_type;
-  };
+{
+  struct type *siginfo_type;
+  int num_disp_step_buffers;
+};
 
 static void *
-init_linux_gdbarch_data (struct gdbarch *gdbarch)
+init_linux_gdbarch_data (struct obstack *obstack)
 {
-  return GDBARCH_OBSTACK_ZALLOC (gdbarch, struct linux_gdbarch_data);
+  return obstack_zalloc<linux_gdbarch_data> (obstack);
 }
 
 static struct linux_gdbarch_data *
 get_linux_gdbarch_data (struct gdbarch *gdbarch)
 {
-  return gdbarch_data (gdbarch, linux_gdbarch_data_handle);
+  return ((struct linux_gdbarch_data *)
+         gdbarch_data (gdbarch, linux_gdbarch_data_handle));
 }
 
-/* Per-inferior data key.  */
-static const struct inferior_data *linux_inferior_data;
-
 /* Linux-specific cached data.  This is used by GDB for caching
    purposes for each inferior.  This helps reduce the overhead of
    transfering data from a remote target to the local host.  */
@@ -187,67 +221,51 @@ struct linux_info
      at this info requires an auxv lookup (which is itself cached),
      and looking through the inferior's mappings (which change
      throughout execution and therefore cannot be cached).  */
-  struct mem_range vsyscall_range;
+  struct mem_range vsyscall_range {};
 
   /* Zero if we haven't tried looking up the vsyscall's range before
      yet.  Positive if we tried looking it up, and found it.  Negative
      if we tried looking it up but failed.  */
-  int vsyscall_range_p;
+  int vsyscall_range_p = 0;
+
+  /* Inferior's displaced step buffers.  */
+  gdb::optional<displaced_step_buffers> disp_step_bufs;
 };
 
+/* Per-inferior data key.  */
+static const struct inferior_key<linux_info> linux_inferior_data;
+
 /* Frees whatever allocated space there is to be freed and sets INF's
    linux cache data pointer to NULL.  */
 
 static void
 invalidate_linux_cache_inf (struct inferior *inf)
 {
-  struct linux_info *info;
-
-  info = inferior_data (inf, linux_inferior_data);
-  if (info != NULL)
-    {
-      xfree (info);
-      set_inferior_data (inf, linux_inferior_data, NULL);
-    }
-}
-
-/* Handles the cleanup of the linux cache for inferior INF.  ARG is
-   ignored.  Callback for the inferior_appeared and inferior_exit
-   events.  */
-
-static void
-linux_inferior_data_cleanup (struct inferior *inf, void *arg)
-{
-  invalidate_linux_cache_inf (inf);
+  linux_inferior_data.clear (inf);
 }
 
 /* Fetch the linux cache info for INF.  This function always returns a
    valid INFO pointer.  */
 
 static struct linux_info *
-get_linux_inferior_data (void)
+get_linux_inferior_data (inferior *inf)
 {
-  struct linux_info *info;
-  struct inferior *inf = current_inferior ();
+  linux_info *info = linux_inferior_data.get (inf);
 
-  info = inferior_data (inf, linux_inferior_data);
-  if (info == NULL)
-    {
-      info = XCNEW (struct linux_info);
-      set_inferior_data (inf, linux_inferior_data, info);
-    }
+  if (info == nullptr)
+    info = linux_inferior_data.emplace (inf);
 
   return info;
 }
 
-/* This function is suitable for architectures that don't
-   extend/override the standard siginfo structure.  */
+/* See linux-tdep.h.  */
 
 struct type *
-linux_get_siginfo_type (struct gdbarch *gdbarch)
+linux_get_siginfo_type_with_fields (struct gdbarch *gdbarch,
+                                   linux_siginfo_extra_fields extra_fields)
 {
   struct linux_gdbarch_data *linux_gdbarch_data;
-  struct type *int_type, *uint_type, *long_type, *void_ptr_type;
+  struct type *int_type, *uint_type, *long_type, *void_ptr_type, *short_type;
   struct type *uid_type, *pid_type;
   struct type *sigval_type, *clock_type;
   struct type *siginfo_type, *sifields_type;
@@ -263,31 +281,34 @@ linux_get_siginfo_type (struct gdbarch *gdbarch)
                                 1, "unsigned int");
   long_type = arch_integer_type (gdbarch, gdbarch_long_bit (gdbarch),
                                 0, "long");
+  short_type = arch_integer_type (gdbarch, gdbarch_long_bit (gdbarch),
+                                0, "short");
   void_ptr_type = lookup_pointer_type (builtin_type (gdbarch)->builtin_void);
 
   /* sival_t */
   sigval_type = arch_composite_type (gdbarch, NULL, TYPE_CODE_UNION);
-  TYPE_NAME (sigval_type) = xstrdup ("sigval_t");
+  sigval_type->set_name (xstrdup ("sigval_t"));
   append_composite_type_field (sigval_type, "sival_int", int_type);
   append_composite_type_field (sigval_type, "sival_ptr", void_ptr_type);
 
   /* __pid_t */
   pid_type = arch_type (gdbarch, TYPE_CODE_TYPEDEF,
-                       TYPE_LENGTH (int_type), "__pid_t");
+                       TYPE_LENGTH (int_type) * TARGET_CHAR_BIT, "__pid_t");
   TYPE_TARGET_TYPE (pid_type) = int_type;
-  TYPE_TARGET_STUB (pid_type) = 1;
+  pid_type->set_target_is_stub (true);
 
   /* __uid_t */
   uid_type = arch_type (gdbarch, TYPE_CODE_TYPEDEF,
-                       TYPE_LENGTH (uint_type), "__uid_t");
+                       TYPE_LENGTH (uint_type) * TARGET_CHAR_BIT, "__uid_t");
   TYPE_TARGET_TYPE (uid_type) = uint_type;
-  TYPE_TARGET_STUB (uid_type) = 1;
+  uid_type->set_target_is_stub (true);
 
   /* __clock_t */
   clock_type = arch_type (gdbarch, TYPE_CODE_TYPEDEF,
-                         TYPE_LENGTH (long_type), "__clock_t");
+                         TYPE_LENGTH (long_type) * TARGET_CHAR_BIT,
+                         "__clock_t");
   TYPE_TARGET_TYPE (clock_type) = long_type;
-  TYPE_TARGET_STUB (clock_type) = 1;
+  clock_type->set_target_is_stub (true);
 
   /* _sifields */
   sifields_type = arch_composite_type (gdbarch, NULL, TYPE_CODE_UNION);
@@ -338,6 +359,18 @@ linux_get_siginfo_type (struct gdbarch *gdbarch)
   /* _sigfault */
   type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT);
   append_composite_type_field (type, "si_addr", void_ptr_type);
+
+  /* Additional bound fields for _sigfault in case they were requested.  */
+  if ((extra_fields & LINUX_SIGINFO_FIELD_ADDR_BND) != 0)
+    {
+      struct type *sigfault_bnd_fields;
+
+      append_composite_type_field (type, "_addr_lsb", short_type);
+      sigfault_bnd_fields = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT);
+      append_composite_type_field (sigfault_bnd_fields, "_lower", void_ptr_type);
+      append_composite_type_field (sigfault_bnd_fields, "_upper", void_ptr_type);
+      append_composite_type_field (type, "_addr_bnd", sigfault_bnd_fields);
+    }
   append_composite_type_field (sifields_type, "_sigfault", type);
 
   /* _sigpoll */
@@ -348,7 +381,7 @@ linux_get_siginfo_type (struct gdbarch *gdbarch)
 
   /* struct siginfo */
   siginfo_type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT);
-  TYPE_NAME (siginfo_type) = xstrdup ("siginfo");
+  siginfo_type->set_name (xstrdup ("siginfo"));
   append_composite_type_field (siginfo_type, "si_signo", int_type);
   append_composite_type_field (siginfo_type, "si_errno", int_type);
   append_composite_type_field (siginfo_type, "si_code", int_type);
@@ -361,6 +394,15 @@ linux_get_siginfo_type (struct gdbarch *gdbarch)
   return siginfo_type;
 }
 
+/* This function is suitable for architectures that don't
+   extend/override the standard siginfo structure.  */
+
+static struct type *
+linux_get_siginfo_type (struct gdbarch *gdbarch)
+{
+  return linux_get_siginfo_type_with_fields (gdbarch, 0);
+}
+
 /* Return true if the target is running on uClinux instead of normal
    Linux kernel.  */
 
@@ -368,9 +410,10 @@ int
 linux_is_uclinux (void)
 {
   CORE_ADDR dummy;
+  target_ops *target = current_inferior ()->top_target ();
 
-  return (target_auxv_search (&current_target, AT_NULL, &dummy) > 0
-         && target_auxv_search (&current_target, AT_PAGESZ, &dummy) == 0);
+  return (target_auxv_search (target, AT_NULL, &dummy) > 0
+         && target_auxv_search (target, AT_PAGESZ, &dummy) == 0);
 }
 
 static int
@@ -381,16 +424,11 @@ linux_has_shared_address_space (struct gdbarch *gdbarch)
 
 /* This is how we want PTIDs from core files to be printed.  */
 
-static char *
+static std::string
 linux_core_pid_to_str (struct gdbarch *gdbarch, ptid_t ptid)
 {
-  static char buf[80];
-
-  if (ptid_get_lwp (ptid) != 0)
-    {
-      snprintf (buf, sizeof (buf), "LWP %ld", ptid_get_lwp (ptid));
-      return buf;
-    }
+  if (ptid.lwp () != 0)
+    return string_printf ("LWP %ld", ptid.lwp ());
 
   return normal_pid_to_str (ptid);
 }
@@ -402,7 +440,7 @@ read_mapping (const char *line,
              ULONGEST *addr, ULONGEST *endaddr,
              const char **permissions, size_t *permissions_len,
              ULONGEST *offset,
-              const char **device, size_t *device_len,
+             const char **device, size_t *device_len,
              ULONGEST *inode,
              const char **filename)
 {
@@ -413,7 +451,7 @@ read_mapping (const char *line,
     p++;
   *endaddr = strtoulst (p, &p, 16);
 
-  p = skip_spaces_const (p);
+  p = skip_spaces (p);
   *permissions = p;
   while (*p && !isspace (*p))
     p++;
@@ -421,7 +459,7 @@ read_mapping (const char *line,
 
   *offset = strtoulst (p, &p, 16);
 
-  p = skip_spaces_const (p);
+  p = skip_spaces (p);
   *device = p;
   while (*p && !isspace (*p))
     p++;
@@ -429,7 +467,7 @@ read_mapping (const char *line,
 
   *inode = strtoulst (p, &p, 10);
 
-  p = skip_spaces_const (p);
+  p = skip_spaces (p);
   *filename = p;
 }
 
@@ -464,9 +502,49 @@ decode_vmflags (char *p, struct smaps_vmflags *v)
        v->exclude_coredump = 1;
       else if (strcmp (s, "sh") == 0)
        v->shared_mapping = 1;
+      else if (strcmp (s, "mt") == 0)
+       v->memory_tagging = 1;
     }
 }
 
+/* Regexes used by mapping_is_anonymous_p.  Put in a structure because
+   they're initialized lazily.  */
+
+struct mapping_regexes
+{
+  /* Matches "/dev/zero" filenames (with or without the "(deleted)"
+     string in the end).  We know for sure, based on the Linux kernel
+     code, that memory mappings whose associated filename is
+     "/dev/zero" are guaranteed to be MAP_ANONYMOUS.  */
+  compiled_regex dev_zero
+    {"^/dev/zero\\( (deleted)\\)\\?$", REG_NOSUB,
+     _("Could not compile regex to match /dev/zero filename")};
+
+  /* Matches "/SYSV%08x" filenames (with or without the "(deleted)"
+     string in the end).  These filenames refer to shared memory
+     (shmem), and memory mappings associated with them are
+     MAP_ANONYMOUS as well.  */
+  compiled_regex shmem_file
+    {"^/\\?SYSV[0-9a-fA-F]\\{8\\}\\( (deleted)\\)\\?$", REG_NOSUB,
+     _("Could not compile regex to match shmem filenames")};
+
+  /* A heuristic we use to try to mimic the Linux kernel's 'n_link ==
+     0' code, which is responsible to decide if it is dealing with a
+     'MAP_SHARED | MAP_ANONYMOUS' mapping.  In other words, if
+     FILE_DELETED matches, it does not necessarily mean that we are
+     dealing with an anonymous shared mapping.  However, there is no
+     easy way to detect this currently, so this is the best
+     approximation we have.
+
+     As a result, GDB will dump readonly pages of deleted executables
+     when using the default value of coredump_filter (0x33), while the
+     Linux kernel will not dump those pages.  But we can live with
+     that.  */
+  compiled_regex file_deleted
+    {" (deleted)$", REG_NOSUB,
+     _("Could not compile regex to match '<file> (deleted)'")};
+};
+
 /* Return 1 if the memory mapping is anonymous, 0 otherwise.
 
    FILENAME is the name of the file present in the first line of the
@@ -480,52 +558,16 @@ decode_vmflags (char *p, struct smaps_vmflags *v)
 static int
 mapping_is_anonymous_p (const char *filename)
 {
-  static regex_t dev_zero_regex, shmem_file_regex, file_deleted_regex;
+  static gdb::optional<mapping_regexes> regexes;
   static int init_regex_p = 0;
 
   if (!init_regex_p)
     {
-      struct cleanup *c = make_cleanup (null_cleanup, NULL);
-
       /* Let's be pessimistic and assume there will be an error while
         compiling the regex'es.  */
       init_regex_p = -1;
 
-      /* DEV_ZERO_REGEX matches "/dev/zero" filenames (with or
-        without the "(deleted)" string in the end).  We know for
-        sure, based on the Linux kernel code, that memory mappings
-        whose associated filename is "/dev/zero" are guaranteed to be
-        MAP_ANONYMOUS.  */
-      compile_rx_or_error (&dev_zero_regex, "^/dev/zero\\( (deleted)\\)\\?$",
-                          _("Could not compile regex to match /dev/zero "
-                            "filename"));
-      /* SHMEM_FILE_REGEX matches "/SYSV%08x" filenames (with or
-        without the "(deleted)" string in the end).  These filenames
-        refer to shared memory (shmem), and memory mappings
-        associated with them are MAP_ANONYMOUS as well.  */
-      compile_rx_or_error (&shmem_file_regex,
-                          "^/\\?SYSV[0-9a-fA-F]\\{8\\}\\( (deleted)\\)\\?$",
-                          _("Could not compile regex to match shmem "
-                            "filenames"));
-      /* FILE_DELETED_REGEX is a heuristic we use to try to mimic the
-        Linux kernel's 'n_link == 0' code, which is responsible to
-        decide if it is dealing with a 'MAP_SHARED | MAP_ANONYMOUS'
-        mapping.  In other words, if FILE_DELETED_REGEX matches, it
-        does not necessarily mean that we are dealing with an
-        anonymous shared mapping.  However, there is no easy way to
-        detect this currently, so this is the best approximation we
-        have.
-
-        As a result, GDB will dump readonly pages of deleted
-        executables when using the default value of coredump_filter
-        (0x33), while the Linux kernel will not dump those pages.
-        But we can live with that.  */
-      compile_rx_or_error (&file_deleted_regex, " (deleted)$",
-                          _("Could not compile regex to match "
-                            "'<file> (deleted)'"));
-      /* We will never release these regexes, so just discard the
-        cleanups.  */
-      discard_cleanups (c);
+      regexes.emplace ();
 
       /* If we reached this point, then everything succeeded.  */
       init_regex_p = 1;
@@ -547,17 +589,17 @@ mapping_is_anonymous_p (const char *filename)
     }
 
   if (*filename == '\0'
-      || regexec (&dev_zero_regex, filename, 0, NULL, 0) == 0
-      || regexec (&shmem_file_regex, filename, 0, NULL, 0) == 0
-      || regexec (&file_deleted_regex, filename, 0, NULL, 0) == 0)
+      || regexes->dev_zero.exec (filename, 0, NULL, 0) == 0
+      || regexes->shmem_file.exec (filename, 0, NULL, 0) == 0
+      || regexes->file_deleted.exec (filename, 0, NULL, 0) == 0)
     return 1;
 
   return 0;
 }
 
 /* Return 0 if the memory mapping (which is related to FILTERFLAGS, V,
-   MAYBE_PRIVATE_P, and MAPPING_ANONYMOUS_P) should not be dumped, or
-   greater than 0 if it should.
+   MAYBE_PRIVATE_P, MAPPING_ANONYMOUS_P, ADDR and OFFSET) should not
+   be dumped, or greater than 0 if it should.
 
    In a nutshell, this is the logic that we follow in order to decide
    if a mapping should be dumped or not.
@@ -595,12 +637,17 @@ mapping_is_anonymous_p (const char *filename)
      see 'p' in the permission flags, then we assume that the mapping
      is private, even though the presence of the 's' flag there would
      mean VM_MAYSHARE, which means the mapping could still be private.
-     This should work OK enough, however.  */
+     This should work OK enough, however.
+
+   - Even if, at the end, we decided that we should not dump the
+     mapping, we still have to check if it is something like an ELF
+     header (of a DSO or an executable, for example).  If it is, and
+     if the user is interested in dump it, then we should dump it.  */
 
 static int
-dump_mapping_p (unsigned int filterflags, const struct smaps_vmflags *v,
+dump_mapping_p (filter_flags filterflags, const struct smaps_vmflags *v,
                int maybe_private_p, int mapping_anon_p, int mapping_file_p,
-               const char *filename)
+               const char *filename, ULONGEST addr, ULONGEST offset)
 {
   /* Initially, we trust in what we received from our caller.  This
      value may not be very precise (i.e., it was probably gathered
@@ -610,6 +657,7 @@ dump_mapping_p (unsigned int filterflags, const struct smaps_vmflags *v,
      (assuming that the version of the Linux kernel being used
      supports it, of course).  */
   int private_p = maybe_private_p;
+  int dump_p;
 
   /* We always dump vDSO and vsyscall mappings, because it's likely that
      there'll be no file to read the contents from at core load time.
@@ -625,7 +673,7 @@ dump_mapping_p (unsigned int filterflags, const struct smaps_vmflags *v,
        return 0;
 
       /* Check if we should exclude this mapping.  */
-      if (v->exclude_coredump)
+      if (!dump_excluded_mappings && v->exclude_coredump)
        return 0;
 
       /* Update our notion of whether this mapping is shared or
@@ -650,13 +698,13 @@ dump_mapping_p (unsigned int filterflags, const struct smaps_vmflags *v,
          /* This is a special situation.  It can happen when we see a
             mapping that is file-backed, but that contains anonymous
             pages.  */
-         return ((filterflags & COREFILTER_ANON_PRIVATE) != 0
-                 || (filterflags & COREFILTER_MAPPED_PRIVATE) != 0);
+         dump_p = ((filterflags & COREFILTER_ANON_PRIVATE) != 0
+                   || (filterflags & COREFILTER_MAPPED_PRIVATE) != 0);
        }
       else if (mapping_anon_p)
-       return (filterflags & COREFILTER_ANON_PRIVATE) != 0;
+       dump_p = (filterflags & COREFILTER_ANON_PRIVATE) != 0;
       else
-       return (filterflags & COREFILTER_MAPPED_PRIVATE) != 0;
+       dump_p = (filterflags & COREFILTER_MAPPED_PRIVATE) != 0;
     }
   else
     {
@@ -665,14 +713,68 @@ dump_mapping_p (unsigned int filterflags, const struct smaps_vmflags *v,
          /* This is a special situation.  It can happen when we see a
             mapping that is file-backed, but that contains anonymous
             pages.  */
-         return ((filterflags & COREFILTER_ANON_SHARED) != 0
-                 || (filterflags & COREFILTER_MAPPED_SHARED) != 0);
+         dump_p = ((filterflags & COREFILTER_ANON_SHARED) != 0
+                   || (filterflags & COREFILTER_MAPPED_SHARED) != 0);
        }
       else if (mapping_anon_p)
-       return (filterflags & COREFILTER_ANON_SHARED) != 0;
+       dump_p = (filterflags & COREFILTER_ANON_SHARED) != 0;
       else
-       return (filterflags & COREFILTER_MAPPED_SHARED) != 0;
+       dump_p = (filterflags & COREFILTER_MAPPED_SHARED) != 0;
+    }
+
+  /* Even if we decided that we shouldn't dump this mapping, we still
+     have to check whether (a) the user wants us to dump mappings
+     containing an ELF header, and (b) the mapping in question
+     contains an ELF header.  If (a) and (b) are true, then we should
+     dump this mapping.
+
+     A mapping contains an ELF header if it is a private mapping, its
+     offset is zero, and its first word is ELFMAG.  */
+  if (!dump_p && private_p && offset == 0
+      && (filterflags & COREFILTER_ELF_HEADERS) != 0)
+    {
+      /* Useful define specifying the size of the ELF magical
+        header.  */
+#ifndef SELFMAG
+#define SELFMAG 4
+#endif
+
+      /* Let's check if we have an ELF header.  */
+      gdb_byte h[SELFMAG];
+      if (target_read_memory (addr, h, SELFMAG) == 0)
+       {
+         /* The EI_MAG* and ELFMAG* constants come from
+            <elf/common.h>.  */
+         if (h[EI_MAG0] == ELFMAG0 && h[EI_MAG1] == ELFMAG1
+             && h[EI_MAG2] == ELFMAG2 && h[EI_MAG3] == ELFMAG3)
+           {
+             /* This mapping contains an ELF header, so we
+                should dump it.  */
+             dump_p = 1;
+           }
+       }
     }
+
+  return dump_p;
+}
+
+/* As above, but return true only when we should dump the NT_FILE
+   entry.  */
+
+static int
+dump_note_entry_p (filter_flags filterflags, const struct smaps_vmflags *v,
+               int maybe_private_p, int mapping_anon_p, int mapping_file_p,
+               const char *filename, ULONGEST addr, ULONGEST offset)
+{
+  /* vDSO and vsyscall mappings will end up in the core file.  Don't
+     put them in the NT_FILE note.  */
+  if (strcmp ("[vdso]", filename) == 0
+      || strcmp ("[vsyscall]", filename) == 0)
+    return 0;
+
+  /* Otherwise, any other file-based mapping should be placed in the
+     note.  */
+  return 1;
 }
 
 /* Implement the "info proc" command.  */
@@ -691,7 +793,6 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args,
   int status_f = (what == IP_STATUS || what == IP_ALL);
   int stat_f = (what == IP_STAT || what == IP_ALL);
   char filename[100];
-  char *data;
   int target_errno;
 
   if (args && isdigit (args[0]))
@@ -703,7 +804,7 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args,
     }
   else
     {
-      if (!target_has_execution)
+      if (!target_has_execution ())
        error (_("No current process: you must name one."));
       if (current_inferior ()->fake_pid_p)
        error (_("Can't determine the current process's PID: you must name one."));
@@ -711,7 +812,7 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args,
       pid = current_inferior ()->pid;
     }
 
-  args = skip_spaces_const (args);
+  args = skip_spaces (args);
   if (args && args[0])
     error (_("Too many parameters: %s"), args);
 
@@ -719,12 +820,21 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args,
   if (cmdline_f)
     {
       xsnprintf (filename, sizeof filename, "/proc/%ld/cmdline", pid);
-      data = target_fileio_read_stralloc (filename);
-      if (data)
+      gdb_byte *buffer;
+      ssize_t len = target_fileio_read_alloc (NULL, filename, &buffer);
+
+      if (len > 0)
        {
-         struct cleanup *cleanup = make_cleanup (xfree, data);
-          printf_filtered ("cmdline = '%s'\n", data);
-         do_cleanups (cleanup);
+         gdb::unique_xmalloc_ptr<char> cmdline ((char *) buffer);
+         ssize_t pos;
+
+         for (pos = 0; pos < len - 1; pos++)
+           {
+             if (buffer[pos] == '\0')
+               buffer[pos] = ' ';
+           }
+         buffer[len - 1] = '\0';
+         printf_filtered ("cmdline = '%s'\n", buffer);
        }
       else
        warning (_("unable to open /proc file '%s'"), filename);
@@ -732,36 +842,30 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args,
   if (cwd_f)
     {
       xsnprintf (filename, sizeof filename, "/proc/%ld/cwd", pid);
-      data = target_fileio_readlink (filename, &target_errno);
-      if (data)
-       {
-         struct cleanup *cleanup = make_cleanup (xfree, data);
-          printf_filtered ("cwd = '%s'\n", data);
-         do_cleanups (cleanup);
-       }
+      gdb::optional<std::string> contents
+       = target_fileio_readlink (NULL, filename, &target_errno);
+      if (contents.has_value ())
+       printf_filtered ("cwd = '%s'\n", contents->c_str ());
       else
        warning (_("unable to read link '%s'"), filename);
     }
   if (exe_f)
     {
       xsnprintf (filename, sizeof filename, "/proc/%ld/exe", pid);
-      data = target_fileio_readlink (filename, &target_errno);
-      if (data)
-       {
-         struct cleanup *cleanup = make_cleanup (xfree, data);
-          printf_filtered ("exe = '%s'\n", data);
-         do_cleanups (cleanup);
-       }
+      gdb::optional<std::string> contents
+       = target_fileio_readlink (NULL, filename, &target_errno);
+      if (contents.has_value ())
+       printf_filtered ("exe = '%s'\n", contents->c_str ());
       else
        warning (_("unable to read link '%s'"), filename);
     }
   if (mappings_f)
     {
       xsnprintf (filename, sizeof filename, "/proc/%ld/maps", pid);
-      data = target_fileio_read_stralloc (filename);
-      if (data)
+      gdb::unique_xmalloc_ptr<char> map
+       = target_fileio_read_stralloc (NULL, filename);
+      if (map != NULL)
        {
-         struct cleanup *cleanup = make_cleanup (xfree, data);
          char *line;
 
          printf_filtered (_("Mapped address spaces:\n\n"));
@@ -771,47 +875,48 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args,
                           "Start Addr",
                           "  End Addr",
                           "      Size", "    Offset", "objfile");
-            }
+           }
          else
-            {
+           {
              printf_filtered ("  %18s %18s %10s %10s %s\n",
                           "Start Addr",
                           "  End Addr",
                           "      Size", "    Offset", "objfile");
            }
 
-         for (line = strtok (data, "\n"); line; line = strtok (NULL, "\n"))
+         char *saveptr;
+         for (line = strtok_r (map.get (), "\n", &saveptr);
+              line;
+              line = strtok_r (NULL, "\n", &saveptr))
            {
              ULONGEST addr, endaddr, offset, inode;
-             const char *permissions, *device, *filename;
+             const char *permissions, *device, *mapping_filename;
              size_t permissions_len, device_len;
 
              read_mapping (line, &addr, &endaddr,
                            &permissions, &permissions_len,
                            &offset, &device, &device_len,
-                           &inode, &filename);
+                           &inode, &mapping_filename);
 
              if (gdbarch_addr_bit (gdbarch) == 32)
-               {
-                 printf_filtered ("\t%10s %10s %10s %10s %s\n",
+               {
+                 printf_filtered ("\t%10s %10s %10s %10s %s\n",
                                   paddress (gdbarch, addr),
                                   paddress (gdbarch, endaddr),
                                   hex_string (endaddr - addr),
                                   hex_string (offset),
-                                  *filename? filename : "");
+                                  *mapping_filename ? mapping_filename : "");
                }
              else
-               {
-                 printf_filtered ("  %18s %18s %10s %10s %s\n",
+               {
+                 printf_filtered ("  %18s %18s %10s %10s %s\n",
                                   paddress (gdbarch, addr),
                                   paddress (gdbarch, endaddr),
                                   hex_string (endaddr - addr),
                                   hex_string (offset),
-                                  *filename? filename : "");
-               }
+                                  *mapping_filename ? mapping_filename : "");
+               }
            }
-
-         do_cleanups (cleanup);
        }
       else
        warning (_("unable to open /proc file '%s'"), filename);
@@ -819,29 +924,26 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args,
   if (status_f)
     {
       xsnprintf (filename, sizeof filename, "/proc/%ld/status", pid);
-      data = target_fileio_read_stralloc (filename);
-      if (data)
-       {
-         struct cleanup *cleanup = make_cleanup (xfree, data);
-          puts_filtered (data);
-         do_cleanups (cleanup);
-       }
+      gdb::unique_xmalloc_ptr<char> status
+       = target_fileio_read_stralloc (NULL, filename);
+      if (status)
+       puts_filtered (status.get ());
       else
        warning (_("unable to open /proc file '%s'"), filename);
     }
   if (stat_f)
     {
       xsnprintf (filename, sizeof filename, "/proc/%ld/stat", pid);
-      data = target_fileio_read_stralloc (filename);
-      if (data)
+      gdb::unique_xmalloc_ptr<char> statstr
+       = target_fileio_read_stralloc (NULL, filename);
+      if (statstr)
        {
-         struct cleanup *cleanup = make_cleanup (xfree, data);
-         const char *p = data;
+         const char *p = statstr.get ();
 
          printf_filtered (_("Process: %s\n"),
                           pulongest (strtoulst (p, &p, 10)));
 
-         p = skip_spaces_const (p);
+         p = skip_spaces (p);
          if (*p == '(')
            {
              /* ps command also relies on no trailing fields
@@ -855,7 +957,7 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args,
                }
            }
 
-         p = skip_spaces_const (p);
+         p = skip_spaces (p);
          if (*p)
            printf_filtered (_("State: %c\n"), *p++);
 
@@ -961,114 +1063,177 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args,
            printf_filtered (_("wchan (system call): %s\n"),
                             hex_string (strtoulst (p, &p, 10)));
 #endif
-         do_cleanups (cleanup);
        }
       else
        warning (_("unable to open /proc file '%s'"), filename);
     }
 }
 
-/* Implement "info proc mappings" for a corefile.  */
+/* Implementation of `gdbarch_read_core_file_mappings', as defined in
+   gdbarch.h.
+   
+   This function reads the NT_FILE note (which BFD turns into the
+   section ".note.linuxcore.file").  The format of this note / section
+   is described as follows in the Linux kernel sources in
+   fs/binfmt_elf.c:
+   
+      long count     -- how many files are mapped
+      long page_size -- units for file_ofs
+      array of [COUNT] elements of
+       long start
+       long end
+       long file_ofs
+      followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL...
+      
+   CBFD is the BFD of the core file.
+
+   PRE_LOOP_CB is the callback function to invoke prior to starting
+   the loop which processes individual entries.  This callback will
+   only be executed after the note has been examined in enough
+   detail to verify that it's not malformed in some way.
+   
+   LOOP_CB is the callback function that will be executed once
+   for each mapping.  */
 
 static void
-linux_core_info_proc_mappings (struct gdbarch *gdbarch, const char *args)
+linux_read_core_file_mappings (struct gdbarch *gdbarch,
+                              struct bfd *cbfd,
+                              gdb::function_view<void (ULONGEST count)>
+                                pre_loop_cb,
+                              gdb::function_view<void (int num,
+                                                       ULONGEST start,
+                                                       ULONGEST end,
+                                                       ULONGEST file_ofs,
+                                                       const char *filename)>
+                                loop_cb)
 {
-  asection *section;
-  ULONGEST count, page_size;
-  unsigned char *descdata, *filenames, *descend, *contents;
-  size_t note_size;
-  unsigned int addr_size_bits, addr_size;
-  struct cleanup *cleanup;
-  struct gdbarch *core_gdbarch = gdbarch_from_bfd (core_bfd);
-  /* We assume this for reading 64-bit core files.  */
+  /* Ensure that ULONGEST is big enough for reading 64-bit core files.  */
   gdb_static_assert (sizeof (ULONGEST) >= 8);
 
-  section = bfd_get_section_by_name (core_bfd, ".note.linuxcore.file");
-  if (section == NULL)
-    {
-      warning (_("unable to find mappings in core file"));
-      return;
-    }
+  /* It's not required that the NT_FILE note exists, so return silently
+     if it's not found.  Beyond this point though, we'll complain
+     if problems are found.  */
+  asection *section = bfd_get_section_by_name (cbfd, ".note.linuxcore.file");
+  if (section == nullptr)
+    return;
 
-  addr_size_bits = gdbarch_addr_bit (core_gdbarch);
-  addr_size = addr_size_bits / 8;
-  note_size = bfd_get_section_size (section);
+  unsigned int addr_size_bits = gdbarch_addr_bit (gdbarch);
+  unsigned int addr_size = addr_size_bits / 8;
+  size_t note_size = bfd_section_size (section);
 
   if (note_size < 2 * addr_size)
-    error (_("malformed core note - too short for header"));
+    {
+      warning (_("malformed core note - too short for header"));
+      return;
+    }
 
-  contents = xmalloc (note_size);
-  cleanup = make_cleanup (xfree, contents);
-  if (!bfd_get_section_contents (core_bfd, section, contents, 0, note_size))
-    error (_("could not get core note contents"));
+  gdb::def_vector<gdb_byte> contents (note_size);
+  if (!bfd_get_section_contents (core_bfd, section, contents.data (),
+                                0, note_size))
+    {
+      warning (_("could not get core note contents"));
+      return;
+    }
 
-  descdata = contents;
-  descend = descdata + note_size;
+  gdb_byte *descdata = contents.data ();
+  char *descend = (char *) descdata + note_size;
 
   if (descdata[note_size - 1] != '\0')
-    error (_("malformed note - does not end with \\0"));
+    {
+      warning (_("malformed note - does not end with \\0"));
+      return;
+    }
 
-  count = bfd_get (addr_size_bits, core_bfd, descdata);
+  ULONGEST count = bfd_get (addr_size_bits, core_bfd, descdata);
   descdata += addr_size;
 
-  page_size = bfd_get (addr_size_bits, core_bfd, descdata);
+  ULONGEST page_size = bfd_get (addr_size_bits, core_bfd, descdata);
   descdata += addr_size;
 
   if (note_size < 2 * addr_size + count * 3 * addr_size)
-    error (_("malformed note - too short for supplied file count"));
-
-  printf_filtered (_("Mapped address spaces:\n\n"));
-  if (gdbarch_addr_bit (gdbarch) == 32)
-    {
-      printf_filtered ("\t%10s %10s %10s %10s %s\n",
-                      "Start Addr",
-                      "  End Addr",
-                      "      Size", "    Offset", "objfile");
-    }
-  else
     {
-      printf_filtered ("  %18s %18s %10s %10s %s\n",
-                      "Start Addr",
-                      "  End Addr",
-                      "      Size", "    Offset", "objfile");
+      warning (_("malformed note - too short for supplied file count"));
+      return;
     }
 
-  filenames = descdata + count * 3 * addr_size;
-  while (--count > 0)
+  char *filenames = (char *) descdata + count * 3 * addr_size;
+
+  /* Make sure that the correct number of filenames exist.  Complain
+     if there aren't enough or are too many.  */
+  char *f = filenames;
+  for (int i = 0; i < count; i++)
     {
-      ULONGEST start, end, file_ofs;
+      if (f >= descend)
+       {
+         warning (_("malformed note - filename area is too small"));
+         return;
+       }
+      f += strnlen (f, descend - f) + 1;
+    }
+  /* Complain, but don't return early if the filename area is too big.  */
+  if (f != descend)
+    warning (_("malformed note - filename area is too big"));
 
-      if (filenames == descend)
-       error (_("malformed note - filenames end too early"));
+  pre_loop_cb (count);
 
-      start = bfd_get (addr_size_bits, core_bfd, descdata);
+  for (int i = 0; i < count; i++)
+    {
+      ULONGEST start = bfd_get (addr_size_bits, core_bfd, descdata);
       descdata += addr_size;
-      end = bfd_get (addr_size_bits, core_bfd, descdata);
+      ULONGEST end = bfd_get (addr_size_bits, core_bfd, descdata);
       descdata += addr_size;
-      file_ofs = bfd_get (addr_size_bits, core_bfd, descdata);
+      ULONGEST file_ofs
+       = bfd_get (addr_size_bits, core_bfd, descdata) * page_size;
       descdata += addr_size;
+      char * filename = filenames;
+      filenames += strlen ((char *) filenames) + 1;
 
-      file_ofs *= page_size;
-
-      if (gdbarch_addr_bit (gdbarch) == 32)
-       printf_filtered ("\t%10s %10s %10s %10s %s\n",
-                        paddress (gdbarch, start),
-                        paddress (gdbarch, end),
-                        hex_string (end - start),
-                        hex_string (file_ofs),
-                        filenames);
-      else
-       printf_filtered ("  %18s %18s %10s %10s %s\n",
-                        paddress (gdbarch, start),
-                        paddress (gdbarch, end),
-                        hex_string (end - start),
-                        hex_string (file_ofs),
-                        filenames);
-
-      filenames += 1 + strlen ((char *) filenames);
+      loop_cb (i, start, end, file_ofs, filename);
     }
+}
+
+/* Implement "info proc mappings" for a corefile.  */
 
-  do_cleanups (cleanup);
+static void
+linux_core_info_proc_mappings (struct gdbarch *gdbarch, const char *args)
+{
+  linux_read_core_file_mappings (gdbarch, core_bfd,
+    [=] (ULONGEST count)
+      {
+       printf_filtered (_("Mapped address spaces:\n\n"));
+       if (gdbarch_addr_bit (gdbarch) == 32)
+         {
+           printf_filtered ("\t%10s %10s %10s %10s %s\n",
+                            "Start Addr",
+                            "  End Addr",
+                            "      Size", "    Offset", "objfile");
+         }
+       else
+         {
+           printf_filtered ("  %18s %18s %10s %10s %s\n",
+                            "Start Addr",
+                            "  End Addr",
+                            "      Size", "    Offset", "objfile");
+         }
+      },
+    [=] (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs,
+        const char *filename)
+      {
+       if (gdbarch_addr_bit (gdbarch) == 32)
+         printf_filtered ("\t%10s %10s %10s %10s %s\n",
+                          paddress (gdbarch, start),
+                          paddress (gdbarch, end),
+                          hex_string (end - start),
+                          hex_string (file_ofs),
+                          filename);
+       else
+         printf_filtered ("  %18s %18s %10s %10s %s\n",
+                          paddress (gdbarch, start),
+                          paddress (gdbarch, end),
+                          hex_string (end - start),
+                          hex_string (file_ofs),
+                          filename);
+      });
 }
 
 /* Implement "info proc" for a corefile.  */
@@ -1098,6 +1263,26 @@ linux_core_info_proc (struct gdbarch *gdbarch, const char *args,
     error (_("unable to handle request"));
 }
 
+/* Read siginfo data from the core, if possible.  Returns -1 on
+   failure.  Otherwise, returns the number of bytes read.  READBUF,
+   OFFSET, and LEN are all as specified by the to_xfer_partial
+   interface.  */
+
+static LONGEST
+linux_core_xfer_siginfo (struct gdbarch *gdbarch, gdb_byte *readbuf,
+                        ULONGEST offset, ULONGEST len)
+{
+  thread_section_name section_name (".note.linuxcore.siginfo", inferior_ptid);
+  asection *section = bfd_get_section_by_name (core_bfd, section_name.c_str ());
+  if (section == NULL)
+    return -1;
+
+  if (!bfd_get_section_contents (core_bfd, section, readbuf, offset, len))
+    return -1;
+
+  return len;
+}
+
 typedef int linux_find_memory_region_ftype (ULONGEST vaddr, ULONGEST size,
                                            ULONGEST offset, ULONGEST inode,
                                            int read, int write,
@@ -1105,21 +1290,202 @@ typedef int linux_find_memory_region_ftype (ULONGEST vaddr, ULONGEST size,
                                            const char *filename,
                                            void *data);
 
+typedef int linux_dump_mapping_p_ftype (filter_flags filterflags,
+                                       const struct smaps_vmflags *v,
+                                       int maybe_private_p,
+                                       int mapping_anon_p,
+                                       int mapping_file_p,
+                                       const char *filename,
+                                       ULONGEST addr,
+                                       ULONGEST offset);
+
+/* Helper function to parse the contents of /proc/<pid>/smaps into a data
+   structure, for easy access.
+
+   DATA is the contents of the smaps file.  The parsed contents are stored
+   into the SMAPS vector.  */
+
+static std::vector<struct smaps_data>
+parse_smaps_data (const char *data,
+                 const std::string maps_filename)
+{
+  char *line, *t;
+
+  gdb_assert (data != nullptr);
+
+  line = strtok_r ((char *) data, "\n", &t);
+
+  std::vector<struct smaps_data> smaps;
+
+  while (line != NULL)
+    {
+      ULONGEST addr, endaddr, offset, inode;
+      const char *permissions, *device, *filename;
+      struct smaps_vmflags v;
+      size_t permissions_len, device_len;
+      int read, write, exec, priv;
+      int has_anonymous = 0;
+      int mapping_anon_p;
+      int mapping_file_p;
+
+      memset (&v, 0, sizeof (v));
+      read_mapping (line, &addr, &endaddr, &permissions, &permissions_len,
+                   &offset, &device, &device_len, &inode, &filename);
+      mapping_anon_p = mapping_is_anonymous_p (filename);
+      /* If the mapping is not anonymous, then we can consider it
+        to be file-backed.  These two states (anonymous or
+        file-backed) seem to be exclusive, but they can actually
+        coexist.  For example, if a file-backed mapping has
+        "Anonymous:" pages (see more below), then the Linux
+        kernel will dump this mapping when the user specified
+        that she only wants anonymous mappings in the corefile
+        (*even* when she explicitly disabled the dumping of
+        file-backed mappings).  */
+      mapping_file_p = !mapping_anon_p;
+
+      /* Decode permissions.  */
+      read = (memchr (permissions, 'r', permissions_len) != 0);
+      write = (memchr (permissions, 'w', permissions_len) != 0);
+      exec = (memchr (permissions, 'x', permissions_len) != 0);
+      /* 'private' here actually means VM_MAYSHARE, and not
+        VM_SHARED.  In order to know if a mapping is really
+        private or not, we must check the flag "sh" in the
+        VmFlags field.  This is done by decode_vmflags.  However,
+        if we are using a Linux kernel released before the commit
+        834f82e2aa9a8ede94b17b656329f850c1471514 (3.10), we will
+        not have the VmFlags there.  In this case, there is
+        really no way to know if we are dealing with VM_SHARED,
+        so we just assume that VM_MAYSHARE is enough.  */
+      priv = memchr (permissions, 'p', permissions_len) != 0;
+
+      /* Try to detect if region should be dumped by parsing smaps
+        counters.  */
+      for (line = strtok_r (NULL, "\n", &t);
+          line != NULL && line[0] >= 'A' && line[0] <= 'Z';
+          line = strtok_r (NULL, "\n", &t))
+       {
+         char keyword[64 + 1];
+
+         if (sscanf (line, "%64s", keyword) != 1)
+           {
+             warning (_("Error parsing {s,}maps file '%s'"),
+                      maps_filename.c_str ());
+             break;
+           }
+
+         if (strcmp (keyword, "Anonymous:") == 0)
+           {
+             /* Older Linux kernels did not support the
+                "Anonymous:" counter.  Check it here.  */
+             has_anonymous = 1;
+           }
+         else if (strcmp (keyword, "VmFlags:") == 0)
+           decode_vmflags (line, &v);
+
+         if (strcmp (keyword, "AnonHugePages:") == 0
+             || strcmp (keyword, "Anonymous:") == 0)
+           {
+             unsigned long number;
+
+             if (sscanf (line, "%*s%lu", &number) != 1)
+               {
+                 warning (_("Error parsing {s,}maps file '%s' number"),
+                          maps_filename.c_str ());
+                 break;
+               }
+             if (number > 0)
+               {
+                 /* Even if we are dealing with a file-backed
+                    mapping, if it contains anonymous pages we
+                    consider it to be *also* an anonymous
+                    mapping, because this is what the Linux
+                    kernel does:
+
+                    // Dump segments that have been written to.
+                    if (vma->anon_vma && FILTER(ANON_PRIVATE))
+                      goto whole;
+
+                   Note that if the mapping is already marked as
+                   file-backed (i.e., mapping_file_p is
+                   non-zero), then this is a special case, and
+                   this mapping will be dumped either when the
+                   user wants to dump file-backed *or* anonymous
+                   mappings.  */
+                 mapping_anon_p = 1;
+               }
+           }
+       }
+      /* Save the smaps entry to the vector.  */
+       struct smaps_data map;
+
+       map.start_address = addr;
+       map.end_address = endaddr;
+       map.filename = filename;
+       map.vmflags = v;
+       map.read = read? true : false;
+       map.write = write? true : false;
+       map.exec = exec? true : false;
+       map.priv = priv? true : false;
+       map.has_anonymous = has_anonymous;
+       map.mapping_anon_p = mapping_anon_p? true : false;
+       map.mapping_file_p = mapping_file_p? true : false;
+       map.offset = offset;
+       map.inode = inode;
+
+       smaps.emplace_back (map);
+    }
+
+  return smaps;
+}
+
+/* See linux-tdep.h.  */
+
+bool
+linux_address_in_memtag_page (CORE_ADDR address)
+{
+  if (current_inferior ()->fake_pid_p)
+    return false;
+
+  pid_t pid = current_inferior ()->pid;
+
+  std::string smaps_file = string_printf ("/proc/%d/smaps", pid);
+
+  gdb::unique_xmalloc_ptr<char> data
+    = target_fileio_read_stralloc (NULL, smaps_file.c_str ());
+
+  if (data == nullptr)
+    return false;
+
+  /* Parse the contents of smaps into a vector.  */
+  std::vector<struct smaps_data> smaps
+    = parse_smaps_data (data.get (), smaps_file);
+
+  for (const smaps_data &map : smaps)
+    {
+      /* Is the address within [start_address, end_address) in a page
+        mapped with memory tagging?  */
+      if (address >= map.start_address
+         && address < map.end_address
+         && map.vmflags.memory_tagging)
+       return true;
+    }
+
+  return false;
+}
+
 /* List memory regions in the inferior for a corefile.  */
 
 static int
 linux_find_memory_regions_full (struct gdbarch *gdbarch,
+                               linux_dump_mapping_p_ftype *should_dump_mapping_p,
                                linux_find_memory_region_ftype *func,
                                void *obfd)
 {
-  char mapsfilename[100];
-  char coredumpfilter_name[100];
-  char *data, *coredumpfilterdata;
   pid_t pid;
   /* Default dump behavior of coredump_filter (0x33), according to
      Documentation/filesystems/proc.txt from the Linux kernel
      tree.  */
-  unsigned int filterflags = (COREFILTER_ANON_PRIVATE
+  filter_flags filterflags = (COREFILTER_ANON_PRIVATE
                              | COREFILTER_ANON_SHARED
                              | COREFILTER_ELF_HEADERS
                              | COREFILTER_HUGETLB_PRIVATE);
@@ -1132,154 +1498,74 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch,
 
   if (use_coredump_filter)
     {
-      xsnprintf (coredumpfilter_name, sizeof (coredumpfilter_name),
-                "/proc/%d/coredump_filter", pid);
-      coredumpfilterdata = target_fileio_read_stralloc (coredumpfilter_name);
+      std::string core_dump_filter_name
+       = string_printf ("/proc/%d/coredump_filter", pid);
+
+      gdb::unique_xmalloc_ptr<char> coredumpfilterdata
+       = target_fileio_read_stralloc (NULL, core_dump_filter_name.c_str ());
+
       if (coredumpfilterdata != NULL)
        {
-         sscanf (coredumpfilterdata, "%x", &filterflags);
-         xfree (coredumpfilterdata);
+         unsigned int flags;
+
+         sscanf (coredumpfilterdata.get (), "%x", &flags);
+         filterflags = (enum filter_flag) flags;
        }
     }
 
-  xsnprintf (mapsfilename, sizeof mapsfilename, "/proc/%d/smaps", pid);
-  data = target_fileio_read_stralloc (mapsfilename);
+  std::string maps_filename = string_printf ("/proc/%d/smaps", pid);
+
+  gdb::unique_xmalloc_ptr<char> data
+    = target_fileio_read_stralloc (NULL, maps_filename.c_str ());
+
   if (data == NULL)
     {
       /* Older Linux kernels did not support /proc/PID/smaps.  */
-      xsnprintf (mapsfilename, sizeof mapsfilename, "/proc/%d/maps", pid);
-      data = target_fileio_read_stralloc (mapsfilename);
+      maps_filename = string_printf ("/proc/%d/maps", pid);
+      data = target_fileio_read_stralloc (NULL, maps_filename.c_str ());
+
+      if (data == nullptr)
+       return 1;
     }
 
-  if (data != NULL)
+  /* Parse the contents of smaps into a vector.  */
+  std::vector<struct smaps_data> smaps
+    = parse_smaps_data (data.get (), maps_filename.c_str ());
+
+  for (const struct smaps_data &map : smaps)
     {
-      struct cleanup *cleanup = make_cleanup (xfree, data);
-      char *line, *t;
+      int should_dump_p = 0;
 
-      line = strtok_r (data, "\n", &t);
-      while (line != NULL)
+      if (map.has_anonymous)
        {
-         ULONGEST addr, endaddr, offset, inode;
-         const char *permissions, *device, *filename;
-         struct smaps_vmflags v;
-         size_t permissions_len, device_len;
-         int read, write, exec, priv;
-         int has_anonymous = 0;
-         int should_dump_p = 0;
-         int mapping_anon_p;
-         int mapping_file_p;
-
-         memset (&v, 0, sizeof (v));
-         read_mapping (line, &addr, &endaddr, &permissions, &permissions_len,
-                       &offset, &device, &device_len, &inode, &filename);
-         mapping_anon_p = mapping_is_anonymous_p (filename);
-         /* If the mapping is not anonymous, then we can consider it
-            to be file-backed.  These two states (anonymous or
-            file-backed) seem to be exclusive, but they can actually
-            coexist.  For example, if a file-backed mapping has
-            "Anonymous:" pages (see more below), then the Linux
-            kernel will dump this mapping when the user specified
-            that she only wants anonymous mappings in the corefile
-            (*even* when she explicitly disabled the dumping of
-            file-backed mappings).  */
-         mapping_file_p = !mapping_anon_p;
-
-         /* Decode permissions.  */
-         read = (memchr (permissions, 'r', permissions_len) != 0);
-         write = (memchr (permissions, 'w', permissions_len) != 0);
-         exec = (memchr (permissions, 'x', permissions_len) != 0);
-         /* 'private' here actually means VM_MAYSHARE, and not
-            VM_SHARED.  In order to know if a mapping is really
-            private or not, we must check the flag "sh" in the
-            VmFlags field.  This is done by decode_vmflags.  However,
-            if we are using a Linux kernel released before the commit
-            834f82e2aa9a8ede94b17b656329f850c1471514 (3.10), we will
-            not have the VmFlags there.  In this case, there is
-            really no way to know if we are dealing with VM_SHARED,
-            so we just assume that VM_MAYSHARE is enough.  */
-         priv = memchr (permissions, 'p', permissions_len) != 0;
-
-         /* Try to detect if region should be dumped by parsing smaps
-            counters.  */
-         for (line = strtok_r (NULL, "\n", &t);
-              line != NULL && line[0] >= 'A' && line[0] <= 'Z';
-              line = strtok_r (NULL, "\n", &t))
-           {
-             char keyword[64 + 1];
-
-             if (sscanf (line, "%64s", keyword) != 1)
-               {
-                 warning (_("Error parsing {s,}maps file '%s'"), mapsfilename);
-                 break;
-               }
-
-             if (strcmp (keyword, "Anonymous:") == 0)
-               {
-                 /* Older Linux kernels did not support the
-                    "Anonymous:" counter.  Check it here.  */
-                 has_anonymous = 1;
-               }
-             else if (strcmp (keyword, "VmFlags:") == 0)
-               decode_vmflags (line, &v);
-
-             if (strcmp (keyword, "AnonHugePages:") == 0
-                 || strcmp (keyword, "Anonymous:") == 0)
-               {
-                 unsigned long number;
-
-                 if (sscanf (line, "%*s%lu", &number) != 1)
-                   {
-                     warning (_("Error parsing {s,}maps file '%s' number"),
-                              mapsfilename);
-                     break;
-                   }
-                 if (number > 0)
-                   {
-                     /* Even if we are dealing with a file-backed
-                        mapping, if it contains anonymous pages we
-                        consider it to be *also* an anonymous
-                        mapping, because this is what the Linux
-                        kernel does:
-
-                        // Dump segments that have been written to.
-                        if (vma->anon_vma && FILTER(ANON_PRIVATE))
-                               goto whole;
-
-                        Note that if the mapping is already marked as
-                        file-backed (i.e., mapping_file_p is
-                        non-zero), then this is a special case, and
-                        this mapping will be dumped either when the
-                        user wants to dump file-backed *or* anonymous
-                        mappings.  */
-                     mapping_anon_p = 1;
-                   }
-               }
-           }
-
-         if (has_anonymous)
-           should_dump_p = dump_mapping_p (filterflags, &v, priv,
-                                           mapping_anon_p, mapping_file_p,
-                                           filename);
-         else
-           {
-             /* Older Linux kernels did not support the "Anonymous:" counter.
-                If it is missing, we can't be sure - dump all the pages.  */
-             should_dump_p = 1;
-           }
-
-         /* Invoke the callback function to create the corefile segment.  */
-         if (should_dump_p)
-           func (addr, endaddr - addr, offset, inode,
-                 read, write, exec, 1, /* MODIFIED is true because we
-                                          want to dump the mapping.  */
-                 filename, obfd);
+         should_dump_p
+           = should_dump_mapping_p (filterflags, &map.vmflags,
+                                    map.priv,
+                                    map.mapping_anon_p,
+                                    map.mapping_file_p,
+                                    map.filename.c_str (),
+                                    map.start_address,
+                                    map.offset);
+       }
+      else
+       {
+         /* Older Linux kernels did not support the "Anonymous:" counter.
+            If it is missing, we can't be sure - dump all the pages.  */
+         should_dump_p = 1;
        }
 
-      do_cleanups (cleanup);
-      return 0;
+      /* Invoke the callback function to create the corefile segment.  */
+      if (should_dump_p)
+       {
+         func (map.start_address, map.end_address - map.start_address,
+               map.offset, map.inode, map.read, map.write, map.exec,
+               1, /* MODIFIED is true because we want to dump
+                     the mapping.  */
+               map.filename.c_str (), obfd);
+       }
     }
 
-  return 1;
+  return 0;
 }
 
 /* A structure for passing information through
@@ -1305,7 +1591,8 @@ linux_find_memory_regions_thunk (ULONGEST vaddr, ULONGEST size,
                                 int read, int write, int exec, int modified,
                                 const char *filename, void *arg)
 {
-  struct linux_find_memory_regions_data *data = arg;
+  struct linux_find_memory_regions_data *data
+    = (struct linux_find_memory_regions_data *) arg;
 
   return data->func (vaddr, size, read, write, exec, modified, data->obfd);
 }
@@ -1323,106 +1610,11 @@ linux_find_memory_regions (struct gdbarch *gdbarch,
   data.obfd = obfd;
 
   return linux_find_memory_regions_full (gdbarch,
+                                        dump_mapping_p,
                                         linux_find_memory_regions_thunk,
                                         &data);
 }
 
-/* Determine which signal stopped execution.  */
-
-static int
-find_signalled_thread (struct thread_info *info, void *data)
-{
-  if (info->suspend.stop_signal != GDB_SIGNAL_0
-      && ptid_get_pid (info->ptid) == ptid_get_pid (inferior_ptid))
-    return 1;
-
-  return 0;
-}
-
-static enum gdb_signal
-find_stop_signal (void)
-{
-  struct thread_info *info =
-    iterate_over_threads (find_signalled_thread, NULL);
-
-  if (info)
-    return info->suspend.stop_signal;
-  else
-    return GDB_SIGNAL_0;
-}
-
-/* Generate corefile notes for SPU contexts.  */
-
-static char *
-linux_spu_make_corefile_notes (bfd *obfd, char *note_data, int *note_size)
-{
-  static const char *spu_files[] =
-    {
-      "object-id",
-      "mem",
-      "regs",
-      "fpcr",
-      "lslr",
-      "decr",
-      "decr_status",
-      "signal1",
-      "signal1_type",
-      "signal2",
-      "signal2_type",
-      "event_mask",
-      "event_status",
-      "mbox_info",
-      "ibox_info",
-      "wbox_info",
-      "dma_info",
-      "proxydma_info",
-   };
-
-  enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
-  gdb_byte *spu_ids;
-  LONGEST i, j, size;
-
-  /* Determine list of SPU ids.  */
-  size = target_read_alloc (&current_target, TARGET_OBJECT_SPU,
-                           NULL, &spu_ids);
-
-  /* Generate corefile notes for each SPU file.  */
-  for (i = 0; i < size; i += 4)
-    {
-      int fd = extract_unsigned_integer (spu_ids + i, 4, byte_order);
-
-      for (j = 0; j < sizeof (spu_files) / sizeof (spu_files[0]); j++)
-       {
-         char annex[32], note_name[32];
-         gdb_byte *spu_data;
-         LONGEST spu_len;
-
-         xsnprintf (annex, sizeof annex, "%d/%s", fd, spu_files[j]);
-         spu_len = target_read_alloc (&current_target, TARGET_OBJECT_SPU,
-                                      annex, &spu_data);
-         if (spu_len > 0)
-           {
-             xsnprintf (note_name, sizeof note_name, "SPU/%s", annex);
-             note_data = elfcore_write_note (obfd, note_data, note_size,
-                                             note_name, NT_SPU,
-                                             spu_data, spu_len);
-             xfree (spu_data);
-
-             if (!note_data)
-               {
-                 xfree (spu_ids);
-                 return NULL;
-               }
-           }
-       }
-    }
-
-  if (size > 0)
-    xfree (spu_ids);
-
-  return note_data;
-}
-
 /* This is used to pass information from
    linux_make_mappings_corefile_notes through
    linux_find_memory_regions_full.  */
@@ -1453,7 +1645,8 @@ linux_make_mappings_callback (ULONGEST vaddr, ULONGEST size,
                              int read, int write, int exec, int modified,
                              const char *filename, void *data)
 {
-  struct linux_make_mappings_data *map_data = data;
+  struct linux_make_mappings_data *map_data
+    = (struct linux_make_mappings_data *) data;
   gdb_byte buf[sizeof (ULONGEST)];
 
   if (*filename == '\0' || inode == 0)
@@ -1475,24 +1668,19 @@ linux_make_mappings_callback (ULONGEST vaddr, ULONGEST size,
 
 /* Write the file mapping data to the core file, if possible.  OBFD is
    the output BFD.  NOTE_DATA is the current note data, and NOTE_SIZE
-   is a pointer to the note size.  Returns the new NOTE_DATA and
-   updates NOTE_SIZE.  */
+   is a pointer to the note size.  Updates NOTE_DATA and NOTE_SIZE.  */
 
-static char *
+static void
 linux_make_mappings_corefile_notes (struct gdbarch *gdbarch, bfd *obfd,
-                                   char *note_data, int *note_size)
+                                   gdb::unique_xmalloc_ptr<char> &note_data,
+                                   int *note_size)
 {
-  struct cleanup *cleanup;
-  struct obstack data_obstack, filename_obstack;
   struct linux_make_mappings_data mapping_data;
   struct type *long_type
     = arch_integer_type (gdbarch, gdbarch_long_bit (gdbarch), 0, "long");
   gdb_byte buf[sizeof (ULONGEST)];
 
-  obstack_init (&data_obstack);
-  cleanup = make_cleanup_obstack_free (&data_obstack);
-  obstack_init (&filename_obstack);
-  make_cleanup_obstack_free (&filename_obstack);
+  auto_obstack data_obstack, filename_obstack;
 
   mapping_data.file_count = 0;
   mapping_data.data_obstack = &data_obstack;
@@ -1506,7 +1694,9 @@ linux_make_mappings_corefile_notes (struct gdbarch *gdbarch, bfd *obfd,
   pack_long (buf, long_type, 1);
   obstack_grow (&data_obstack, buf, TYPE_LENGTH (long_type));
 
-  linux_find_memory_regions_full (gdbarch, linux_make_mappings_callback,
+  linux_find_memory_regions_full (gdbarch, 
+                                 dump_note_entry_p,
+                                 linux_make_mappings_callback,
                                  &mapping_data);
 
   if (mapping_data.file_count != 0)
@@ -1516,196 +1706,87 @@ linux_make_mappings_corefile_notes (struct gdbarch *gdbarch, bfd *obfd,
                 long_type, mapping_data.file_count);
 
       /* Copy the filenames to the data obstack.  */
+      int size = obstack_object_size (&filename_obstack);
       obstack_grow (&data_obstack, obstack_base (&filename_obstack),
-                   obstack_object_size (&filename_obstack));
+                   size);
 
-      note_data = elfcore_write_note (obfd, note_data, note_size,
-                                     "CORE", NT_FILE,
-                                     obstack_base (&data_obstack),
-                                     obstack_object_size (&data_obstack));
+      note_data.reset (elfcore_write_file_note (obfd, note_data.release (), note_size,
+                                               obstack_base (&data_obstack),
+                                               obstack_object_size (&data_obstack)));
     }
-
-  do_cleanups (cleanup);
-  return note_data;
-}
-
-/* Structure for passing information from
-   linux_collect_thread_registers via an iterator to
-   linux_collect_regset_section_cb. */
-
-struct linux_collect_regset_section_cb_data
-{
-  struct gdbarch *gdbarch;
-  const struct regcache *regcache;
-  bfd *obfd;
-  char *note_data;
-  int *note_size;
-  unsigned long lwp;
-  enum gdb_signal stop_signal;
-  int abort_iteration;
-};
-
-/* Callback for iterate_over_regset_sections that records a single
-   regset in the corefile note section.  */
-
-static void
-linux_collect_regset_section_cb (const char *sect_name, int size,
-                                const struct regset *regset,
-                                const char *human_name, void *cb_data)
-{
-  char *buf;
-  struct linux_collect_regset_section_cb_data *data = cb_data;
-
-  if (data->abort_iteration)
-    return;
-
-  gdb_assert (regset && regset->collect_regset);
-
-  buf = xmalloc (size);
-  regset->collect_regset (regset, data->regcache, -1, buf, size);
-
-  /* PRSTATUS still needs to be treated specially.  */
-  if (strcmp (sect_name, ".reg") == 0)
-    data->note_data = (char *) elfcore_write_prstatus
-      (data->obfd, data->note_data, data->note_size, data->lwp,
-       gdb_signal_to_host (data->stop_signal), buf);
-  else
-    data->note_data = (char *) elfcore_write_register_note
-      (data->obfd, data->note_data, data->note_size,
-       sect_name, buf, size);
-  xfree (buf);
-
-  if (data->note_data == NULL)
-    data->abort_iteration = 1;
-}
-
-/* Records the thread's register state for the corefile note
-   section.  */
-
-static char *
-linux_collect_thread_registers (const struct regcache *regcache,
-                               ptid_t ptid, bfd *obfd,
-                               char *note_data, int *note_size,
-                               enum gdb_signal stop_signal)
-{
-  struct gdbarch *gdbarch = get_regcache_arch (regcache);
-  struct linux_collect_regset_section_cb_data data;
-
-  data.gdbarch = gdbarch;
-  data.regcache = regcache;
-  data.obfd = obfd;
-  data.note_data = note_data;
-  data.note_size = note_size;
-  data.stop_signal = stop_signal;
-  data.abort_iteration = 0;
-
-  /* For remote targets the LWP may not be available, so use the TID.  */
-  data.lwp = ptid_get_lwp (ptid);
-  if (!data.lwp)
-    data.lwp = ptid_get_tid (ptid);
-
-  gdbarch_iterate_over_regset_sections (gdbarch,
-                                       linux_collect_regset_section_cb,
-                                       &data, regcache);
-  return data.note_data;
 }
 
-/* Fetch the siginfo data for the current thread, if it exists.  If
-   there is no data, or we could not read it, return NULL.  Otherwise,
-   return a newly malloc'd buffer holding the data and fill in *SIZE
-   with the size of the data.  The caller is responsible for freeing
-   the data.  */
+/* Fetch the siginfo data for the specified thread, if it exists.  If
+   there is no data, or we could not read it, return an empty
+   buffer.  */
 
-static gdb_byte *
-linux_get_siginfo_data (struct gdbarch *gdbarch, LONGEST *size)
+static gdb::byte_vector
+linux_get_siginfo_data (thread_info *thread, struct gdbarch *gdbarch)
 {
   struct type *siginfo_type;
-  gdb_byte *buf;
   LONGEST bytes_read;
-  struct cleanup *cleanups;
 
   if (!gdbarch_get_siginfo_type_p (gdbarch))
-    return NULL;
-  
+    return gdb::byte_vector ();
+
+  scoped_restore_current_thread save_current_thread;
+  switch_to_thread (thread);
+
   siginfo_type = gdbarch_get_siginfo_type (gdbarch);
 
-  buf = xmalloc (TYPE_LENGTH (siginfo_type));
-  cleanups = make_cleanup (xfree, buf);
+  gdb::byte_vector buf (TYPE_LENGTH (siginfo_type));
 
-  bytes_read = target_read (&current_target, TARGET_OBJECT_SIGNAL_INFO, NULL,
-                           buf, 0, TYPE_LENGTH (siginfo_type));
-  if (bytes_read == TYPE_LENGTH (siginfo_type))
-    {
-      discard_cleanups (cleanups);
-      *size = bytes_read;
-    }
-  else
-    {
-      do_cleanups (cleanups);
-      buf = NULL;
-    }
+  bytes_read = target_read (current_inferior ()->top_target (),
+                           TARGET_OBJECT_SIGNAL_INFO, NULL,
+                           buf.data (), 0, TYPE_LENGTH (siginfo_type));
+  if (bytes_read != TYPE_LENGTH (siginfo_type))
+    buf.clear ();
 
   return buf;
 }
 
 struct linux_corefile_thread_data
 {
+  linux_corefile_thread_data (struct gdbarch *gdbarch, bfd *obfd,
+                             gdb::unique_xmalloc_ptr<char> &note_data,
+                             int *note_size, gdb_signal stop_signal)
+    : gdbarch (gdbarch), obfd (obfd), note_data (note_data),
+      note_size (note_size), stop_signal (stop_signal)
+  {}
+
   struct gdbarch *gdbarch;
-  int pid;
   bfd *obfd;
-  char *note_data;
+  gdb::unique_xmalloc_ptr<char> &note_data;
   int *note_size;
   enum gdb_signal stop_signal;
 };
 
-/* Called by gdbthread.c once per thread.  Records the thread's
-   register state for the corefile note section.  */
+/* Records the thread's register state for the corefile note
+   section.  */
 
-static int
-linux_corefile_thread_callback (struct thread_info *info, void *data)
+static void
+linux_corefile_thread (struct thread_info *info,
+                      struct linux_corefile_thread_data *args)
 {
-  struct linux_corefile_thread_data *args = data;
-
-  /* It can be current thread
-     which cannot be removed by update_thread_list.  */
-  if (info->state == THREAD_EXITED)
-    return 0;
-
-  if (ptid_get_pid (info->ptid) == args->pid)
+  gcore_elf_build_thread_register_notes (args->gdbarch, info,
+                                        args->stop_signal,
+                                        args->obfd, &args->note_data,
+                                        args->note_size);
+
+  /* Don't return anything if we got no register information above,
+     such a core file is useless.  */
+  if (args->note_data != NULL)
     {
-      struct cleanup *old_chain;
-      struct regcache *regcache;
-      gdb_byte *siginfo_data;
-      LONGEST siginfo_size = 0;
-
-      regcache = get_thread_arch_regcache (info->ptid, args->gdbarch);
-
-      old_chain = save_inferior_ptid ();
-      inferior_ptid = info->ptid;
-      target_fetch_registers (regcache, -1);
-      siginfo_data = linux_get_siginfo_data (args->gdbarch, &siginfo_size);
-      do_cleanups (old_chain);
-
-      old_chain = make_cleanup (xfree, siginfo_data);
-
-      args->note_data = linux_collect_thread_registers
-       (regcache, info->ptid, args->obfd, args->note_data,
-        args->note_size, args->stop_signal);
-
-      /* Don't return anything if we got no register information above,
-         such a core file is useless.  */
-      if (args->note_data != NULL)
-       if (siginfo_data != NULL)
-         args->note_data = elfcore_write_note (args->obfd,
-                                               args->note_data,
-                                               args->note_size,
-                                               "CORE", NT_SIGINFO,
-                                               siginfo_data, siginfo_size);
-
-      do_cleanups (old_chain);
+      gdb::byte_vector siginfo_data
+       = linux_get_siginfo_data (info, args->gdbarch);
+      if (!siginfo_data.empty ())
+       args->note_data.reset (elfcore_write_note (args->obfd,
+                                                  args->note_data.release (),
+                                                  args->note_size,
+                                                  "CORE", NT_SIGINFO,
+                                                  siginfo_data.data (),
+                                                  siginfo_data.size ()));
     }
-
-  return !args->note_data;
 }
 
 /* Fill the PRPSINFO structure with information about the process being
@@ -1721,15 +1802,9 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
   /* The filename which we will use to obtain some info about the process.
      We will basically use this to store the `/proc/PID/FILENAME' file.  */
   char filename[100];
-  /* The full name of the program which generated the corefile.  */
-  char *fname;
   /* The basename of the executable.  */
   const char *basename;
-  /* The arguments of the program.  */
-  char *psargs;
-  char *infargs;
-  /* The contents of `/proc/PID/stat' and `/proc/PID/status' files.  */
-  char *proc_stat, *proc_status;
+  const char *infargs;
   /* Temporary buffer.  */
   char *tmpstr;
   /* The valid states of a process, according to the Linux kernel.  */
@@ -1746,57 +1821,54 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
   long pr_nice;
   /* The number of fields read by `sscanf'.  */
   int n_fields = 0;
-  /* Cleanups.  */
-  struct cleanup *c;
-  int i;
 
   gdb_assert (p != NULL);
 
   /* Obtaining PID and filename.  */
-  pid = ptid_get_pid (inferior_ptid);
+  pid = inferior_ptid.pid ();
   xsnprintf (filename, sizeof (filename), "/proc/%d/cmdline", (int) pid);
-  fname = target_fileio_read_stralloc (filename);
+  /* The full name of the program which generated the corefile.  */
+  gdb::unique_xmalloc_ptr<char> fname
+    = target_fileio_read_stralloc (NULL, filename);
 
-  if (fname == NULL || *fname == '\0')
+  if (fname == NULL || fname.get ()[0] == '\0')
     {
       /* No program name was read, so we won't be able to retrieve more
         information about the process.  */
-      xfree (fname);
       return 0;
     }
 
-  c = make_cleanup (xfree, fname);
   memset (p, 0, sizeof (*p));
 
   /* Defining the PID.  */
   p->pr_pid = pid;
 
   /* Copying the program name.  Only the basename matters.  */
-  basename = lbasename (fname);
-  strncpy (p->pr_fname, basename, sizeof (p->pr_fname));
+  basename = lbasename (fname.get ());
+  strncpy (p->pr_fname, basename, sizeof (p->pr_fname) - 1);
   p->pr_fname[sizeof (p->pr_fname) - 1] = '\0';
 
   infargs = get_inferior_args ();
 
-  psargs = xstrdup (fname);
+  /* The arguments of the program.  */
+  std::string psargs = fname.get ();
   if (infargs != NULL)
-    psargs = reconcat (psargs, psargs, " ", infargs, NULL);
-
-  make_cleanup (xfree, psargs);
+    psargs = psargs + " " + infargs;
 
-  strncpy (p->pr_psargs, psargs, sizeof (p->pr_psargs));
+  strncpy (p->pr_psargs, psargs.c_str (), sizeof (p->pr_psargs) - 1);
   p->pr_psargs[sizeof (p->pr_psargs) - 1] = '\0';
 
   xsnprintf (filename, sizeof (filename), "/proc/%d/stat", (int) pid);
-  proc_stat = target_fileio_read_stralloc (filename);
-  make_cleanup (xfree, proc_stat);
+  /* The contents of `/proc/PID/stat'.  */
+  gdb::unique_xmalloc_ptr<char> proc_stat_contents
+    = target_fileio_read_stralloc (NULL, filename);
+  char *proc_stat = proc_stat_contents.get ();
 
   if (proc_stat == NULL || *proc_stat == '\0')
     {
       /* Despite being unable to read more information about the
         process, we return 1 here because at least we have its
         command line, PID and arguments.  */
-      do_cleanups (c);
       return 1;
     }
 
@@ -1818,10 +1890,7 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
   /* ps command also relies on no trailing fields ever contain ')'.  */
   proc_stat = strrchr (proc_stat, ')');
   if (proc_stat == NULL)
-    {
-      do_cleanups (c);
-      return 1;
-    }
+    return 1;
   proc_stat++;
 
   proc_stat = skip_spaces (proc_stat);
@@ -1847,7 +1916,6 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
       /* Again, we couldn't read the complementary information about
         the process state.  However, we already have minimal
         information, so we just return 1 here.  */
-      do_cleanups (c);
       return 1;
     }
 
@@ -1869,13 +1937,14 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
   /* Finally, obtaining the UID and GID.  For that, we read and parse the
      contents of the `/proc/PID/status' file.  */
   xsnprintf (filename, sizeof (filename), "/proc/%d/status", (int) pid);
-  proc_status = target_fileio_read_stralloc (filename);
-  make_cleanup (xfree, proc_status);
+  /* The contents of `/proc/PID/status'.  */
+  gdb::unique_xmalloc_ptr<char> proc_status_contents
+    = target_fileio_read_stralloc (NULL, filename);
+  char *proc_status = proc_status_contents.get ();
 
   if (proc_status == NULL || *proc_status == '\0')
     {
       /* Returning 1 since we already have a bunch of information.  */
-      do_cleanups (c);
       return 1;
     }
 
@@ -1905,90 +1974,88 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
        p->pr_gid = strtol (tmpstr, &tmpstr, 10);
     }
 
-  do_cleanups (c);
-
   return 1;
 }
 
 /* Build the note section for a corefile, and return it in a malloc
    buffer.  */
 
-static char *
+static gdb::unique_xmalloc_ptr<char>
 linux_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size)
 {
-  struct linux_corefile_thread_data thread_args;
   struct elf_internal_linux_prpsinfo prpsinfo;
-  char *note_data = NULL;
-  gdb_byte *auxv;
-  int auxv_len;
+  gdb::unique_xmalloc_ptr<char> note_data;
 
   if (! gdbarch_iterate_over_regset_sections_p (gdbarch))
     return NULL;
 
   if (linux_fill_prpsinfo (&prpsinfo))
     {
-      if (gdbarch_elfcore_write_linux_prpsinfo_p (gdbarch))
-       {
-         note_data = gdbarch_elfcore_write_linux_prpsinfo (gdbarch, obfd,
-                                                           note_data, note_size,
-                                                           &prpsinfo);
-       }
+      if (gdbarch_ptr_bit (gdbarch) == 64)
+       note_data.reset (elfcore_write_linux_prpsinfo64 (obfd,
+                                                        note_data.release (),
+                                                        note_size, &prpsinfo));
       else
-       {
-         if (gdbarch_ptr_bit (gdbarch) == 64)
-           note_data = elfcore_write_linux_prpsinfo64 (obfd,
-                                                       note_data, note_size,
-                                                       &prpsinfo);
-         else
-           note_data = elfcore_write_linux_prpsinfo32 (obfd,
-                                                       note_data, note_size,
-                                                       &prpsinfo);
-       }
+       note_data.reset (elfcore_write_linux_prpsinfo32 (obfd,
+                                                        note_data.release (),
+                                                        note_size, &prpsinfo));
     }
 
   /* Thread register information.  */
-  TRY
+  try
     {
       update_thread_list ();
     }
-  CATCH (e, RETURN_MASK_ERROR)
+  catch (const gdb_exception_error &e)
     {
       exception_print (gdb_stderr, e);
     }
-  END_CATCH
-
-  thread_args.gdbarch = gdbarch;
-  thread_args.pid = ptid_get_pid (inferior_ptid);
-  thread_args.obfd = obfd;
-  thread_args.note_data = note_data;
-  thread_args.note_size = note_size;
-  thread_args.stop_signal = find_stop_signal ();
-  iterate_over_threads (linux_corefile_thread_callback, &thread_args);
-  note_data = thread_args.note_data;
+
+  /* Like the kernel, prefer dumping the signalled thread first.
+     "First thread" is what tools use to infer the signalled
+     thread.  */
+  thread_info *signalled_thr = gcore_find_signalled_thread ();
+  gdb_signal stop_signal;
+  if (signalled_thr != nullptr)
+    stop_signal = signalled_thr->suspend.stop_signal;
+  else
+    stop_signal = GDB_SIGNAL_0;
+
+  linux_corefile_thread_data thread_args (gdbarch, obfd, note_data, note_size,
+                                         stop_signal);
+
+  if (signalled_thr != nullptr)
+    linux_corefile_thread (signalled_thr, &thread_args);
+  for (thread_info *thr : current_inferior ()->non_exited_threads ())
+    {
+      if (thr == signalled_thr)
+       continue;
+
+      linux_corefile_thread (thr, &thread_args);
+    }
+
   if (!note_data)
     return NULL;
 
   /* Auxillary vector.  */
-  auxv_len = target_read_alloc (&current_target, TARGET_OBJECT_AUXV,
-                               NULL, &auxv);
-  if (auxv_len > 0)
+  gdb::optional<gdb::byte_vector> auxv =
+    target_read_alloc (current_inferior ()->top_target (),
+                      TARGET_OBJECT_AUXV, NULL);
+  if (auxv && !auxv->empty ())
     {
-      note_data = elfcore_write_note (obfd, note_data, note_size,
-                                     "CORE", NT_AUXV, auxv, auxv_len);
-      xfree (auxv);
+      note_data.reset (elfcore_write_note (obfd, note_data.release (),
+                                          note_size, "CORE", NT_AUXV,
+                                          auxv->data (), auxv->size ()));
 
       if (!note_data)
        return NULL;
     }
 
-  /* SPU information.  */
-  note_data = linux_spu_make_corefile_notes (obfd, note_data, note_size);
-  if (!note_data)
-    return NULL;
-
   /* File mappings.  */
-  note_data = linux_make_mappings_corefile_notes (gdbarch, obfd,
-                                                 note_data, note_size);
+  linux_make_mappings_corefile_notes (gdbarch, obfd, note_data, note_size);
+
+  /* Target description.  */
+  gcore_elf_make_tdesc_note (obfd, &note_data, note_size);
 
   return note_data;
 }
@@ -2244,39 +2311,92 @@ linux_gdb_signal_to_target (struct gdbarch *gdbarch,
   return -1;
 }
 
-/* Rummage through mappings to find a mapping's size.  */
+/* Helper for linux_vsyscall_range that does the real work of finding
+   the vsyscall's address range.  */
 
 static int
-find_mapping_size (CORE_ADDR vaddr, unsigned long size,
-                  int read, int write, int exec, int modified,
-                  void *data)
+linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
 {
-  struct mem_range *range = data;
+  char filename[100];
+  long pid;
 
-  if (vaddr == range->start)
+  if (target_auxv_search (current_inferior ()->top_target (),
+                         AT_SYSINFO_EHDR, &range->start) <= 0)
+    return 0;
+
+  /* It doesn't make sense to access the host's /proc when debugging a
+     core file.  Instead, look for the PT_LOAD segment that matches
+     the vDSO.  */
+  if (!target_has_execution ())
     {
-      range->length = size;
-      return 1;
-    }
-  return 0;
-}
+      long phdrs_size;
+      int num_phdrs, i;
 
-/* Helper for linux_vsyscall_range that does the real work of finding
-   the vsyscall's address range.  */
+      phdrs_size = bfd_get_elf_phdr_upper_bound (core_bfd);
+      if (phdrs_size == -1)
+       return 0;
 
-static int
-linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
-{
-  if (target_auxv_search (&current_target, AT_SYSINFO_EHDR, &range->start) <= 0)
+      gdb::unique_xmalloc_ptr<Elf_Internal_Phdr>
+       phdrs ((Elf_Internal_Phdr *) xmalloc (phdrs_size));
+      num_phdrs = bfd_get_elf_phdrs (core_bfd, phdrs.get ());
+      if (num_phdrs == -1)
+       return 0;
+
+      for (i = 0; i < num_phdrs; i++)
+       if (phdrs.get ()[i].p_type == PT_LOAD
+           && phdrs.get ()[i].p_vaddr == range->start)
+         {
+           range->length = phdrs.get ()[i].p_memsz;
+           return 1;
+         }
+
+      return 0;
+    }
+
+  /* We need to know the real target PID to access /proc.  */
+  if (current_inferior ()->fake_pid_p)
     return 0;
 
-  /* This is installed by linux_init_abi below, so should always be
-     available.  */
-  gdb_assert (gdbarch_find_memory_regions_p (target_gdbarch ()));
+  pid = current_inferior ()->pid;
 
-  range->length = 0;
-  gdbarch_find_memory_regions (gdbarch, find_mapping_size, range);
-  return 1;
+  /* Note that reading /proc/PID/task/PID/maps (1) is much faster than
+     reading /proc/PID/maps (2).  The later identifies thread stacks
+     in the output, which requires scanning every thread in the thread
+     group to check whether a VMA is actually a thread's stack.  With
+     Linux 4.4 on an Intel i7-4810MQ @ 2.80GHz, with an inferior with
+     a few thousand threads, (1) takes a few miliseconds, while (2)
+     takes several seconds.  Also note that "smaps", what we read for
+     determining core dump mappings, is even slower than "maps".  */
+  xsnprintf (filename, sizeof filename, "/proc/%ld/task/%ld/maps", pid, pid);
+  gdb::unique_xmalloc_ptr<char> data
+    = target_fileio_read_stralloc (NULL, filename);
+  if (data != NULL)
+    {
+      char *line;
+      char *saveptr = NULL;
+
+      for (line = strtok_r (data.get (), "\n", &saveptr);
+          line != NULL;
+          line = strtok_r (NULL, "\n", &saveptr))
+       {
+         ULONGEST addr, endaddr;
+         const char *p = line;
+
+         addr = strtoulst (p, &p, 16);
+         if (addr == range->start)
+           {
+             if (*p == '-')
+               p++;
+             endaddr = strtoulst (p, &p, 16);
+             range->length = endaddr - addr;
+             return 1;
+           }
+       }
+    }
+  else
+    warning (_("unable to open /proc file '%s'"), filename);
+
+  return 0;
 }
 
 /* Implementation of the "vsyscall_range" gdbarch hook.  Handles
@@ -2285,7 +2405,7 @@ linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
 static int
 linux_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range)
 {
-  struct linux_info *info = get_linux_inferior_data ();
+  struct linux_info *info = get_linux_inferior_data (current_inferior ());
 
   if (info->vsyscall_range_p == 0)
     {
@@ -2317,7 +2437,7 @@ linux_infcall_mmap (CORE_ADDR size, unsigned prot)
      "mmap" uses 64-bit off_t on x86_64 and 32-bit off_t on i386 and x32.  */
   struct value *mmap_val = find_function_in_inferior ("mmap64", &objf);
   struct value *addr_val;
-  struct gdbarch *gdbarch = get_objfile_arch (objf);
+  struct gdbarch *gdbarch = objf->arch ();
   CORE_ADDR retval;
   enum
     {
@@ -2340,7 +2460,7 @@ linux_infcall_mmap (CORE_ADDR size, unsigned prot)
   arg[ARG_FD] = value_from_longest (builtin_type (gdbarch)->builtin_int, -1);
   arg[ARG_OFFSET] = value_from_longest (builtin_type (gdbarch)->builtin_int64,
                                        0);
-  addr_val = call_function_by_hand (mmap_val, ARG_LAST, arg);
+  addr_val = call_function_by_hand (mmap_val, NULL, arg);
   retval = value_as_address (addr_val);
   if (retval == (CORE_ADDR) -1)
     error (_("Failed inferior mmap call for %s bytes, errno is changed."),
@@ -2356,7 +2476,7 @@ linux_infcall_munmap (CORE_ADDR addr, CORE_ADDR size)
   struct objfile *objf;
   struct value *munmap_val = find_function_in_inferior ("munmap", &objf);
   struct value *retval_val;
-  struct gdbarch *gdbarch = get_objfile_arch (objf);
+  struct gdbarch *gdbarch = objf->arch ();
   LONGEST retval;
   enum
     {
@@ -2369,7 +2489,7 @@ linux_infcall_munmap (CORE_ADDR addr, CORE_ADDR size)
   /* Assuming sizeof (unsigned long) == sizeof (size_t).  */
   arg[ARG_LENGTH] = value_from_ulongest
                    (builtin_type (gdbarch)->builtin_unsigned_long, size);
-  retval_val = call_function_by_hand (munmap_val, ARG_LAST, arg);
+  retval_val = call_function_by_hand (munmap_val, NULL, arg);
   retval = value_as_long (retval_val);
   if (retval != 0)
     warning (_("Failed inferior munmap call at %s for %s bytes, "
@@ -2391,13 +2511,15 @@ linux_displaced_step_location (struct gdbarch *gdbarch)
      local-store address and is thus not usable as displaced stepping
      location.  The auxiliary vector gets us the PowerPC-side entry
      point address instead.  */
-  if (target_auxv_search (&current_target, AT_ENTRY, &addr) <= 0)
-    error (_("Cannot find AT_ENTRY auxiliary vector entry."));
+  if (target_auxv_search (current_inferior ()->top_target (),
+                         AT_ENTRY, &addr) <= 0)
+    throw_error (NOT_SUPPORTED_ERROR,
+                _("Cannot find AT_ENTRY auxiliary vector entry."));
 
   /* Make certain that the address points at real code, and not a
      function descriptor.  */
-  addr = gdbarch_convert_from_func_ptr_addr (gdbarch, addr,
-                                            &current_target);
+  addr = gdbarch_convert_from_func_ptr_addr
+    (gdbarch, addr, current_inferior ()->top_target ());
 
   /* Inferior calls also use the entry point as a breakpoint location.
      We don't want displaced stepping to interfere with those
@@ -2408,6 +2530,97 @@ linux_displaced_step_location (struct gdbarch *gdbarch)
   return addr;
 }
 
+/* See linux-tdep.h.  */
+
+displaced_step_prepare_status
+linux_displaced_step_prepare (gdbarch *arch, thread_info *thread,
+                             CORE_ADDR &displaced_pc)
+{
+  linux_info *per_inferior = get_linux_inferior_data (thread->inf);
+
+  if (!per_inferior->disp_step_bufs.has_value ())
+    {
+      /* Figure out the location of the buffers.  They are contiguous, starting
+        at DISP_STEP_BUF_ADDR.  They are all of size BUF_LEN.  */
+      CORE_ADDR disp_step_buf_addr
+       = linux_displaced_step_location (thread->inf->gdbarch);
+      int buf_len = gdbarch_max_insn_length (arch);
+
+      linux_gdbarch_data *gdbarch_data = get_linux_gdbarch_data (arch);
+      gdb_assert (gdbarch_data->num_disp_step_buffers > 0);
+
+      std::vector<CORE_ADDR> buffers;
+      for (int i = 0; i < gdbarch_data->num_disp_step_buffers; i++)
+       buffers.push_back (disp_step_buf_addr + i * buf_len);
+
+      per_inferior->disp_step_bufs.emplace (buffers);
+    }
+
+  return per_inferior->disp_step_bufs->prepare (thread, displaced_pc);
+}
+
+/* See linux-tdep.h.  */
+
+displaced_step_finish_status
+linux_displaced_step_finish (gdbarch *arch, thread_info *thread, gdb_signal sig)
+{
+  linux_info *per_inferior = get_linux_inferior_data (thread->inf);
+
+  gdb_assert (per_inferior->disp_step_bufs.has_value ());
+
+  return per_inferior->disp_step_bufs->finish (arch, thread, sig);
+}
+
+/* See linux-tdep.h.  */
+
+const displaced_step_copy_insn_closure *
+linux_displaced_step_copy_insn_closure_by_addr (inferior *inf, CORE_ADDR addr)
+{
+  linux_info *per_inferior = linux_inferior_data.get (inf);
+
+  if (per_inferior == nullptr
+      || !per_inferior->disp_step_bufs.has_value ())
+    return nullptr;
+
+  return per_inferior->disp_step_bufs->copy_insn_closure_by_addr (addr);
+}
+
+/* See linux-tdep.h.  */
+
+void
+linux_displaced_step_restore_all_in_ptid (inferior *parent_inf, ptid_t ptid)
+{
+  linux_info *per_inferior = linux_inferior_data.get (parent_inf);
+
+  if (per_inferior == nullptr
+      || !per_inferior->disp_step_bufs.has_value ())
+    return;
+
+  per_inferior->disp_step_bufs->restore_in_ptid (ptid);
+}
+
+/* See linux-tdep.h.  */
+
+CORE_ADDR
+linux_get_hwcap (struct target_ops *target)
+{
+  CORE_ADDR field;
+  if (target_auxv_search (target, AT_HWCAP, &field) != 1)
+    return 0;
+  return field;
+}
+
+/* See linux-tdep.h.  */
+
+CORE_ADDR
+linux_get_hwcap2 (struct target_ops *target)
+{
+  CORE_ADDR field;
+  if (target_auxv_search (target, AT_HWCAP2, &field) != 1)
+    return 0;
+  return field;
+}
+
 /* Display whether the gcore command is using the
    /proc/PID/coredump_filter file.  */
 
@@ -2419,15 +2632,46 @@ show_use_coredump_filter (struct ui_file *file, int from_tty,
                            " corefiles is %s.\n"), value);
 }
 
+/* Display whether the gcore command is dumping mappings marked with
+   the VM_DONTDUMP flag.  */
+
+static void
+show_dump_excluded_mappings (struct ui_file *file, int from_tty,
+                            struct cmd_list_element *c, const char *value)
+{
+  fprintf_filtered (file, _("Dumping of mappings marked with the VM_DONTDUMP"
+                           " flag is %s.\n"), value);
+}
+
 /* To be called from the various GDB_OSABI_LINUX handlers for the
-   various GNU/Linux architectures and machine types.  */
+   various GNU/Linux architectures and machine types.
+
+   NUM_DISP_STEP_BUFFERS is the number of displaced step buffers to use.  If 0,
+   displaced stepping is not supported. */
 
 void
-linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch,
+               int num_disp_step_buffers)
 {
+  if (num_disp_step_buffers > 0)
+    {
+      linux_gdbarch_data *gdbarch_data = get_linux_gdbarch_data (gdbarch);
+      gdbarch_data->num_disp_step_buffers = num_disp_step_buffers;
+
+      set_gdbarch_displaced_step_prepare (gdbarch,
+                                         linux_displaced_step_prepare);
+      set_gdbarch_displaced_step_finish (gdbarch, linux_displaced_step_finish);
+      set_gdbarch_displaced_step_copy_insn_closure_by_addr
+       (gdbarch, linux_displaced_step_copy_insn_closure_by_addr);
+      set_gdbarch_displaced_step_restore_all_in_ptid
+       (gdbarch, linux_displaced_step_restore_all_in_ptid);
+    }
+
   set_gdbarch_core_pid_to_str (gdbarch, linux_core_pid_to_str);
   set_gdbarch_info_proc (gdbarch, linux_info_proc);
   set_gdbarch_core_info_proc (gdbarch, linux_core_info_proc);
+  set_gdbarch_core_xfer_siginfo (gdbarch, linux_core_xfer_siginfo);
+  set_gdbarch_read_core_file_mappings (gdbarch, linux_read_core_file_mappings);
   set_gdbarch_find_memory_regions (gdbarch, linux_find_memory_regions);
   set_gdbarch_make_corefile_notes (gdbarch, linux_make_corefile_notes);
   set_gdbarch_has_shared_address_space (gdbarch,
@@ -2439,23 +2683,23 @@ linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
   set_gdbarch_vsyscall_range (gdbarch, linux_vsyscall_range);
   set_gdbarch_infcall_mmap (gdbarch, linux_infcall_mmap);
   set_gdbarch_infcall_munmap (gdbarch, linux_infcall_munmap);
+  set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type);
 }
 
-/* Provide a prototype to silence -Wmissing-prototypes.  */
-extern initialize_file_ftype _initialize_linux_tdep;
-
+void _initialize_linux_tdep ();
 void
-_initialize_linux_tdep (void)
+_initialize_linux_tdep ()
 {
   linux_gdbarch_data_handle =
-    gdbarch_data_register_post_init (init_linux_gdbarch_data);
+    gdbarch_data_register_pre_init (init_linux_gdbarch_data);
 
-  /* Set a cache per-inferior.  */
-  linux_inferior_data
-    = register_inferior_data_with_cleanup (NULL, linux_inferior_data_cleanup);
   /* Observers used to invalidate the cache when needed.  */
-  observer_attach_inferior_exit (invalidate_linux_cache_inf);
-  observer_attach_inferior_appeared (invalidate_linux_cache_inf);
+  gdb::observers::inferior_exit.attach (invalidate_linux_cache_inf,
+                                       "linux-tdep");
+  gdb::observers::inferior_appeared.attach (invalidate_linux_cache_inf,
+                                           "linux-tdep");
+  gdb::observers::inferior_execd.attach (invalidate_linux_cache_inf,
+                                        "linux-tdep");
 
   add_setshow_boolean_cmd ("use-coredump-filter", class_files,
                           &use_coredump_filter, _("\
@@ -2468,4 +2712,16 @@ of /proc/PID/coredump_filter when generating the corefile.  For more information
 about this file, refer to the manpage of core(5)."),
                           NULL, show_use_coredump_filter,
                           &setlist, &showlist);
+
+  add_setshow_boolean_cmd ("dump-excluded-mappings", class_files,
+                          &dump_excluded_mappings, _("\
+Set whether gcore should dump mappings marked with the VM_DONTDUMP flag."),
+                          _("\
+Show whether gcore should dump mappings marked with the VM_DONTDUMP flag."),
+                          _("\
+Use this command to set whether gcore should dump mappings marked with the\n\
+VM_DONTDUMP flag (\"dd\" in /proc/PID/smaps) when generating the corefile.  For\n\
+more information about this file, refer to the manpage of proc(5) and core(5)."),
+                          NULL, show_dump_excluded_mappings,
+                          &setlist, &showlist);
 }
This page took 0.05414 seconds and 4 git commands to generate.