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

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