Blame SOURCES/gdb-rhbz1371380-gcore-elf-headers.patch

475228
From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
475228
From: Sergio Durigan Junior <sergiodj@redhat.com>
475228
Date: Tue, 23 Apr 2019 18:17:57 -0400
475228
Subject: gdb-rhbz1371380-gcore-elf-headers.patch
475228
475228
;; Implement dump of mappings with ELF headers by gcore
475228
;; RHBZ 1371380, Sergio Durigan Junior.
475228
475228
Implement dump of mappings with ELF headers by gcore
475228
475228
This patch has a long story, but it all started back in 2015, with
475228
commit df8411da087dc05481926f4c4a82deabc5bc3859 ("Implement support
475228
for checking /proc/PID/coredump_filter").  The purpose of that commit
475228
was to bring GDB's corefile generation closer to what the Linux kernel
475228
does.  However, back then, I did not implement the full support for
475228
the dumping of memory mappings containing ELF headers (like mappings
475228
of DSOs or executables).  These mappings were being dumped most of
475228
time, though, because the default value of /proc/PID/coredump_filter
475228
is 0x33, which would cause anonymous private mappings (DSOs/executable
475228
code mappings have this type) to be dumped.  Well, until something
475228
happened on binutils...
475228
475228
A while ago, I noticed something strange was happening with one of our
475228
local testcases on Fedora GDB: it was failing due to some strange
475228
build-id problem.  On Fedora GDB, we (unfortunately) carry a bunch of
475228
"local" patches, and some of these patches actually extend upstream's
475228
build-id support in order to generate more useful information for the
475228
user of a Fedora system (for example, when the user loads a corefile
475228
into GDB, we detect whether the executable that generated that
475228
corefile is present, and if it's not we issue a warning suggesting
475228
that it should be installed, while also providing the build-id of the
475228
executable).  A while ago, Fedora GDB stopped printing those warnings.
475228
475228
I wanted to investigate this right away, and spent some time trying to
475228
determine what was going on, but other things happened and I got
475228
sidetracked.  Meanwhile, the bug started to be noticed by some of our
475228
users, and its priority started changing.  Then, someone on IRC also
475228
mentioned the problem, and when I tried helping him, I noticed he
475228
wasn't running Fedora.  Hm...  So maybe the bug was *also* present
475228
upstream.
475228
475228
After "some" time investigating, and with a lot of help from Keith and
475228
others, I was finally able to determine that yes, the bug is also
475228
present upstream, and that even though it started with a change in ld,
475228
it is indeed a GDB issue.
475228
475228
So, as I said, the problem started with binutils, more specifically
475228
after the following commit was pushed:
475228
475228
  commit f6aec96dce1ddbd8961a3aa8a2925db2021719bb
475228
  Author: H.J. Lu <hjl.tools@gmail.com>
475228
  Date:   Tue Feb 27 11:34:20 2018 -0800
475228
475228
      ld: Add --enable-separate-code
475228
475228
This commit makes ld use "-z separate-code" by default on x86-64
475228
machines.  What this means is that code pages and data pages are now
475228
separated in the binary, which is confusing GDB when it tries to decide
475228
what to dump.
475228
475228
BTW, Fedora 28 binutils doesn't have this code, which means that
475228
Fedora 28 GDB doesn't have the problem.  From Fedora 29 on, binutils
475228
was rebased and incorporated the commit above, which started causing
475228
Fedora GDB to fail.
475228
475228
Anyway, the first thing I tried was to pass "-z max-page-size" and
475228
specify a bigger page size (I saw a patch that did this and was
475228
proposed to Linux, so I thought it might help).  Obviously, this
475228
didn't work, because the real "problem" is that ld will always use
475228
separate pages for code and data.  So I decided to look into how GDB
475228
dumped the pages, and that's where I found the real issue.
475228
475228
What happens is that, because of "-z separate-code", the first two pages
475228
of the ELF binary are (from /proc/PID/smaps):
475228
475228
  00400000-00401000 r--p 00000000 fc:01 799548                             /file
475228
  Size:                  4 kB
475228
  KernelPageSize:        4 kB
475228
  MMUPageSize:           4 kB
475228
  Rss:                   4 kB
475228
  Pss:                   4 kB
475228
  Shared_Clean:          0 kB
475228
  Shared_Dirty:          0 kB
475228
  Private_Clean:         4 kB
475228
  Private_Dirty:         0 kB
475228
  Referenced:            4 kB
475228
  Anonymous:             0 kB
475228
  LazyFree:              0 kB
475228
  AnonHugePages:         0 kB
475228
  ShmemPmdMapped:        0 kB
475228
  Shared_Hugetlb:        0 kB
475228
  Private_Hugetlb:       0 kB
475228
  Swap:                  0 kB
475228
  SwapPss:               0 kB
475228
  Locked:                0 kB
475228
  THPeligible:    0
475228
  VmFlags: rd mr mw me dw sd
475228
  00401000-00402000 r-xp 00001000 fc:01 799548                             /file
475228
  Size:                  4 kB
475228
  KernelPageSize:        4 kB
475228
  MMUPageSize:           4 kB
475228
  Rss:                   4 kB
475228
  Pss:                   4 kB
475228
  Shared_Clean:          0 kB
475228
  Shared_Dirty:          0 kB
475228
  Private_Clean:         0 kB
475228
  Private_Dirty:         4 kB
475228
  Referenced:            4 kB
475228
  Anonymous:             4 kB
475228
  LazyFree:              0 kB
475228
  AnonHugePages:         0 kB
475228
  ShmemPmdMapped:        0 kB
475228
  Shared_Hugetlb:        0 kB
475228
  Private_Hugetlb:       0 kB
475228
  Swap:                  0 kB
475228
  SwapPss:               0 kB
475228
  Locked:                0 kB
475228
  THPeligible:    0
475228
  VmFlags: rd ex mr mw me dw sd
475228
475228
Whereas before, we had only one:
475228
475228
  00400000-00401000 r-xp 00000000 fc:01 798593                             /file
475228
  Size:                  4 kB
475228
  KernelPageSize:        4 kB
475228
  MMUPageSize:           4 kB
475228
  Rss:                   4 kB
475228
  Pss:                   4 kB
475228
  Shared_Clean:          0 kB
475228
  Shared_Dirty:          0 kB
475228
  Private_Clean:         0 kB
475228
  Private_Dirty:         4 kB
475228
  Referenced:            4 kB
475228
  Anonymous:             4 kB
475228
  LazyFree:              0 kB
475228
  AnonHugePages:         0 kB
475228
  ShmemPmdMapped:        0 kB
475228
  Shared_Hugetlb:        0 kB
475228
  Private_Hugetlb:       0 kB
475228
  Swap:                  0 kB
475228
  SwapPss:               0 kB
475228
  Locked:                0 kB
475228
  THPeligible:    0
475228
  VmFlags: rd ex mr mw me dw sd
475228
475228
Notice how we have "Anonymous" data mapped into the page.  This will be
475228
important.
475228
475228
So, the way GDB decides which pages it should dump has been revamped
475228
by my patch in 2015, and now it takes the contents of
475228
/proc/PID/coredump_filter into account.  The default value for Linux
475228
is 0x33, which means:
475228
475228
  Dump anonymous private, anonymous shared, ELF headers and HugeTLB
475228
  private pages.
475228
475228
Or:
475228
475228
  filter_flags filterflags = (COREFILTER_ANON_PRIVATE
475228
			      | COREFILTER_ANON_SHARED
475228
			      | COREFILTER_ELF_HEADERS
475228
			      | COREFILTER_HUGETLB_PRIVATE);
475228
475228
Now, it is important to keep in mind that GDB doesn't always have *all*
475228
of the necessary information to exactly determine the type of a page, so
475228
the whole algorithm is based on heuristics (you can take a look at
475228
linux-tdep.c:dump_mapping_p and
475228
linux-tdep.c:linux_find_memory_regions_full for more info).
475228
475228
Before the patch to make ld use "-z separate-code", the (single) page
475228
containing data and code was being flagged as an anonymous (due to the
475228
non-zero "Anonymous:" field) private (due to the "r-xp" permission),
475228
which means that it was being dumped into the corefile.  That's why it
475228
was working fine.
475228
475228
Now, as you can imagine, when "-z separate-code" is used, the *data*
475228
page (which is where the ELF notes are, including the build-id one) now
475228
doesn't have any "Anonymous:" mapping, so the heuristic is flagging it
475228
as file-backed private, which is *not* dumped by default.
475228
475228
The next question I had to answer was: how come a corefile generated by
475228
the Linux kernel was correct?  Well, the answer is that GDB, unlike
475228
Linux, doesn't actually implement the COREFILTER_ELF_HEADERS support.
475228
On Linux, even though the data page is also treated as a file-backed
475228
private mapping, it is also checked to see if there are any ELF headers
475228
in the page, and then, because we *do* have ELF headers there, it is
475228
dumped.
475228
475228
So, after more time trying to think of ways to fix this, I was able to
475228
implement an algorithm that reads the first few bytes of the memory
475228
mapping being processed, and checks to see if the ELF magic code is
475228
present.  This is basically what Linux does as well, except that, if
475228
it finds the ELF magic code, it just dumps one page to the corefile,
475228
whereas GDB will dump the whole mapping.  But I don't think that's a
475228
big issue, to be honest.
475228
475228
It's also important to explain that we *only* perform the ELF magic
475228
code check if:
475228
475228
  - The algorithm has decided *not* to dump the mapping so far, and;
475228
  - The mapping is private, and;
475228
  - The mapping's offset is zero, and;
475228
  - The user has requested us to dump mappings with ELF headers.
475228
475228
IOW, we're not going to blindly check every mapping.
475228
475228
As for the testcase, I struggled even more trying to write it.  Since
475228
our build-id support on upstream GDB is not very extensive, it's not
475228
really possible to determine whether a corefile contains build-id
475228
information or not just by using GDB.  So, after thinking a lot about
475228
the problem, I decided to rely on an external tool, eu-unstrip, in
475228
order to verify whether the dump was successful.  I verified the test
475228
here on my machine, and everything seems to work as expected (i.e., it
475228
fails without the patch, and works with the patch applied).  We are
475228
working hard to upstream our "local" Fedora GDB patches, and we intend
475228
to submit our build-id extension patches "soon", so hopefully we'll be
475228
able to use GDB itself to perform this verification.
475228
475228
I built and regtested this on the BuildBot, and no problems were
475228
found.
475228
475228
gdb/ChangeLog:
475228
2019-04-25  Sergio Durigan Junior  <sergiodj@redhat.com>
475228
475228
	PR corefiles/11608
475228
	PR corefiles/18187
475228
	* linux-tdep.c (dump_mapping_p): Add new parameters ADDR and
475228
	OFFSET.  Verify if current mapping contains an ELF header.
475228
	(linux_find_memory_regions_full): Adjust call to
475228
	dump_mapping_p.
475228
475228
gdb/testsuite/ChangeLog:
475228
2019-04-25  Sergio Durigan Junior  <sergiodj@redhat.com>
475228
475228
	PR corefiles/11608
475228
	PR corefiles/18187
475228
	* gdb.base/coredump-filter-build-id.exp: New file.
475228
475228
diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
475228
--- a/gdb/linux-tdep.c
475228
+++ b/gdb/linux-tdep.c
475228
@@ -591,8 +591,8 @@ mapping_is_anonymous_p (const char *filename)
475228
 }
475228
 
475228
 /* Return 0 if the memory mapping (which is related to FILTERFLAGS, V,
475228
-   MAYBE_PRIVATE_P, and MAPPING_ANONYMOUS_P) should not be dumped, or
475228
-   greater than 0 if it should.
475228
+   MAYBE_PRIVATE_P, MAPPING_ANONYMOUS_P, ADDR and OFFSET) should not
475228
+   be dumped, or greater than 0 if it should.
475228
 
475228
    In a nutshell, this is the logic that we follow in order to decide
475228
    if a mapping should be dumped or not.
475228
@@ -630,12 +630,17 @@ mapping_is_anonymous_p (const char *filename)
475228
      see 'p' in the permission flags, then we assume that the mapping
475228
      is private, even though the presence of the 's' flag there would
475228
      mean VM_MAYSHARE, which means the mapping could still be private.
475228
-     This should work OK enough, however.  */
475228
+     This should work OK enough, however.
475228
+
475228
+   - Even if, at the end, we decided that we should not dump the
475228
+     mapping, we still have to check if it is something like an ELF
475228
+     header (of a DSO or an executable, for example).  If it is, and
475228
+     if the user is interested in dump it, then we should dump it.  */
475228
 
475228
 static int
475228
 dump_mapping_p (filter_flags filterflags, const struct smaps_vmflags *v,
475228
 		int maybe_private_p, int mapping_anon_p, int mapping_file_p,
475228
-		const char *filename)
475228
+		const char *filename, ULONGEST addr, ULONGEST offset)
475228
 {
475228
   /* Initially, we trust in what we received from our caller.  This
475228
      value may not be very precise (i.e., it was probably gathered
475228
@@ -645,6 +650,7 @@ dump_mapping_p (filter_flags filterflags, const struct smaps_vmflags *v,
475228
      (assuming that the version of the Linux kernel being used
475228
      supports it, of course).  */
475228
   int private_p = maybe_private_p;
475228
+  int dump_p;
475228
 
475228
   /* We always dump vDSO and vsyscall mappings, because it's likely that
475228
      there'll be no file to read the contents from at core load time.
475228
@@ -685,13 +691,13 @@ dump_mapping_p (filter_flags filterflags, const struct smaps_vmflags *v,
475228
 	  /* This is a special situation.  It can happen when we see a
475228
 	     mapping that is file-backed, but that contains anonymous
475228
 	     pages.  */
475228
-	  return ((filterflags & COREFILTER_ANON_PRIVATE) != 0
475228
-		  || (filterflags & COREFILTER_MAPPED_PRIVATE) != 0);
475228
+	  dump_p = ((filterflags & COREFILTER_ANON_PRIVATE) != 0
475228
+		    || (filterflags & COREFILTER_MAPPED_PRIVATE) != 0);
475228
 	}
475228
       else if (mapping_anon_p)
475228
-	return (filterflags & COREFILTER_ANON_PRIVATE) != 0;
475228
+	dump_p = (filterflags & COREFILTER_ANON_PRIVATE) != 0;
475228
       else
475228
-	return (filterflags & COREFILTER_MAPPED_PRIVATE) != 0;
475228
+	dump_p = (filterflags & COREFILTER_MAPPED_PRIVATE) != 0;
475228
     }
475228
   else
475228
     {
475228
@@ -700,14 +706,55 @@ dump_mapping_p (filter_flags filterflags, const struct smaps_vmflags *v,
475228
 	  /* This is a special situation.  It can happen when we see a
475228
 	     mapping that is file-backed, but that contains anonymous
475228
 	     pages.  */
475228
-	  return ((filterflags & COREFILTER_ANON_SHARED) != 0
475228
-		  || (filterflags & COREFILTER_MAPPED_SHARED) != 0);
475228
+	  dump_p = ((filterflags & COREFILTER_ANON_SHARED) != 0
475228
+		    || (filterflags & COREFILTER_MAPPED_SHARED) != 0);
475228
 	}
475228
       else if (mapping_anon_p)
475228
-	return (filterflags & COREFILTER_ANON_SHARED) != 0;
475228
+	dump_p = (filterflags & COREFILTER_ANON_SHARED) != 0;
475228
       else
475228
-	return (filterflags & COREFILTER_MAPPED_SHARED) != 0;
475228
+	dump_p = (filterflags & COREFILTER_MAPPED_SHARED) != 0;
475228
     }
475228
+
475228
+  /* Even if we decided that we shouldn't dump this mapping, we still
475228
+     have to check whether (a) the user wants us to dump mappings
475228
+     containing an ELF header, and (b) the mapping in question
475228
+     contains an ELF header.  If (a) and (b) are true, then we should
475228
+     dump this mapping.
475228
+
475228
+     A mapping contains an ELF header if it is a private mapping, its
475228
+     offset is zero, and its first word is ELFMAG.  */
475228
+  if (!dump_p && private_p && offset == 0
475228
+      && (filterflags & COREFILTER_ELF_HEADERS) != 0)
475228
+    {
475228
+      /* Let's check if we have an ELF header.  */
475228
+      gdb::unique_xmalloc_ptr<char> header;
475228
+      int errcode;
475228
+
475228
+      /* Useful define specifying the size of the ELF magical
475228
+	 header.  */
475228
+#ifndef SELFMAG
475228
+#define SELFMAG 4
475228
+#endif
475228
+
475228
+      /* Read the first SELFMAG bytes and check if it is ELFMAG.  */
475228
+      if (target_read_string (addr, &header, SELFMAG, &errcode) == SELFMAG
475228
+	  && errcode == 0)
475228
+	{
475228
+	  const char *h = header.get ();
475228
+
475228
+	  /* The EI_MAG* and ELFMAG* constants come from
475228
+	     <elf/common.h>.  */
475228
+	  if (h[EI_MAG0] == ELFMAG0 && h[EI_MAG1] == ELFMAG1
475228
+	      && h[EI_MAG2] == ELFMAG2 && h[EI_MAG3] == ELFMAG3)
475228
+	    {
475228
+	      /* This mapping contains an ELF header, so we
475228
+		 should dump it.  */
475228
+	      dump_p = 1;
475228
+	    }
475228
+	}
475228
+    }
475228
+
475228
+  return dump_p;
475228
 }
475228
 
475228
 /* Implement the "info proc" command.  */
475228
@@ -1311,7 +1358,7 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch,
475228
 	  if (has_anonymous)
475228
 	    should_dump_p = dump_mapping_p (filterflags, &v, priv,
475228
 					    mapping_anon_p, mapping_file_p,
475228
-					    filename);
475228
+					    filename, addr, offset);
475228
 	  else
475228
 	    {
475228
 	      /* Older Linux kernels did not support the "Anonymous:" counter.
475228
diff --git a/gdb/testsuite/gdb.base/coredump-filter-build-id.exp b/gdb/testsuite/gdb.base/coredump-filter-build-id.exp
475228
new file mode 100644
475228
--- /dev/null
475228
+++ b/gdb/testsuite/gdb.base/coredump-filter-build-id.exp
475228
@@ -0,0 +1,69 @@
475228
+# Copyright 2019 Free Software Foundation, Inc.
475228
+
475228
+# This program is free software; you can redistribute it and/or modify
475228
+# it under the terms of the GNU General Public License as published by
475228
+# the Free Software Foundation; either version 3 of the License, or
475228
+# (at your option) any later version.
475228
+#
475228
+# This program is distributed in the hope that it will be useful,
475228
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
475228
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
475228
+# GNU General Public License for more details.
475228
+#
475228
+# You should have received a copy of the GNU General Public License
475228
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
475228
+
475228
+# Test whether GDB's gcore/generate-core-file command can dump memory
475228
+# mappings with ELF headers, containing a build-id note.
475228
+#
475228
+# Due to the fact that we don't have an easy way to process a corefile
475228
+# and look for specific notes using GDB/dejagnu, we rely on an
475228
+# external tool, eu-unstrip, to verify if the corefile contains
475228
+# build-ids.
475228
+
475228
+standard_testfile "normal.c"
475228
+
475228
+# This test is Linux x86_64 only.
475228
+if { ![istarget *-*-linux*] } {
475228
+    untested "$testfile.exp"
475228
+    return -1
475228
+}
475228
+if { ![istarget "x86_64-*-*"] || ![is_lp64_target] } {
475228
+    untested "$testfile.exp"
475228
+    return -1
475228
+}
475228
+
475228
+if { [prepare_for_testing "failed to prepare" $testfile $srcfile debug] } {
475228
+    return -1
475228
+}
475228
+
475228
+if { ![runto_main] } {
475228
+    untested "could not run to main"
475228
+    return -1
475228
+}
475228
+
475228
+# First we need to generate a corefile.
475228
+set corefilename "[standard_output_file gcore.test]"
475228
+if { ![gdb_gcore_cmd "$corefilename" "save corefile"] } {
475228
+    verbose -log "Could not save corefile"
475228
+    untested "$testfile.exp"
475228
+    return -1
475228
+}
475228
+
475228
+# Determine if GDB dumped the mapping containing the build-id.  This
475228
+# is done by invoking an external program (eu-unstrip).
475228
+if { [catch "exec [gdb_find_eu-unstrip] -n --core $corefilename" output] == 0 } {
475228
+    set line [lindex [split $output "\n"] 0]
475228
+    set test "gcore dumped mapping with build-id"
475228
+
475228
+    verbose -log "First line of eu-unstrip: $line"
475228
+
475228
+    if { [regexp "^${hex}\\+${hex} \[a-f0-9\]+@${hex}.*[string_to_regexp $binfile]$" $line] } {
475228
+	pass "$test"
475228
+    } else {
475228
+	fail "$test"
475228
+    }
475228
+} else {
475228
+    verbose -log "Could not execute eu-unstrip program"
475228
+    untested "$testfile.exp"
475228
+}
475228
diff --git a/gdb/testsuite/lib/future.exp b/gdb/testsuite/lib/future.exp
475228
--- a/gdb/testsuite/lib/future.exp
475228
+++ b/gdb/testsuite/lib/future.exp
475228
@@ -162,6 +162,16 @@ proc gdb_find_readelf {} {
475228
     return $readelf
475228
 }
475228
 
475228
+proc gdb_find_eu-unstrip {} {
475228
+    global EU_UNSTRIP_FOR_TARGET
475228
+    if [info exists EU_UNSTRIP_FOR_TARGET] {
475228
+	set eu_unstrip $EU_UNSTRIP_FOR_TARGET
475228
+    } else {
475228
+	set eu_unstrip [transform eu-unstrip]
475228
+    }
475228
+    return $eu_unstrip
475228
+}
475228
+
475228
 proc gdb_default_target_compile {source destfile type options} {
475228
     global target_triplet
475228
     global tool_root_dir