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

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