public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
From: Michal Kolar <mkolar@redhat.com>
To: git-commits@fedoraproject.org
Subject: [rpms/gdb] f44: Rebase to FSF GDB 17.2.
Date: Thu, 25 Jun 2026 00:56:46 GMT [thread overview]
Message-ID: <178234900640.1.5318356091770756049.rpms-gdb-f17655f37931@fedoraproject.org> (raw)
A new commit has been pushed.
Repo : rpms/gdb
Branch : f44
Commit : f17655f37931fdb7e2d9e0bb4ce1bea33a4514d3
Author : Michal Kolar <mkolar@redhat.com>
Date : 2026-06-24T21:39:53+00:00
Stats : +2987/-163 in 10 file(s)
URL : https://src.fedoraproject.org/rpms/gdb/c/f17655f37931fdb7e2d9e0bb4ce1bea33a4514d3?branch=f44
Log:
Rebase to FSF GDB 17.2.
Deleted: gdb-rhbz2435950-skip-revert.patch
Backport e492fb22b70, gdb: backport DAP core file support
Replace manual, multi-file patch inclusion with modern autosetup
Add rpminspect.yaml to define package-specific testing policy
---
diff --git a/.gitignore b/.gitignore
index 5c18b65..e69a5fb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,4 @@
/new-fedora-release
/gdb-libstdc++-v3-python-8.1.1-20180626.tar.xz
/v2.0.5.tar.gz
-/gdb-16.3.tar.xz
-/gdb-17.1.tar.xz
+/gdb-17.2.tar.xz
diff --git a/_gdb.spec.Patch.include b/_gdb.spec.Patch.include
index 127de30..339b216 100644
--- a/_gdb.spec.Patch.include
+++ b/_gdb.spec.Patch.include
@@ -37,36 +37,32 @@ Patch006: gdb-rhbz2424325-c++20-implicit-lambda-capture.patch
# when backporting this fix.
Patch007: gdb-rhbz2403580-misplaced-symtabs.patch
-# Backport of upstream commit f08ffbbf2691bad2d5df660ee644647687775f0c
-# Can be dropped on a rebase to gdb 17.2 or 18.1
-Patch008: gdb-rhbz2435950-skip-revert.patch
-
# Backport of upstream commit c1da013915e from Kevin Buettner
# (RHBZ 2413405).
-Patch009: gdb-rhbz2413405-gcore-unreadable-pages.patch
+Patch008: gdb-rhbz2413405-gcore-unreadable-pages.patch
# Backport of upstream commit d2cc16cd7fc from Jan Vrany fixing
# FAILs in gdb.base/fileio.exp caused by macro expansion of
# path components in OUTDIR.gdb/testsuite: fix FAILs in fileio.exp
-Patch010: gdb-fileio-test-fixes.patch
+Patch009: gdb-fileio-test-fixes.patch
# Backport upstream commit 8bd08ee92c4 to address rhbz2366461. This
# commit will drop out with GDB 18.
-Patch011: gdb-rhbz2366461-missing-thread.patch
+Patch010: gdb-rhbz2366461-missing-thread.patch
# Backport upstream commit cd289df068e to address rhbz2366461. This
# commit will drop out with GDB 18.
-Patch012: gdb-rhbz2366461-bad-solib-entry-addr.patch
+Patch011: gdb-rhbz2366461-bad-solib-entry-addr.patch
# Fix use of deprecated trace variable subcommand
# (Tom de Vries)
-Patch013: gdb-fix-testsuite-newer-tcl.patch
+Patch012: gdb-fix-testsuite-newer-tcl.patch
# Backport the following upstream commits in order to address RHBZ
# 2467251: d980317c7f1, 958d06262a7, 7d1d7386561, 8915de0883c,
# 8f65ab7b71f. These commits will all drop out when we rebase to GDB
# 18.
-Patch014: gdb-rhbz2467251-computed-location-synthetic-pointers.patch
+Patch013: gdb-rhbz2467251-computed-location-synthetic-pointers.patch
# Fix EILSEQ problems for UTF8 related tests when using expect
# enabled with Tcl 9 and full set of Tcl 9 compatibility fixes
@@ -77,5 +73,9 @@ Patch014: gdb-rhbz2467251-computed-location-synthetic-pointers.patch
# For its status, see:
#
# https://inbox.sourceware.org/gdb-patches/20260526192701.3835262-2-kevinb@redhat.com/T/#u
-Patch015: gdb-tcl9-utf8-encoding-fix.patch
+Patch014: gdb-tcl9-utf8-encoding-fix.patch
+
+# Backport of upstream commit e492fb22b70
+# gdb: backport DAP core file support
+Patch015: gdb-backport-dap-core-file-support.patch
diff --git a/_git_upstream_commit b/_git_upstream_commit
index 452ca53..da4c92e 100644
--- a/_git_upstream_commit
+++ b/_git_upstream_commit
@@ -1 +1 @@
-631a49c452a4a456dd9889d172541ea789f8bcae
+7adf9fa6b1ccb3c70f86cb630368b9d3dffcf3aa
diff --git a/_patch_order b/_patch_order
index 04f732f..cddb41b 100644
--- a/_patch_order
+++ b/_patch_order
@@ -5,7 +5,6 @@ gdb-rhbz2424325-c23-const-build-warnings.patch
gdb-rhbz2424325-c23-more-const-fixes.patch
gdb-rhbz2424325-c++20-implicit-lambda-capture.patch
gdb-rhbz2403580-misplaced-symtabs.patch
-gdb-rhbz2435950-skip-revert.patch
gdb-rhbz2413405-gcore-unreadable-pages.patch
gdb-fileio-test-fixes.patch
gdb-rhbz2366461-missing-thread.patch
@@ -13,3 +12,4 @@ gdb-rhbz2366461-bad-solib-entry-addr.patch
gdb-fix-testsuite-newer-tcl.patch
gdb-rhbz2467251-computed-location-synthetic-pointers.patch
gdb-tcl9-utf8-encoding-fix.patch
+gdb-backport-dap-core-file-support.patch
diff --git a/gdb-backport-dap-core-file-support.patch b/gdb-backport-dap-core-file-support.patch
new file mode 100644
index 0000000..9110805
--- /dev/null
+++ b/gdb-backport-dap-core-file-support.patch
@@ -0,0 +1,2956 @@
+From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
+From: Michal Kolar <mkolar@redhat.com>
+Date: Wed, 24 Jun 2026 21:25:33 +0000
+Subject: gdb-backport-dap-core-file-support.patch
+
+;; Backport of upstream commit e492fb22b70
+;; gdb: backport DAP core file support
+
+Back-port the following upstream commits to enable core file support
+within GDB's DAP protocol implementation:
+
+ * 0a481bb9a6d gdb/dap: add support for opening core files
+ * 4ecaac39c26 gdb/python: new events.corefile_changed event
+ * ecac42af735 Always propagate exceptions in DAP
+ * fc8e5a565b3 gdb: make structured core file mappings processing global
+ * f69c1d03c4d gdb/python: add Corefile.mapped_files method
+ * 7862554bcf4 gdb/python: introduce gdb.Corefile API
+
+The commit messages for each individual commit follow:
+
+--- 0a481bb9a6d:
+
+gdb/dap: add support for opening core files
+
+This patch adds core file support to GDB's DAP interface.
+
+Core files are supported as a GDB specific argument to 'attach', the
+new argument is 'coreFile', the name of the core file to debug.
+
+I think handling core files via attach makes the most sense; attach is
+for connecting to existing processes, but these targets are (usually)
+stopped as soon as GDB attaches, and that's what a core file looks
+like, a target that was running, but is now stopped. It just happens
+that core file targets are special in that the target cannot be
+resumed again, nor can the user modify the program state (e.g. write
+to memory or registers).
+
+Prior to starting this work I took a look at what lldb does. The
+documentation is not super clear, but this page seems to indicate that
+lldb might also use the 'coreFile' argument to 'attach':
+
+ https://lldb.llvm.org/use/lldbdap.html#configuration-settings-reference
+
+Like I said, it's not very clear, but search for "coreFile" and you'll
+see it mentioned, just once, under the "attach" header. In order to
+be compatible with lldb I used the same argument name with the same
+capitalisation.
+
+The new argument is added to the documentation and mentioned in NEWS.
+
+I had to make some changes to testsuite/lib/dap-support.exp to support
+this new feature. There's a new dap_corefile proc to handle setting
+up the initial connection. This seemed cleaner that overloading
+dap_attach, even though under the hood it is still an 'attach' request
+that gets sent.
+
+The new test tries to write to memory and registers with the core file
+target in place, neither of these requests succeed, which is what we
+want, but the exceptions are logged into the dap log file. The
+dap_shutdown proc calls dap_check_log_file to check the log for
+exceptions, and these two exceptions are spotted and trigger a FAIL.
+To avoid this I've added a new "expected_exception_count" argument
+for dap_shutdown. Now we check that we see the expected number of
+exceptions. We don't check for the specific exception types right
+now, but as the test is already checking that the expected requests
+fail, I think we're OK.
+
+Approved-By: Tom Tromey <tom@tromey.com>
+
+--- 4ecaac39c26:
+
+gdb/python: new events.corefile_changed event
+
+Add a new Python event registry, events.corefile_changed. This event
+is emitted each time the corefile within an inferior changes.
+
+The event object has a single 'inferior' attribute which is the
+gdb.Inferior object for which the core file changed. The user can
+then inspect Inferior.corefile to see details about the new core file,
+or this will be None if the core file was removed from the inferior.
+
+I've updated the existing test to cover this new event.
+
+The new test covers both the corefile_changed event, but also monitors
+the exited event. This ties into the work done in the previous
+commit where we use whether the inferior has exited or not as a guard
+for whether core_target::exit_core_file_inferior should be called.
+Unloading a core file should result in a single corefile_changed event
+and a single exited event.
+
+Reviewed-By: Eli Zaretskii <eliz@gnu.org>
+
+--- ecac42af735:
+
+Always propagate exceptions in DAP
+
+This changes the DAP exec_and_log function to always transform an
+exception into a DAPException and propagate it.
+
+As the bug points out, we haven't always wrapped calls when
+appropriate. I think it's better to cause the request to fail by
+default; if any spot truly needs to ignore errors, that is readily
+done at the point of call.
+
+Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33346
+
+--- fc8e5a565b3:
+
+gdb: make structured core file mappings processing global
+
+In corelow.c, within core_target::build_file_mappings, we have code
+that wraps around a call to gdbarch_read_core_file_mappings and
+provides more structure to the results.
+
+Specifically, gdbarch_read_core_file_mappings calls a callback once
+for every region of every mapped file. The wrapper code groups all of
+the mappings for one file into an instance of 'struct mapped_file',
+this allows all of the mapped regions to be associated with the
+build-id and filename of a file.
+
+In the next commit I plan to make this information available via the
+Python API, and so I need to allow access to this structured wrapping
+outside of corelow.c.
+
+This commit renames 'struct mapped_file' to 'struct core_mapped_file'
+and moves the struct into gdbcore.h. Then a new global function
+gdb_read_core_file_mappings is created into which I move the code to
+build the structured data.
+
+Then corelow.c is updated to call gdb_read_core_file_mappings.
+
+This commit does not extend the Python API, that is for the next
+commit.
+
+There should be no user visible changes after this commit.
+
+Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32844
+
+Approved-By: Tom Tromey <tom@tromey.com>
+
+--- f69c1d03c4d:
+
+gdb/python: add Corefile.mapped_files method
+
+Add a new Corefile.mapped_files method which returns a list of
+gdb.CorefileMappedFile objects.
+
+Each gdb.CorefileMappedFile object represents a file that was mapped
+into the process when the core file was created.
+
+A gdb.CorefileMappedFile has attributes:
+
+ + filename -- A string, the name of the mapped file.
+ + build_id -- A string or None, the build-id of the mapped file if
+ GDB could find it (None if not).
+ + is_main_executable -- A boolean, True if this mapping is the main
+ executable.
+ + regions -- A list containing the regions of this file that were
+ mapped into the process.
+
+The 'regions' list is a list of gdb.CorefileMappedFileRegion objects,
+each of these objects has the following attributes:
+
+ + start -- the start address within the inferior.
+ + end -- the end address within the inferior.
+ + file_offset -- the offset within the mapped file for this mapping.
+
+There are docs and tests.
+
+Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32844
+
+Approved-By: Tom Tromey <tom@tromey.com>
+
+--- 7862554bcf4:
+
+gdb/python: introduce gdb.Corefile API
+
+This commit starts adding some core file related features to the
+Python API.
+
+In this initial commit I've tried to keep the changes as small as
+possible for easy review.
+
+There's a new Python class gdb.Corefile, which represents a loaded
+core file. This API doesn't allow the user to create their own
+gdb.Corefile objects, a core file must be loaded using the 'core-file'
+command, then a gdb.Corefile object can be obtained by querying the
+inferior in which the core file was loaded.
+
+There's a new attribute gdb.Inferior.corefile, this is None when no
+core file is loaded, or contains a gdb.Corefile object if a core file
+has been loaded.
+
+Currently, the gdb.Corefile object has one attribute, and one method,
+these are:
+
+ gdb.Corefile.filename -- the file name of the loaded core file.
+
+ gdb.Corefile.is_valid() -- indicates if a gdb.Corefile object is
+ valid or not. See notes below.
+
+A gdb.Corefile object is only valid while the corresponding core file
+is loaded into an inferior. Unloading the core file, or loading a
+different one will cause a gdb.Corefile object to become invalid. For
+example:
+
+ (gdb) core-file /tmp/core.54313
+ ... snip ...
+ (gdb) python core=gdb.selected_inferior().corefile
+ (gdb) python print(core)
+ <gdb.Corefile inferior=1 filename='/tmp/core.54313'>
+ (gdb) python print(core.is_valid())
+ True
+ (gdb) core-file
+ No core file now.
+ (gdb) python print(core)
+ <gdb.Corefile (invalid)>
+ (gdb) python print(core.is_valid())
+ False
+ (gdb)
+
+In order to track changes to the core file, there is a new observable
+'core_file_changed', which accounts for the changes in corelow.c,
+observable,c, and observable.h. Currently, this observable is not
+visible as a Python event.
+
+I chose to access the core file via the inferior even though the core
+file BFD object is actually stored within the program_space. As such,
+it might seem that the natural choice would be to add the attribute as
+gdb.Progspace.corefile.
+
+For background reading on my choice, please see:
+
+ https://inbox.sourceware.org/gdb-patches/577f2c47793acb501c2611c0e6c7ea379f774830.1668789658.git.aburgess@redhat.com
+
+This patch was never merged, it is still on my backlog, but the
+observation in that work is that some targets are not really
+shareable. For example, the core_target (corelow.c) stores
+information about the loaded core file within the target instance. As
+such, each target instance represents a single loaded core file.
+
+Except that the BFD part of the core file is stored in the
+program_space, which is a little weird.
+
+During review, Tom made the observation, that maybe we should
+investigate moving the core file BFD into the core_target. I'm
+inclined to agree with this as a direction of travel.
+
+All this leaves us with two observations:
+
+ 1. Currently, loading a core file into an inferior, then using
+ 'add-inferior' will try to share the core_target between
+ inferiors. This is broken, and can trigger GDB crashes. The
+ obvious fix, without reworking core_target, is just to prevent
+ this sharing, making core_target per-inferior.
+
+ 2. Having the core file information split between the core_target
+ instance, and the BFD stored in the program_space is a little
+ weird, and is really just historical. Planning for a future
+ where the BFD is also stored in the core_target might be wise.
+
+So, if we imagine that the BFD is (one day) moved into the
+core_target, and that the core_target really becomes non-shareable,
+then it is, I think, clearer that the corefile attribute should live
+on the gdb.Inferior object, not the gdb.Progspace object.
+
+There's testing for all the functionality added in this commit.
+
+Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32844
+
+Reviewed-By: Eli Zaretskii <eliz@gnu.org>
+Approved-By: Tom Tromey <tom@tromey.com>
+
+diff --git a/gdb/Makefile.in b/gdb/Makefile.in
+--- a/gdb/Makefile.in
++++ b/gdb/Makefile.in
+@@ -399,6 +399,7 @@ SUBDIR_PYTHON_SRCS = \
+ python/py-color.c \
+ python/py-connection.c \
+ python/py-continueevent.c \
++ python/py-corefile.c \
+ python/py-dap.c \
+ python/py-disasm.c \
+ python/py-event.c \
+diff --git a/gdb/NEWS b/gdb/NEWS
+--- a/gdb/NEWS
++++ b/gdb/NEWS
+@@ -13,6 +13,8 @@
+
+ ** GDB now supports the "completions" request.
+
++ ** The attach request now accepts the coreFile parameter.
++
+ * "set style" commands now supports numeric format for basic colors
+ from 0 to 255 and #RRGGBB format for TrueColor.
+
+@@ -176,6 +178,32 @@ info threads [-gid] [-stopped] [-running] [ID]...
+ unavailability like gdb.Value.is_optimized_out checks for
+ optimized out values.
+
++ ** New gdb.Corefile class which represents a loaded core file. This
++ has an attribute Corefile.filename, the file name of the loaded
++ core file, and a method Corefile.is_valid(), which returns False
++ when a Corefile object becomes invalid (e.g. when the core file
++ is unloaded). There is also Corefile.mapped_files() which
++ returns a list of CorefileMappedFile objects, representing files
++ that were mapped into the core file when it was created.
++
++ ** New gdb.CorefileMappedFile type representing a file that was
++ mapped when the core file was created. Has read-only attributes
++ filename (string), build_id (string), is_main_executable
++ (boolean), and regions (list of CorefileMappedFileRegion objects).
++
++ ** New gdb.CorefileMappedFileRegion type, which represents a mapped
++ region of a file (see gdb.CorefileMappedFile above). Has
++ read-only attributes start, end, and file_offset.
++
++ ** New Inferior.corefile attribute. This read only attribute
++ contains the gdb.Corefile object if a core file is loaded into
++ the inferior, otherwise, this contains None.
++
++ ** New event registry gdb.events.corefile_changed, which emits a
++ CorefileChangedEvent whenever the core file associated with an
++ inferior changes. The event has an 'inferior' attribute which is
++ the gdb.Inferior in which the core file has changed.
++
+ * Guile API
+
+ ** New type <gdb:color> for dealing with colors.
+diff --git a/gdb/corelow.c b/gdb/corelow.c
+--- a/gdb/corelow.c
++++ b/gdb/corelow.c
+@@ -53,6 +53,7 @@
+ #include "xml-tdesc.h"
+ #include "memtag.h"
+ #include "cli/cli-style.h"
++#include "observable.h"
+
+ #ifndef O_LARGEFILE
+ #define O_LARGEFILE 0
+@@ -365,108 +366,27 @@ core_target::core_target ()
+ void
+ core_target::build_file_mappings ()
+ {
+- /* Type holding information about a single file mapped into the inferior
+- at the point when the core file was created. Associates a build-id
+- with the list of regions the file is mapped into. */
+- struct mapped_file
+- {
+- /* Type for a region of a file that was mapped into the inferior when
+- the core file was generated. */
+- struct region
+- {
+- /* Constructor. See member variables for argument descriptions. */
+- region (CORE_ADDR start_, CORE_ADDR end_, CORE_ADDR file_ofs_)
+- : start (start_),
+- end (end_),
+- file_ofs (file_ofs_)
+- { /* Nothing. */ }
+-
+- /* The inferior address for the start of the mapped region. */
+- CORE_ADDR start;
+-
+- /* The inferior address immediately after the mapped region. */
+- CORE_ADDR end;
+-
+- /* The offset within the mapped file for this content. */
+- CORE_ADDR file_ofs;
+- };
+-
+- /* If not nullptr, then this is the build-id associated with this
+- file. */
+- const bfd_build_id *build_id = nullptr;
+-
+- /* If true then we have seen multiple different build-ids associated
+- with the same filename. The build_id field will have been set back
+- to nullptr, and we should not set build_id in future. */
+- bool ignore_build_id_p = false;
+-
+- /* All the mapped regions of this file. */
+- std::vector<region> regions;
+- };
+-
+ gdb::unordered_map<std::string, struct bfd *> bfd_map;
+ gdb::unordered_set<std::string> unavailable_paths;
+
+ /* All files mapped into the core file. The key is the filename. */
+- gdb::unordered_map<std::string, mapped_file> mapped_files;
+-
+- /* See linux_read_core_file_mappings() in linux-tdep.c for an example
+- read_core_file_mappings method. */
+- gdbarch_read_core_file_mappings (m_core_gdbarch,
+- current_program_space->core_bfd (),
+-
+- /* After determining the number of mappings, read_core_file_mappings
+- will invoke this lambda. */
+- [&] (ULONGEST)
+- {
+- },
+-
+- /* read_core_file_mappings will invoke this lambda for each mapping
+- that it finds. */
+- [&] (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs,
+- const char *filename, const bfd_build_id *build_id)
+- {
+- /* Architecture-specific read_core_mapping methods are expected to
+- weed out non-file-backed mappings. */
+- gdb_assert (filename != nullptr);
++ std::vector<core_mapped_file> mapped_files
++ = gdb_read_core_file_mappings (m_core_gdbarch,
++ current_program_space->core_bfd ());
+
+- /* Add this mapped region to the data for FILENAME. */
+- mapped_file &file_data = mapped_files[filename];
+- file_data.regions.emplace_back (start, end, file_ofs);
+- if (build_id != nullptr && !file_data.ignore_build_id_p)
+- {
+- if (file_data.build_id == nullptr)
+- file_data.build_id = build_id;
+- else if (!build_id_equal (build_id, file_data.build_id))
+- {
+- warning (_("Multiple build-ids found for %ps"),
+- styled_string (file_name_style.style (), filename));
+- file_data.build_id = nullptr;
+- file_data.ignore_build_id_p = true;
+- }
+- }
+- });
+-
+- /* Get the build-id of the core file. */
+- const bfd_build_id *core_build_id
+- = build_id_bfd_get (current_program_space->core_bfd ());
+-
+- for (const auto &[filename, file_data] : mapped_files)
++ for (const core_mapped_file &file_data : mapped_files)
+ {
+- /* If this mapped file has the same build-id as was discovered for
+- the core-file itself, then we assume this is the main
+- executable. Record the filename as we can use this later. */
+- if (file_data.build_id != nullptr
+- && m_expected_exec_filename.empty ()
+- && build_id_equal (file_data.build_id, core_build_id))
+- m_expected_exec_filename = filename;
++ /* If this mapped file is marked as the main executable then record
++ the filename as we can use this later. */
++ if (file_data.is_main_exec && m_expected_exec_filename.empty ())
++ m_expected_exec_filename = file_data.filename;
+
+ /* Use exec_file_find() to do sysroot expansion. It'll
+ also strip the potential sysroot "target:" prefix. If
+ there is no sysroot, an equivalent (possibly more
+ canonical) pathname will be provided. */
+ gdb::unique_xmalloc_ptr<char> expanded_fname
+- = exec_file_find (filename.c_str (), nullptr);
++ = exec_file_find (file_data.filename.c_str (), nullptr);
+
+ bool build_id_mismatch = false;
+ if (expanded_fname != nullptr && file_data.build_id != nullptr)
+@@ -508,7 +428,7 @@ core_target::build_file_mappings ()
+ {
+ abfd = find_objfile_by_build_id (current_program_space,
+ file_data.build_id,
+- filename.c_str ());
++ file_data.filename.c_str ());
+
+ if (abfd != nullptr)
+ {
+@@ -526,7 +446,7 @@ core_target::build_file_mappings ()
+ }
+
+ std::vector<mem_range> ranges;
+- for (const mapped_file::region ®ion : file_data.regions)
++ for (const core_mapped_file::region ®ion : file_data.regions)
+ ranges.emplace_back (region.start, region.end - region.start);
+
+ if (expanded_fname == nullptr
+@@ -544,7 +464,7 @@ core_target::build_file_mappings ()
+ bool content_is_in_core_file_p = true;
+
+ /* Record all regions for this file as unavailable. */
+- for (const mapped_file::region ®ion : file_data.regions)
++ for (const core_mapped_file::region ®ion : file_data.regions)
+ {
+ /* Check to see if the region is available within the core
+ file. */
+@@ -576,33 +496,33 @@ core_target::build_file_mappings ()
+ if (build_id_mismatch)
+ {
+ if (expanded_fname == nullptr
+- || filename == expanded_fname.get ())
++ || file_data.filename == expanded_fname.get ())
+ warning (_("File %ps doesn't match build-id from core-file "
+ "during file-backed mapping processing"),
+ styled_string (file_name_style.style (),
+- filename.c_str ()));
++ file_data.filename.c_str ()));
+ else
+ warning (_("File %ps which was expanded to %ps, doesn't match "
+ "build-id from core-file during file-backed "
+ "mapping processing"),
+ styled_string (file_name_style.style (),
+- filename.c_str ()),
++ file_data.filename.c_str ()),
+ styled_string (file_name_style.style (),
+ expanded_fname.get ()));
+ }
+ else if (!content_is_in_core_file_p)
+ {
+ if (expanded_fname == nullptr
+- || filename == expanded_fname.get ())
++ || file_data.filename == expanded_fname.get ())
+ warning (_("Can't open file %ps during file-backed mapping "
+ "note processing"),
+ styled_string (file_name_style.style (),
+- filename.c_str ()));
++ file_data.filename.c_str ()));
+ else
+ warning (_("Can't open file %ps which was expanded to %ps "
+ "during file-backed mapping note processing"),
+ styled_string (file_name_style.style (),
+- filename.c_str ()),
++ file_data.filename.c_str ()),
+ styled_string (file_name_style.style (),
+ expanded_fname.get ()));
+ }
+@@ -616,7 +536,7 @@ core_target::build_file_mappings ()
+ abfd.get ());
+
+ /* Create sections for each mapped region. */
+- for (const mapped_file::region ®ion : file_data.regions)
++ for (const core_mapped_file::region ®ion : file_data.regions)
+ {
+ /* Make new BFD section. All sections have the same name,
+ which is permitted by bfd_make_section_anyway(). */
+@@ -652,7 +572,7 @@ core_target::build_file_mappings ()
+ soname = gdb_bfd_read_elf_soname (actual_filename);
+ }
+
+- m_mapped_file_info.add (soname.get (), filename.c_str (),
++ m_mapped_file_info.add (soname.get (), file_data.filename.c_str (),
+ actual_filename, std::move (ranges),
+ file_data.build_id);
+ }
+@@ -678,6 +598,9 @@ core_target::clear_core ()
+ clear_solib (current_program_space);
+
+ current_program_space->cbfd.reset (nullptr);
++
++ /* Notify that the core file has changed. */
++ gdb::observers::core_file_changed.notify (current_inferior ());
+ }
+ }
+
+@@ -1278,6 +1201,9 @@ core_target_open (const char *arg, int from_tty)
+ exception_print (gdb_stderr, except);
+ }
+ }
++
++ /* Notify that the core file has changed. */
++ gdb::observers::core_file_changed.notify (current_inferior ());
+ }
+
+ void
+@@ -2156,6 +2082,103 @@ mapped_file_info::lookup (const char *filename,
+
+ /* See gdbcore.h. */
+
++std::vector<core_mapped_file>
++gdb_read_core_file_mappings (struct gdbarch *gdbarch, struct bfd *cbfd)
++{
++ std::vector<core_mapped_file> results;
++
++ /* A map entry used while building RESULTS. */
++ struct map_entry
++ {
++ explicit map_entry (core_mapped_file *ptr)
++ : file_data (ptr)
++ { /* Nothing. */ }
++
++ /* Points to an entry in RESULTS, this allows entries to be quickly
++ looked up and updated as new mappings are read. */
++ core_mapped_file *file_data = nullptr;
++
++ /* If true then we have seen multiple different build-ids associated
++ with the filename of FILE_DATA. The FILE_DATA->build_id field will
++ have been set to nullptr, and we should not set FILE_DATA->build_id
++ in future. */
++ bool ignore_build_id_p = false;
++ };
++
++ /* All files mapped into the core file. The key is the filename. */
++ gdb::unordered_map<std::string, map_entry> mapped_files;
++
++ /* Get the build-id of the core file. At least on Linux, this will be
++ the build-id for the main executable. If other targets add the
++ gdbarch_read_core_file_mappings method, then it might turn out that
++ this logic is no longer true, in which case this might need to move
++ into the gdbarch_read_core_file_mappings method. */
++ const bfd_build_id *core_build_id = build_id_bfd_get (cbfd);
++
++ /* See linux_read_core_file_mappings() in linux-tdep.c for an example
++ read_core_file_mappings method. */
++ gdbarch_read_core_file_mappings (gdbarch, cbfd,
++ /* After determining the number of mappings, read_core_file_mappings
++ will invoke this lambda. */
++ [&] (ULONGEST)
++ {
++ },
++
++ /* read_core_file_mappings will invoke this lambda for each mapping
++ that it finds. */
++ [&] (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs,
++ const char *filename, const bfd_build_id *build_id)
++ {
++ /* Architecture-specific read_core_mapping methods are expected to
++ weed out non-file-backed mappings. */
++ gdb_assert (filename != nullptr);
++
++ /* Add this mapped region to the data for FILENAME. */
++ auto iter = mapped_files.find (filename);
++ if (iter == mapped_files.end ())
++ {
++ /* Create entry in results list. */
++ results.emplace_back ();
++
++ /* The entry to be added to the lookup map. */
++ map_entry entry (&results.back ());
++ entry.file_data->filename = filename;
++
++ /* Add entry to the quick lookup map and update ITER. */
++ auto inserted_result
++ = mapped_files.insert ({filename, std::move (entry)});
++ gdb_assert (inserted_result.second);
++ iter = inserted_result.first;
++ }
++
++ core_mapped_file &file_data = *iter->second.file_data;
++ bool &ignore_build_id_p = iter->second.ignore_build_id_p;
++
++ file_data.regions.emplace_back (start, end, file_ofs);
++ if (build_id != nullptr && !ignore_build_id_p)
++ {
++ if (file_data.build_id == nullptr)
++ file_data.build_id = build_id;
++ else if (!build_id_equal (build_id, file_data.build_id))
++ {
++ warning (_("Multiple build-ids found for %ps"),
++ styled_string (file_name_style.style (), filename));
++ file_data.build_id = nullptr;
++ ignore_build_id_p = true;
++ }
++ }
++
++ if (build_id != nullptr
++ && core_build_id != nullptr
++ && build_id_equal (build_id, core_build_id))
++ file_data.is_main_exec = true;
++ });
++
++ return results;
++}
++
++/* See gdbcore.h. */
++
+ std::optional <core_target_mapped_file_info>
+ core_target_find_mapped_file (const char *filename,
+ std::optional<CORE_ADDR> addr)
+diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
+--- a/gdb/doc/gdb.texinfo
++++ b/gdb/doc/gdb.texinfo
+@@ -40029,10 +40029,10 @@ will set a temporary breakpoint at the program's first instruction, using
+ the same approach as the @code{starti} command. @xref{Starting}.
+ @end table
+
+-@value{GDBN} defines some parameters that can be passed to the
+-@code{attach} request. Either @code{pid} or @code{target} must be
+-specified, but if both are specified then @code{target} will be
+-ignored.
++@value{GDBN} defines some additional parameters that can be passed to
++the @code{attach} request. One of @code{pid}, @code{target}, or
++@code{coreFile} must be specified. If multiple are specified, they
++are checked for in that order, and the first one found is used.
+
+ @table @code
+ @item pid
+@@ -40048,6 +40048,10 @@ should be supplied.
+ @item target
+ The target to which @value{GDBN} should connect. This is a string and
+ is passed to the @code{target remote} command. @xref{Connecting}.
++
++@item coreFile
++A string that specifies a core file to use. This corresponds to the
++@kbd{core-file} command. @xref{core-file command}.
+ @end table
+
+ In response to the @code{disassemble} request, DAP allows the client
+diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
+--- a/gdb/doc/python.texi
++++ b/gdb/doc/python.texi
+@@ -233,6 +233,7 @@ optional arguments while skipping others. Example:
+ * Disassembly In Python:: Instruction Disassembly In Python
+ * Missing Debug Info In Python:: Handle missing debug info from Python.
+ * Missing Objfiles In Python:: Handle objfiles from Python.
++* Core Files In Python:: Python representation of core files.
+ @end menu
+
+ @node Basic Python
+@@ -3626,6 +3627,15 @@ necessary quoting for the shell; when a sequence is assigned, the
+ quoting is applied by @value{GDBN}.
+ @end defvar
+
++@defvar Inferior.corefile
++If a core file has been loaded into this inferior (@pxref{core-file
++command}), then this contains a @code{gdb.Corefile} object that
++represents the loaded core file (@pxref{Core Files In Python}).
++
++If no core file has been loaded into this inferior, then this
++attribute contains @code{None}.
++@end defvar
++
+ A @code{gdb.Inferior} object has the following methods:
+
+ @defun Inferior.is_valid ()
+@@ -4094,6 +4104,17 @@ file is updated first, so when this event is emitted, the executable
+ filename will have changed, but the symbol filename might still hold
+ its previous value.
+
++@item events.corefile_changed
++Emits @code{gdb.CorefileChangedEvent} which indicates that the core
++file associated with a @code{gdb.Inferior} has changed, either a new
++core file has been loaded, or the existing core file has been
++unloaded (@pxref{Core Files In Python}).
++
++@defvar CorefileChangedEvent.inferior
++The @code{gdb.Inferior} in which the core file has changed
++(@pxref{Inferiors In Python}).
++@end defvar
++
+ @item events.new_progspace
+ This is emitted when @value{GDBN} adds a new program space
+ (@pxref{Progspaces In Python,,Program Spaces In Python}). The event
+@@ -8624,6 +8645,106 @@ handlers, all of the matching handlers are enabled. The
+ @code{enabled} field of each matching handler is set to @code{True}.
+ @end table
+
++@node Core Files In Python
++@subsubsection Core Files In Python
++@cindex python, core files
++
++When a core file is loaded into an inferior (@pxref{Inferiors In
++Python}) for examination (@pxref{core-file command}), information
++about the core file is contained in a @code{gdb.Corefile} object.
++
++The @code{gdb.Corefile} for an inferior can be accessed using the
++@code{Inferior.corefile} attribute. This will be @code{None} if
++no core file is loaded.
++
++A @code{gdb.Corefile} object has the following attributes:
++
++@defvar Corefile.filename
++This read only attribute contains a non-empty string, the file name of
++the core file. Attempting to access this attribute on an invalid
++@code{gdb.Corefile} object will raise a @code{RuntimeError} exception.
++@end defvar
++
++A @code{gdb.Corefile} object has the following methods:
++
++@defun Corefile.is_valid ()
++Returns @code{True} if the @code{gdb.Corefile} object is valid,
++@code{False} if not. A @code{gdb.Corefile} object will become invalid
++when the core file is unloaded from the inferior using the
++@kbd{core-file} command (@pxref{core-file command}), or if the
++inferior in which the core file is loaded is deleted. All other
++@code{gdb.Corefile} methods and attributes will throw an exception if
++it is invalid at the time the method is called, or the attribute
++accessed.
++@end defun
++
++@defun Corefile.mapped_files ()
++Return a list of @code{gdb.CorefileMappedFile} (see below) objects
++representing files that were mapped into the process when the core
++file was created. This information is read from the @samp{NT_FILE}
++core file note on Linux. Not every target supports accessing this
++information, for targets without support, an empty list will be
++returned.
++@end defun
++
++One may add arbitrary attributes to @code{gdb.Corefile} objects in the
++usual Python way. This is useful if, for example, one needs to do
++some extra record keeping associated with the corefile.
++@xref{choosing attribute names}, for guidance on selecting a suitable
++name for new attributes.
++
++The @code{Corefile.mapped_files ()} method returns a list of
++@code{gdb.CorefileMappedFile} objects. Each of these objects
++represents a file that was fully, or partially, mapped into the
++processes address space when the core file was created.
++
++A @code{gdb.CorefileMappedFile} object has the following attributes:
++
++@defvar CorefileMappedFile.filename
++This read only attribute contains a non-empty string, the file name of
++the mapped file.
++@end defvar
++
++@defvar CorefileMappedFile.build_id
++This read only attribute contains a non-empty string or @code{None}.
++This is the build-id of the mapped file extracted from the core file,
++or @code{None} if there was no build-id, or @value{GDBN} was unable to
++extract the build-id.
++@end defvar
++
++@defvar CorefileMappedFile.is_main_executable
++This read only attribute is @code{True} if @value{GDBN} believes this
++mapping represents the main executable for which this core file was
++created. This will be @code{False} for all other mappings.
++@end defvar
++
++@defvar CorefileMappedFile.regions
++This read only attribute contains a list of
++@code{gdb.CorefileMappedFileRegion} objects. Each of these objects
++describes a region of the file that was mapped into the process when
++the core file was created, further details are given below.
++@end defvar
++
++The @code{gdb.CorefileMappedFileRegion} object describes which part of
++a file that was mapped into a process when the core file was created.
++
++A @code{gdb.CorefileMappedFile} object has the following attributes:
++
++@defvar CorefileMappedFileRegion.start
++This read only attribute contains the start address of this mapping
++within the inferior.
++@end defvar
++
++@defvar CorefileMappedFileRegion.end
++This read only attribute contains end address of this mapping within
++the inferior.
++@end defvar
++
++@defvar CorefileMappedFileRegion.file_offset
++This read only attribute contains the offset within the mapped file
++for this mapping.
++@end defvar
++
+ @node Python Auto-loading
+ @subsection Python Auto-loading
+ @cindex Python auto-loading
+diff --git a/gdb/gdbcore.h b/gdb/gdbcore.h
+--- a/gdb/gdbcore.h
++++ b/gdb/gdbcore.h
+@@ -258,4 +258,47 @@ std::optional<core_target_mapped_file_info>
+ core_target_find_mapped_file (const char *filename,
+ std::optional<CORE_ADDR> addr);
+
++/* Type holding information about a single file mapped into the inferior
++ at the point when the core file was created. Associates a build-id
++ with the list of regions the file is mapped into. */
++struct core_mapped_file
++{
++ /* Type for a region of a file that was mapped into the inferior when
++ the core file was generated. */
++ struct region
++ {
++ /* Constructor. See member variables for argument descriptions. */
++ region (CORE_ADDR start_, CORE_ADDR end_, CORE_ADDR file_ofs_)
++ : start (start_),
++ end (end_),
++ file_ofs (file_ofs_)
++ { /* Nothing. */ }
++
++ /* The inferior address for the start of the mapped region. */
++ CORE_ADDR start;
++
++ /* The inferior address immediately after the mapped region. */
++ CORE_ADDR end;
++
++ /* The offset within the mapped file for this content. */
++ CORE_ADDR file_ofs;
++ };
++
++ /* The filename as recorded in the core file. */
++ std::string filename;
++
++ /* If not nullptr, then this is the build-id associated with this
++ file. */
++ const bfd_build_id *build_id = nullptr;
++
++ /* All the mapped regions of this file. */
++ std::vector<region> regions;
++
++ /* True if this is the main executable. */
++ bool is_main_exec = false;
++};
++
++extern std::vector<core_mapped_file> gdb_read_core_file_mappings
++ (struct gdbarch *gdbarch, struct bfd *cbfd);
++
+ #endif /* GDB_GDBCORE_H */
+diff --git a/gdb/observable.c b/gdb/observable.c
+--- a/gdb/observable.c
++++ b/gdb/observable.c
+@@ -76,6 +76,7 @@ DEFINE_OBSERVABLE (target_post_wait);
+ DEFINE_OBSERVABLE (new_program_space);
+ DEFINE_OBSERVABLE (free_program_space);
+ DEFINE_OBSERVABLE (tui_enabled);
++DEFINE_OBSERVABLE (core_file_changed);
+
+ } /* namespace observers */
+ } /* namespace gdb */
+diff --git a/gdb/observable.h b/gdb/observable.h
+--- a/gdb/observable.h
++++ b/gdb/observable.h
+@@ -260,6 +260,12 @@ extern observable <program_space */* pspace */> free_program_space;
+
+ extern observable<bool /* enabled */> tui_enabled;
+
++/* The core file loaded into the program space inferior INF has changed.
++ The process of changing has completed, i.e. when unloading, the unload
++ is now complete. When loading a new core file, the load is complete,
++ shared libraries have been loaded, registers and threads read in, etc. */
++extern observable<inferior */* inf */> core_file_changed;
++
+ } /* namespace observers */
+
+ } /* namespace gdb */
+diff --git a/gdb/python/lib/gdb/dap/events.py b/gdb/python/lib/gdb/dap/events.py
+--- a/gdb/python/lib/gdb/dap/events.py
++++ b/gdb/python/lib/gdb/dap/events.py
+@@ -161,7 +161,7 @@ _expected_pause = False
+
+
+ @in_gdb_thread
+-def exec_and_expect_stop(cmd, expected_pause=False, propagate_exception=False):
++def exec_and_expect_stop(cmd, expected_pause=False):
+ """A wrapper for exec_and_log that sets the continue-suppression flag.
+
+ When EXPECTED_PAUSE is True, a stop that looks like a pause (e.g.,
+@@ -174,7 +174,7 @@ def exec_and_expect_stop(cmd, expected_pause=False, propagate_exception=False):
+ # continuing.
+ _suppress_cont = not expected_pause
+ # FIXME if the call fails should we clear _suppress_cont?
+- exec_and_log(cmd, propagate_exception)
++ exec_and_log(cmd)
+
+
+ # Map from gdb stop reasons to DAP stop reasons. Some of these can't
+@@ -276,6 +276,31 @@ def _on_inferior_call(event):
+ send_event("stopped", obj)
+
+
++@in_gdb_thread
++def _on_corefile_changed(event):
++ # Ignore events relating to corefile being unloaded.
++ if event.inferior.corefile is None:
++ return
++
++ # Corefiles are usually attached via the 'attach' request, which
++ # sets the global _expected_stop_reason to 'attach'. It is
++ # because of this that it is safe to forward to _on_stop, as when
++ # _expected_stop_reason is set _on_stop doesn't read the
++ # event.details, which EVENT doesn't have.
++ #
++ # However, if the user loads a core file via some mechanism other
++ # than the 'attach' request, e.g. they use the repl to issue a GDB
++ # 'core-file' command, then when we get here _expected_stop_reason
++ # will not be set.
++ #
++ # So, in either case, set _expected_stop_reason now.
++ global _expected_stop_reason
++ _expected_stop_reason = "attach"
++
++ # A corefile was loaded, announce that the inferior has stopped.
++ _on_stop(event)
++
++
+ gdb.events.stop.connect(_on_stop)
+ gdb.events.exited.connect(_on_exit)
+ gdb.events.new_thread.connect(_new_thread)
+@@ -284,3 +309,4 @@ gdb.events.cont.connect(_cont)
+ gdb.events.new_objfile.connect(_new_objfile)
+ gdb.events.free_objfile.connect(_objfile_removed)
+ gdb.events.inferior_call.connect(_on_inferior_call)
++gdb.events.corefile_changed.connect(_on_corefile_changed)
+diff --git a/gdb/python/lib/gdb/dap/launch.py b/gdb/python/lib/gdb/dap/launch.py
+--- a/gdb/python/lib/gdb/dap/launch.py
++++ b/gdb/python/lib/gdb/dap/launch.py
+@@ -59,12 +59,16 @@ class _LaunchOrAttachDeferredRequest(DeferredRequest):
+ super().reschedule()
+
+
++# Handle whitespace, quotes, and backslashes here. Exactly what
++# to quote depends on libiberty's buildargv and safe-ctype.
++def escape_filename(filename):
++ return re.sub("[ \t\n\r\f\v\\\\'\"]", "\\\\\\g<0>", filename)
++
++
+ # A wrapper for the 'file' command that correctly quotes its argument.
+ @in_gdb_thread
+ def file_command(program):
+- # Handle whitespace, quotes, and backslashes here. Exactly what
+- # to quote depends on libiberty's buildargv and safe-ctype.
+- program = re.sub("[ \t\n\r\f\v\\\\'\"]", "\\\\\\g<0>", program)
++ program = escape_filename(program)
+ exec_and_log("file " + program)
+
+
+@@ -132,6 +136,7 @@ def attach(
+ program: Optional[str] = None,
+ pid: Optional[int] = None,
+ target: Optional[str] = None,
++ coreFile: Optional[str] = None,
+ **args,
+ ):
+ # The actual attach is handled by this function.
+@@ -143,11 +148,14 @@ def attach(
+ cmd = "attach " + str(pid)
+ elif target is not None:
+ cmd = "target remote " + target
++ elif coreFile is not None:
++ cmd = "core-file " + escape_filename(coreFile)
+ else:
+- raise DAPException("attach requires either 'pid' or 'target'")
++ raise DAPException("attach requires either 'pid', 'target', or 'coreFile'")
+ expect_process("attach")
+ expect_stop("attach")
+ exec_and_log(cmd)
++
+ # Attach response does not have a body.
+ return None
+
+diff --git a/gdb/python/lib/gdb/dap/next.py b/gdb/python/lib/gdb/dap/next.py
+--- a/gdb/python/lib/gdb/dap/next.py
++++ b/gdb/python/lib/gdb/dap/next.py
+@@ -76,7 +76,7 @@ def step_in(
+ @request("stepOut")
+ def step_out(*, threadId: int, singleThread: bool = False, **args):
+ _handle_thread_step(threadId, singleThread, True)
+- exec_and_expect_stop("finish &", propagate_exception=True)
++ exec_and_expect_stop("finish &")
+
+
+ @request("continue")
+diff --git a/gdb/python/lib/gdb/dap/server.py b/gdb/python/lib/gdb/dap/server.py
+--- a/gdb/python/lib/gdb/dap/server.py
++++ b/gdb/python/lib/gdb/dap/server.py
+@@ -19,6 +19,7 @@ import inspect
+ import json
+ import threading
+ from contextlib import contextmanager
++from typing import Optional
+
+ import gdb
+
+@@ -610,11 +611,29 @@ def terminate(**args):
+ exec_and_log("kill")
+
+
++@in_gdb_thread
++def _disconnect_or_kill(terminate: Optional[bool]):
++ inf = gdb.selected_inferior()
++ if inf.connection is None:
++ # Nothing to do here.
++ return
++ if terminate is None:
++ # The default depends on whether the inferior was attached or
++ # launched.
++ terminate = not inf.was_attached
++
++ if inf.corefile is not None:
++ exec_and_log("core-file")
++ elif terminate:
++ exec_and_log("kill")
++ elif inf.was_attached:
++ exec_and_log("detach")
++
++
+ @request("disconnect", on_dap_thread=True, expect_stopped=False)
+ @capability("supportTerminateDebuggee")
+-def disconnect(*, terminateDebuggee: bool = False, **args):
+- if terminateDebuggee:
+- send_gdb_with_response("kill")
++def disconnect(*, terminateDebuggee: Optional[bool] = None, **args):
++ send_gdb_with_response(lambda: _disconnect_or_kill(terminateDebuggee))
+ _server.shutdown()
+
+
+diff --git a/gdb/python/lib/gdb/dap/startup.py b/gdb/python/lib/gdb/dap/startup.py
+--- a/gdb/python/lib/gdb/dap/startup.py
++++ b/gdb/python/lib/gdb/dap/startup.py
+@@ -204,7 +204,7 @@ def log_stack(level=LogLevel.DEFAULT):
+
+
+ @in_gdb_thread
+-def exec_and_log(cmd, propagate_exception=False):
++def exec_and_log(cmd):
+ """Execute the gdb command CMD.
+ If logging is enabled, log the command and its output."""
+ log("+++ " + cmd)
+@@ -213,10 +213,10 @@ def exec_and_log(cmd, propagate_exception=False):
+ if output != "":
+ log(">>> " + output)
+ except gdb.error as e:
+- if propagate_exception:
+- raise DAPException(str(e)) from e
+- else:
+- log_stack()
++ # Don't normally want to see this, as it interferes with the
++ # test suite.
++ log_stack(LogLevel.FULL)
++ raise DAPException(str(e)) from e
+
+
+ @in_gdb_thread
+diff --git a/gdb/python/py-all-events.def b/gdb/python/py-all-events.def
+--- a/gdb/python/py-all-events.def
++++ b/gdb/python/py-all-events.def
+@@ -46,3 +46,4 @@ GDB_PY_DEFINE_EVENT(executable_changed)
+ GDB_PY_DEFINE_EVENT(new_progspace)
+ GDB_PY_DEFINE_EVENT(free_progspace)
+ GDB_PY_DEFINE_EVENT(tui_enabled)
++GDB_PY_DEFINE_EVENT(corefile_changed)
+diff --git a/gdb/python/py-corefile.c b/gdb/python/py-corefile.c
+new file mode 100644
+--- /dev/null
++++ b/gdb/python/py-corefile.c
+@@ -0,0 +1,723 @@
++/* Python interface to core files.
++
++ Copyright (C) 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/>. */
++
++#include "python-internal.h"
++#include "progspace.h"
++#include "observable.h"
++#include "inferior.h"
++#include "gdbcore.h"
++#include "gdbsupport/rsp-low.h"
++#include "py-event.h"
++
++/* A gdb.Corefile object. */
++
++struct corefile_object
++{
++ PyObject_HEAD
++
++ /* The inferior this core file is attached to. This will be set to NULL
++ when the inferior is deleted, or if a different core file is loaded
++ for the inferior. When this is NULL the gdb.Corefile object is
++ considered invalid.*/
++ struct inferior *inferior;
++
++ /* Dictionary holding user-added attributes. This is the __dict__
++ attribute of the object. This is an owning reference. */
++ PyObject *dict;
++
++ /* A Tuple of gdb.CorefileMappedFile objects. This tuple is only created
++ the first time the user calls gdb.Corefile.mapped_files(), the result
++ is cached here. If this pointer is not NULL then this is an owning
++ pointer (i.e. this owns a reference to the Tuple). */
++ PyObject *mapped_files;
++};
++
++extern PyTypeObject corefile_object_type
++ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("corefile_object");
++
++/* A gdb.CorefileMapped object. */
++
++struct corefile_mapped_file_object
++{
++ PyObject_HEAD
++
++ /* The name of a file that was mapped when the core file was created.
++ This is a 'str' object. */
++ PyObject *filename;
++
++ /* The build-id of a file that was mapped when the core file was
++ created. This is either a 'str' if the file had a build-id, or
++ 'None' if there was no build-id for this file. */
++ PyObject *build_id;
++
++ /* A List of gdb.CorefileMappedFileRegion objects. */
++ PyObject *regions;
++
++ /* True if this represents the main executable from which the core file
++ was created. */
++ bool is_main_exec_p;
++};
++
++extern PyTypeObject corefile_mapped_file_object_type
++ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("corefile_mapped_file_object");
++
++/* A gdb.CorefileMappedFileRegion object. */
++
++struct corefile_mapped_file_region_object
++{
++ PyObject_HEAD
++
++ /* The start and end addresses for this mapping, these are addresses
++ within the inferior's address space. */
++ CORE_ADDR start;
++ CORE_ADDR end;
++
++ /* The offset within the mapped file for this mapping. */
++ ULONGEST file_offset;
++};
++
++extern PyTypeObject corefile_mapped_file_region_object_type
++ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("corefile_mapped_file_region_object");
++
++/* Clear the inferior pointer in a Corefile object OBJ when an inferior is
++ deleted. */
++
++struct inferior_corefile_deleter
++{
++ void operator() (corefile_object *obj)
++ {
++ if (!gdb_python_initialized)
++ return;
++
++ gdbpy_enter enter_py;
++
++ /* When OBJECT goes out of scope this will Py_DECREF on OBJ. */
++ gdbpy_ref<corefile_object> object (obj);
++
++ /* Clearing the inferior pointer marks the gdb.Corefile as invalid. */
++ object->inferior = nullptr;
++ }
++};
++
++/* Store a gdb.Corefile object in an inferior's registry. */
++
++static const registry<inferior>::key<corefile_object,
++ inferior_corefile_deleter>
++ cfpy_inferior_corefile_data_key;
++
++/* See python-internal.h. */
++
++gdbpy_ref<>
++gdbpy_core_file_from_inferior (inferior *inf)
++{
++ gdb_assert (inf != nullptr);
++ gdb_assert (inf->pspace != nullptr);
++
++ program_space *pspace = inf->pspace;
++
++ if (pspace->core_bfd () == nullptr)
++ return gdbpy_ref<>::new_reference (Py_None);
++
++ PyObject *result = (PyObject *) cfpy_inferior_corefile_data_key.get (inf);
++ if (result == nullptr)
++ {
++ gdbpy_ref<corefile_object> object
++ (PyObject_New (corefile_object, &corefile_object_type));
++ if (object == nullptr)
++ return nullptr;
++
++ /* Ensure the 'inferior' field is set to NULL. If the PyDict_New
++ call fails then the gdb.Corefile will be discarded and
++ cfpy_dealloc will be called, which requires that the 'inferior' be
++ set to NULL. */
++ object->inferior = nullptr;
++ object->mapped_files = nullptr;
++ object->dict = PyDict_New ();
++ if (object->dict == nullptr)
++ return nullptr;
++
++ /* Now that the gdb.Corefile has been successfully initialised and we
++ know that it is going to be passed back to the user, move it out
++ of the invalid state by setting the 'inferior' field to a non NULL
++ value. */
++ object->inferior = inf;
++ cfpy_inferior_corefile_data_key.set (inf, object.get ());
++ result = (PyObject *) object.release ();
++ }
++
++ return gdbpy_ref<>::new_reference (result);
++}
++
++/* Return true if OBJ is valid. */
++
++static bool
++cfpy_corefile_object_is_valid (const corefile_object *obj)
++{
++ if (obj->inferior == nullptr)
++ return false;
++
++ gdb_assert (obj->inferior->pspace != nullptr);
++
++ return obj->inferior->pspace->core_bfd () != nullptr;
++}
++
++/* Require that COREFILE_OBJ be a valid core file. A valid core file
++ object has a valid program space, and the program space has a core file
++ loaded into it. */
++#define CFPY_REQUIRE_VALID(corefile_obj) \
++ do { \
++ if (!cfpy_corefile_object_is_valid (corefile_obj)) \
++ { \
++ PyErr_SetString (PyExc_RuntimeError, \
++ _("Corefile no longer exists.")); \
++ return nullptr; \
++ } \
++ } while (0)
++
++/* Read the gdb.Corefile.filename attribute. */
++
++static PyObject *
++cfpy_get_filename (PyObject *self, void *closure)
++{
++ corefile_object *obj = (corefile_object *) self;
++
++ CFPY_REQUIRE_VALID (obj);
++
++ /* If the program space's core file had been cleared, then this Corefile
++ object would have been invalidated. */
++ bfd *abfd = obj->inferior->pspace->core_bfd ();
++ gdb_assert (abfd != nullptr);
++
++ return host_string_to_python_string (bfd_get_filename (abfd)).release ();
++}
++
++/* Implementation of gdb.Corefile.is_valid (self) -> Boolean.
++ Returns True if this core file object is associated with a program space
++ that still exists, an the program space still has a core file loaded. */
++
++static PyObject *
++cfpy_is_valid (PyObject *self, PyObject *args)
++{
++ corefile_object *obj = (corefile_object *) self;
++
++ if (!cfpy_corefile_object_is_valid (obj))
++ Py_RETURN_FALSE;
++
++ Py_RETURN_TRUE;
++}
++
++/* Implement gdb.Corefile.mapped_files (). Return a List of
++ gdb.CorefileMappedFile objects. The list is created the first time
++ this method is called, and then cached within the gdb.Corefile object,
++ future calls just return a reference to the same list. */
++
++static PyObject *
++cfpy_mapped_files (PyObject *self, PyObject *args)
++{
++ corefile_object *obj = (corefile_object *) self;
++
++ CFPY_REQUIRE_VALID (obj);
++
++ /* If we have already created the List then just return another reference
++ to the existing list. */
++ if (obj->mapped_files != nullptr)
++ {
++ Py_INCREF (obj->mapped_files);
++ return obj->mapped_files;
++ }
++
++ /* Get all the mapping data from GDB. */
++ std::vector<core_mapped_file> mapped_files;
++ try
++ {
++ mapped_files
++ = gdb_read_core_file_mappings (obj->inferior->arch (),
++ current_program_space->core_bfd ());
++ }
++ catch (const gdb_exception &except)
++ {
++ return gdbpy_handle_gdb_exception (nullptr, except);
++ }
++
++ /* Create a new list to hold the results. */
++ gdbpy_ref<> tuple (PyTuple_New (mapped_files.size ()));
++ if (tuple == nullptr)
++ return nullptr;
++
++ /* Create each gdb.CorefileMappedFile object. */
++ Py_ssize_t tuple_idx = 0;
++ for (const core_mapped_file &file : mapped_files)
++ {
++ /* The filename 'str' object. */
++ gdbpy_ref<> filename
++ = host_string_to_python_string (file.filename.c_str ());
++ if (filename == nullptr)
++ return nullptr;
++
++ /* The build-id object. Either a 'str' or 'None'. */
++ gdbpy_ref<> build_id;
++ if (file.build_id != nullptr)
++ {
++ std::string hex_form = bin2hex (file.build_id->data,
++ file.build_id->size);
++
++ build_id
++ = host_string_to_python_string (hex_form.c_str ());
++ if (build_id == nullptr)
++ return nullptr;
++ }
++ else
++ build_id = gdbpy_ref<>::new_reference (Py_None);
++
++ /* List to hold all the gdb.CorefileMappedFileRegion objects. */
++ gdbpy_ref<> regions (PyTuple_New (file.regions.size ()));
++ if (regions == nullptr)
++ return nullptr;
++
++ /* Create all the gdb.CorefileMappedFileRegion objects. */
++ Py_ssize_t regions_idx = 0;
++ for (const core_mapped_file::region &r : file.regions)
++ {
++ /* Actually create the object. */
++ gdbpy_ref<corefile_mapped_file_region_object> region_obj
++ (PyObject_New (corefile_mapped_file_region_object,
++ &corefile_mapped_file_region_object_type));
++ if (region_obj == nullptr)
++ return nullptr;
++
++ /* Initialise the object. */
++ region_obj->start = r.start;
++ region_obj->end = r.end;
++ region_obj->file_offset = r.file_ofs;
++
++ /* Add to the gdb.CorefileMappedFileRegion list. */
++ if (PyTuple_SetItem (regions.get (), regions_idx++,
++ (PyObject *) region_obj.release ()) < 0)
++ return nullptr;
++ }
++
++ /* Actually create the gdb.CorefileMappedFile object. */
++ gdbpy_ref<corefile_mapped_file_object> entry
++ (PyObject_New (corefile_mapped_file_object,
++ &corefile_mapped_file_object_type));
++ if (entry == nullptr)
++ return nullptr;
++
++ /* Initialise the object. */
++ entry->filename = filename.release ();
++ entry->build_id = build_id.release ();
++ entry->regions = regions.release ();
++ entry->is_main_exec_p = file.is_main_exec;
++
++ /* Add to the gdb.CorefileMappedFile list. */
++ if (PyTuple_SetItem (tuple.get (), tuple_idx++,
++ (PyObject *) entry.release ()) < 0)
++ return nullptr;
++ }
++
++ /* No errors. Move the reference currently in LIST into the Corefile
++ object itself. Then create a new reference and hand this back to the
++ user. */
++ obj->mapped_files = tuple.release ();
++ Py_INCREF (obj->mapped_files);
++ return obj->mapped_files;
++}
++
++/* Emit a CorefileChangedEvent event, INF is the inferior in which the core
++ file changed. Return 0 on success, or a negative value on error. */
++
++static int
++emit_corefile_changed_event (inferior *inf)
++{
++ /* If there are no listeners then we are done. */
++ if (evregpy_no_listeners_p (gdb_py_events.corefile_changed))
++ return 0;
++
++ gdbpy_ref<> event_obj
++ = create_event_object (&corefile_changed_event_object_type);
++ if (event_obj == nullptr)
++ return -1;
++
++ gdbpy_ref<inferior_object> inf_obj = inferior_to_inferior_object (inf);
++ if (inf_obj == nullptr
++ || evpy_add_attribute (event_obj.get (), "inferior",
++ (PyObject *) inf_obj.get ()) < 0)
++ return -1;
++
++ return evpy_emit_event (event_obj.get (), gdb_py_events.corefile_changed);
++}
++
++/* Callback from gdb::observers::core_file_changed. The core file for
++ INF has been changed. */
++
++static void
++cfpy_corefile_changed (inferior *inf)
++{
++ /* It's safe to do this even if Python is not initialized, but there
++ should be nothing to clear in that case. */
++ cfpy_inferior_corefile_data_key.clear (inf);
++
++ if (!gdb_python_initialized)
++ return;
++
++ gdbpy_enter enter_py;
++
++ if (emit_corefile_changed_event (inf) < 0)
++ gdbpy_print_stack ();
++}
++
++/* Called when a gdb.Corefile is destroyed. */
++
++static void
++cfpy_dealloc (PyObject *obj)
++{
++ corefile_object *corefile = (corefile_object *) obj;
++
++ /* Every gdb.Corefile is cached in an inferior's registry. The only way
++ for a gdb.Corefile to be deallocated is to remove the object reference
++ from the registry (and dec its ref count), but before we do that, we
++ set the object's inferior pointer to NULL. */
++ gdb_assert (corefile->inferior == nullptr);
++
++ Py_XDECREF (corefile->dict);
++ Py_XDECREF (corefile->mapped_files);
++
++ Py_TYPE (obj)->tp_free (obj);
++}
++
++/* __repr__ implementation for gdb.Corefile. */
++
++static PyObject *
++cfpy_repr (PyObject *self)
++{
++ corefile_object *obj = (corefile_object *) self;
++
++ if (!cfpy_corefile_object_is_valid (obj))
++ return gdb_py_invalid_object_repr (self);
++
++ program_space *pspace = obj->inferior->pspace;
++ gdb_assert (pspace != nullptr);
++ return PyUnicode_FromFormat ("<%s inferior=%d filename='%s'>",
++ Py_TYPE (self)->tp_name,
++ obj->inferior->num,
++ bfd_get_filename (pspace->core_bfd ()));
++}
++
++\f
++
++/* Called when a gdb.CorefileMappedFile is destroyed. */
++
++static void
++cfmfpy_dealloc (PyObject *obj)
++{
++ corefile_mapped_file_object *mapped_file
++ = (corefile_mapped_file_object *) obj;
++
++ Py_XDECREF (mapped_file->filename);
++ Py_XDECREF (mapped_file->build_id);
++ Py_XDECREF (mapped_file->regions);
++
++ Py_TYPE (obj)->tp_free (obj);
++}
++
++/* Read the gdb.CorefileMappedFile.filename attribute. */
++
++static PyObject *
++cfmfpy_get_filename (PyObject *self, void *closure)
++{
++ corefile_mapped_file_object *obj
++ = (corefile_mapped_file_object *) self;
++
++ gdb_assert (obj->filename != nullptr);
++
++ Py_INCREF (obj->filename);
++ return obj->filename;
++}
++
++/* Read the gdb.CorefileMappedFile.build_id attribute. */
++
++static PyObject *
++cfmfpy_get_build_id (PyObject *self, void *closure)
++{
++ corefile_mapped_file_object *obj
++ = (corefile_mapped_file_object *) self;
++
++ gdb_assert (obj->build_id != nullptr);
++
++ Py_INCREF (obj->build_id);
++ return obj->build_id;
++}
++
++/* Read the gdb.CorefileMappedFile.regions attribute. */
++
++static PyObject *
++cfmfpy_get_regions (PyObject *self, void *closure)
++{
++ corefile_mapped_file_object *obj
++ = (corefile_mapped_file_object *) self;
++
++ gdb_assert (obj->regions != nullptr);
++
++ Py_INCREF (obj->regions);
++ return obj->regions;
++}
++
++/* Read the gdb.CorefileMappedFile.is_main_executable attribute. */
++
++static PyObject *
++cfmf_is_main_exec (PyObject *self, void *closure)
++{
++ corefile_mapped_file_object *obj
++ = (corefile_mapped_file_object *) self;
++
++ if (obj->is_main_exec_p)
++ Py_RETURN_TRUE;
++ else
++ Py_RETURN_FALSE;
++}
++
++\f
++
++/* Read the gdb.CorefileMappedFileRegion.start attribute. */
++
++static PyObject *
++cfmfrpy_get_start (PyObject *self, void *closure)
++{
++ corefile_mapped_file_region_object *obj
++ = (corefile_mapped_file_region_object *) self;
++
++ return gdb_py_object_from_ulongest (obj->start).release ();
++}
++
++/* Read the gdb.CorefileMappedFileRegion.end attribute. */
++
++static PyObject *
++cfmfrpy_get_end (PyObject *self, void *closure)
++{
++ corefile_mapped_file_region_object *obj
++ = (corefile_mapped_file_region_object *) self;
++
++ return gdb_py_object_from_ulongest (obj->end).release ();
++}
++
++/* Read the gdb.CorefileMappedFileRegion.file_offset attribute. */
++
++static PyObject *
++cfmfrpy_get_file_offset (PyObject *self, void *closure)
++{
++ corefile_mapped_file_region_object *obj
++ = (corefile_mapped_file_region_object *) self;
++
++ return gdb_py_object_from_ulongest (obj->file_offset).release ();
++}
++
++\f
++
++static int
++gdbpy_initialize_corefile ()
++{
++ gdb::observers::core_file_changed.attach (cfpy_corefile_changed,
++ "py-corefile");
++
++ if (gdbpy_type_ready (&corefile_object_type) < 0)
++ return -1;
++
++ if (gdbpy_type_ready (&corefile_mapped_file_object_type) < 0)
++ return -1;
++
++ if (gdbpy_type_ready (&corefile_mapped_file_region_object_type) < 0)
++ return -1;
++
++ return 0;
++}
++
++GDBPY_INITIALIZE_FILE (gdbpy_initialize_corefile);
++
++\f
++
++static gdb_PyGetSetDef corefile_getset[] =
++{
++ { "__dict__", gdb_py_generic_dict, nullptr,
++ "The __dict__ for the gdb.Corefile.", &corefile_object_type },
++ { "filename", cfpy_get_filename, nullptr,
++ "The filename of a valid Corefile object.", nullptr },
++ { nullptr }
++};
++
++static PyMethodDef corefile_object_methods[] =
++{
++ { "is_valid", cfpy_is_valid, METH_NOARGS,
++ "is_valid () -> Boolean.\n\
++Return true if this Corefile is valid, false if not." },
++ { "mapped_files", cfpy_mapped_files, METH_NOARGS,
++ "mapped_files () -> List of mapping tuples.\n\
++Return a list of tuples. Each tuple represents a mapping from the\
++core file." },
++ { nullptr }
++};
++
++PyTypeObject corefile_object_type =
++{
++ PyVarObject_HEAD_INIT (nullptr, 0)
++ "gdb.Corefile", /*tp_name*/
++ sizeof (corefile_object), /*tp_basicsize*/
++ 0, /*tp_itemsize*/
++ cfpy_dealloc, /*tp_dealloc*/
++ 0, /*tp_print*/
++ 0, /*tp_getattr*/
++ 0, /*tp_setattr*/
++ 0, /*tp_compare*/
++ cfpy_repr, /*tp_repr*/
++ 0, /*tp_as_number*/
++ 0, /*tp_as_sequence*/
++ 0, /*tp_as_mapping*/
++ 0, /*tp_hash */
++ 0, /*tp_call*/
++ 0, /*tp_str*/
++ 0, /*tp_getattro*/
++ 0, /*tp_setattro*/
++ 0, /*tp_as_buffer*/
++ Py_TPFLAGS_DEFAULT, /*tp_flags*/
++ "GDB corefile object", /* tp_doc */
++ 0, /* tp_traverse */
++ 0, /* tp_clear */
++ 0, /* tp_richcompare */
++ 0, /* tp_weaklistoffset */
++ 0, /* tp_iter */
++ 0, /* tp_iternext */
++ corefile_object_methods, /* tp_methods */
++ 0, /* tp_members */
++ corefile_getset, /* tp_getset */
++ 0, /* tp_base */
++ 0, /* tp_dict */
++ 0, /* tp_descr_get */
++ 0, /* tp_descr_set */
++ offsetof (corefile_object, dict), /* tp_dictoffset */
++ 0, /* tp_init */
++ 0, /* tp_alloc */
++ 0, /* tp_new */
++};
++
++static gdb_PyGetSetDef corefile_mapped_file_object_getset[] =
++{
++ { "filename", cfmfpy_get_filename, nullptr,
++ "The filename of a CorefileMappedFile object.", nullptr },
++ { "build_id", cfmfpy_get_build_id, nullptr,
++ "The build-id of a CorefileMappedFile object or None.", nullptr },
++ { "regions", cfmfpy_get_regions, nullptr,
++ "The list of regions from a CorefileMappedFile object.", nullptr },
++ { "is_main_executable", cfmf_is_main_exec, nullptr,
++ "True for the main executable mapping, otherwise False.", nullptr },
++ { nullptr }
++};
++
++PyTypeObject corefile_mapped_file_object_type =
++{
++ PyVarObject_HEAD_INIT (NULL, 0)
++ "gdb.CorefileMappedFile", /*tp_name*/
++ sizeof (corefile_mapped_file_object), /*tp_basicsize*/
++ 0, /*tp_itemsize*/
++ cfmfpy_dealloc, /*tp_dealloc*/
++ 0, /*tp_print*/
++ 0, /*tp_getattr*/
++ 0, /*tp_setattr*/
++ 0, /*tp_compare*/
++ 0, /*tp_repr*/
++ 0, /*tp_as_number*/
++ 0, /*tp_as_sequence*/
++ 0, /*tp_as_mapping*/
++ 0, /*tp_hash */
++ 0, /*tp_call*/
++ 0, /*tp_str*/
++ 0, /*tp_getattro*/
++ 0, /*tp_setattro*/
++ 0, /*tp_as_buffer*/
++ Py_TPFLAGS_DEFAULT, /*tp_flags*/
++ "GDB corefile mapped file object", /* tp_doc */
++ 0, /* tp_traverse */
++ 0, /* tp_clear */
++ 0, /* tp_richcompare */
++ 0, /* tp_weaklistoffset */
++ 0, /* tp_iter */
++ 0, /* tp_iternext */
++ 0, /* tp_methods */
++ 0, /* tp_members */
++ corefile_mapped_file_object_getset, /* tp_getset */
++ 0, /* tp_base */
++ 0, /* tp_dict */
++ 0, /* tp_descr_get */
++ 0, /* tp_descr_set */
++ 0, /* tp_dictoffset */
++ 0, /* tp_init */
++ 0, /* tp_alloc */
++ 0, /* tp_new */
++};
++
++static gdb_PyGetSetDef corefile_mapped_file_region_object_getset[] =
++{
++ { "start", cfmfrpy_get_start, nullptr,
++ "The start address of a CorefileMappedFileRegion object.", nullptr },
++ { "end", cfmfrpy_get_end, nullptr,
++ "The end address of a CorefileMappedFileRegion object.", nullptr },
++ { "file_offset", cfmfrpy_get_file_offset, nullptr,
++ "The file offset of a CorefileMappedFileRegion object.", nullptr },
++ { nullptr }
++};
++
++PyTypeObject corefile_mapped_file_region_object_type =
++{
++ PyVarObject_HEAD_INIT (NULL, 0)
++ "gdb.CorefileMappedFileRegion", /*tp_name*/
++ sizeof (corefile_mapped_file_region_object), /*tp_basicsize*/
++ 0, /*tp_itemsize*/
++ 0, /*tp_dealloc*/
++ 0, /*tp_print*/
++ 0, /*tp_getattr*/
++ 0, /*tp_setattr*/
++ 0, /*tp_compare*/
++ 0, /*tp_repr*/
++ 0, /*tp_as_number*/
++ 0, /*tp_as_sequence*/
++ 0, /*tp_as_mapping*/
++ 0, /*tp_hash */
++ 0, /*tp_call*/
++ 0, /*tp_str*/
++ 0, /*tp_getattro*/
++ 0, /*tp_setattro*/
++ 0, /*tp_as_buffer*/
++ Py_TPFLAGS_DEFAULT, /*tp_flags*/
++ "GDB corefile mapped file region object", /* tp_doc */
++ 0, /* tp_traverse */
++ 0, /* tp_clear */
++ 0, /* tp_richcompare */
++ 0, /* tp_weaklistoffset */
++ 0, /* tp_iter */
++ 0, /* tp_iternext */
++ 0, /* tp_methods */
++ 0, /* tp_members */
++ corefile_mapped_file_region_object_getset, /* tp_getset */
++ 0, /* tp_base */
++ 0, /* tp_dict */
++ 0, /* tp_descr_get */
++ 0, /* tp_descr_set */
++ 0, /* tp_dictoffset */
++ 0, /* tp_init */
++ 0, /* tp_alloc */
++ 0, /* tp_new */
++};
+diff --git a/gdb/python/py-event-types.def b/gdb/python/py-event-types.def
+--- a/gdb/python/py-event-types.def
++++ b/gdb/python/py-event-types.def
+@@ -145,3 +145,8 @@ GDB_PY_DEFINE_EVENT_TYPE (tui_enabled,
+ "TuiEnabledEvent",
+ "GDB TUI enabled event object",
+ event_object_type);
++
++GDB_PY_DEFINE_EVENT_TYPE (corefile_changed,
++ "CorefileChangedEvent",
++ "GDB corefile changed event",
++ event_object_type);
+diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c
+--- a/gdb/python/py-inferior.c
++++ b/gdb/python/py-inferior.c
+@@ -973,6 +973,22 @@ infpy_get_main_name (PyObject *self, void *closure)
+ return host_string_to_python_string (name).release ();
+ }
+
++/* Implement the Inferior.corefile getter. Returns a gdb.Corefile
++ object, or None. */
++
++static PyObject *
++infpy_get_core_file (PyObject *self, void *closure)
++{
++ inferior_object *inf = (inferior_object *) self;
++
++ INFPY_REQUIRE_VALID (inf);
++
++ inferior *inferior = inf->inferior;
++ gdb_assert (inferior != nullptr);
++
++ return gdbpy_core_file_from_inferior (inferior).release ();
++}
++
+ static void
+ infpy_dealloc (PyObject *obj)
+ {
+@@ -1062,6 +1078,8 @@ static gdb_PyGetSetDef inferior_object_getset[] =
+ { "progspace", infpy_get_progspace, NULL, "Program space of this inferior" },
+ { "main_name", infpy_get_main_name, nullptr,
+ "Name of 'main' function, if known.", nullptr },
++ { "corefile", infpy_get_core_file, nullptr,
++ "The corefile loaded in to this inferior, or None.", nullptr },
+ { NULL }
+ };
+
+diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
+--- a/gdb/python/python-internal.h
++++ b/gdb/python/python-internal.h
+@@ -1113,6 +1113,14 @@ extern std::optional<int> gdbpy_print_insn (struct gdbarch *gdbarch,
+ CORE_ADDR address,
+ disassemble_info *info);
+
++/* Return the gdb.Corefile object representing the core file loaded into
++ the program space of INF, or None if there is no core file loaded. INF
++ must not be NULL. If an error occurs then NULL is returned, and a
++ suitable Python error will be set. */
++
++extern gdbpy_ref<> gdbpy_core_file_from_inferior (inferior *inf);
++
++
+ /* A wrapper for PyType_Ready that also automatically registers the
+ type in the appropriate module. Returns 0 on success, -1 on error.
+ If MOD is supplied, then the type is added to that module. If MOD
+diff --git a/gdb/testsuite/gdb.dap/attach-fail.exp b/gdb/testsuite/gdb.dap/attach-fail.exp
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.dap/attach-fail.exp
+@@ -0,0 +1,32 @@
++# 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/>.
++
++# Test "attach" failure in DAP.
++
++require can_spawn_for_attach allow_dap_tests
++
++load_lib dap-support.exp
++
++# Passing an empty remote name here will guarantee a failure without
++# trying to find a real remote.
++set id [dap_target_remote {""}]
++
++dap_check_request_and_response "configurationDone" configurationDone
++
++set resp [lindex [dap_read_response attach $id] 0]
++gdb_assert {[dict get $resp success] == "false"} \
++ "attach failed"
++
++dap_shutdown
+diff --git a/gdb/testsuite/gdb.dap/corefile.c b/gdb/testsuite/gdb.dap/corefile.c
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.dap/corefile.c
+@@ -0,0 +1,45 @@
++/* This testcase is part of GDB, the GNU debugger.
++
++ Copyright 2026 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/>. */
++
++#include <stdlib.h>
++
++int global_var = 0;
++
++void
++baz (void)
++{
++ abort ();
++}
++
++void
++bar (void)
++{
++ baz ();
++}
++
++void
++foo (void)
++{
++ bar ();
++}
++
++int
++main (void)
++{
++ foo ();
++ return 0;
++}
+diff --git a/gdb/testsuite/gdb.dap/corefile.exp b/gdb/testsuite/gdb.dap/corefile.exp
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.dap/corefile.exp
+@@ -0,0 +1,196 @@
++# Copyright 2026 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/>.
++
++# Test using "attach" in DAP for opening a core file.
++
++require allow_dap_tests
++
++load_lib dap-support.exp
++
++standard_testfile
++
++if {[build_executable ${testfile}.exp $testfile] == -1} {
++ return
++}
++
++set corefile [core_find $binfile {}]
++if {$corefile == ""} {
++ untested "unable to create or find corefile"
++ return
++}
++
++set other_corefile [standard_output_file "${testfile} with spaces.core"]
++remote_exec build "cp \"$corefile\" \"$other_corefile\""
++
++# Test that attaching to a core file works at all.
++set attach_id [dap_corefile $corefile $binfile]
++
++dap_check_request_and_response "configurationDone" configurationDone
++
++dap_check_response "attach response" attach $attach_id
++
++dap_wait_for_event_and_check "stopped" stopped \
++ "body reason" attach
++
++# Try 'continue', this should fail.
++set obj [dap_request_and_response continue \
++ {o threadId [i 1]}]
++set response [lindex $obj 0]
++gdb_assert { [dict get $response success] == "false" } \
++ "continue with core file target"
++
++# Get a backtrace from the core file.
++set bt [lindex [dap_check_request_and_response "backtrace" stackTrace \
++ {o threadId [i 1]}] 0]
++set frame_id [dict get [lindex [dict get $bt body stackFrames] 0] id]
++
++# Get all scopes for frame 0. Search through scopes to find the
++# register scope.
++set scopes [dap_check_request_and_response "get scopes" scopes \
++ [format {o frameId [i %d]} $frame_id]]
++set scopes [dict get [lindex $scopes 0] body scopes]
++set reg_scope ""
++foreach s $scopes {
++ if {[dict get $s name] == "Registers"} {
++ set reg_scope $s
++ }
++}
++gdb_assert { $reg_scope ne "" } "found register scope"
++
++# Read all the registers from the register scope.
++set num [dict get $reg_scope variablesReference]
++set reply [lindex [dap_check_request_and_response "fetch all registers" \
++ "variables" \
++ [format {o variablesReference [i %d] count [i %d]} $num\
++ [dict get $reg_scope namedVariables]]] 0]
++
++# Find the name and value of a register, we'll use this to try setting
++# a register below.
++set regs [dict get $reply body variables]
++gdb_assert {[llength $regs] > 0} "got at least one register"
++set reg_name [dict get [lindex $regs 0] name]
++set reg_value [dict get [lindex $regs 0] value]
++
++set obj [dap_request_and_response setExpression \
++ {o expression [s global_var] value [s 23]}]
++set response [lindex $obj 0]
++gdb_assert { [dict get $response success] == "false" } \
++ "set global variable fails"
++set expected_exception_count 1
++
++# Try setting a register, this should fail as registers are not
++# writable for a core file target. We need to write back a different
++# register value, so we add one to the current value. This means we
++# can only run the test if the current register value is an integer.
++if {[string is integer -strict $reg_value]} {
++ set new_value [expr {$reg_value + 1}]
++ set obj [dap_request_and_response setExpression \
++ {o expression [s \$$reg_name] value [s $new_value]}]
++ set response [lindex $obj 0]
++ gdb_assert { [dict get $response success] == "false" } \
++ "set register fails"
++ incr expected_exception_count
++}
++
++dap_shutdown false $expected_exception_count
++
++# Reconnect to the core file. This time when we shutdown we will
++# request that the target be terminated, GDB should still just
++# disconnect though as core file targets cannot be killed.
++with_test_prefix "reattach" {
++ set attach_id [dap_corefile $corefile $binfile]
++
++ dap_check_request_and_response "configurationDone" configurationDone
++
++ dap_check_response "attach response" attach $attach_id
++
++ dap_wait_for_event_and_check "stopped" stopped \
++ "body reason" attach
++
++ # Request the target be terminated. This doesn't make sense for
++ # core file targets (which cannot be killed), but GDB should
++ # handle this gracefully and just disconnect.
++ dap_shutdown true
++}
++
++# Test loading a core file with spaces in its name.
++with_test_prefix "core file with spaces" {
++ gdb_exit
++
++ # Test that attaching to a core file works at all.
++ set attach_id [dap_corefile $other_corefile]
++
++ dap_check_request_and_response "configurationDone" configurationDone
++
++ dap_check_response "attach response" attach $attach_id
++
++ dap_wait_for_event_and_check "stopped" stopped \
++ "body reason" attach
++
++ # Use the repl to issue an 'info inferiors' command.
++ set obj [dap_check_request_and_response "command repl" \
++ evaluate {o expression [s "info inferiors"] context [s repl]}]
++ set response [lindex $obj 0]
++ set result [dict get $response body result]
++ set result [string map {\\n \n \\t \t} $result]
++ verbose -log "Info Inferiors Output:\n$result\n\n"
++
++ # Check that the output contains the header line, the executable
++ # name, and the core file name. We didn't pass the executable
++ # name when loading the core file, but GDB should have been able
++ # to find the executable from the core file.
++ gdb_assert { [regexp "Num\\s+Description\\s+Connection\\s+Executable" $result] } \
++ "info inferiors column headers found"
++
++ gdb_assert { [regexp "[string_to_regexp $binfile]\\s*\n" $result] } \
++ "executable name was set during core file load"
++
++ dap_shutdown
++}
++
++# Test loading a core file via the repl.
++with_test_prefix "load core file via repl" {
++ gdb_exit
++
++ if {[dap_initialize] == ""} {
++ return
++ }
++
++ # Use the repl to issue a 'core-file' command.
++ set obj [dap_check_request_and_response "command repl" \
++ evaluate [format {o expression [s "core-file %s"] context [s repl]} $corefile]]
++ set response [lindex $obj 0]
++ set result [dict get $response body result]
++ set result [string map {\\n \n \\t \t} $result]
++ verbose -log "Core-File Command Output:\n$result\n\n"
++
++ # By the time we got the response from the 'core-file' command,
++ # the stopped event, sent when we attach to a core file, should
++ # already have been seen, check for it now.
++ set ok false
++ foreach d [lindex $obj 1] {
++ if {[dict get $d type] != "event"
++ || [dict get $d event] != "stopped"} {
++ continue
++ }
++ if {[dict get $d body reason] == "attach"} {
++ set ok true
++ break
++ }
++ }
++ gdb_assert { $ok } "saw stopped event"
++
++ dap_shutdown
++}
+diff --git a/gdb/testsuite/gdb.python/py-corefile.c b/gdb/testsuite/gdb.python/py-corefile.c
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.python/py-corefile.c
+@@ -0,0 +1,25 @@
++/* Copyright 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/>. */
++
++#include <stdlib.h>
++
++int
++main (void)
++{
++ /* With correct ulimit, etc. this should cause a core dump. */
++ abort ();
++}
+diff --git a/gdb/testsuite/gdb.python/py-corefile.exp b/gdb/testsuite/gdb.python/py-corefile.exp
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.python/py-corefile.exp
+@@ -0,0 +1,365 @@
++# Copyright (C) 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/>.
++
++# This file is part of the GDB testsuite. It tests the core file
++# support in Python.
++
++require isnative
++require {!is_remote host}
++
++load_lib gdb-python.exp
++
++require allow_python_tests
++
++standard_testfile
++
++if {[build_executable "build executable" $testfile $srcfile] == -1} {
++ return
++}
++
++set remote_python_file \
++ [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
++
++set corefile [core_find $binfile]
++if {$corefile == ""} {
++ unsupported "couldn't create or find corefile"
++ return
++}
++
++# Helper proc to run the 'core-file' command. Takes optional arguments:
++#
++# -corefile FILENAME : Load FILENAME as the new core file. If this
++# argument is not given then the current core
++# file will be unloaded.
++#
++# -inferior NUM : The inferior in which the corefile is being changed.
++# This is used to match the corefile_changed events
++# that will be emitted.
++#
++# -prefix STRING : A test prefix, to make test names unique.
++#
++# -replacement : There's already a core file loaded when this command
++# is being run.
++proc core_file_cmd { args } {
++ parse_some_args {
++ {corefile ""}
++ {inferior 1}
++ {prefix ""}
++ {replacement}
++ }
++
++ if { $prefix eq "" } {
++ if { $corefile eq "" } {
++ set prefix "unload corefile"
++ } else {
++ set prefix "load corefile"
++ }
++ }
++
++ with_test_prefix $prefix {
++ gdb_test "events corefile_changed check" \
++ "^No corefile_changed event has been seen\\." \
++ "no corefile event has been seen"
++
++ gdb_test "events exited check" \
++ "^No exited event has been seen\\." \
++ "no exited event has been seen"
++
++ if { $corefile eq "" } {
++ gdb_test "core-file" "^No core file now\\." "unload current core file"
++
++ gdb_test "events corefile_changed check" \
++ "Event 1/1, Inferior $inferior, Corefile None" \
++ "expected corefile event has been seen"
++
++ gdb_test "events exited check" \
++ "Event 1/1, Inferior $inferior, Exit Code None" \
++ "expected exited event has been seen"
++ } else {
++ gdb_test "core-file $corefile" ".*" "load core file"
++
++ if { $replacement } {
++ gdb_test "events corefile_changed check" \
++ [multi_line \
++ "Event 1/2, Inferior $inferior, Corefile None" \
++ "Event 2/2, Inferior $inferior, Corefile [string_to_regexp $corefile]"] \
++ "expected corefile event has been seen"
++
++ gdb_test "events exited check" \
++ "Event 1/1, Inferior $inferior, Exit Code None" \
++ "expected exited event has been seen"
++ } else {
++ gdb_test "events corefile_changed check" \
++ "Event 1/1, Inferior $inferior, Corefile [string_to_regexp $corefile]" \
++ "expected corefile event has been seen"
++
++ gdb_test "events exited check" \
++ "^No exited event has been seen\\." \
++ "no exited event was emitted"
++ }
++ }
++ }
++
++ gdb_test_no_output -nopass "events corefile_changed reset"
++ gdb_test_no_output -nopass "events exited reset"
++}
++
++# A helper proc runs clean_restart passing through ARGS, and then loads the
++# test's Python script.
++proc clean_restart_and_load_py_script { args } {
++ clean_restart {*}$args
++
++ # Load the Python script into GDB.
++ gdb_test "source $::remote_python_file" "^Success" \
++ "source python script"
++}
++
++# Create a copy of the corefile.
++set other_corefile [standard_output_file ${testfile}-other.core]
++remote_exec build "cp $corefile $other_corefile"
++
++clean_restart_and_load_py_script
++
++gdb_test_no_output "python inf = gdb.selected_inferior()" \
++ "capture current inferior"
++
++gdb_test "python print(inf.corefile)" "^None" \
++ "Inferior.corefile is None before loading a core file"
++
++core_file_cmd -corefile $corefile
++
++set file_re [string_to_regexp $corefile]
++gdb_test "python print(inf.corefile)" "^<gdb\\.Corefile inferior=1 filename='$file_re'>" \
++ "Inferior.corefile is a valid object after loading a core file"
++
++gdb_test_no_output "python core1=inf.corefile" "capture gdb.Corefile object"
++
++gdb_test "python print(core1.__dict__)" "^\\{\\}" \
++ "print Corefile.__dict__ when empty"
++
++gdb_test_no_output "python core1._my_attribute = \"Hello\"" \
++ "write new attribute into Corefile object"
++
++gdb_test "python print(core1._my_attribute)" "^Hello" \
++ "immediately read new attribute"
++
++gdb_test "python print(core1.__dict__)" "^\\{'_my_attribute': 'Hello'\\}" \
++ "print Corefile.__dict__ after adding an attribute"
++
++gdb_test "python print(core1.filename)" "^$file_re" \
++ "Corefile.filename attribute works as expected"
++
++gdb_test "python print(core1.is_valid())" "^True" \
++ "Corefile.is_valid() is True while corefile is loaded"
++
++core_file_cmd
++
++gdb_test "python print(core1.is_valid())" "^False" \
++ "Corefile.is_valid() is False after corefile is unloaded"
++
++gdb_test "python print(core1.__dict__)" "^\\{'_my_attribute': 'Hello'\\}" \
++ "print Corefile.__dict__ with attribute when invalid"
++
++gdb_test "python print(core1)" "^<gdb\\.Corefile \\(invalid\\)>" \
++ "print an invalid gdb.Corefile object"
++
++gdb_test "python print(core1.filename)" \
++ [multi_line \
++ "Python Exception <class 'RuntimeError'>: Corefile no longer exists\\." \
++ "Error occurred in Python: Corefile no longer exists\\."] \
++ "error when reading filename from invalid Corefile"
++
++gdb_test "python print(inf.corefile)" "^None" \
++ "Inferior.corefile is None again after corefile unload"
++
++gdb_test "python print(core1._my_attribute)" "^Hello" \
++ "read new attribute from invalid core file"
++
++# Create a second inferior.
++gdb_test "add-inferior"
++gdb_test "inferior 2"
++
++with_test_prefix "in second inferior" {
++ core_file_cmd -corefile $corefile -inferior 2
++
++ gdb_test "python print(inf.corefile)" "^None" \
++ "first inferior still has no core file"
++
++ gdb_test_no_output "python core2=gdb.selected_inferior().corefile" \
++ "capture gdb.Corefile object"
++
++ # The _my_attribute was added to CORE1, not CORE2. Check it
++ # doesn't somehow appear on CORE2.
++ gdb_test "python print(core2._my_attribute)" \
++ "AttributeError.*: 'gdb\\.Corefile' object has no attribute '_my_attribute'" \
++ "try to read attribute that doesn't exist"
++
++ gdb_test "python print(core2.filename)" "^$file_re" \
++ "Corefile.filename attribute works as expected"
++
++ gdb_test "inferior 1"
++}
++
++# Read the name of the core file from the second program space while
++# the current program space is the first one.
++gdb_test "python print(core2.filename)" "^$file_re" \
++ "Corefile.filename attribute works from different progspace"
++
++# Load the other corefile into the first inferior.
++core_file_cmd -corefile $other_corefile \
++ -prefix "load other corefile into inferior 1"
++
++# Delete the second inferior. We need to switch to the second
++# inferior and unload its corefile before we can do that. Then,
++# switch back to the first inferior, delete the second, and try to
++# read the filename of the core file from the (now deleted) second
++# inferior. We should get an error about the gdb.Corefile being
++# invalid.
++with_test_prefix "remove second inferior" {
++ gdb_test "inferior 2"
++
++ gdb_test "python print(inf.corefile.filename)" \
++ "^[string_to_regexp $other_corefile]" \
++ "read inferior 1 corefile when in inferior 2"
++
++ gdb_test_no_output "python core1=inf.corefile" \
++ "capture inferior 1 gdb.Corefile while in inferior 2"
++
++ # This is a new CORE1 object, check that _my_attribute is gone.
++ gdb_test "python print(core1._my_attribute)" \
++ "AttributeError.*: 'gdb\\.Corefile' object has no attribute '_my_attribute'" \
++ "try to read attribute that doesn't exist"
++
++ core_file_cmd -inferior 2
++
++ gdb_test "python print(core2.filename)" \
++ [multi_line \
++ "Python Exception <class 'RuntimeError'>: Corefile no longer exists\\." \
++ "Error occurred in Python: Corefile no longer exists\\."] \
++ "error when reading filename from invalid Corefile"
++
++ gdb_test "inferior 1"
++
++ gdb_test "remove-inferiors 2"
++
++ gdb_test "python print(core2.is_valid())" "^False" \
++ "Corefile.is_valid() is False after corefile is unloaded, and Progspace is deleted"
++
++ gdb_test "python print(core2.filename)" \
++ [multi_line \
++ "Python Exception <class 'RuntimeError'>: Corefile no longer exists\\." \
++ "Error occurred in Python: Corefile no longer exists\\."] \
++ "error when reading filename of an invalid Corefile, from deleted program space"
++
++ gdb_test "python print(core1.is_valid())" "^True" \
++ "check inferior 1 core file is still valid"
++}
++
++# Test the Corefile.mapped_files() API. The Python script that is
++# sourced here implements 'info proc mappings' in Python using the
++# mapped_files API. The output from the built-in command, and the
++# Python command should be identical.
++with_test_prefix "test mapped files data" {
++ clean_restart_and_load_py_script
++
++ # Load the core file.
++ core_file_cmd -corefile $corefile
++
++ # Two files to write the output to.
++ set out_1 [standard_output_file ${gdb_test_file_name}-out-1.txt]
++ set out_2 [standard_output_file ${gdb_test_file_name}-out-2.txt]
++
++ # Run the built-in command, then the new Python command, capture
++ # the output.
++ gdb_test "pipe info proc mappings | tee $out_1" ".*" \
++ "capture built-in mappings output"
++ gdb_test "pipe info proc py-mappings | tee $out_2" ".*" \
++ "capture Python based mappings data"
++
++ # Check the output is identical.
++ gdb_test "shell diff -s $out_1 $out_2" \
++ "Files \[^\r\n\]+-out-1.txt and \[^\r\n\]+-out-2.txt are identical" \
++ "diff input and output one"
++
++ # Check build-ids within the core file mapping data.
++ gdb_test "check-build-ids" "^PASS"
++
++ # Check the is_main_executable flag in the mapping data.
++ gdb_test "check-main-executable" "^PASS"
++
++ # Check that the mapped files "list" is actually an immutable
++ # tuple.
++ gdb_test_no_output "python core = gdb.selected_inferior().corefile"
++ gdb_test_no_output "python mapped_files = core.mapped_files()"
++ gdb_test "python print(type(mapped_files))" \
++ "^<class 'tuple'>"
++ gdb_test "python mapped_files\[0\] = None" \
++ "'tuple' object does not support item assignment"
++ gdb_test "python print(mapped_files\[0\] is None)" "^False"
++
++ # And same for the list of regions for a mapped file.
++ gdb_test_no_output "python regions = mapped_files\[0\].regions"
++ gdb_test "python print(type(regions))" \
++ "^<class 'tuple'>"
++ gdb_test "python regions\[0\] = None" \
++ "'tuple' object does not support item assignment"
++}
++
++# Load a core file. GDB should figure out which file is being debugged.
++# Then use 'start' to run this executable, this will replace the core file
++# target. At least on Linux, this replacement is done without calling
++# target_detach. This test checks that the expected core file changed and
++# inferior exited events are still seen.
++with_test_prefix "start from corefile" {
++ if { [gdb_protocol_is_native] } {
++ clean_restart_and_load_py_script
++
++ # Load the core file.
++ core_file_cmd -corefile $corefile
++
++ # Check GDB figured out the executable.
++ gdb_test "info inferiors 1" \
++ "\[^\r\n\]+[string_to_regexp $binfile]\\s*" \
++ "check executable was detected correctly"
++
++ gdb_test "start" \
++ "Temporary breakpoint $::decimal, main \\(\\).*" \
++
++ gdb_test "events corefile_changed check" \
++ "Event 1/1, Inferior 1, Corefile None" \
++ "expected corefile event has been seen"
++
++ gdb_test "events exited check" \
++ "Event 1/1, Inferior 1, Exit Code None" \
++ "expected exited event has been seen"
++
++ gdb_test_no_output -nopass "events corefile_changed reset"
++ gdb_test_no_output -nopass "events exited reset"
++ }
++}
++
++# Load a core file, then load a different core file to replace it.
++# Check that the events that are emitted are as expected.
++with_test_prefix "load one core file over another" {
++ clean_restart_and_load_py_script
++
++ # Load the core file.
++ core_file_cmd -corefile $corefile \
++ -prefix "load first corefile"
++
++ core_file_cmd -corefile $other_corefile \
++ -prefix "load second corefile" \
++ -replacement
++}
+diff --git a/gdb/testsuite/gdb.python/py-corefile.py b/gdb/testsuite/gdb.python/py-corefile.py
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.python/py-corefile.py
+@@ -0,0 +1,261 @@
++# Copyright (C) 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/>.
++
++import pathlib
++
++
++class Mapping:
++ def __init__(self, mapping, region):
++ self._mapping = mapping
++ self._region = region
++
++ @property
++ def start(self):
++ return self._region.start
++
++ @property
++ def end(self):
++ return self._region.end
++
++ @property
++ def offset(self):
++ return self._region.file_offset
++
++ @property
++ def filename(self):
++ return self._mapping.filename
++
++
++def info_proc_mappings():
++ print("Mapped address spaces:")
++ print("")
++ format_str = "%-18s %-18s %-18s %-18s %s "
++ print(format_str % ("Start Addr", "End Addr", "Size", "Offset", "File"))
++
++ core = gdb.selected_inferior().corefile
++ mappings = core.mapped_files()
++
++ result = []
++ for m in mappings:
++ for r in m.regions:
++ result.append(Mapping(m, r))
++
++ result.sort(key=lambda x: x.start)
++ for r in result:
++ sz = r.end - r.start
++ print(
++ format_str
++ % (
++ "0x%016x" % r.start,
++ "0x%016x" % r.end,
++ "0x%-16x" % sz,
++ "0x%-16x" % r.offset,
++ "%s" % r.filename,
++ )
++ )
++
++
++class InfoProcPyMappings(gdb.Command):
++ def __init__(self):
++ gdb.Command.__init__(self, "info proc py-mappings", gdb.COMMAND_DATA)
++
++ def invoke(self, args, from_tty):
++ info_proc_mappings()
++
++
++InfoProcPyMappings()
++
++
++class CheckBuildIds(gdb.Command):
++ def __init__(self):
++ gdb.Command.__init__(self, "check-build-ids", gdb.COMMAND_DATA)
++
++ def invoke(self, args, from_tty):
++ inf = gdb.selected_inferior()
++ objfiles = inf.progspace.objfiles()
++
++ path_to_build_id = {}
++
++ for o in objfiles:
++ if not o.is_file or o.build_id is None:
++ continue
++ p = pathlib.Path(o.filename).resolve()
++ b = o.build_id
++ path_to_build_id[p] = b
++
++ count = 0
++ core_mapped_files = inf.corefile.mapped_files()
++ for m in core_mapped_files:
++ p = pathlib.Path(m.filename).resolve()
++ b = m.build_id
++
++ if p in path_to_build_id:
++ count += 1
++ assert path_to_build_id[p] == b, "build-id mismatch for %s" % p
++
++ assert count > 0, "no mapped files checked"
++
++ print("PASS")
++
++
++CheckBuildIds()
++
++
++class CheckMainExec(gdb.Command):
++ def __init__(self):
++ gdb.Command.__init__(self, "check-main-executable", gdb.COMMAND_DATA)
++
++ def invoke(self, args, from_tty):
++ inf = gdb.selected_inferior()
++ pspace = inf.progspace
++ exec_filename = pathlib.Path(pspace.executable_filename).resolve()
++
++ count = 0
++ core_mapped_files = inf.corefile.mapped_files()
++ for m in core_mapped_files:
++ if not m.is_main_executable:
++ continue
++
++ p = pathlib.Path(m.filename).resolve()
++
++ count += 1
++ assert exec_filename == p, "main exec filename mismatch"
++
++ assert count == 1, "invalid main executable count"
++
++ print("PASS")
++
++
++CheckMainExec()
++
++
++# An 'events' prefix command.
++class events_cmd(gdb.Command):
++ """Information about recent Python events."""
++
++ def __init__(self):
++ gdb.Command.__init__(self, "events", gdb.COMMAND_USER, prefix=True)
++
++
++# An 'events corefile_changed' sub-command.
++class events_corefile_changed_cmd(gdb.Command):
++ """Check recent corefile_changed events.
++
++ Requires a single argument either 'check' or 'reset'. With
++ 'check', print details of every recent corefile_changed event.
++ With 'reset' clear the list of recent corefile_changed events."""
++
++ def __init__(self):
++ gdb.Command.__init__(self, "events corefile_changed", gdb.COMMAND_USER)
++ self._events = []
++ gdb.events.corefile_changed.connect(lambda e: self._corefile_changed_handler(e))
++
++ def _corefile_changed_handler(self, event):
++ assert isinstance(event, gdb.CorefileChangedEvent)
++ inf = event.inferior
++ assert isinstance(inf, gdb.Inferior)
++
++ corefile = inf.corefile
++ if corefile is not None:
++ assert corefile.is_valid()
++ corefile = corefile.filename
++
++ obj = {"inferior": inf.num, "corefile": corefile}
++ self._events.append(obj)
++
++ def invoke(self, args, from_tty):
++ if args == "check":
++ if len(self._events) == 0:
++ print("No corefile_changed event has been seen.")
++ else:
++ total = len(self._events)
++ for idx, obj in enumerate(self._events, start=1):
++ inf_num = obj["inferior"]
++ corefile = obj["corefile"]
++
++ if corefile is None:
++ msg = "None"
++ else:
++ msg = corefile
++
++ print(
++ "Event {}/{}, Inferior {}, Corefile {}".format(
++ idx, total, inf_num, msg
++ )
++ )
++ elif args == "reset":
++ self._events = []
++ else:
++ raise gdb.GdbError("Unknown command args: {}".format(args))
++
++
++# An 'events exited' sub-command.
++class events_exited_cmd(gdb.Command):
++ """Check recent exited events.
++
++ Requires a single argument either 'check' or 'reset'. With
++ 'check', print details of every recent exited event. With 'reset'
++ clear the list of recent exited events."""
++
++ def __init__(self):
++ gdb.Command.__init__(self, "events exited", gdb.COMMAND_USER)
++ self._events = []
++ gdb.events.exited.connect(lambda e: self._exited_handler(e))
++
++ def _exited_handler(self, event):
++ assert isinstance(event, gdb.ExitedEvent)
++ inf = event.inferior
++ assert isinstance(inf, gdb.Inferior)
++
++ if hasattr(event, "exit_code"):
++ assert isinstance(event.exit_code, int)
++ exit_code = event.exit_code
++ else:
++ exit_code = None
++
++ obj = {"inferior": inf.num, "exit_code": exit_code}
++ self._events.append(obj)
++
++ def invoke(self, args, from_tty):
++ if args == "check":
++ if len(self._events) == 0:
++ print("No exited event has been seen.")
++ else:
++ total = len(self._events)
++ for idx, obj in enumerate(self._events, start=1):
++ inf_num = obj["inferior"]
++ exit_code = obj["exit_code"]
++
++ if exit_code is None:
++ msg = "None"
++ else:
++ msg = exit_code
++
++ print(
++ "Event {}/{}, Inferior {}, Exit Code {}".format(
++ idx, total, inf_num, msg
++ )
++ )
++ elif args == "reset":
++ self._events = []
++ else:
++ raise gdb.GdbError("Unknown command args: {}".format(args))
++
++
++events_cmd()
++events_corefile_changed_cmd()
++events_exited_cmd()
++
++print("Success")
+diff --git a/gdb/testsuite/lib/dap-support.exp b/gdb/testsuite/lib/dap-support.exp
+--- a/gdb/testsuite/lib/dap-support.exp
++++ b/gdb/testsuite/lib/dap-support.exp
+@@ -362,6 +362,22 @@ proc dap_attach {pid {prog ""}} {
+ return [dap_send_request attach $args]
+ }
+
++# Start gdb, send a DAP initialize request, and then an attach request
++# specifying COREFILE as the core file to attach to. Returns the
++# empty string on failure, or the attach request sequence ID.
++proc dap_corefile {corefile {prog ""}} {
++ if {[dap_initialize "startup - initialize"] == ""} {
++ return ""
++ }
++
++ set args [format {o coreFile [s "%s"]} $corefile]
++ if {$prog != ""} {
++ append args [format { program [s "%s"]} $prog]
++ }
++
++ return [dap_send_request attach $args]
++}
++
+ # Start gdb, send a DAP initialize request, and then an attach request
+ # specifying TARGET as the remote target. Returns the empty string on
+ # failure, or the attach request sequence ID.
+@@ -372,26 +388,33 @@ proc dap_target_remote {target} {
+ return [dap_send_request attach [format {o target [s %s]} $target]]
+ }
+
+-# Read the most recent DAP log file and check it for exceptions.
+-proc dap_check_log_file {} {
++# Read the most recent DAP log file and check it for exceptions. We
++# expect to see exactly EXPECTED_EXCEPTION_COUNT exceptions in the log.
++proc dap_check_log_file { {expected_exception_count 0} } {
+ set fd [open [current_dap_log_file]]
+ set contents [read $fd]
+ close $fd
+
+- set ok 1
++ set exception_count 0
+ foreach line [split $contents "\n"] {
+ if {[regexp "^Traceback" $line]} {
+- set ok 0
+- break
++ incr exception_count
++ if { $exception_count > $expected_exception_count} {
++ break
++ }
+ }
+ }
+
+- if {$ok} {
++ if {$exception_count == $expected_exception_count} {
+ pass "exceptions in log file"
+ } else {
+ verbose -log -- "--- DAP LOG START ---"
+ verbose -log -- $contents
+ verbose -log -- "--- DAP LOG END ---"
++ if { $expected_exception_count > 0 } {
++ verbose -log -- [join [list "Expected $expected_exception_count" \
++ "exception(s), saw $exception_count"]]
++ }
+ fail "exceptions in log file"
+ }
+ }
+@@ -407,8 +430,9 @@ proc dap_check_log_file_re { re } {
+ }
+
+ # Cleanly shut down gdb. TERMINATE is passed as the terminateDebuggee
+-# parameter to the request.
+-proc dap_shutdown {{terminate false}} {
++# parameter to the request. The EXPECTED_EXCEPTION_COUNT is the
++# number of exceptions that we expect to see in the latest DAP log.
++proc dap_shutdown {{terminate false} {expected_exception_count 0}} {
+ dap_check_request_and_response "shutdown" disconnect \
+ [format {o terminateDebuggee [l %s]} $terminate]
+
+@@ -420,7 +444,7 @@ proc dap_shutdown {{terminate false}} {
+
+ clear_gdb_spawn_id
+
+- dap_check_log_file
++ dap_check_log_file $expected_exception_count
+ }
+
+ # Search the event list EVENTS for an output event matching the regexp
diff --git a/gdb-rhbz2403580-misplaced-symtabs.patch b/gdb-rhbz2403580-misplaced-symtabs.patch
index 7996d82..35fdaad 100644
--- a/gdb-rhbz2403580-misplaced-symtabs.patch
+++ b/gdb-rhbz2403580-misplaced-symtabs.patch
@@ -92,7 +92,7 @@ diff --git a/gdb/dwarf2/line-header.h b/gdb/dwarf2/line-header.h
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
-@@ -6222,8 +6222,8 @@ dwarf2_cu::setup_type_unit_groups (struct die_info *die)
+@@ -6227,8 +6227,8 @@ dwarf2_cu::setup_type_unit_groups (struct die_info *die)
sf->symtab = allocate_symtab (cust, name, name_for_id);
}
@@ -103,7 +103,7 @@ diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
}
}
else
-@@ -6241,7 +6241,7 @@ dwarf2_cu::setup_type_unit_groups (struct die_info *die)
+@@ -6246,7 +6246,7 @@ dwarf2_cu::setup_type_unit_groups (struct die_info *die)
for (i = 0; i < file_names.size (); ++i)
{
file_entry &fe = file_names[i];
@@ -112,7 +112,7 @@ diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
}
}
-@@ -11584,7 +11584,7 @@ process_structure_scope (struct die_info *die, struct dwarf2_cu *cu)
+@@ -11589,7 +11589,7 @@ process_structure_scope (struct die_info *die, struct dwarf2_cu *cu)
{
/* Any related symtab will do. */
symtab
@@ -121,7 +121,7 @@ diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
}
else
{
-@@ -16587,6 +16587,9 @@ dwarf_decode_lines (struct line_header *lh, struct dwarf2_cu *cu,
+@@ -16592,6 +16592,9 @@ dwarf_decode_lines (struct line_header *lh, struct dwarf2_cu *cu,
if (decode_mapping)
dwarf_decode_lines_1 (lh, cu, lowpc);
@@ -131,7 +131,7 @@ diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
/* Make sure a symtab is created for every file, even files
which contain only variables (i.e. no code with associated
line numbers). */
-@@ -16602,7 +16605,7 @@ dwarf_decode_lines (struct line_header *lh, struct dwarf2_cu *cu,
+@@ -16607,7 +16610,7 @@ dwarf_decode_lines (struct line_header *lh, struct dwarf2_cu *cu,
sf->symtab = allocate_symtab (cust, sf->name.c_str (),
sf->name_for_id.c_str ());
@@ -140,7 +140,7 @@ diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
}
}
-@@ -16879,7 +16882,7 @@ new_symbol (struct die_info *die, struct type *type, struct dwarf2_cu *cu,
+@@ -16884,7 +16887,7 @@ new_symbol (struct die_info *die, struct type *type, struct dwarf2_cu *cu,
if (fe == NULL)
complaint (_("file index out of range"));
else
diff --git a/gdb-rhbz2435950-skip-revert.patch b/gdb-rhbz2435950-skip-revert.patch
deleted file mode 100644
index 931d063..0000000
--- a/gdb-rhbz2435950-skip-revert.patch
+++ /dev/null
@@ -1,138 +0,0 @@
-From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
-From: Andrew Burgess <aburgess@redhat.com>
-Date: Mon, 9 Feb 2026 16:31:23 +0000
-Subject: gdb-rhbz2435950-skip-revert.patch
-
-;; Backport of upstream commit f08ffbbf2691bad2d5df660ee644647687775f0c
-;; Can be dropped on a rebase to gdb 17.2 or 18.1
-
-Revert "skip -gfile: call fnmatch without FNM_FILE_NAME"
-
-This reverts commit 02646a4c561ec88491114b87950cbb827c7d614c. See:
-
- https://inbox.sourceware.org/gdb-patches/20260203185528.946918-1-guinevere@redhat.com
-
-This commit introduced a non backward compatible change to how GDB
-handled skip files. Something like:
-
- skip -gfile dir/*.c
-
-no longer matches every file within 'dir/', but now matches every file
-in 'dir/' and within every sub-directory of 'dir/', which might not be
-what the user wanted.
-
-The original intention behind the commit is solid, we just need to
-find a better implementation.
-
-Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33872
-
-diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
---- a/gdb/doc/gdb.texinfo
-+++ b/gdb/doc/gdb.texinfo
-@@ -6825,9 +6825,7 @@ Functions in @var{file} will be skipped over when stepping.
- @itemx -gfi @var{file-glob-pattern}
- @cindex skipping over files via glob-style patterns
- Functions in files matching @var{file-glob-pattern} will be skipped
--over when stepping. The directory separator character @file{/} is treated as a
--regular character, so it can be matched by wildcard characters @file{*} and
--@file{?}.
-+over when stepping.
-
- @smallexample
- (@value{GDBP}) skip -gfi utils/*.c
-diff --git a/gdb/skip.c b/gdb/skip.c
---- a/gdb/skip.c
-+++ b/gdb/skip.c
-@@ -531,7 +531,7 @@ skiplist_entry::do_skip_gfile_p (const symtab_and_line &function_sal) const
- /* Check first sole SYMTAB->FILENAME. It may not be a substring of
- symtab_to_fullname as it may contain "./" etc. */
- if (gdb_filename_fnmatch (m_file.c_str (), function_sal.symtab->filename,
-- FNM_NOESCAPE) == 0)
-+ FNM_FILE_NAME | FNM_NOESCAPE) == 0)
- result = true;
-
- /* Before we invoke symtab_to_fullname, which is expensive, do a quick
-@@ -542,14 +542,14 @@ skiplist_entry::do_skip_gfile_p (const symtab_and_line &function_sal) const
- else if (!basenames_may_differ
- && gdb_filename_fnmatch (lbasename (m_file.c_str ()),
- lbasename (function_sal.symtab->filename),
-- FNM_NOESCAPE) != 0)
-+ FNM_FILE_NAME | FNM_NOESCAPE) != 0)
- result = false;
- else
- {
- /* Note: symtab_to_fullname caches its result, thus we don't have to. */
- const char *fullname = symtab_to_fullname (function_sal.symtab);
-
-- result = gdb_filename_fnmatch (m_file.c_str (), fullname, FNM_NOESCAPE);
-+ result = compare_glob_filenames_for_search (fullname, m_file.c_str ());
- }
-
- if (debug_skip)
-diff --git a/gdb/symtab.c b/gdb/symtab.c
---- a/gdb/symtab.c
-+++ b/gdb/symtab.c
-@@ -698,6 +698,40 @@ iterate_over_some_symtabs (const char *name,
- return false;
- }
-
-+/* Same as compare_filenames_for_search, but for glob-style patterns.
-+ Heads up on the order of the arguments. They match the order of
-+ compare_filenames_for_search, but it's the opposite of the order of
-+ arguments to gdb_filename_fnmatch. */
-+
-+bool
-+compare_glob_filenames_for_search (const char *filename,
-+ const char *search_name)
-+{
-+ /* We rely on the property of glob-style patterns with FNM_FILE_NAME that
-+ all /s have to be explicitly specified. */
-+ int file_path_elements = count_path_elements (filename);
-+ int search_path_elements = count_path_elements (search_name);
-+
-+ if (search_path_elements > file_path_elements)
-+ return false;
-+
-+ if (IS_ABSOLUTE_PATH (search_name))
-+ {
-+ return (search_path_elements == file_path_elements
-+ && gdb_filename_fnmatch (search_name, filename,
-+ FNM_FILE_NAME | FNM_NOESCAPE) == 0);
-+ }
-+
-+ {
-+ const char *file_to_compare
-+ = strip_leading_path_elements (filename,
-+ file_path_elements - search_path_elements);
-+
-+ return gdb_filename_fnmatch (search_name, file_to_compare,
-+ FNM_FILE_NAME | FNM_NOESCAPE) == 0;
-+ }
-+}
-+
- /* See symtab.h. */
-
- void
-diff --git a/gdb/utils.c b/gdb/utils.c
---- a/gdb/utils.c
-+++ b/gdb/utils.c
-@@ -3466,8 +3466,8 @@ wait_to_die_with_timeout (pid_t pid, int *status, int timeout)
-
- #endif /* HAVE_WAITPID */
-
--/* Provide fnmatch compatible function for matching of host files.
-- FNM_NOESCAPE must be set in FLAGS.
-+/* Provide fnmatch compatible function for FNM_FILE_NAME matching of host files.
-+ Both FNM_FILE_NAME and FNM_NOESCAPE must be set in FLAGS.
-
- It handles correctly HAVE_DOS_BASED_FILE_SYSTEM and
- HAVE_CASE_INSENSITIVE_FILE_SYSTEM. */
-@@ -3475,6 +3475,8 @@ wait_to_die_with_timeout (pid_t pid, int *status, int timeout)
- int
- gdb_filename_fnmatch (const char *pattern, const char *string, int flags)
- {
-+ gdb_assert ((flags & FNM_FILE_NAME) != 0);
-+
- /* It is unclear how '\' escaping vs. directory separator should coexist. */
- gdb_assert ((flags & FNM_NOESCAPE) != 0);
-
diff --git a/gdb-rpm-suggestion-script.patch b/gdb-rpm-suggestion-script.patch
index 07a68d5..28df14b 100644
--- a/gdb-rpm-suggestion-script.patch
+++ b/gdb-rpm-suggestion-script.patch
@@ -33,7 +33,7 @@ diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
* Man Pages:: Manual pages
* Copying:: GNU General Public License says
how you can copy and share @value{GDBN}
-@@ -50988,6 +50989,111 @@ Show the current verbosity setting.
+@@ -50986,6 +50987,111 @@ Show the current verbosity setting.
@end table
diff --git a/gdb.spec b/gdb.spec
index 32766eb..2ee1863 100644
--- a/gdb.spec
+++ b/gdb.spec
@@ -41,11 +41,11 @@ Name: %{?scl_prefix}gdb
# See timestamp of source gnulib installed into gnulib/ .
%global snapgnulib 20220501
%global tarname gdb-%{version}
-Version: 17.1
+Version: 17.2
# The release always contains a leading reserved number, start it at 1.
# `upstream' is not a part of `name' to stay fully rpm dependencies compatible for the testing.
-Release: 6%{?dist}
+Release: 1%{?dist}
License: GPL-3.0-or-later AND BSD-3-Clause AND FSFAP AND LGPL-2.1-or-later AND GPL-2.0-or-later AND LGPL-2.0-or-later AND LicenseRef-Fedora-Public-Domain AND GFDL-1.3-or-later AND LGPL-2.0-or-later WITH GCC-exception-2.0 AND GPL-3.0-or-later WITH GCC-exception-3.1 AND GPL-2.0-or-later WITH GNU-compiler-exception AND MIT
# Do not provide URL for snapshots as the file lasts there only for 2 days.
@@ -925,6 +925,13 @@ fi
# endif scl
%changelog
+* Wed Jun 24 2026 Michal Kolar <mkolar@redhat.com> - 17.2-1
+- Rebase to FSF GDB 17.2.
+ Deleted: gdb-rhbz2435950-skip-revert.patch
+ Backport e492fb22b70, gdb: backport DAP core file support
+ Replace manual, multi-file patch inclusion with modern autosetup
+ Add rpminspect.yaml to define package-specific testing policy
+
* Wed May 27 2026 Kevin Buettner <kevinb@redhat.com>
- Applied proposed patch "[gdb/testsuite, Tcl 9] Fix EILSEQ problems for
UTF8 related tests". This is required in order to complete a build
diff --git a/sources b/sources
index fd3c204..f76e77c 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-SHA512 (gdb-17.1.tar.xz) = f1a6751e439a2128fecf3eae8b57c1608a0dc7cfe79b4356a937874e5a42bb2df0aba36eb6a9452c41966908b9a59076c7cad9720f684688ab956b65080f1d7c
+SHA512 (gdb-17.2.tar.xz) = 7794c5a185be7ed5e7ad1000c4ff7d8497c80425a1bc108aab8fd3dd8ecdde034e294dfd65b25c6b0dcd8ed2a240caf07293f3e73791b6cfc890d580d0af4581
reply other threads:[~2026-06-25 0:56 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=178234900640.1.5318356091770756049.rpms-gdb-f17655f37931@fedoraproject.org \
--to=mkolar@redhat.com \
--cc=git-commits@fedoraproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox