gdb: silence some 'Can't open file' warnings from core file loading

Message ID 2757ddaa89bf778689ef1bad206126e263ecf59f.1741795857.git.aburgess@redhat.com
State New
Headers
Series gdb: silence some 'Can't open file' warnings from core file loading |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_gdb_check--master-arm success Test passed

Commit Message

Andrew Burgess March 12, 2025, 4:11 p.m. UTC
  But PR gdb/20126 highlights a case where GDB emits a large number of
warnings like:

  warning: Can't open file /anon_hugepage (deleted) during file-backed mapping note processing
  warning: Can't open file /dev/shm/PostgreSQL.1150234652 during file-backed mapping note processing
  warning: Can't open file /dev/shm/PostgreSQL.535700290 during file-backed mapping note processing
  warning: Can't open file /SYSV604b7d00 (deleted) during file-backed mapping note processing
  ... etc ...

when opening a core file.  This commit aims to avoid at least some of
these warnings.

What we know is that, for at least some of these cases, (e.g. the
'(deleted)' mappings), the content of the mapping will have been
written into the core file itself.  As such, the fact that the file
isn't available ('/SYSV604b7d00' at least is a shared memory mapping),
isn't really relevant, GDB can still provide access to the mapping, by
reading the content from the core file itself.

What I propose is that, when processing the file backed mappings, if
all of the mappings for a file are covered by segments within the core
file itself, then there is no need to warn the user that the file
can't be opened again.  The debug experience should be unchanged, as
GDB would have read from the in-core mapping anyway.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30126
---
 gdb/corelow.c                        |  38 ++++++++-
 gdb/testsuite/gdb.base/corefile3.c   | 118 +++++++++++++++++++++++++++
 gdb/testsuite/gdb.base/corefile3.exp |  73 +++++++++++++++++
 3 files changed, 225 insertions(+), 4 deletions(-)
 create mode 100644 gdb/testsuite/gdb.base/corefile3.c
 create mode 100644 gdb/testsuite/gdb.base/corefile3.exp


base-commit: a6bc00ff35a42d6d555aa8de97d427074151ae47
  

Patch

diff --git a/gdb/corelow.c b/gdb/corelow.c
index 59c16677109..213b53808bd 100644
--- a/gdb/corelow.c
+++ b/gdb/corelow.c
@@ -540,11 +540,41 @@  core_target::build_file_mappings ()
 	  /* If ABFD was opened, but the wrong format, close it now.  */
 	  abfd = nullptr;
 
+	  /* When true, this indicates that the mapped contents of this
+	     file are available within the core file.  When false, some of
+	     the mapped contents are not available.  If the contents are
+	     available within the core file, then we don't need to warn
+	     the user if the file is no longer available.  */
+	  bool content_is_in_core_file_p = true;
+
 	  /* Record all regions for this file as unavailable.  */
 	  for (const mapped_file::region &region : file_data.regions)
-	    m_core_unavailable_mappings.emplace_back (region.start,
-						      region.end
-						      - region.start);
+	    {
+	      /* Check to see if the region is available within the core
+		 file.  */
+	      bool found_region_in_core_file = false;
+	      for (const target_section &ts : m_core_section_table)
+		{
+		  if (ts.addr <= region.start && ts.endaddr >= region.end
+		      && (ts.the_bfd_section->flags & SEC_HAS_CONTENTS) != 0)
+		    {
+		      found_region_in_core_file = true;
+		      break;
+		    }
+		}
+
+	      /* This region is not available within the core file.
+		 Without the file available to read from it is not possible
+		 for GDB to read this mapping within the inferior.  Warn
+		 the user about this case.  */
+	      if (!found_region_in_core_file)
+		content_is_in_core_file_p = false;
+
+	      /* Record the unavailable region.  */
+	      m_core_unavailable_mappings.emplace_back (region.start,
+							region.end
+							- region.start);
+	    }
 
 	  /* And give the user an appropriate warning.  */
 	  if (build_id_mismatch)
@@ -564,7 +594,7 @@  core_target::build_file_mappings ()
 			 styled_string (file_name_style.style (),
 					expanded_fname.get ()));
 	    }
-	  else
+	  else if (!content_is_in_core_file_p)
 	    {
 	      if (expanded_fname == nullptr
 		  || filename == expanded_fname.get ())
diff --git a/gdb/testsuite/gdb.base/corefile3.c b/gdb/testsuite/gdb.base/corefile3.c
new file mode 100644
index 00000000000..16030dd017c
--- /dev/null
+++ b/gdb/testsuite/gdb.base/corefile3.c
@@ -0,0 +1,118 @@ 
+/* Copyright 1992-2025 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* This file is based on coremaker.c.  */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+#define MAPSIZE (8 * 1024)
+
+/* Global pointers so it's easier to access them from GDB.  */
+
+char *rw_mapping = NULL;
+char *malloc_buffer = NULL;
+char *anon_mapping = NULL;
+char *shm_mapping = NULL;
+
+/* Create mappings within this process.  */
+
+void
+mmapdata ()
+{
+  /* Allocate and initialize a buffer that will be used to write the file
+     that is later mapped in.  */
+
+  malloc_buffer = (char *) malloc (MAPSIZE);
+  for (int j = 0; j < MAPSIZE; ++j)
+    malloc_buffer[j] = j;
+
+  /* Write the file to map in.  */
+
+  int fd = open ("coremmap.data", O_CREAT | O_RDWR, 0666);
+  assert (fd != -1);
+  write (fd, malloc_buffer, MAPSIZE);
+
+  /* Now map the file into our address space as RW_MAPPING.  */
+
+  rw_mapping
+    = (char *) mmap (0, MAPSIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+  assert (rw_mapping != (char *) MAP_FAILED);
+
+  /* Verify that the original data and the mapped data are identical.  If
+     not, we'd rather fail now than when trying to access the mapped data
+     from the core file. */
+
+  for (int j = 0; j < MAPSIZE; ++j)
+    assert (malloc_buffer[j] == rw_mapping[j]);
+
+  /* Touch RW_MAPPING so the kernel writes it out into 'core'.  */
+  rw_mapping[0] = malloc_buffer[0];
+
+  /* Create yet another region which is allocated, but not written to.  */
+  anon_mapping = mmap (NULL, MAPSIZE, PROT_READ | PROT_WRITE,
+		       MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+  assert (anon_mapping != MAP_FAILED);
+
+  /* Create a shared memory mapping.  */
+  int sid = shmget (IPC_PRIVATE, MAPSIZE, IPC_CREAT | IPC_EXCL | 0777);
+  assert (sid != -1);
+  shm_mapping = (char *) shmat (sid, NULL, 0);
+  int res = shmctl (sid, IPC_RMID, NULL);
+  assert (res == 0);
+  assert (shm_mapping != MAP_FAILED);
+}
+
+void
+func2 ()
+{
+#ifdef SA_FULLDUMP
+  /* Force a corefile that includes the data section for AIX.  */
+  {
+    struct sigaction sa;
+
+    sigaction (SIGABRT, (struct sigaction *)0, &sa);
+    sa.sa_flags |= SA_FULLDUMP;
+    sigaction (SIGABRT, &sa, (struct sigaction *)0);
+  }
+#endif
+
+  abort ();
+}
+
+void
+func1 ()
+{
+  func2 ();
+}
+
+int
+main (void)
+{
+  mmapdata ();
+  func1 ();
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/corefile3.exp b/gdb/testsuite/gdb.base/corefile3.exp
new file mode 100644
index 00000000000..08e417eec10
--- /dev/null
+++ b/gdb/testsuite/gdb.base/corefile3.exp
@@ -0,0 +1,73 @@ 
+# Copyright 2025 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Create a core file with some mapped regions, but ensure that the
+# types region mean that the kernel should write the region into the
+# core file (e.g. r/w file backed mapping).
+#
+# We then delete the file that backed the mapping and load the core
+# file into GDB.
+#
+# GDB shouldn't warn about the file being missing.  It doesn't matter;
+# the file contents can all be found in the core file itself.
+
+require isnative
+require {!is_remote host}
+
+standard_testfile
+
+if {[build_executable $testfile.exp $testfile $srcfile] == -1} {
+    return
+}
+
+# Do not delete coremap.data when calling core_find.  This file is
+# required for GDB to find mmap'd data in the "accessing read-only
+# mmapped data in core file" test.
+set corefile [core_find $binfile {}]
+if {$corefile == ""} {
+    return
+}
+
+# Move coremap.data file out of the way, so it cannot be found when we
+# later load the core file into GDB.
+set data_filename \
+    [standard_output_file coredir.[getpid]/coremmap.data]
+set backup_filename \
+    [standard_output_file coredir.[getpid]/coremmap.data.backup]
+remote_exec host "mv ${data_filename} ${backup_filename}"
+
+clean_restart $binfile
+
+# Load the core file.  The 'coremap.data' file cannot be found by GDB,
+# but all the mappings for that file are r/w and should be present in
+# the core file, so we shouldn't get any warnings from GDB about it.
+set warnings_seen 0
+gdb_test_multiple "core-file $corefile" "core-file command" {
+    -re "^warning: Can't open file \[^\r\n\]+ during file-backed mapping note processing\r\n" {
+	incr warnings_seen
+	exp_continue
+    }
+    -re "^$gdb_prompt $" {
+	gdb_assert { $warnings_seen == 0 } $gdb_test_name
+    }
+    -re "^\[^\r\n\]*\r\n" {
+	exp_continue
+    }
+}
+
+# Check the mappings are all readable.
+foreach label { rw_mapping malloc_buffer anon_mapping shm_mapping } {
+    gdb_test "x/1wd $label" "^$hex:\\s+$decimal"
+}