[1/2] Implement support for checking /proc/PID/coredump_filter

Message ID 1427241443-5939-2-git-send-email-sergiodj@redhat.com
State New, archived
Headers

Commit Message

Sergio Durigan Junior March 24, 2015, 11:57 p.m. UTC
  This patch, as the subject says, extends GDB so that it is able to use
the contents of the file /proc/PID/coredump_filter when generating a
corefile.  This file contains a bit mask that is a representation of
the different types of memory mappings in the Linux kernel; the user
can choose to dump or not dump a certain type of memory mapping by
enabling/disabling the respective bit in the bit mask.  Currently,
here is what is supported:

  bit 0  Dump anonymous private mappings.
  bit 1  Dump anonymous shared mappings.
  bit 2  Dump file-backed private mappings.
  bit 3  Dump file-backed shared mappings.
  bit 4 (since Linux 2.6.24)
         Dump ELF headers.
  bit 5 (since Linux 2.6.28)
         Dump private huge pages.
  bit 6 (since Linux 2.6.28)
         Dump shared huge pages.

(This table has been taken from core(5), but you can also read about it
on Documentation/filesystems/proc.txt inside the Linux kernel source
tree).

The default value for this file, used by the Linux kernel, is 0x33,
which means that bits 0, 1, 4 and 5 are enabled.  This is also the
default for GDB implemented in this patch, FWIW.

Well, reading the file is obviously trivial.  The hard part, mind you,
is how to determine the types of the memory mappings.  For that, I
extended the code of gdb/linux-tdep.c:linux_find_memory_regions_full and
made it rely *much more* on the information gathered from
/proc/<PID>/smaps.  This file contains a "verbose dump" of the
inferior's memory mappings, and we were not using as much information as
we could from it.  If you want to read more about this file, take a look
at the proc(5) manpage (I will also write a blog post soon about
everything I had to learn to get this patch done, and when I it is ready
I will post it here).

With Oleg Nesterov's help, we could improve the current algorithm for
determining whether a memory mapping is anonymous/file-backed,
private/shared.  GDB now also respects the MADV_DONTDUMP flag and does
not dump the memory mapping marked as so, and will always dump
"[vsyscall]" or "[vdso]" mappings (just like the Linux kernel).

In a nutshell, what the new code is doing is:

- If the mapping is associated to a file whose name ends with
  " (deleted)", or if the file is "/dev/zero", or if it is "/SYSV%08x"
  (shared memory), or if there is no file associated with it, or if
  the AnonHugePages: or the Anonymous: fields in the /proc/PID/smaps
  have contents, then GDB considers this mapping to be anonymous.
  There is a special case in this, though: if the memory mapping is a
  file-backed one, but *also* contains "Anonymous:" or
  "AnonHugePages:" pages, then GDB considers this mapping to be *both*
  anonymous and file-backed, just like the Linux kernel does.  What
  that means is simple: this mapping will be dumped if the user
  requested anonymous mappings *or* if the user requested file-backed
  mappings to be present in the corefile.

  It is worth mentioning that, from all those checks described above,
  the most fragile is the one to see if the file name ends with
  " (deleted)".  This does not necessarily mean that the mapping is
  anonymous, because the deleted file associated with the mapping may
  have been a hard link to another file, for example.  The Linux
  kernel checks to see if "i_nlink == 0", but GDB cannot easily do
  this check (as it has been discussed, GDB would need to run as root,
  and would need to check the contents of the /proc/PID/map_files/
  directory in order to determine whether the deleted was a hardlink
  or not).  Therefore, we made a compromise here, and we assume that
  if the file name ends with " (deleted)", then the mapping is indeed
  anonymous.  FWIW, this is something the Linux kernel could do
  better: expose this information in a more direct way.

- If we see the flag "sh" in the VmFlags: field (in /proc/PID/smaps),
  then certainly the memory mapping is shared (VM_SHARED).  If we have
  access to the VmFlags, and we don't see the "sh" there, then
  certainly the mapping is private.  However, older Linux kernels (see
  the code for more details) do not have the VmFlags field; in that
  case, we use another heuristic: if we 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.

Finally, it is worth mentioning that I added a new command, 'set
use-coredump-filter on/off'.  When it is 'on', it will read the
coredump_filter' file (if it exists) and use its value; otherwise, it
will use the default value mentioned above (0x33) to decide which memory
mappings to dump.

Changes from v4:

  - GDB is now handling the "file-backed mapping with anonymous pages"
    case just like the Linux kernel.

gdb/ChangeLog:
2015-03-24  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Oleg Nesterov  <oleg@redhat.com>

	PR corefiles/16092
	* linux-tdep.c: Include 'gdbcmd.h' and 'gdb_regex.h'.
	New enum identifying the various options of the coredump_filter
	file.
	(struct smaps_vmflags): New struct.
	(use_coredump_filter): New variable.
	(decode_vmflags): New function.
	(mapping_is_anonymous_p): Likewise.
	(dump_mapping_p): Likewise.
	(linux_find_memory_regions_full): New variables
	'coredumpfilter_name', 'coredumpfilterdata', 'pid', 'filterflags'.
	Removed variable 'modified'.  Read /proc/<PID>/smaps file; improve
	parsing of its information.  Implement memory mapping filtering
	based on its contents.
	(show_use_coredump_filter): New function.
	(_initialize_linux_tdep): New command 'set use-coredump-filter'.
---
 gdb/linux-tdep.c | 455 +++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 427 insertions(+), 28 deletions(-)
  

Comments

Pedro Alves March 27, 2015, 9:53 a.m. UTC | #1
OK.

Thanks,
Pedro Alves
  
Sergio Durigan Junior March 31, 2015, 11:40 p.m. UTC | #2
On Friday, March 27 2015, Pedro Alves wrote:

> OK.

Thanks, pushed both patches:

  <https://sourceware.org/ml/gdb-cvs/2015-03/msg00275.html>
  df8411da087dc05481926f4c4a82deabc5bc3859
  
Pierre Muller April 8, 2015, 2:07 p.m. UTC | #3
Hi all,

The approved patch "Implement support for checking /proc/PID/coredump_filter
commit"
committed April 1. 2015:
https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=commitdiff;h=df84
11da087dc05481926f4c4a82deabc5bc3859

breaks mingw64 with --enable-targets=all configure option.

The reason is the introduction of strtok_r function in gdb/linux-tdep.c
source.
This function is not present in my version of x86_64-w64-mingw32
cross-system (on a Cygwin system).

The function is found in string.h header on native Cygwin, but inside a
conditional:

/usr/include/string.h-46-#if __POSIX_VISIBLE
/usr/include/string.h:47:char   *_EXFUN(strtok_r,(char *__restrict, const
char *__restrict, char **__restrict));
/usr/include/string.h-48-#endif

I did not find any explicit inclusion of string header inside linux-tdep.c
source...

There is a workaround in mingw64 pthread.h:
/usr/x86_64-w64-mingw32/sys-root/mingw/include/pthread.h:455:#ifndef
strtok_r
/usr/x86_64-w64-mingw32/sys-root/mingw/include/pthread.h:456:#define
strtok_r(__s, __sep, __last)  (*(__last) = strtok((__s), (__sep)))
/usr/x86_64-w64-mingw32/sys-root/mingw/include/pthread.h-457-#endif

Does this suggest that strtok_r is not a required string.h function?
If yes, should this function be tested by configure...

Or maybe we should use the gnulib version of strtok_r?
I don't really know how to do this, but if someone can tell me,
I can test the patch.


Pierre Muller



> -----Message d'origine-----
> De : gdb-patches-owner@sourceware.org [mailto:gdb-patches-
> owner@sourceware.org] De la part de Sergio Durigan Junior
> Envoyé : mercredi 1 avril 2015 01:40
> À : Pedro Alves
> Cc : GDB Patches
> Objet : Re: [PATCH 1/2] Implement support for checking
> /proc/PID/coredump_filter
> 
> On Friday, March 27 2015, Pedro Alves wrote:
> 
> > OK.
> 
> Thanks, pushed both patches:
> 
>   <https://sourceware.org/ml/gdb-cvs/2015-03/msg00275.html>
>   df8411da087dc05481926f4c4a82deabc5bc3859
> 
> --
> Sergio
> GPG key ID: 0x65FC5E36
> Please send encrypted e-mail if possible
> http://sergiodj.net/
  
Pedro Alves April 8, 2015, 2:43 p.m. UTC | #4
On 04/08/2015 03:07 PM, Pierre Muller wrote:

> Or maybe we should use the gnulib version of strtok_r?

Yes.

> I don't really know how to do this, but if someone can tell me,
> I can test the patch.

Let me give that a try, and I'll post a branch for you to test.

There's also the "users/palves/gnulib-update" branch that
updates gdb's copy of gnulib to recent gnulib master.  It'd
be great if someone confirmed that a gdb build of that branch
actually works on Windows, so we can move forward with that.
(the branch predates Sergio's patch, so isn't affected by the strtok_r
issue).  I'd be nice to update our gnulib copy before we pull in
more modules, to avoid tripping on bugs that have meanwhile already
been fixed upstream, or avoid module dependencies that may
have been reduced upstream...

Thanks,
Pedro Alves
  
Pedro Alves April 8, 2015, 3:13 p.m. UTC | #5
On 04/08/2015 03:43 PM, Pedro Alves wrote:
> On 04/08/2015 03:07 PM, Pierre Muller wrote:
> 
>> Or maybe we should use the gnulib version of strtok_r?
> 
> Yes.
> 
>> I don't really know how to do this, but if someone can tell me,
>> I can test the patch.
> 
> Let me give that a try, and I'll post a branch for you to test.

Please try the users/palves/gnulib-strtok_r branch.

A nice thing is that the strtok_r module didn't pull in any
other new module.

> There's also the "users/palves/gnulib-update" branch that
> updates gdb's copy of gnulib to recent gnulib master.  It'd
> be great if someone confirmed that a gdb build of that branch
> actually works on Windows, so we can move forward with that.
> (the branch predates Sergio's patch, so isn't affected by the strtok_r
> issue).  I'd be nice to update our gnulib copy before we pull in
> more modules, to avoid tripping on bugs that have meanwhile already
> been fixed upstream, or avoid module dependencies that may
> have been reduced upstream...

Thanks,
Pedro Alves
  
Sergio Durigan Junior April 8, 2015, 4:08 p.m. UTC | #6
On Wednesday, April 08 2015, Pedro Alves wrote:

> On 04/08/2015 03:43 PM, Pedro Alves wrote:
>> On 04/08/2015 03:07 PM, Pierre Muller wrote:
>> 
>>> Or maybe we should use the gnulib version of strtok_r?
>> 
>> Yes.
>> 
>>> I don't really know how to do this, but if someone can tell me,
>>> I can test the patch.
>> 
>> Let me give that a try, and I'll post a branch for you to test.
>
> Please try the users/palves/gnulib-strtok_r branch.
>
> A nice thing is that the strtok_r module didn't pull in any
> other new module.

Sorry about the breakage, Pierre.  And thanks for taking care of that,
Pedro.

It seems that if this gnulib import succeed everything will be fine, so
I won't do anything for now.  But I will keep an eye on the discussion.
  
Yao Qi April 8, 2015, 4:27 p.m. UTC | #7
Pedro Alves <palves@redhat.com> writes:

> There's also the "users/palves/gnulib-update" branch that
> updates gdb's copy of gnulib to recent gnulib master.  It'd
> be great if someone confirmed that a gdb build of that branch
> actually works on Windows, so we can move forward with that.
> (the branch predates Sergio's patch, so isn't affected by the strtok_r
> issue).  I'd be nice to update our gnulib copy before we pull in
> more modules, to avoid tripping on bugs that have meanwhile already
> been fixed upstream, or avoid module dependencies that may
> have been reduced upstream...

On the other hand, updating our gnulib copy from upstream may introduce
new bugs.  strtok_r exists in the gnulib
8d5bd1402003bd0153984b138735adf537d960b0 commit we are using, I don't
see why we need to update our gnulib copy, unless there are some known
bug fixes in gnulib upstream.
  
Pierre Muller April 8, 2015, 4:28 p.m. UTC | #8
> -----Message d'origine-----
> De : gdb-patches-owner@sourceware.org [mailto:gdb-patches-
> owner@sourceware.org] De la part de Pedro Alves
> Envoyé : mercredi 8 avril 2015 17:14
> À : Pierre Muller; 'Sergio Durigan Junior'
> Cc : 'GDB Patches'
> Objet : Re: Build failure following: Implement support for checking
> /proc/PID/coredump_filter commit
> 
> On 04/08/2015 03:43 PM, Pedro Alves wrote:
> > On 04/08/2015 03:07 PM, Pierre Muller wrote:
> >
> >> Or maybe we should use the gnulib version of strtok_r?
> >
> > Yes.
> >
> >> I don't really know how to do this, but if someone can tell me,
> >> I can test the patch.
> >
> > Let me give that a try, and I'll post a branch for you to test.
> 
> Please try the users/palves/gnulib-strtok_r branch.

I can confirm that I am able to build mingw32 64bit gdb.exe executable,
and it contains gnulib/import/strtok_r.c source.

  I am not really able to test that the code works OK,
as I don't really know how to trigger that code...

  I have no idea what we should do now...

Pierre Muller
  
Sergio Durigan Junior April 8, 2015, 4:46 p.m. UTC | #9
On Wednesday, April 08 2015, Pierre Muller wrote:

>> Please try the users/palves/gnulib-strtok_r branch.
>
> I can confirm that I am able to build mingw32 64bit gdb.exe executable,
> and it contains gnulib/import/strtok_r.c source.

That is great, thanks Pierre.

>   I am not really able to test that the code works OK,
> as I don't really know how to trigger that code...

The code will not work on native Windows debugging, so there is nothing
to test there.  If you debug a remote GNU/Linux target, you should be
able to tweak the value of /proc/PID/coredump_filter on the target and
generate different kinds of corefiles based on it.  But I don't know how
hard it would be for you to test this.

>   I have no idea what we should do now...

IMO the easiest and safest thing to do now would be to just import the
strtok_r module from gnulib (without updating it).
  
Pedro Alves April 8, 2015, 4:47 p.m. UTC | #10
On 04/08/2015 05:27 PM, Yao Qi wrote:
> Pedro Alves <palves@redhat.com> writes:
> 
>> There's also the "users/palves/gnulib-update" branch that
>> updates gdb's copy of gnulib to recent gnulib master.  It'd
>> be great if someone confirmed that a gdb build of that branch
>> actually works on Windows, so we can move forward with that.
>> (the branch predates Sergio's patch, so isn't affected by the strtok_r
>> issue).  I'd be nice to update our gnulib copy before we pull in
>> more modules, to avoid tripping on bugs that have meanwhile already
>> been fixed upstream, or avoid module dependencies that may
>> have been reduced upstream...
> 
> On the other hand, updating our gnulib copy from upstream may introduce
> new bugs.

Sure.  And we can report and fix them, or downgrade a few revisions to
before when the bug was introduced.

> strtok_r exists in the gnulib
> 8d5bd1402003bd0153984b138735adf537d960b0 commit we are using, I don't

That's actually what I did in the branch I pointed Pierre at.

> see why we need to update our gnulib copy, unless there are some known
> bug fixes in gnulib upstream.

Why risk stumbling on bugs that might have been fixed in the 3
years since the last import?  The more modules we import off the older
copy, the wider the potential-bugs surface.  And then what to do if we
need to patch gnulib?  If the requirement is to send the fix upstream
first, and then refresh our import, it'll be much easier if the
re-import is just a small incremental update, rather than pulling in
multiple years of gnulib work.  The further apart our update is, the
harder it is to update.  If we keep our copy reasonably up to date, we
have a better chance of whoever introduced the bug upstream to still have
the issue fresh in mind and provide a prompt fix.  If we wait 3 years
to report a bug, then the chances are good that we get to fix
it ourselves.

I currently want to update gnulib for at least 3 reasons:

 - GDB-in-C++ on mingw:

   https://sourceware.org/ml/gdb-patches/2015-03/msg00447.html

   That will have to be fixed on gnulib.  Haven't checked if
   already fixed there, but most probably it is.

 - Antoine's use of canonicalize:

   https://sourceware.org/ml/gdb-patches/2015-03/msg00917.html

   That pulls in a truck load of modules.  I'd hope the dependencies
   are fewer in a newer gnulib, or at least, if the dependencies are the
   same, I'd much prefer importing up-to-date versions.  Otherwise, the
   poor soul that next updates gnulib is likely to have a "fun" time.

 - Pull in the fts module to get rid of the "system" call in compile/,
   as discussed a while ago.

I think now's the best time, as we still have time till the next
gdb release.

Thanks,
Pedro Alves
  
Pedro Alves April 8, 2015, 5:21 p.m. UTC | #11
On 04/08/2015 05:46 PM, Sergio Durigan Junior wrote:
>> >   I have no idea what we should do now...
> IMO the easiest and safest thing to do now would be to just import the
> strtok_r module from gnulib (without updating it).

Yeah.  I posted the branch as proper patch series now.

 https://sourceware.org/ml/gdb-patches/2015-04/msg00288.html

Thanks,
Pedro Alves
  

Patch

diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index ea0d4cd..4af1d01 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -35,9 +35,61 @@ 
 #include "observer.h"
 #include "objfiles.h"
 #include "infcall.h"
+#include "gdbcmd.h"
+#include "gdb_regex.h"
 
 #include <ctype.h>
 
+/* This enum represents the values that the user can choose when
+   informing the Linux kernel about which memory mappings will be
+   dumped in a corefile.  They are described in the file
+   Documentation/filesystems/proc.txt, inside the Linux kernel
+   tree.  */
+
+enum
+  {
+    COREFILTER_ANON_PRIVATE = 1 << 0,
+    COREFILTER_ANON_SHARED = 1 << 1,
+    COREFILTER_MAPPED_PRIVATE = 1 << 2,
+    COREFILTER_MAPPED_SHARED = 1 << 3,
+    COREFILTER_ELF_HEADERS = 1 << 4,
+    COREFILTER_HUGETLB_PRIVATE = 1 << 5,
+    COREFILTER_HUGETLB_SHARED = 1 << 6,
+  };
+
+/* This struct is used to map flags found in the "VmFlags:" field (in
+   the /proc/<PID>/smaps file).  */
+
+struct smaps_vmflags
+  {
+    /* Zero if this structure has not been initialized yet.  It
+       probably means that the Linux kernel being used does not emit
+       the "VmFlags:" field on "/proc/PID/smaps".  */
+
+    unsigned int initialized_p : 1;
+
+    /* Memory mapped I/O area (VM_IO, "io").  */
+
+    unsigned int io_page : 1;
+
+    /* Area uses huge TLB pages (VM_HUGETLB, "ht").  */
+
+    unsigned int uses_huge_tlb : 1;
+
+    /* Do not include this memory region on the coredump (VM_DONTDUMP, "dd").  */
+
+    unsigned int exclude_coredump : 1;
+
+    /* Is this a MAP_SHARED mapping (VM_SHARED, "sh").  */
+
+    unsigned int shared_mapping : 1;
+  };
+
+/* Whether to take the /proc/PID/coredump_filter into account when
+   generating a corefile.  */
+
+static int use_coredump_filter = 1;
+
 /* This enum represents the signals' numbers on a generic architecture
    running the Linux kernel.  The definition of "generic" comes from
    the file <include/uapi/asm-generic/signal.h>, from the Linux kernel
@@ -381,6 +433,248 @@  read_mapping (const char *line,
   *filename = p;
 }
 
+/* Helper function to decode the "VmFlags" field in /proc/PID/smaps.
+
+   This function was based on the documentation found on
+   <Documentation/filesystems/proc.txt>, on the Linux kernel.
+
+   Linux kernels before commit
+   834f82e2aa9a8ede94b17b656329f850c1471514 (3.10) do not have this
+   field on smaps.  */
+
+static void
+decode_vmflags (char *p, struct smaps_vmflags *v)
+{
+  char *saveptr;
+  const char *s;
+
+  v->initialized_p = 1;
+  p = skip_to_space (p);
+  p = skip_spaces (p);
+
+  for (s = strtok_r (p, " ", &saveptr);
+       s != NULL;
+       s = strtok_r (NULL, " ", &saveptr))
+    {
+      if (strcmp (s, "io") == 0)
+	v->io_page = 1;
+      else if (strcmp (s, "ht") == 0)
+	v->uses_huge_tlb = 1;
+      else if (strcmp (s, "dd") == 0)
+	v->exclude_coredump = 1;
+      else if (strcmp (s, "sh") == 0)
+	v->shared_mapping = 1;
+    }
+}
+
+/* Return 1 if the memory mapping is anonymous, 0 otherwise.
+
+   FILENAME is the name of the file present in the first line of the
+   memory mapping, in the "/proc/PID/smaps" output.  For example, if
+   the first line is:
+
+   7fd0ca877000-7fd0d0da0000 r--p 00000000 fd:02 2100770   /path/to/file
+
+   Then FILENAME will be "/path/to/file".  */
+
+static int
+mapping_is_anonymous_p (const char *filename)
+{
+  static regex_t dev_zero_regex, shmem_file_regex, file_deleted_regex;
+  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);
+
+      /* If we reached this point, then everything succeeded.  */
+      init_regex_p = 1;
+    }
+
+  if (init_regex_p == -1)
+    {
+      const char deleted[] = " (deleted)";
+      size_t del_len = sizeof (deleted) - 1;
+      size_t filename_len = strlen (filename);
+
+      /* There was an error while compiling the regex'es above.  In
+	 order to try to give some reliable information to the caller,
+	 we just try to find the string " (deleted)" in the filename.
+	 If we managed to find it, then we assume the mapping is
+	 anonymous.  */
+      return (filename_len >= del_len
+	      && strcmp (filename + filename_len - del_len, deleted) == 0);
+    }
+
+  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)
+    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.
+
+   In a nutshell, this is the logic that we follow in order to decide
+   if a mapping should be dumped or not.
+
+   - If the mapping is associated to a file whose name ends with
+     " (deleted)", or if the file is "/dev/zero", or if it is
+     "/SYSV%08x" (shared memory), or if there is no file associated
+     with it, or if the AnonHugePages: or the Anonymous: fields in the
+     /proc/PID/smaps have contents, then GDB considers this mapping to
+     be anonymous.  Otherwise, GDB considers this mapping to be a
+     file-backed mapping (because there will be a file associated with
+     it).
+ 
+     It is worth mentioning that, from all those checks described
+     above, the most fragile is the one to see if the file name ends
+     with " (deleted)".  This does not necessarily mean that the
+     mapping is anonymous, because the deleted file associated with
+     the mapping may have been a hard link to another file, for
+     example.  The Linux kernel checks to see if "i_nlink == 0", but
+     GDB cannot easily (and normally) do this check (iff running as
+     root, it could find the mapping in /proc/PID/map_files/ and
+     determine whether there still are other hard links to the
+     inode/file).  Therefore, we made a compromise here, and we assume
+     that if the file name ends with " (deleted)", then the mapping is
+     indeed anonymous.  FWIW, this is something the Linux kernel could
+     do better: expose this information in a more direct way.
+ 
+   - If we see the flag "sh" in the "VmFlags:" field (in
+     /proc/PID/smaps), then certainly the memory mapping is shared
+     (VM_SHARED).  If we have access to the VmFlags, and we don't see
+     the "sh" there, then certainly the mapping is private.  However,
+     Linux kernels before commit
+     834f82e2aa9a8ede94b17b656329f850c1471514 (3.10) do not have the
+     "VmFlags:" field; in that case, we use another heuristic: if we
+     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.  */
+
+static int
+dump_mapping_p (unsigned int filterflags, const struct smaps_vmflags *v,
+		int maybe_private_p, int mapping_anon_p, int mapping_file_p,
+		const char *filename)
+{
+  /* Initially, we trust in what we received from our caller.  This
+     value may not be very precise (i.e., it was probably gathered
+     from the permission line in the /proc/PID/smaps list, which
+     actually refers to VM_MAYSHARE, and not VM_SHARED), but it is
+     what we have until we take a look at the "VmFlags:" field
+     (assuming that the version of the Linux kernel being used
+     supports it, of course).  */
+  int private_p = maybe_private_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.
+     The kernel does the same.  */
+  if (strcmp ("[vdso]", filename) == 0
+      || strcmp ("[vsyscall]", filename) == 0)
+    return 1;
+
+  if (v->initialized_p)
+    {
+      /* We never dump I/O mappings.  */
+      if (v->io_page)
+	return 0;
+
+      /* Check if we should exclude this mapping.  */
+      if (v->exclude_coredump)
+	return 0;
+
+      /* Update our notion of whether this mapping is shared or
+	 private based on a trustworthy value.  */
+      private_p = !v->shared_mapping;
+
+      /* HugeTLB checking.  */
+      if (v->uses_huge_tlb)
+	{
+	  if ((private_p && (filterflags & COREFILTER_HUGETLB_PRIVATE))
+	      || (!private_p && (filterflags & COREFILTER_HUGETLB_SHARED)))
+	    return 1;
+
+	  return 0;
+	}
+    }
+
+  if (private_p)
+    {
+      if (mapping_anon_p && mapping_file_p)
+	{
+	  /* 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);
+	}
+      else if (mapping_anon_p)
+	return (filterflags & COREFILTER_ANON_PRIVATE) != 0;
+      else
+	return (filterflags & COREFILTER_MAPPED_PRIVATE) != 0;
+    }
+  else
+    {
+      if (mapping_anon_p && mapping_file_p)
+	{
+	  /* 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);
+	}
+      else if (mapping_anon_p)
+	return (filterflags & COREFILTER_ANON_SHARED) != 0;
+      else
+	return (filterflags & COREFILTER_MAPPED_SHARED) != 0;
+    }
+}
+
 /* Implement the "info proc" command.  */
 
 static void
@@ -819,48 +1113,97 @@  linux_find_memory_regions_full (struct gdbarch *gdbarch,
 				void *obfd)
 {
   char mapsfilename[100];
-  char *data;
+  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
+			      | COREFILTER_ANON_SHARED
+			      | COREFILTER_ELF_HEADERS
+			      | COREFILTER_HUGETLB_PRIVATE);
 
   /* We need to know the real target PID to access /proc.  */
   if (current_inferior ()->fake_pid_p)
     return 1;
 
-  xsnprintf (mapsfilename, sizeof mapsfilename,
-	     "/proc/%d/smaps", current_inferior ()->pid);
+  pid = current_inferior ()->pid;
+
+  if (use_coredump_filter)
+    {
+      xsnprintf (coredumpfilter_name, sizeof (coredumpfilter_name),
+		 "/proc/%d/coredump_filter", pid);
+      coredumpfilterdata = target_fileio_read_stralloc (coredumpfilter_name);
+      if (coredumpfilterdata != NULL)
+	{
+	  sscanf (coredumpfilterdata, "%x", &filterflags);
+	  xfree (coredumpfilterdata);
+	}
+    }
+
+  xsnprintf (mapsfilename, sizeof mapsfilename, "/proc/%d/smaps", pid);
   data = target_fileio_read_stralloc (mapsfilename);
   if (data == NULL)
     {
       /* Older Linux kernels did not support /proc/PID/smaps.  */
-      xsnprintf (mapsfilename, sizeof mapsfilename,
-		 "/proc/%d/maps", current_inferior ()->pid);
+      xsnprintf (mapsfilename, sizeof mapsfilename, "/proc/%d/maps", pid);
       data = target_fileio_read_stralloc (mapsfilename);
     }
-  if (data)
+
+  if (data != NULL)
     {
       struct cleanup *cleanup = make_cleanup (xfree, data);
-      char *line;
+      char *line, *t;
 
-      line = strtok (data, "\n");
-      while (line)
+      line = strtok_r (data, "\n", &t);
+      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;
-	  int modified = 0, has_anonymous = 0;
+	  int read, write, exec, private;
+	  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);
-
-	  /* Try to detect if region was modified by parsing smaps counters.  */
-	  for (line = strtok (NULL, "\n");
-	       line && line[0] >= 'A' && line[0] <= 'Z';
-	       line = strtok (NULL, "\n"))
+	  /* '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.  */
+	  private = 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];
 
@@ -869,11 +1212,17 @@  linux_find_memory_regions_full (struct gdbarch *gdbarch,
 		  warning (_("Error parsing {s,}maps file '%s'"), mapsfilename);
 		  break;
 		}
+
 	      if (strcmp (keyword, "Anonymous:") == 0)
-		has_anonymous = 1;
-	      if (strcmp (keyword, "Shared_Dirty:") == 0
-		  || strcmp (keyword, "Private_Dirty:") == 0
-		  || strcmp (keyword, "Swap:") == 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;
@@ -884,19 +1233,46 @@  linux_find_memory_regions_full (struct gdbarch *gdbarch,
 			       mapsfilename);
 		      break;
 		    }
-		  if (number != 0)
-		    modified = 1;
+		  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;
+		    }
 		}
 	    }
 
-	  /* Older Linux kernels did not support the "Anonymous:" counter.
-	     If it is missing, we can't be sure - dump all the pages.  */
-	  if (!has_anonymous)
-	    modified = 1;
+	  if (has_anonymous)
+	    should_dump_p = dump_mapping_p (filterflags, &v, private,
+					    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.  */
-	  func (addr, endaddr - addr, offset, inode,
-		read, write, exec, modified, filename, obfd);
+	  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);
 	}
 
       do_cleanups (cleanup);
@@ -1972,6 +2348,17 @@  linux_infcall_mmap (CORE_ADDR size, unsigned prot)
   return retval;
 }
 
+/* Display whether the gcore command is using the
+   /proc/PID/coredump_filter file.  */
+
+static void
+show_use_coredump_filter (struct ui_file *file, int from_tty,
+			  struct cmd_list_element *c, const char *value)
+{
+  fprintf_filtered (file, _("Use of /proc/PID/coredump_filter file to generate"
+			    " corefiles is %s.\n"), value);
+}
+
 /* To be called from the various GDB_OSABI_LINUX handlers for the
    various GNU/Linux architectures and machine types.  */
 
@@ -2008,4 +2395,16 @@  _initialize_linux_tdep (void)
   /* Observers used to invalidate the cache when needed.  */
   observer_attach_inferior_exit (invalidate_linux_cache_inf);
   observer_attach_inferior_appeared (invalidate_linux_cache_inf);
+
+  add_setshow_boolean_cmd ("use-coredump-filter", class_files,
+			   &use_coredump_filter, _("\
+Set whether gcore should consider /proc/PID/coredump_filter."),
+			   _("\
+Show whether gcore should consider /proc/PID/coredump_filter."),
+			   _("\
+Use this command to set whether gcore should consider the contents\n\
+of /proc/PID/coredump_filter when generating the corefile.  For more information\n\
+about this file, refer to the manpage of core(5)."),
+			   NULL, show_use_coredump_filter,
+			   &setlist, &showlist);
 }