diff --git a/.gitignore b/.gitignore index f288cc8..8733cd3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /v2.0.5.tar.gz /gdb-14.2.tar.xz /gdb-16.3.tar.xz +/gdb-17.2.tar.xz diff --git a/README.local-patches.md b/README.local-patches.md new file mode 100644 index 0000000..b9ef2c6 --- /dev/null +++ b/README.local-patches.md @@ -0,0 +1,102 @@ +# Fedora GDB local patches policy + +In order to make things easier for the Fedora GDB maintainer, we +choose to auto-generate the local patches by making use of an upstream +git repository. Below you can find a few instructions on how to work +using this method. + +You need to run the following commands from the directory that +contains the "gdb.spec" file. + +## Importing the GDB patches into a git repository + +1) The local patches (`*.patch`) need to be imported into an upstream +git repository. For example, let's assume you cloned the repository +by doing: + +`$ git clone git://sourceware.org/git/binutils-gdb.git` + +> TIP: if you already have the repository cloned somewhere in your +> system, you can pass a "--reference " to the "git clone" +> command and it will use your local repository as much as possible +> to make the clone, speeding up things. + +2) After cloning the upstream repository, you can import your patches +by using the script "generate-git-repo-from-patches.sh": + +`$ sh generate-git-repo-from-patches.sh ` + +The script will basically cd into the repository, checkout the +revision specified in the file `_git_upstream_commit`, iterate through +the file `_patch_order` and "git-am" every patch *in that order*. +This operation should complete without errors; if you find a problem +with `git-am`, it probably means that the revision specified in the +file `_git_upstream_commit` is wrong. + +## Rebasing the patches against a newer version/release + +1) First, cd into the upstream repository. All you have to do is +choose the revision against which you plan to rebase the patches, and +`git rebase `. git will do the rest, and you will be able +to perform conflict resolution by git's algorithm, which is smarter. + +## Creating new patches + +1) Create the new patch on top of the the others, as usual. Note that +you can use `git rebase` whenever you want to reorder patch order, or +even to delete a patch. + +2) When writing the commit log, you must obey a few rules. The +subject line *must* be the filename of the patch. This line will be +used when exporting the patches from the git repository, and +(obviously) it gives the filename that should be used for this +specific patch. + +3) You can also add comments that will go into the auto-generated +`Patch:` file (see below). To do that, use the special marker `;;` at +the beginning of the line. This way, a commit log that says: + +~~~~~~~~~~~ + test-patch.patch + + ;; This is a test patch + ;; Second line +~~~~~~~~~~~ + +Will generate the following entry in the auto-generated `Patch:` file: + +~~~~~~~~~~~ + # This is a test patch + # Second line + PatchXYZ: test-patch.patch +~~~~~~~~~~~ + +## Exporting the GDB patches from the git repository + +1) When you're done working with the patches, go back to the directory +that contains the `gdb.spec` file, and from there you run: + +`$ sh generate-patches-from-git-repo.sh ` + +This will regenerate all of the `*.patch` files (excluding the ones that +were also excluded from the git repository), and also regenerate a few +control files. These control files are: + + - `_gdb.spec.Patch.include`: This file contains the `Patch:` directives. + + - `_patch_order`: This file contains the patches, in the exact order + that they must be applied. It is used when importing the patches + into the git repository. + + - `_git_upstream_commit`: This file contains the last upstream commit + against which the patches were rebased. It is used when importing + the patches into the git repository. + +NOTE: If you did a rebase against a newer upstream version, you need +to specify the commit/tag/branch against which you rebased: + +`$ sh generate-patches-from-git-repo.sh ` + +For example, if you rebased against `gdb-8.1-release`: + +`$ sh generate-patches-from-git-repo.sh gdb-8.1-release` diff --git a/_gdb.spec.Patch.include b/_gdb.spec.Patch.include index b7ab9d0..bed92a5 100644 --- a/_gdb.spec.Patch.include +++ b/_gdb.spec.Patch.include @@ -1,43 +1,5 @@ # Check distro name is included in the version output. -Patch001: gdb-6.3-rh-testversion-20041202.patch - -# Test sideeffects of skipping ppc .so libs trampolines (BZ 218379). -#=fedoratest -Patch002: gdb-6.5-bz218379-ppc-solib-trampoline-test.patch - -# Support for stepping over PPC atomic instruction sequences (BZ 237572). -#=fedoratest -Patch003: gdb-6.6-bz237572-ppc-atomic-sequence-test.patch - -# Test gcore memory and time requirements for large inferiors. -#=fedoratest -Patch004: gdb-6.5-gcore-buffer-limit-test.patch - -# Test GCORE for shmid 0 shared memory mappings. -#=fedoratest: But it is broken anyway, sometimes the case being tested is not reproducible. -Patch005: gdb-6.3-mapping-zero-inode-test.patch - -# Fix resolving of variables at locations lists in prelinked libs (BZ 466901). -#=fedoratest -Patch006: gdb-6.8-bz466901-backtrace-full-prelinked.patch - -# Fix follow-exec for C++ programs (bugreported by Martin Stransky). -#=fedoratest -Patch007: gdb-archer-next-over-throw-cxx-exec.patch - -# Testcase for `Setting solib-absolute-prefix breaks vDSO' (BZ 818343). -#=fedoratest -Patch008: gdb-rhbz-818343-set-solib-absolute-prefix-testcase.patch - -# Fix '`catch syscall' doesn't work for parent after `fork' is called' -# (Philippe Waroquiers, RH BZ 1149205). -#=fedoratest -Patch009: gdb-rhbz1149205-catch-syscall-after-fork-test.patch - -# Fix '[ppc64] and [s390x] wrong prologue skip on -O2 -g code' (Jan -# Kratochvil, RH BZ 1084404). -#=fedoratest -Patch010: gdb-rhbz1084404-ppc64-s390x-wrong-prologue-skip-O2-g-3of3.patch +Patch001: gdb-test-show-version.patch # Update gdb-add-index.sh such that, when the GDB environment # variable is not set, the script is smarter than just looking for @@ -51,72 +13,85 @@ Patch010: gdb-rhbz1084404-ppc64-s390x-wrong-prologue-skip-O2-g-3of3.patch # https://fedoraproject.org/wiki/Changes/Minimal_GDB_in_buildroot # #=fedora -Patch011: gdb-add-index.patch +Patch002: gdb-add-index.patch # Not a backport. Add a new script which hooks into GDB and suggests # RPMs to install when GDB finds an objfile with no debug info. -Patch012: gdb-add-rpm-suggestion-script.patch +Patch003: gdb-rpm-suggestion-script.patch -# Backport "s390: Add support for z17 as CPU name" -# (Jens Remus, RHEL-50069) -Patch013: gdb-rhel-50069-support-z17.patch +# Backport Guinevere Larsen's build warning fixes (RH BZ 2424325). +Patch004: gdb-rhbz2424325-c23-const-build-warnings.patch -# [gdb/tdep] Refactor amd64_get_unused_input_int_reg, part 1 -# (Tom de Vries, RHEL-7329) -Patch014: gdb-rhel-7329-vandps-clobbers-registers-1-of-13.patch +# Backport Keith Seitz's C23 const-correctness fixes (pending upstream review). +# Mailing list: https://sourceware.org/pipermail/binutils/2026-January/... +# Posted 2026-01-07, not yet approved. +Patch005: gdb-rhbz2424325-c23-more-const-fixes.patch -# [gdb/tdep] Refactor amd64_get_unused_input_int_reg, part 2 -# (Tom de Vries, RHEL-7329) -Patch015: gdb-rhel-7329-vandps-clobbers-registers-2-of-13.patch +# Backport Tom de Vries fix regarding implicit lambda captures +# (RH BZ 2424325). +Patch006: gdb-rhbz2424325-c++20-implicit-lambda-capture.patch -# [gdb/tdep] Refactor amd64_get_unused_input_int_reg, part 3 -# (Tom de Vries, RHEL-7329) -Patch016: gdb-rhel-7329-vandps-clobbers-registers-3-of-13.patch +# Backport of upstream commit 70b66cf338b14336 to address RHBZ +# 2402580. This backport can be dropped when rebasing to GDB 18. +# There were some moderate merge conflicts which needed resolving +# when backporting this fix. +Patch007: gdb-rhbz2403580-misplaced-symtabs.patch -# [gdb/tdep] Factor out amd64_get_used_input_int_regs -# (Tom de Vries, RHEL-7329) -Patch017: gdb-rhel-7329-vandps-clobbers-registers-4-of-13.patch +# Backport of upstream commit c1da013915e from Kevin Buettner +# (RHBZ 2413405). +Patch008: gdb-rhbz2413405-gcore-unreadable-pages.patch -# [gdb/tdep] Add amd64-insn-decode selftest -# (Tom de Vries, RHEL-7329) -Patch018: gdb-rhel-7329-vandps-clobbers-registers-5-of-13.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 +Patch009: gdb-fileio-test-fixes.patch -# [gdb/tdep] Factor out rip_relative_p -# (Tom de Vries, RHEL-7329) -Patch019: gdb-rhel-7329-vandps-clobbers-registers-6-of-13.patch +# Backport upstream commit 8bd08ee92c4 to address rhbz2366461. This +# commit will drop out with GDB 18. +Patch010: gdb-rhbz2366461-missing-thread.patch -# [gdb/tdep] Fix rip-relative insn handling in amd64_get_used_input_int_reg -# (Tom de Vries, RHEL-7329) -Patch020: gdb-rhel-7329-vandps-clobbers-registers-7-of-13.patch +# Backport upstream commit cd289df068e to address rhbz2366461. This +# commit will drop out with GDB 18. +Patch011: gdb-rhbz2366461-bad-solib-entry-addr.patch -# [gdb/tdep] Factor out part of fixup_riprel -# (Tom de Vries, RHEL-7329) -Patch021: gdb-rhel-7329-vandps-clobbers-registers-8-of-13.patch +# Fix use of deprecated trace variable subcommand +# (Tom de Vries) +Patch012: gdb-fix-testsuite-newer-tcl.patch -# [gdb/tdep] Add vex2_to_vex3 -# (Tom de Vries, RHEL-7329) -Patch022: gdb-rhel-7329-vandps-clobbers-registers-9-of-13.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. +Patch013: gdb-rhbz2467251-computed-location-synthetic-pointers.patch -# [gdb/tdep] Add vzeroupper and vzeroall in amd64-insn-decode selftest -# (Tom de Vries, RHEL-7329) -Patch023: gdb-rhel-7329-vandps-clobbers-registers-10-of-13.patch - -# [gdb/tdep] Make amd64_get_insn_details more regular -# (Tom de Vries, RHEL-7329) -Patch024: gdb-rhel-7329-vandps-clobbers-registers-11-of-13.patch - -# [gdb/tdep] Fix vmovdqu decoding -# (Tom de Vries, RHEL-7329) -Patch025: gdb-rhel-7329-vandps-clobbers-registers-12-of-13.patch - -# [gdb/tdep] Support REX2 and EVEX prefix -# (Tom de Vries, RHEL-7329) -Patch026: gdb-rhel-7329-vandps-clobbers-registers-13-of-13.patch - -# Backport of three upstream patches. The first two relate to index -# generation, while the third relates to symbol lookup, but is needed -# so that the tests from the earlier two patches will pass. +# Fix EILSEQ problems for UTF8 related tests when using expect +# enabled with Tcl 9 and full set of Tcl 9 compatibility fixes +# from upstream PR80674 branch. Required for testing GDB on +# Fedora 44 and rawhide (Fedora 45). # -# (Tom Tromey and Andrew Burgess, RHEL-150992) -Patch027: gdb-rhel-150992-index-generation-fixes.patch +# At the time of this Fedora commit, this patch was not upstream yet. +# For its status, see: +# +# https://inbox.sourceware.org/gdb-patches/20260526192701.3835262-2-kevinb@redhat.com/T/#u +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 + +# Backport of upstream commit be3a9405ceb4d6d6 to fix RHBZ 2368350, an +# out of memory issue with the D demangler. +# +# The commit being pulled in here is a commit that syncs multiple +# commits to libiberty from gcc to binutils-gdb. Most of these commits +# are spelling and white space fixes, which should have no real impact. +# The only commit we actually care about is gcc commit bc4d9b6aeb3, +# which fixes the D demangler issue. +# +# This commit will not be needed once we rebase to GDB 18. +Patch016: gdb-backport-libiberty-sync.patch + +# Backport of upstream commit 93f536d813c41527e8c939a5f8a90a4 to fix +# RHBZ2498034. +Patch017: gdb-backport-corefile-use-after-free-fix.patch diff --git a/_gdb.spec.patch.include b/_gdb.spec.patch.include index efd00da..f630fc2 100644 --- a/_gdb.spec.patch.include +++ b/_gdb.spec.patch.include @@ -13,15 +13,3 @@ %patch -p1 -P013 %patch -p1 -P014 %patch -p1 -P015 -%patch -p1 -P016 -%patch -p1 -P017 -%patch -p1 -P018 -%patch -p1 -P019 -%patch -p1 -P020 -%patch -p1 -P021 -%patch -p1 -P022 -%patch -p1 -P023 -%patch -p1 -P024 -%patch -p1 -P025 -%patch -p1 -P026 -%patch -p1 -P027 diff --git a/_git_upstream_commit b/_git_upstream_commit index 1e79418..da4c92e 100644 --- a/_git_upstream_commit +++ b/_git_upstream_commit @@ -1 +1 @@ -140ba011c003fda0fb2f746cf2bc0f010bf4ac03 +7adf9fa6b1ccb3c70f86cb630368b9d3dffcf3aa diff --git a/_patch_order b/_patch_order index 5a9f83c..aed5ae3 100644 --- a/_patch_order +++ b/_patch_order @@ -1,27 +1,17 @@ -gdb-6.3-rh-testversion-20041202.patch -gdb-6.5-bz218379-ppc-solib-trampoline-test.patch -gdb-6.6-bz237572-ppc-atomic-sequence-test.patch -gdb-6.5-gcore-buffer-limit-test.patch -gdb-6.3-mapping-zero-inode-test.patch -gdb-6.8-bz466901-backtrace-full-prelinked.patch -gdb-archer-next-over-throw-cxx-exec.patch -gdb-rhbz-818343-set-solib-absolute-prefix-testcase.patch -gdb-rhbz1149205-catch-syscall-after-fork-test.patch -gdb-rhbz1084404-ppc64-s390x-wrong-prologue-skip-O2-g-3of3.patch +gdb-test-show-version.patch gdb-add-index.patch -gdb-add-rpm-suggestion-script.patch -gdb-rhel-50069-support-z17.patch -gdb-rhel-7329-vandps-clobbers-registers-1-of-13.patch -gdb-rhel-7329-vandps-clobbers-registers-2-of-13.patch -gdb-rhel-7329-vandps-clobbers-registers-3-of-13.patch -gdb-rhel-7329-vandps-clobbers-registers-4-of-13.patch -gdb-rhel-7329-vandps-clobbers-registers-5-of-13.patch -gdb-rhel-7329-vandps-clobbers-registers-6-of-13.patch -gdb-rhel-7329-vandps-clobbers-registers-7-of-13.patch -gdb-rhel-7329-vandps-clobbers-registers-8-of-13.patch -gdb-rhel-7329-vandps-clobbers-registers-9-of-13.patch -gdb-rhel-7329-vandps-clobbers-registers-10-of-13.patch -gdb-rhel-7329-vandps-clobbers-registers-11-of-13.patch -gdb-rhel-7329-vandps-clobbers-registers-12-of-13.patch -gdb-rhel-7329-vandps-clobbers-registers-13-of-13.patch -gdb-rhel-150992-index-generation-fixes.patch +gdb-rpm-suggestion-script.patch +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-rhbz2413405-gcore-unreadable-pages.patch +gdb-fileio-test-fixes.patch +gdb-rhbz2366461-missing-thread.patch +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 +gdb-backport-libiberty-sync.patch +gdb-backport-corefile-use-after-free-fix.patch diff --git a/gdb-6.3-mapping-zero-inode-test.patch b/gdb-6.3-mapping-zero-inode-test.patch deleted file mode 100644 index 32b62d5..0000000 --- a/gdb-6.3-mapping-zero-inode-test.patch +++ /dev/null @@ -1,247 +0,0 @@ -From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 -From: Fedora GDB patches -Date: Fri, 27 Oct 2017 21:07:50 +0200 -Subject: gdb-6.3-mapping-zero-inode-test.patch - -;; Test GCORE for shmid 0 shared memory mappings. -;;=fedoratest: But it is broken anyway, sometimes the case being tested is not reproducible. - -diff --git a/gdb/testsuite/gdb.base/gcore-shmid0.c b/gdb/testsuite/gdb.base/gcore-shmid0.c -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.base/gcore-shmid0.c -@@ -0,0 +1,128 @@ -+/* Copyright 2007, 2009 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 2 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, write to the Free Software -+ Foundation, Inc., 59 Temple Place - Suite 330, -+ Boston, MA 02111-1307, USA. */ -+ -+/* -+ * Test GDB's handling of gcore for mapping with a name but zero inode. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* The same test running in a parallel testsuite may steal us the zero SID, -+ even if we never get any EEXIST. Just try a while. */ -+ -+#define TIMEOUT_SEC 10 -+ -+static volatile int v; -+ -+static void -+initialized (void) -+{ -+ v++; -+} -+ -+static void -+unresolved (void) -+{ -+ v++; -+} -+ -+int -+main (void) -+{ -+ int sid; -+ unsigned int *addr = (void *) -1L; -+ int attempt, round = 0; -+ time_t ts_start, ts; -+ -+ if (time (&ts_start) == (time_t) -1) -+ { -+ printf ("time (): %m\n"); -+ exit (1); -+ } -+ -+ /* The generated SID will cycle with an increment of 32768, attempt until it -+ * wraps to 0. */ -+ -+ for (attempt = 0; addr == (void *) -1L; attempt++) -+ { -+ /* kernel-2.6.25-8.fc9.x86_64 just never returns the value 0 by -+ shmget(2). shmget returns SID range 0..1<<31 in steps of 32768, -+ 0x1000 should be enough but wrap the range it to be sure. */ -+ -+ if (attempt > 0x21000) -+ { -+ if (time (&ts) == (time_t) -1) -+ { -+ printf ("time (): %m\n"); -+ exit (1); -+ } -+ -+ if (ts >= ts_start && ts < ts_start + TIMEOUT_SEC) -+ { -+ attempt = 0; -+ round++; -+ continue; -+ } -+ -+ printf ("Problem is not reproducible on this kernel (attempt %d, " -+ "round %d)\n", attempt, round); -+ unresolved (); -+ exit (1); -+ } -+ -+ sid = shmget ((key_t) rand (), 0x1000, IPC_CREAT | IPC_EXCL | 0777); -+ if (sid == -1) -+ { -+ if (errno == EEXIST) -+ continue; -+ -+ printf ("shmget (%d, 0x1000, IPC_CREAT): errno %d\n", 0, errno); -+ exit (1); -+ } -+ -+ /* Use SID only if it is 0, retry it otherwise. */ -+ -+ if (sid == 0) -+ { -+ addr = shmat (sid, NULL, SHM_RND); -+ if (addr == (void *) -1L) -+ { -+ printf ("shmat (%d, NULL, SHM_RND): errno %d\n", sid, -+ errno); -+ exit (1); -+ } -+ } -+ if (shmctl (sid, IPC_RMID, NULL) != 0) -+ { -+ printf ("shmctl (%d, IPC_RMID, NULL): errno %d\n", sid, errno); -+ exit (1); -+ } -+ } -+ -+ initialized (); -+ -+ return 0; -+} -diff --git a/gdb/testsuite/gdb.base/gcore-shmid0.exp b/gdb/testsuite/gdb.base/gcore-shmid0.exp -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.base/gcore-shmid0.exp -@@ -0,0 +1,101 @@ -+# Copyright 2007, 2009 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 2 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, write to the Free Software -+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ -+# Test GDB's handling of gcore for mapping with a name but zero inode. -+ -+if { [prepare_for_testing gcore-shmid0.exp gcore-shmid0] } { -+ return -1 -+} -+ -+# Does this gdb support gcore? -+set test "help gcore" -+gdb_test_multiple $test $test { -+ -re "Undefined command: .gcore.*$gdb_prompt $" { -+ # gcore command not supported -- nothing to test here. -+ unsupported "gdb does not support gcore on this target" -+ return -1; -+ } -+ -re "Save a core file .*$gdb_prompt $" { -+ pass $test -+ } -+} -+ -+if { ! [ runto_main ] } then { -+ untested gcore-shmid0.exp -+ return -1 -+} -+ -+gdb_breakpoint "initialized" -+gdb_breakpoint "unresolved" -+ -+set oldtimeout $timeout -+set timeout [expr $oldtimeout + 120] -+ -+set test "Continue to initialized." -+gdb_test_multiple "continue" $test { -+ -re "Breakpoint .*, initialized .* at .*\r\n$gdb_prompt $" { -+ pass $test -+ } -+ -re "Breakpoint .*, unresolved .* at .*\r\n$gdb_prompt $" { -+ set timeout $oldtimeout -+ unsupported $test -+ return -1 -+ } -+} -+set timeout $oldtimeout -+ -+set escapedfilename [string_to_regexp [standard_output_file gcore-shmid0.test]] -+ -+set test "save a corefile" -+gdb_test_multiple "gcore [standard_output_file gcore-shmid0.test]" $test { -+ -re "Saved corefile ${escapedfilename}\[\r\n\]+$gdb_prompt $" { -+ pass $test -+ } -+ -re "Can't create a corefile\[\r\n\]+$gdb_prompt $" { -+ unsupported $test -+ } -+} -+ -+# Be sure to remove the handle first. -+# But it would get removed even on a kill by GDB as the handle is already -+# deleted, just it is still attached. -+gdb_continue_to_end "finish" -+ -+set test "core-file command" -+gdb_test_multiple "core-file [standard_output_file gcore-shmid0.test]" $test { -+ -re ".* program is being debugged already.*y or n. $" { -+ # gdb_load may connect us to a gdbserver. -+ send_gdb "y\n" -+ exp_continue; -+ } -+ -re "Core was generated by .*\r\n\#0 .*\\\(\\\).*\r\n$gdb_prompt $" { -+ # The filename does not fit there anyway so do not check it. -+ pass $test -+ } -+ -re ".*registers from core file: File in wrong format.* $" { -+ fail "core-file command (could not read registers from core file)" -+ } -+} -+ -+set test "backtrace" -+gdb_test_multiple "bt" $test { -+ -re "#0 *initialized \\\(\\\) at .*#1 .* main \\\(.*$gdb_prompt $" { -+ pass $test -+ } -+ -re "#0 *initialized \\\(\\\) at .*Cannot access memory at address .*$gdb_prompt $" { -+ fail $test -+ } -+} diff --git a/gdb-6.5-bz218379-ppc-solib-trampoline-test.patch b/gdb-6.5-bz218379-ppc-solib-trampoline-test.patch deleted file mode 100644 index db4229d..0000000 --- a/gdb-6.5-bz218379-ppc-solib-trampoline-test.patch +++ /dev/null @@ -1,107 +0,0 @@ -From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 -From: Fedora GDB patches -Date: Fri, 27 Oct 2017 21:07:50 +0200 -Subject: gdb-6.5-bz218379-ppc-solib-trampoline-test.patch - -;; Test sideeffects of skipping ppc .so libs trampolines (BZ 218379). -;;=fedoratest - -https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=218379 - -diff --git a/gdb/testsuite/gdb.base/step-over-trampoline.c b/gdb/testsuite/gdb.base/step-over-trampoline.c -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.base/step-over-trampoline.c -@@ -0,0 +1,28 @@ -+/* This testcase is part of GDB, the GNU debugger. -+ -+ Copyright 2006 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 2 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, write to the Free Software -+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ -+ Please email any bugs, comments, and/or additions to this file to: -+ bug-gdb@prep.ai.mit.edu */ -+ -+#include -+ -+int main (void) -+{ -+ puts ("hello world"); -+ return 0; -+} -diff --git a/gdb/testsuite/gdb.base/step-over-trampoline.exp b/gdb/testsuite/gdb.base/step-over-trampoline.exp -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.base/step-over-trampoline.exp -@@ -0,0 +1,59 @@ -+# Copyright 2006 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 2 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, write to the Free Software -+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ -+if {[use_gdb_stub]} { -+ untested "skipping test because of use_gdb_stub" -+ return -1 -+} -+ -+if $tracelevel then { -+ strace $tracelevel -+} -+ -+set testfile step-over-trampoline -+set srcfile ${testfile}.c -+set binfile [standard_output_file ${testfile}] -+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { -+ untested "Couldn't compile test program" -+ return -1 -+} -+ -+# Get things started. -+ -+gdb_exit -+gdb_start -+gdb_reinitialize_dir $srcdir/$subdir -+gdb_load ${binfile} -+ -+# For C programs, "start" should stop in main(). -+ -+gdb_test "start" \ -+ "main \\(\\) at .*$srcfile.*" \ -+ "start" -+ -+# main () at hello2.c:5 -+# 5 puts("hello world\n"); -+# (gdb) next -+# 0x100007e0 in call___do_global_ctors_aux () -+ -+gdb_test_multiple "next" "invalid `next' output" { -+ -re "\nhello world.*return 0;.*" { -+ pass "stepped over" -+ } -+ -re " in call___do_global_ctors_aux \\(\\).*" { -+ fail "stepped into trampoline" -+ } -+} diff --git a/gdb-6.5-gcore-buffer-limit-test.patch b/gdb-6.5-gcore-buffer-limit-test.patch deleted file mode 100644 index 07ba1e9..0000000 --- a/gdb-6.5-gcore-buffer-limit-test.patch +++ /dev/null @@ -1,154 +0,0 @@ -From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 -From: Fedora GDB patches -Date: Fri, 27 Oct 2017 21:07:50 +0200 -Subject: gdb-6.5-gcore-buffer-limit-test.patch - -;; Test gcore memory and time requirements for large inferiors. -;;=fedoratest - -diff --git a/gdb/testsuite/gdb.base/gcore-excessive-memory.c b/gdb/testsuite/gdb.base/gcore-excessive-memory.c -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.base/gcore-excessive-memory.c -@@ -0,0 +1,37 @@ -+/* This testcase is part of GDB, the GNU debugger. -+ -+ Copyright 2008 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 2 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, write to the Free Software -+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ -+ Please email any bugs, comments, and/or additions to this file to: -+ bug-gdb@prep.ai.mit.edu */ -+ -+#include -+#include -+ -+#define MEGS 64 -+ -+int main() -+{ -+ void *mem; -+ -+ mem = malloc (MEGS * 1024ULL * 1024ULL); -+ -+ for (;;) -+ sleep (1); -+ -+ return 0; -+} -diff --git a/gdb/testsuite/gdb.base/gcore-excessive-memory.exp b/gdb/testsuite/gdb.base/gcore-excessive-memory.exp -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.base/gcore-excessive-memory.exp -@@ -0,0 +1,99 @@ -+# Copyright 2008 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 2 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, write to the Free Software -+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ -+if {[use_gdb_stub]} { -+ untested "skipping test because of use_gdb_stub" -+ return -1 -+} -+ -+set testfile gcore-excessive-memory -+set srcfile ${testfile}.c -+set shfile [standard_output_file ${testfile}-gdb.sh] -+set corefile [standard_output_file ${testfile}.core] -+set binfile [standard_output_file ${testfile}] -+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { -+ untested "Couldn't compile test program" -+ return -1 -+} -+ -+set f [open "|getconf PAGESIZE" "r"] -+gets $f pagesize -+close $f -+ -+set pid_of_bin [eval exec $binfile &] -+sleep 2 -+ -+# Get things started. -+ -+gdb_exit -+gdb_start -+gdb_reinitialize_dir $srcdir/$subdir -+gdb_load ${binfile} -+ -+set pid_of_gdb [exp_pid -i [board_info host fileid]] -+ -+gdb_test "attach $pid_of_bin" "Attaching to .*" "attach" -+gdb_test "up 99" "in main .*" "verify we can get to main" -+ -+proc memory_v_pages_get {} { -+ global pid_of_gdb pagesize -+ set fd [open "/proc/$pid_of_gdb/statm"] -+ gets $fd line -+ close $fd -+ # number of pages of virtual memory -+ scan $line "%d" drs -+ return $drs -+} -+ -+set pages_found [memory_v_pages_get] -+ -+# It must be definitely less than `MEGS' of `gcore-excessive-memory.c'. -+set mb_gcore_reserve 4 -+verbose -log "pages_found = $pages_found, mb_gcore_reserve = $mb_gcore_reserve" -+set kb_found [expr $pages_found * $pagesize / 1024] -+set kb_permit [expr $kb_found + 1 * 1024 + $mb_gcore_reserve * 1024] -+verbose -log "kb_found = $kb_found, kb_permit = $kb_permit" -+ -+# Create the ulimit wrapper. -+set f [open $shfile "w"] -+puts $f "#! /bin/sh" -+puts $f "ulimit -v $kb_permit" -+puts $f "exec $GDB \"\$@\"" -+close $f -+remote_exec host "chmod +x $shfile" -+ -+gdb_exit -+set GDBold $GDB -+set GDB "$shfile" -+gdb_start -+set GDB $GDBold -+ -+gdb_reinitialize_dir $srcdir/$subdir -+gdb_load ${binfile} -+ -+set pid_of_gdb [exp_pid -i [board_info host fileid]] -+ -+gdb_test "attach $pid_of_bin" "Attaching to .*" "attach" -+gdb_test "up 99" "in main .*" "verify we can get to main" -+ -+verbose -log "kb_found before gcore = [expr [memory_v_pages_get] * $pagesize / 1024]" -+ -+gdb_test "gcore $corefile" "Saved corefile \[^\n\r\]*" "Save the core file" -+ -+verbose -log "kb_found after gcore = [expr [memory_v_pages_get] * $pagesize / 1024]" -+ -+# Cleanup. -+exec kill -9 $pid_of_bin diff --git a/gdb-6.6-bz237572-ppc-atomic-sequence-test.patch b/gdb-6.6-bz237572-ppc-atomic-sequence-test.patch deleted file mode 100644 index 4faef13..0000000 --- a/gdb-6.6-bz237572-ppc-atomic-sequence-test.patch +++ /dev/null @@ -1,278 +0,0 @@ -From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 -From: Jan Kratochvil -Date: Fri, 27 Oct 2017 21:07:50 +0200 -Subject: gdb-6.6-bz237572-ppc-atomic-sequence-test.patch - -;; Support for stepping over PPC atomic instruction sequences (BZ 237572). -;;=fedoratest - -2007-06-25 Jan Kratochvil - - * gdb.threads/atomic-seq-threaded.c, - gdb.threads/atomic-seq-threaded.exp: New files. - -diff --git a/gdb/testsuite/gdb.threads/atomic-seq-threaded.c b/gdb/testsuite/gdb.threads/atomic-seq-threaded.c -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.threads/atomic-seq-threaded.c -@@ -0,0 +1,171 @@ -+/* This testcase is part of GDB, the GNU debugger. -+ -+ Copyright 2007 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 2 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, write to the Free Software -+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -+ MA 02110-1301, USA. */ -+ -+/* Test stepping over RISC atomic sequences. -+ This variant testcases the code for stepping another thread while skipping -+ over the atomic sequence in the former thread -+ (STEPPING_PAST_SINGLESTEP_BREAKPOINT). -+ Code comes from gcc/testsuite/gcc.dg/sync-2.c */ -+ -+/* { dg-options "-march=i486" { target { { i?86-*-* x86_64-*-* } && ilp32 } } } */ -+/* { dg-options "-mcpu=v9" { target sparc*-*-* } } */ -+ -+/* Test functionality of the intrinsics for 'short' and 'char'. */ -+ -+#include -+#include -+#include -+#include -+#include -+ -+#define LOOPS 2 -+ -+static int unused; -+ -+static char AI[18]; -+static char init_qi[18] = { 3,5,7,9,0,0,0,0,-1,0,0,0,0,0,-1,0,0,0 }; -+static char test_qi[18] = { 3,5,7,9,1,4,22,-12,7,8,9,7,1,-12,7,8,9,7 }; -+ -+static void -+do_qi (void) -+{ -+ if (__sync_fetch_and_add(AI+4, 1) != 0) -+ abort (); -+ if (__sync_fetch_and_add(AI+5, 4) != 0) -+ abort (); -+ if (__sync_fetch_and_add(AI+6, 22) != 0) -+ abort (); -+ if (__sync_fetch_and_sub(AI+7, 12) != 0) -+ abort (); -+ if (__sync_fetch_and_and(AI+8, 7) != (char)-1) -+ abort (); -+ if (__sync_fetch_and_or(AI+9, 8) != 0) -+ abort (); -+ if (__sync_fetch_and_xor(AI+10, 9) != 0) -+ abort (); -+ if (__sync_fetch_and_nand(AI+11, 7) != 0) -+ abort (); -+ -+ if (__sync_add_and_fetch(AI+12, 1) != 1) -+ abort (); -+ if (__sync_sub_and_fetch(AI+13, 12) != (char)-12) -+ abort (); -+ if (__sync_and_and_fetch(AI+14, 7) != 7) -+ abort (); -+ if (__sync_or_and_fetch(AI+15, 8) != 8) -+ abort (); -+ if (__sync_xor_and_fetch(AI+16, 9) != 9) -+ abort (); -+ if (__sync_nand_and_fetch(AI+17, 7) != 7) -+ abort (); -+} -+ -+static short AL[18]; -+static short init_hi[18] = { 3,5,7,9,0,0,0,0,-1,0,0,0,0,0,-1,0,0,0 }; -+static short test_hi[18] = { 3,5,7,9,1,4,22,-12,7,8,9,7,1,-12,7,8,9,7 }; -+ -+static void -+do_hi (void) -+{ -+ if (__sync_fetch_and_add(AL+4, 1) != 0) -+ abort (); -+ if (__sync_fetch_and_add(AL+5, 4) != 0) -+ abort (); -+ if (__sync_fetch_and_add(AL+6, 22) != 0) -+ abort (); -+ if (__sync_fetch_and_sub(AL+7, 12) != 0) -+ abort (); -+ if (__sync_fetch_and_and(AL+8, 7) != -1) -+ abort (); -+ if (__sync_fetch_and_or(AL+9, 8) != 0) -+ abort (); -+ if (__sync_fetch_and_xor(AL+10, 9) != 0) -+ abort (); -+ if (__sync_fetch_and_nand(AL+11, 7) != 0) -+ abort (); -+ -+ if (__sync_add_and_fetch(AL+12, 1) != 1) -+ abort (); -+ if (__sync_sub_and_fetch(AL+13, 12) != -12) -+ abort (); -+ if (__sync_and_and_fetch(AL+14, 7) != 7) -+ abort (); -+ if (__sync_or_and_fetch(AL+15, 8) != 8) -+ abort (); -+ if (__sync_xor_and_fetch(AL+16, 9) != 9) -+ abort (); -+ if (__sync_nand_and_fetch(AL+17, 7) != 7) -+ abort (); -+} -+ -+static void * -+start1 (void *arg) -+{ -+ unsigned loop; -+ sleep(1); -+ -+ for (loop = 0; loop < LOOPS; loop++) -+ { -+ memcpy(AI, init_qi, sizeof(init_qi)); -+ -+ do_qi (); -+ -+ if (memcmp (AI, test_qi, sizeof(test_qi))) -+ abort (); -+ } -+ -+ return arg; /* _delete1_ */ -+} -+ -+static void * -+start2 (void *arg) -+{ -+ unsigned loop; -+ -+ for (loop = 0; loop < LOOPS; loop++) -+ { -+ memcpy(AL, init_hi, sizeof(init_hi)); -+ -+ do_hi (); -+ -+ if (memcmp (AL, test_hi, sizeof(test_hi))) -+ abort (); -+ } -+ -+ return arg; /* _delete2_ */ -+} -+ -+int -+main (int argc, char **argv) -+{ -+ pthread_t thread; -+ int i; -+ -+ i = pthread_create (&thread, NULL, start1, NULL); /* _create_ */ -+ assert (i == 0); /* _create_after_ */ -+ -+ sleep (1); -+ -+ start2 (NULL); -+ -+ i = pthread_join (thread, NULL); /* _delete_ */ -+ assert (i == 0); -+ -+ return 0; /* _exit_ */ -+} -diff --git a/gdb/testsuite/gdb.threads/atomic-seq-threaded.exp b/gdb/testsuite/gdb.threads/atomic-seq-threaded.exp -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.threads/atomic-seq-threaded.exp -@@ -0,0 +1,84 @@ -+# atomic-seq-threaded.exp -- Test case for stepping over RISC atomic code seqs. -+# This variant testcases the code for stepping another thread while skipping -+# over the atomic sequence in the former thread -+# (STEPPING_PAST_SINGLESTEP_BREAKPOINT). -+# Copyright (C) 2007 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 2 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, write to the Free Software -+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -+ -+# Please email any bugs, comments, and/or additions to this file to: -+# bug-gdb@prep.ai.mit.edu -+ -+set testfile atomic-seq-threaded -+set srcfile ${testfile}.c -+set binfile [standard_output_file ${testfile}] -+ -+foreach opts {{} {compiler=gcc4} {FAIL}} { -+ if {$opts eq "FAIL"} { -+ return -1 -+ } -+ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable [list debug $opts]] eq "" } { -+ break -+ } -+} -+ -+gdb_exit -+gdb_start -+gdb_reinitialize_dir $srcdir/$subdir -+ -+gdb_load ${binfile} -+if ![runto_main] then { -+ fail "Can't run to main" -+ return 0 -+} -+ -+# pthread_create () will not pass even on x86_64 with software watchpoint. -+# Pass after pthread_create () without any watchpoint active. -+set line [gdb_get_line_number "_create_after_"] -+gdb_test "tbreak $line" \ -+ "reakpoint (\[0-9\]+) at .*$srcfile, line $line\..*" \ -+ "set breakpoint after pthread_create ()" -+gdb_test "c" \ -+ ".*/\\* _create_after_ \\*/.*" \ -+ "run till after pthread_create ()" -+ -+# Without a watchpoint being software no single-stepping would be used. -+set test "Start (software) watchpoint" -+gdb_test_multiple "watch unused" $test { -+ -re "Watchpoint \[0-9\]+: unused.*$gdb_prompt $" { -+ pass $test -+ } -+ -re "Hardware watchpoint \[0-9\]+: unused.*$gdb_prompt $" { -+ # We do not test the goal but still the whole testcase should pass. -+ unsupported $test -+ } -+} -+ -+# More thorough testing of the scheduling logic. -+gdb_test "set scheduler-locking step" "" -+ -+# Critical code path is stepped through at this point. -+set line [gdb_get_line_number "_exit_"] -+gdb_test "tbreak $line" \ -+ "reakpoint \[0-9\]+ at .*$srcfile, line $line\..*" \ -+ "set breakpoint at _exit_" -+gdb_test "c" \ -+ ".*/\\* _exit_ \\*/.*" \ -+ "run till _exit_" -+ -+# Just a nonproblematic program exit. -+gdb_test "c" \ -+ ".*Program exited normally\\..*" \ -+ "run till program exit" diff --git a/gdb-6.8-bz466901-backtrace-full-prelinked.patch b/gdb-6.8-bz466901-backtrace-full-prelinked.patch deleted file mode 100644 index 39d8947..0000000 --- a/gdb-6.8-bz466901-backtrace-full-prelinked.patch +++ /dev/null @@ -1,481 +0,0 @@ -From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 -From: Fedora GDB patches -Date: Fri, 27 Oct 2017 21:07:50 +0200 -Subject: gdb-6.8-bz466901-backtrace-full-prelinked.patch - -;; Fix resolving of variables at locations lists in prelinked libs (BZ 466901). -;;=fedoratest - -Fix resolving of variables at locations lists in prelinked libs (BZ 466901). - -diff --git a/gdb/testsuite/gdb.dwarf2/dw2-loclist-prelinked-func.S b/gdb/testsuite/gdb.dwarf2/dw2-loclist-prelinked-func.S -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.dwarf2/dw2-loclist-prelinked-func.S -@@ -0,0 +1,328 @@ -+/* This testcase is part of GDB, the GNU debugger. -+ -+ Copyright 2008 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 . */ -+ -+/* -+#include -+ -+void -+func (void) -+{ -+ int i; -+ -+ abort (); -+} -+*/ -+ .file "dw2-loclist-prelinked.c" -+ .section .debug_abbrev,"",@progbits -+.Ldebug_abbrev0: -+ .section .debug_info,"",@progbits -+.Ldebug_info0: -+ .section .debug_line,"",@progbits -+.Ldebug_line0: -+ .text -+.Ltext0: -+.globl func -+ .type func, @function -+func: -+.LFB2: -+ .file 1 "dw2-loclist-prelinked.c" -+ .loc 1 5 0 -+ pushl %ebp -+.LCFI0: -+ movl %esp, %ebp -+.LCFI1: -+ subl $24, %esp -+.LCFI2: -+ .loc 1 8 0 -+ call abort -+.LFE2: -+ .size func, .-func -+ .section .debug_frame,"",@progbits -+.Lframe0: -+ .long .LECIE0-.LSCIE0 -+.LSCIE0: -+ .long 0xffffffff -+ .byte 0x1 -+ .string "" -+ .uleb128 0x1 -+ .sleb128 -4 -+ .byte 0x8 -+ .byte 0xc -+ .uleb128 0x4 -+ .uleb128 0x4 -+ .byte 0x88 -+ .uleb128 0x1 -+ .align 4 -+.LECIE0: -+.LSFDE0: -+ .long .LEFDE0-.LASFDE0 -+.LASFDE0: -+ .long .Lframe0 -+ .long .LFB2 -+ .long .LFE2-.LFB2 -+ .byte 0x4 -+ .long .LCFI0-.LFB2 -+ .byte 0xe -+ .uleb128 0x8 -+ .byte 0x85 -+ .uleb128 0x2 -+ .byte 0x4 -+ .long .LCFI1-.LCFI0 -+ .byte 0xd -+ .uleb128 0x5 -+ .align 4 -+.LEFDE0: -+ .text -+.Letext0: -+ .section .debug_loc,"",@progbits -+.Ldebug_loc0: -+.LLST0: -+ .long .LFB2-.Ltext0 -+ .long .LCFI0-.Ltext0 -+ .value 0x2 -+ .byte 0x74 -+ .sleb128 4 -+ .long .LCFI0-.Ltext0 -+ .long .LCFI1-.Ltext0 -+ .value 0x2 -+ .byte 0x74 -+ .sleb128 8 -+ .long .LCFI1-.Ltext0 -+ .long .LFE2-.Ltext0 -+ .value 0x2 -+ .byte 0x75 -+ .sleb128 8 -+ .long 0x0 -+ .long 0x0 -+ .section .debug_info -+ .long 0x94 -+ .value 0x2 -+ .long .Ldebug_abbrev0 -+ .byte 0x4 -+ .uleb128 0x1 -+ .long .LASF10 -+ .byte 0x1 -+ .long .LASF11 -+ .long .LASF12 -+ .long .Ltext0 -+ .long .Letext0 -+ .long .Ldebug_line0 -+ .uleb128 0x2 -+ .byte 0x4 -+ .byte 0x7 -+ .long .LASF0 -+ .uleb128 0x3 -+ .byte 0x4 -+ .byte 0x5 -+ .string "int" -+ .uleb128 0x2 -+ .byte 0x4 -+ .byte 0x5 -+ .long .LASF1 -+ .uleb128 0x2 -+ .byte 0x1 -+ .byte 0x8 -+ .long .LASF2 -+ .uleb128 0x2 -+ .byte 0x2 -+ .byte 0x7 -+ .long .LASF3 -+ .uleb128 0x2 -+ .byte 0x4 -+ .byte 0x7 -+ .long .LASF4 -+ .uleb128 0x2 -+ .byte 0x1 -+ .byte 0x6 -+ .long .LASF5 -+ .uleb128 0x2 -+ .byte 0x2 -+ .byte 0x5 -+ .long .LASF6 -+ .uleb128 0x2 -+ .byte 0x8 -+ .byte 0x5 -+ .long .LASF7 -+ .uleb128 0x2 -+ .byte 0x8 -+ .byte 0x7 -+ .long .LASF8 -+ .uleb128 0x4 -+ .byte 0x4 -+ .byte 0x7 -+ .uleb128 0x2 -+ .byte 0x1 -+ .byte 0x6 -+ .long .LASF9 -+ .uleb128 0x5 -+ .byte 0x1 -+ .long .LASF13 -+ .byte 0x1 -+ .byte 0x5 -+ .byte 0x1 -+ .long .LFB2 -+ .long .LFE2 -+ .long .LLST0 -+ .uleb128 0x6 -+ .string "i" -+ .byte 0x1 -+ .byte 0x6 -+ .long 0x2c -+ .byte 0x2 -+ .byte 0x91 -+ .sleb128 -12 -+ .byte 0x0 -+ .byte 0x0 -+ .section .debug_abbrev -+ .uleb128 0x1 -+ .uleb128 0x11 -+ .byte 0x1 -+ .uleb128 0x25 -+ .uleb128 0xe -+ .uleb128 0x13 -+ .uleb128 0xb -+ .uleb128 0x3 -+ .uleb128 0xe -+ .uleb128 0x1b -+ .uleb128 0xe -+ .uleb128 0x11 -+ .uleb128 0x1 -+ .uleb128 0x12 -+ .uleb128 0x1 -+ .uleb128 0x10 -+ .uleb128 0x6 -+ .byte 0x0 -+ .byte 0x0 -+ .uleb128 0x2 -+ .uleb128 0x24 -+ .byte 0x0 -+ .uleb128 0xb -+ .uleb128 0xb -+ .uleb128 0x3e -+ .uleb128 0xb -+ .uleb128 0x3 -+ .uleb128 0xe -+ .byte 0x0 -+ .byte 0x0 -+ .uleb128 0x3 -+ .uleb128 0x24 -+ .byte 0x0 -+ .uleb128 0xb -+ .uleb128 0xb -+ .uleb128 0x3e -+ .uleb128 0xb -+ .uleb128 0x3 -+ .uleb128 0x8 -+ .byte 0x0 -+ .byte 0x0 -+ .uleb128 0x4 -+ .uleb128 0x24 -+ .byte 0x0 -+ .uleb128 0xb -+ .uleb128 0xb -+ .uleb128 0x3e -+ .uleb128 0xb -+ .byte 0x0 -+ .byte 0x0 -+ .uleb128 0x5 -+ .uleb128 0x2e -+ .byte 0x1 -+ .uleb128 0x3f -+ .uleb128 0xc -+ .uleb128 0x3 -+ .uleb128 0xe -+ .uleb128 0x3a -+ .uleb128 0xb -+ .uleb128 0x3b -+ .uleb128 0xb -+ .uleb128 0x27 -+ .uleb128 0xc -+ .uleb128 0x11 -+ .uleb128 0x1 -+ .uleb128 0x12 -+ .uleb128 0x1 -+ .uleb128 0x40 -+ .uleb128 0x6 -+ .byte 0x0 -+ .byte 0x0 -+ .uleb128 0x6 -+ .uleb128 0x34 -+ .byte 0x0 -+ .uleb128 0x3 -+ .uleb128 0x8 -+ .uleb128 0x3a -+ .uleb128 0xb -+ .uleb128 0x3b -+ .uleb128 0xb -+ .uleb128 0x49 -+ .uleb128 0x13 -+ .uleb128 0x2 -+ .uleb128 0xa -+ .byte 0x0 -+ .byte 0x0 -+ .byte 0x0 -+ .section .debug_pubnames,"",@progbits -+ .long 0x17 -+ .value 0x2 -+ .long .Ldebug_info0 -+ .long 0x98 -+ .long 0x75 -+ .string "func" -+ .long 0x0 -+ .section .debug_aranges,"",@progbits -+ .long 0x1c -+ .value 0x2 -+ .long .Ldebug_info0 -+ .byte 0x4 -+ .byte 0x0 -+ .value 0x0 -+ .value 0x0 -+ .long .Ltext0 -+ .long .Letext0-.Ltext0 -+ .long 0x0 -+ .long 0x0 -+ .section .debug_str,"MS",@progbits,1 -+.LASF7: -+ .string "long long int" -+.LASF0: -+ .string "unsigned int" -+.LASF11: -+ .string "dw2-loclist-prelinked.c" -+.LASF12: -+ .string "gdb-6.8/gdb/testsuite/gdb.dwarf2" -+.LASF4: -+ .string "long unsigned int" -+.LASF8: -+ .string "long long unsigned int" -+.LASF2: -+ .string "unsigned char" -+.LASF9: -+ .string "char" -+.LASF1: -+ .string "long int" -+.LASF3: -+ .string "short unsigned int" -+.LASF5: -+ .string "signed char" -+.LASF10: -+ .string "GNU C 4.3.2 20081007 (Red Hat 4.3.2-6)" -+.LASF13: -+ .string "func" -+.LASF6: -+ .string "short int" -+ .ident "GCC: (GNU) 4.3.2 20081007 (Red Hat 4.3.2-6)" -+ .section .note.GNU-stack,"",@progbits -diff --git a/gdb/testsuite/gdb.dwarf2/dw2-loclist-prelinked-main.c b/gdb/testsuite/gdb.dwarf2/dw2-loclist-prelinked-main.c -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.dwarf2/dw2-loclist-prelinked-main.c -@@ -0,0 +1,26 @@ -+/* This testcase is part of GDB, the GNU debugger. -+ -+ Copyright 2008 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 . */ -+ -+/* dw2-loclist-prelinked-func.S */ -+extern void func (void); -+ -+int -+main (void) -+{ -+ func (); -+ return 0; -+} -diff --git a/gdb/testsuite/gdb.dwarf2/dw2-loclist-prelinked.exp b/gdb/testsuite/gdb.dwarf2/dw2-loclist-prelinked.exp -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.dwarf2/dw2-loclist-prelinked.exp -@@ -0,0 +1,102 @@ -+# Copyright 2008 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 2 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, write to the Free Software -+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -+ -+# Minimal DWARF-2 unit test -+ -+# This test can only be run on i386/x86_64 targets which support DWARF-2. -+# For now pick a sampling of likely targets. -+if {(![istarget *-*-linux*] -+ && ![istarget *-*-gnu*] -+ && ![istarget *-*-elf*] -+ && ![istarget *-*-openbsd*]) -+ || (![istarget "i?86-*-*"] && ![istarget "x86_64-*-*"])} { -+ return 0 -+} -+ -+set testfile "dw2-loclist-prelinked" -+set srcfuncfile ${testfile}-func.S -+set binsharedfuncfile [standard_output_file ${testfile}.so] -+set srcmainfile ${testfile}-main.c -+set binfile [standard_output_file ${testfile}] -+ -+remote_exec build "rm -f ${binfile}" -+ -+# get the value of gcc_compiled -+if [get_compiler_info ${binfile}] { -+ return -1 -+} -+ -+# This test can only be run on gcc as we use additional_flags=FIXME -+if {$gcc_compiled == 0} { -+ return 0 -+} -+ -+if { [gdb_compile_shlib "${srcdir}/${subdir}/${srcfuncfile}" "${binsharedfuncfile}" {debug additional_flags=-m32}] != "" } { -+ untested "Couldn't compile test library" -+ return -1 -+} -+ -+# The new separate debug info file will be stored in the .debug subdirectory. -+ -+if [gdb_gnu_strip_debug ${binsharedfuncfile}] { -+ # check that you have a recent version of strip and objcopy installed -+ unsupported "cannot produce separate debug info files" -+ return -1 -+} -+ -+if {[catch "system \"/usr/sbin/prelink -qNR --no-exec-shield ${binsharedfuncfile}\""] != 0} { -+ # Maybe we don't have prelink. -+ return -1 -+} -+ -+if { [gdb_compile "${srcdir}/${subdir}/${srcmainfile}" \ -+ "${binfile}" executable [list debug additional_flags=-m32 shlib=${binsharedfuncfile}]] != "" } { -+ return -1 -+} -+ -+gdb_exit -+gdb_start -+gdb_reinitialize_dir $srcdir/$subdir -+gdb_load ${binfile} -+ -+gdb_run_cmd -+ -+gdb_test "" "Program received signal SIGABRT, Aborted..*" "Enter abort()" -+ -+# Incorrect: -+# #0 0x00110430 in __kernel_vsyscall () -+# No symbol table info available. -+# #1 0x003d44c0 in raise () from /lib/libc.so.6 -+# No symbol table info available. -+# #2 0x003d5e88 in abort () from /lib/libc.so.6 -+# No symbol table info available. -+# #3 0x44f10437 in func () at dw2-loclist-prelinked.c:8 -+# i = Could not find the frame base for "func". -+ -+# Correct: -+# #0 0x00110430 in __kernel_vsyscall () -+# No symbol table info available. -+# #1 0x003d44c0 in raise () from /lib/libc.so.6 -+# No symbol table info available. -+# #2 0x003d5e88 in abort () from /lib/libc.so.6 -+# No symbol table info available. -+# #3 0x4ae36437 in func () at dw2-loclist-prelinked.c:8 -+# i = 3827288 -+# #4 0x0804851a in main () at ../../../gdb/testsuite/gdb.dwarf2/dw2-loclist-prelinked-main.c:24 -+# No locals. -+ -+# `abort' can get expressed as `*__GI_abort'. -+gdb_test "bt full" "in \[^ \]*abort \\(.*in func \\(.*\r\n\[\t \]+i = -?\[0-9\].*in main \\(.*" "Backtrace after abort()" diff --git a/gdb-add-index.patch b/gdb-add-index.patch index 52a3145..69175d7 100644 --- a/gdb-add-index.patch +++ b/gdb-add-index.patch @@ -19,7 +19,7 @@ Subject: gdb-add-index.patch diff --git a/gdb/contrib/gdb-add-index.sh b/gdb/contrib/gdb-add-index.sh --- a/gdb/contrib/gdb-add-index.sh +++ b/gdb/contrib/gdb-add-index.sh -@@ -16,14 +16,52 @@ +@@ -16,9 +16,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . @@ -31,7 +31,9 @@ diff --git a/gdb/contrib/gdb-add-index.sh b/gdb/contrib/gdb-add-index.sh OBJCOPY=${OBJCOPY:=objcopy} READELF=${READELF:=readelf} - myname="${0##*/}" +@@ -52,6 +51,45 @@ print_version() { + echo "GNU gdb-add-index (${PKGVERSION}) ${VERSION}" + } +# For GDB itself we need to be a little smarter. If GDB is set in the +# environment then we will use that. But if GDB is not set in the @@ -73,5 +75,5 @@ diff --git a/gdb/contrib/gdb-add-index.sh b/gdb/contrib/gdb-add-index.sh +fi + dwarf5="" - if [ "$1" = "-dwarf-5" ]; then - dwarf5="$1" + + # Parse options. diff --git a/gdb-archer-next-over-throw-cxx-exec.patch b/gdb-archer-next-over-throw-cxx-exec.patch deleted file mode 100644 index c5d58b3..0000000 --- a/gdb-archer-next-over-throw-cxx-exec.patch +++ /dev/null @@ -1,88 +0,0 @@ -From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 -From: Fedora GDB patches -Date: Fri, 27 Oct 2017 21:07:50 +0200 -Subject: gdb-archer-next-over-throw-cxx-exec.patch - -;; Fix follow-exec for C++ programs (bugreported by Martin Stransky). -;;=fedoratest - -Archer-upstreamed: -http://sourceware.org/ml/archer/2010-q2/msg00031.html - -diff --git a/gdb/testsuite/gdb.cp/cxxexec.cc b/gdb/testsuite/gdb.cp/cxxexec.cc -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.cp/cxxexec.cc -@@ -0,0 +1,25 @@ -+/* This test script is part of GDB, the GNU debugger. -+ -+ Copyright 2010 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 . */ -+ -+#include -+ -+int -+main() -+{ -+ execlp ("true", "true", NULL); -+ return 1; -+} -diff --git a/gdb/testsuite/gdb.cp/cxxexec.exp b/gdb/testsuite/gdb.cp/cxxexec.exp -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.cp/cxxexec.exp -@@ -0,0 +1,42 @@ -+# Copyright 2010 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 . -+ -+require allow_cplus_tests -+ -+set testfile cxxexec -+if { [prepare_for_testing ${testfile}.exp ${testfile} ${testfile}.cc {c++ debug}] } { -+ return -1 -+} -+ -+runto_main -+ -+# We could stop after `continue' again at `main'. -+delete_breakpoints -+ -+set test "p _Unwind_DebugHook" -+gdb_test_multiple $test $test { -+ -re " = .* 0x\[0-9a-f\].*\r\n$gdb_prompt $" { -+ pass $test -+ } -+ -re "\r\nNo symbol .*\r\n$gdb_prompt $" { -+ xfail $test -+ untested ${testfile}.exp -+ return -1 -+ } -+} -+ -+# Run to end. The buggy GDB failed instead with: -+# Cannot access memory at address ADDR. -+gdb_continue_to_end "" "continue" 1 diff --git a/gdb-backport-corefile-use-after-free-fix.patch b/gdb-backport-corefile-use-after-free-fix.patch new file mode 100644 index 0000000..64aa23e --- /dev/null +++ b/gdb-backport-corefile-use-after-free-fix.patch @@ -0,0 +1,77 @@ +From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 +From: Lancelot SIX +Date: Mon, 13 Oct 2025 15:11:09 +0100 +Subject: gdb-backport-corefile-use-after-free-fix.patch + +;; Backport of upstream commit 93f536d813c41527e8c939a5f8a90a4 to fix +;; RHBZ2498034. + +gdb/corelow: Fix use-after-free in gdb_read_core_file_mappings + +A recent refactor (fc8e5a565b3 -- gdb: make structured core file +mappings processing global) in gdb/corelow.c:gdb_read_core_file_mappings +introduced a use-after-free bug detected by address sanitizer. + +In this change, a cache is built which holds addresses to elements of a +std::vector. However, as elements as inserted in the vector, the +addresses in the cache should be invalidated, but are not, leading to +the use-after-free issue. + +This patch proposes to store the index in the vector in the cache +instead of the address of the element, solving the invalidation issue. +An alternative approach could be to use a std::list which does not need +invalidation of addresses/references/iterators as the container is +grown. + +Change-Id: Ib57d87c5d0405ffa3b7d38557fb33f7283c5d063 +Approved-By: Andrew Burgess + +diff --git a/gdb/corelow.c b/gdb/corelow.c +--- a/gdb/corelow.c ++++ b/gdb/corelow.c +@@ -2090,19 +2090,20 @@ gdb_read_core_file_mappings (struct gdbarch *gdbarch, struct bfd *cbfd) + /* A map entry used while building RESULTS. */ + struct map_entry + { +- explicit map_entry (core_mapped_file *ptr) +- : file_data (ptr) ++ explicit map_entry (size_t idx) ++ : file_data_index (idx), ++ ignore_build_id_p (false) + { /* 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; ++ size_t file_data_index; + + /* 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; ++ bool ignore_build_id_p; + }; + + /* All files mapped into the core file. The key is the filename. */ +@@ -2141,8 +2142,9 @@ gdb_read_core_file_mappings (struct gdbarch *gdbarch, struct bfd *cbfd) + results.emplace_back (); + + /* The entry to be added to the lookup map. */ +- map_entry entry (&results.back ()); +- entry.file_data->filename = filename; ++ map_entry entry (std::distance (&results.front (), ++ &results.back ())); ++ results[entry.file_data_index].filename = filename; + + /* Add entry to the quick lookup map and update ITER. */ + auto inserted_result +@@ -2151,7 +2153,7 @@ gdb_read_core_file_mappings (struct gdbarch *gdbarch, struct bfd *cbfd) + iter = inserted_result.first; + } + +- core_mapped_file &file_data = *iter->second.file_data; ++ core_mapped_file &file_data = results[iter->second.file_data_index]; + bool &ignore_build_id_p = iter->second.ignore_build_id_p; + + file_data.regions.emplace_back (start, end, file_ofs); diff --git a/gdb-backport-dap-core-file-support.patch b/gdb-backport-dap-core-file-support.patch new file mode 100644 index 0000000..deb0ea7 --- /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 +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 + +~~~ 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 + +~~~ 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 + +~~~ 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 + +~~~ 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) python print(core.is_valid()) + True + (gdb) core-file + No core file now. + (gdb) python print(core) + + (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 +Approved-By: Tom Tromey + +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 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 regions; +- }; +- + gdb::unordered_map bfd_map; + gdb::unordered_set unavailable_paths; + + /* All files mapped into the core file. The key is the filename. */ +- gdb::unordered_map 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 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 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 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 ++gdb_read_core_file_mappings (struct gdbarch *gdbarch, struct bfd *cbfd) ++{ ++ std::vector 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 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_find_mapped_file (const char *filename, + std::optional 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_find_mapped_file (const char *filename, + std::optional 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 regions; ++ ++ /* True if this is the main executable. */ ++ bool is_main_exec = false; ++}; ++ ++extern std::vector 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 free_program_space; + + extern observable 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 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 . */ ++ ++#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 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::key ++ 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 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 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 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 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 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 ())); ++} ++ ++ ++ ++/* 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; ++} ++ ++ ++ ++/* 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 (); ++} ++ ++ ++ ++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); ++ ++ ++ ++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 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 . ++ ++# 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 . */ ++ ++#include ++ ++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 . ++ ++# 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 . */ ++ ++#include ++ ++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 . ++ ++# 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)" "^" \ ++ "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)" "^" \ ++ "print an invalid gdb.Corefile object" ++ ++gdb_test "python print(core1.filename)" \ ++ [multi_line \ ++ "Python Exception : 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 : 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 : 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))" \ ++ "^" ++ 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))" \ ++ "^" ++ 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 . ++ ++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-backport-libiberty-sync.patch b/gdb-backport-libiberty-sync.patch new file mode 100644 index 0000000..a84d5e7 --- /dev/null +++ b/gdb-backport-libiberty-sync.patch @@ -0,0 +1,616 @@ +From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 +From: Andrew Burgess +Date: Thu, 14 May 2026 09:22:42 +0000 +Subject: gdb-backport-libiberty-sync.patch + +;; Backport of upstream commit be3a9405ceb4d6d6 to fix RHBZ 2368350, an +;; out of memory issue with the D demangler. +;; +;; The commit being pulled in here is a commit that syncs multiple +;; commits to libiberty from gcc to binutils-gdb. Most of these commits +;; are spelling and white space fixes, which should have no real impact. +;; The only commit we actually care about is gcc commit bc4d9b6aeb3, +;; which fixes the D demangler issue. +;; +;; This commit will not be needed once we rebase to GDB 18. + +libiberty: sync with gcc commit df3510f80e7c02d9 + +Sync the libiberty/ directory with gcc commit +df3510f80e7c02d9c19d7cb33759cd925880f53a. This pulls in the following +gcc commits and the associated updates to 'libiberty/ChangeLog': + + * 06fb00cb66d libiberty: Fix typos in various files + * fbf903e5b9f Re-flow lines made longer than 80 characters by typo fixes + * cfb945957ee Regenerate some autoconf configure scripts after recent spelling fixes. + * bc4d9b6aeb3 libiberty: avoid exponential back reference issue in D demangler + +I have retained the changes that are in the following binutils-gdb +commit, which are not in gcc: + + commit 219822fd5db6305592b45677a3b38c02b523360e + Date: Tue Apr 9 06:39:21 2024 -0700 + + mmap: Avoid the sanitizer configure check failure + +diff --git a/libiberty/ChangeLog b/libiberty/ChangeLog +--- a/libiberty/ChangeLog ++++ b/libiberty/ChangeLog +@@ -1,3 +1,62 @@ ++2026-06-07 Andrew Burgess ++ ++ * d-demangle.c (struct dlang_info::options): New field, ++ carries options passed to the demangler. ++ (struct dlang_info::num_backrefs): Count total number of back ++ referenced types that have been processed. ++ (struct dlang_info::backref_depth): Count the depth of ++ recursive back references. ++ (dlang_type_backref): Track recursive back reference depth, ++ and the total number of back references encountered. Bail out ++ early if the total number gets too high. ++ (dlang_demangle_init_info): Initialise new 'struct dlang_info' ++ fields. ++ (dlang_demangle): Pass demangler options to ++ dlang_demangle_init_info. ++ * testsuite/d-demangle-expected: Add new test. ++ ++2026-06-01 Jakub Jelinek ++ ++ * configure: Regenerate. ++ ++2026-05-30 Dhruv Chawla ++ ++ * simple-object-mach-o.c (simple_object_mach_o_write_segment): Reflow ++ long line. ++ ++2026-05-30 Dhruv Chawla ++ ++ * bcopy.c: Fix typos. ++ * bsearch.c: Likewise. ++ * bsearch_r.c: Likewise. ++ * configure.ac: Likewise. ++ * cp-demangle.c (d_print_comp_inner): Likewise. ++ (d_print_comp): Likewise. ++ * d-demangle.c (dlang_identifier): Likewise. ++ * dyn-string.c: Likewise. ++ * ldirname.c: Likewise. ++ * make-relative-prefix.c (make_relative_prefix_1): Likewise. ++ * obstacks.texi: Likewise. ++ * pex-win32.c (argv_to_cmdline): Likewise. ++ (spawn_script): Likewise. ++ * random.c (random): Likewise. ++ (setstate): Likewise. ++ * regex.c (WIDE_CHAR_SUPPORT): Likewise. ++ (convert_mbs_to_wcs): Likewise. ++ (PREFIX): Likewise. ++ (wcs_compile_range): Likewise. ++ (count_mbs_length): Likewise. ++ (wcs_re_match_2_internal): Likewise. ++ (byte_re_match_2_internal): Likewise. ++ * sigsetmask.c: Likewise. ++ * simple-object-elf.c (SHT_SYMTAB_SHNDX): Likewise. ++ (STV_HIDDEN): Likewise. ++ (simple_object_elf_copy_lto_debug_sections): Likewise. ++ * simple-object-mach-o.c (struct mach_o_header_32): Likewise. ++ (struct mach_o_header_64): Likewise. ++ (simple_object_mach_o_write_segment): Likewise. ++ * strsignal.c (defined): Likewise. ++ + 2025-08-06 Matthieu Longo + + * testsuite/test-doubly-linked-list.c: disable debug logging on +diff --git a/libiberty/bcopy.c b/libiberty/bcopy.c +--- a/libiberty/bcopy.c ++++ b/libiberty/bcopy.c +@@ -1,4 +1,4 @@ +-/* bcopy -- copy memory regions of arbitary length ++/* bcopy -- copy memory regions of arbitrary length + + @deftypefn Supplemental void bcopy (char *@var{in}, char *@var{out}, int @var{length}) + +diff --git a/libiberty/bsearch.c b/libiberty/bsearch.c +--- a/libiberty/bsearch.c ++++ b/libiberty/bsearch.c +@@ -60,7 +60,7 @@ is respectively less than, matching, or greater than the array member. + * is odd, moving left simply involves halving lim: e.g., when lim + * is 5 we look at item 2, so we change lim to 2 so that we will + * look at items 0 & 1. If lim is even, the same applies. If lim +- * is odd, moving right again involes halving lim, this time moving ++ * is odd, moving right again involves halving lim, this time moving + * the base up one item past p: e.g., when lim is 5 we change base + * to item 3 and make lim 2 so that we will look at items 3 and 4. + * If lim is even, however, we have to shrink it by one before +diff --git a/libiberty/bsearch_r.c b/libiberty/bsearch_r.c +--- a/libiberty/bsearch_r.c ++++ b/libiberty/bsearch_r.c +@@ -61,7 +61,7 @@ is respectively less than, matching, or greater than the array member. + * is odd, moving left simply involves halving lim: e.g., when lim + * is 5 we look at item 2, so we change lim to 2 so that we will + * look at items 0 & 1. If lim is even, the same applies. If lim +- * is odd, moving right again involes halving lim, this time moving ++ * is odd, moving right again involves halving lim, this time moving + * the base up one item past p: e.g., when lim is 5 we change base + * to item 3 and make lim 2 so that we will look at items 3 and 4. + * If lim is even, however, we have to shrink it by one before +diff --git a/libiberty/configure b/libiberty/configure +--- a/libiberty/configure ++++ b/libiberty/configure +@@ -5967,7 +5967,7 @@ _ACEOF + + + +-# Check for presense of long long ++# Check for presence of long long + ac_fn_c_check_type "$LINENO" "long long" "ac_cv_type_long_long" "$ac_includes_default" + if test "x$ac_cv_type_long_long" = xyes; then : + +diff --git a/libiberty/configure.ac b/libiberty/configure.ac +--- a/libiberty/configure.ac ++++ b/libiberty/configure.ac +@@ -302,7 +302,7 @@ AC_CHECK_SIZEOF([int]) + AC_CHECK_SIZEOF([long]) + AC_CHECK_SIZEOF([size_t]) + +-# Check for presense of long long ++# Check for presence of long long + AC_CHECK_TYPE([long long], + [AC_DEFINE(HAVE_LONG_LONG, 1, [Define if you have the `long long' type.]) AC_CHECK_SIZEOF([long long])], + []) +diff --git a/libiberty/cp-demangle.c b/libiberty/cp-demangle.c +--- a/libiberty/cp-demangle.c ++++ b/libiberty/cp-demangle.c +@@ -82,7 +82,7 @@ + + IN_GLIBCPP_V3 + If defined, this file defines only __cxa_demangle() and +- __gcclibcxx_demangle_callback(), and no other publically visible ++ __gcclibcxx_demangle_callback(), and no other publicly visible + functions or variables. + + STANDALONE_DEMANGLER +@@ -5549,7 +5549,7 @@ d_print_comp_inner (struct d_print_info *dpi, int options, + const struct d_component_stack *dcse; + int found_self_or_parent = 0; + +- /* This traversal is reentering SUB as a substition. ++ /* This traversal is reentering SUB as a substitution. + If we are not beneath SUB or DC in the tree then we + need to restore SUB's template stack temporarily. */ + for (dcse = dpi->component_stack; dcse != NULL; +@@ -6341,7 +6341,7 @@ d_print_comp (struct d_print_info *dpi, int options, + dpi->recursion--; + } + +-/* Print a Java dentifier. For Java we try to handle encoded extended ++/* Print a Java identifier. For Java we try to handle encoded extended + Unicode characters. The C++ ABI doesn't mention Unicode encoding, + so we don't it for C++. Characters are encoded as + __U+_. */ +diff --git a/libiberty/d-demangle.c b/libiberty/d-demangle.c +--- a/libiberty/d-demangle.c ++++ b/libiberty/d-demangle.c +@@ -175,8 +175,33 @@ struct dlang_info + { + /* The string we are demangling. */ + const char *s; ++ ++ /* The options passed to the demangler. */ ++ int options; ++ + /* The index of the last back reference. */ + int last_backref; ++ ++ /* The total number of back referenced types, including nested back ++ referenced types. This is reset to zero each time a back ++ reference is processed at the "top level" (i.e. not a nested back ++ reference). ++ ++ Cases have been encountered where a back reference type includes ++ references to other back reference types, which include further ++ back references. This was observed to a depth of 57. If each ++ layer only references the layer before twice then the innermost ++ string will be duplicated 2^57 times! ++ ++ We count the number of nested back references and compare this to ++ the recursion limit, even though this isn't strictly recursion. */ ++ int num_backrefs; ++ ++ /* Track the depth of back references. Depth 0 is considered the ++ top level. When we encounter a back reference at this depth the ++ NUM_BACKREFS field is reset to 0. At greater depths, ++ NUM_BACKREFS will be incremented. */ ++ int backref_depth; + }; + + /* Pass as the LEN to dlang_parse_template if symbol length is not known. */ +@@ -411,6 +436,24 @@ dlang_type_backref (string *decl, const char *mangled, struct dlang_info *info, + if (mangled - info->s >= info->last_backref) + return NULL; + ++ /* A back reference might point to a type that itself contains back ++ references, which might themselves contain back references. ++ There might not be recursion going on, but we can run into ++ problems of exponential growth caused by the inner type appearing ++ to be referenced 10s of millions of times. */ ++ if (info->backref_depth > 0) ++ { ++ if ((info->options & DMGL_NO_RECURSE_LIMIT) == 0 ++ && info->num_backrefs > DEMANGLE_RECURSION_LIMIT) ++ return NULL; ++ ++ info->num_backrefs++; ++ } ++ else ++ info->num_backrefs = 0; ++ ++ info->backref_depth++; ++ + int save_refpos = info->last_backref; + info->last_backref = mangled - info->s; + +@@ -424,6 +467,7 @@ dlang_type_backref (string *decl, const char *mangled, struct dlang_info *info, + backref = dlang_type (decl, backref, info); + + info->last_backref = save_refpos; ++ info->backref_depth--; + + if (backref == NULL) + return NULL; +@@ -1067,7 +1111,7 @@ dlang_identifier (string *decl, const char *mangled, struct dlang_info *info) + } + + /* Extract the plain identifier from MANGLED and prepend/append it to DECL +- with special treatment for some magic compiler generted symbols. ++ with special treatment for some magic compiler generated symbols. + Return the remaining string on success or NULL on failure. */ + static const char * + dlang_lname (string *decl, const char *mangled, unsigned long len) +@@ -1931,17 +1975,20 @@ dlang_parse_template (string *decl, const char *mangled, + /* Initialize the information structure we use to pass around information. */ + static void + dlang_demangle_init_info (const char *mangled, int last_backref, +- struct dlang_info *info) ++ int options, struct dlang_info *info) + { + info->s = mangled; ++ info->options = options; + info->last_backref = last_backref; ++ info->num_backrefs = 0; ++ info->backref_depth = 0; + } + + /* Extract and demangle the symbol in MANGLED. Returns the demangled + signature on success or NULL on failure. */ + + char * +-dlang_demangle (const char *mangled, int option ATTRIBUTE_UNUSED) ++dlang_demangle (const char *mangled, int options) + { + string decl; + char *demangled = NULL; +@@ -1962,7 +2009,7 @@ dlang_demangle (const char *mangled, int option ATTRIBUTE_UNUSED) + { + struct dlang_info info; + +- dlang_demangle_init_info (mangled, strlen (mangled), &info); ++ dlang_demangle_init_info (mangled, strlen (mangled), options, &info); + mangled = dlang_parse_mangle (&decl, mangled, &info); + + /* Check that the entire symbol was successfully demangled. */ +diff --git a/libiberty/dyn-string.c b/libiberty/dyn-string.c +--- a/libiberty/dyn-string.c ++++ b/libiberty/dyn-string.c +@@ -49,8 +49,8 @@ Boston, MA 02110-1301, USA. */ + function can be used with a dyn_string struct on the stack or + embedded in another object. The contents of the string itself + are still dynamically allocated. The string initially is capable +- of holding at least SPACE characeters, including the terminating +- NUL. If SPACE is 0, it will silently be increated to 1. ++ of holding at least SPACE characters, including the terminating ++ NUL. If SPACE is 0, it will silently be increased to 1. + + If RETURN_ON_ALLOCATION_FAILURE is defined and memory allocation + fails, returns 0. Otherwise returns 1. */ +diff --git a/libiberty/ldirname.c b/libiberty/ldirname.c +--- a/libiberty/ldirname.c ++++ b/libiberty/ldirname.c +@@ -26,7 +26,7 @@ Given a pointer to a string containing a typical pathname + (@samp{/usr/src/cmd/ls/ls.c} for example), returns a string containing the + passed string up to, but not including, the final directory separator. + +-If the given pathname doesn't contain a directory separator then this funtion ++If the given pathname doesn't contain a directory separator then this function + returns the empty string; this includes an empty given pathname. @code{NULL} + is returned on memory allocation error. + +diff --git a/libiberty/make-relative-prefix.c b/libiberty/make-relative-prefix.c +--- a/libiberty/make-relative-prefix.c ++++ b/libiberty/make-relative-prefix.c +@@ -410,7 +410,7 @@ make_relative_prefix_1 (const char *progname, const char *bin_prefix, + /* Do the full job, including symlink resolution. + This path will find files installed in the same place as the + program even when a soft link has been made to the program +- from somwhere else. */ ++ from somewhere else. */ + + char * + make_relative_prefix (const char *progname, const char *bin_prefix, +diff --git a/libiberty/obstacks.texi b/libiberty/obstacks.texi +--- a/libiberty/obstacks.texi ++++ b/libiberty/obstacks.texi +@@ -695,7 +695,7 @@ Initialize use of an obstack, with an initial chunk of + @var{chunk_size} bytes. + + @item int obstack_specify_allocation (struct obstack *@var{obstack-ptr}, size_t chunk_size, size_t alignment, void *(*chunkfun) (size_t), void (*freefun) (void *)) +-Initialize use of an obstack, specifying intial chunk size, chunk ++Initialize use of an obstack, specifying initial chunk size, chunk + alignment, and memory allocation functions. + + @item int obstack_specify_allocation_with_arg (struct obstack *@var{obstack-ptr}, size_t chunk_size, size_t alignment, void *(*chunkfun) (void *, size_t), void (*freefun) (void *, void *), void *arg) +diff --git a/libiberty/pex-win32.c b/libiberty/pex-win32.c +--- a/libiberty/pex-win32.c ++++ b/libiberty/pex-win32.c +@@ -350,7 +350,7 @@ argv_to_cmdline (char *const *argv) + /* We only quote arguments that contain spaces, \t or " characters to + prevent wasting 2 chars per argument of the CreateProcess 32k char + limit. We need only escape embedded double-quotes and immediately +- preceeding backslash characters. A sequence of backslach characters ++ preceding backslash characters. A sequence of backslach characters + that is not followed by a double quote character will not be + escaped. */ + needs_quotes = 0; +@@ -363,7 +363,7 @@ argv_to_cmdline (char *const *argv) + + if (argv[i][j] == '"') + { +- /* Escape preceeding backslashes. */ ++ /* Escape preceding backslashes. */ + for (k = j - 1; k >= 0 && argv[i][k] == '\\'; k--) + cmdline_len++; + /* Escape the quote character. */ +@@ -695,7 +695,7 @@ spawn_script (struct pex_obj *obj, + int fd = _open (executable, _O_RDONLY); + + /* Try to open script, check header format, extract interpreter path, +- and spawn script using that interpretter. */ ++ and spawn script using that interpreter. */ + if (fd >= 0) + { + char buf[MAX_PATH + 5]; +diff --git a/libiberty/random.c b/libiberty/random.c +--- a/libiberty/random.c ++++ b/libiberty/random.c +@@ -88,7 +88,7 @@ long int random (void); + then initialized to contain information for random number generation with + that much state information. Good sizes for the amount of state + information are 32, 64, 128, and 256 bytes. The state can be switched by +- calling the setstate() function with the same array as was initiallized ++ calling the setstate() function with the same array as was initialized + with initstate(). By default, the package runs with 128 bytes of state + information and generates far better random numbers than a linear + congruential generator. If the amount of state information is less than +@@ -362,7 +362,7 @@ setstate (void *arg_state) + + /* If we are using the trivial TYPE_0 R.N.G., just do the old linear + congruential bit. Otherwise, we do our fancy trinomial stuff, which is the +- same in all ther other cases due to all the global variables that have been ++ same in all the other cases due to all the global variables that have been + set up. The basic operation is to add the number at the rear pointer into + the one at the front pointer. Then both pointers are advanced to the next + location cyclically in the table. The value returned is the sum generated, +diff --git a/libiberty/regex.c b/libiberty/regex.c +--- a/libiberty/regex.c ++++ b/libiberty/regex.c +@@ -58,7 +58,7 @@ + + # define WIDE_CHAR_SUPPORT (HAVE_WCTYPE_H && HAVE_WCHAR_H && HAVE_BTOWC) + +-/* For platform which support the ISO C amendement 1 functionality we ++/* For platform which support the ISO C amendment 1 functionality we + support user defined character classes. */ + # if defined _LIBC || WIDE_CHAR_SUPPORT + /* Solaris 2.5 has a bug: must be included before . */ +@@ -1235,7 +1235,7 @@ printchar (int c) + + # ifdef WCHAR + /* This convert a multibyte string to a wide character string. +- And write their correspondances to offset_buffer(see below) ++ And write their correspondences to offset_buffer(see below) + and write whether each wchar_t is binary data to is_binary. + This assume invalid multibyte sequences as binary data. + We assume offset_buffer and is_binary is already allocated +@@ -1247,7 +1247,7 @@ static size_t convert_mbs_to_wcs (CHAR_T *dest, const unsigned char* src, + static size_t + convert_mbs_to_wcs (CHAR_T *dest, const unsigned char*src, size_t len, + int *offset_buffer, char *is_binary) +- /* It hold correspondances between src(char string) and ++ /* It hold correspondences between src(char string) and + dest(wchar_t string) for optimization. + e.g. src = "xxxyzz" + dest = {'X', 'Y', 'Z'} +@@ -1281,7 +1281,7 @@ convert_mbs_to_wcs (CHAR_T *dest, const unsigned char*src, size_t len, + + if (consumed <= 0) + /* failed to convert. maybe src contains binary data. +- So we consume 1 byte manualy. */ ++ So we consume 1 byte manually. */ + { + *pdest = *psrc; + consumed = 1; +@@ -2174,7 +2174,7 @@ typedef struct + # ifndef DEFINED_ONCE + # if defined _LIBC || WIDE_CHAR_SUPPORT + /* The GNU C library provides support for user-defined character classes +- and the functions from ISO C amendement 1. */ ++ and the functions from ISO C amendment 1. */ + # ifdef CHARCLASS_NAME_MAX + # define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX + # else +@@ -3416,7 +3416,7 @@ PREFIX(regex_compile) (const char *ARG_PREFIX(pattern), + int32_t idx2 = table[ch]; + size_t len = weights[idx2]; + +- /* Test whether the lenghts match. */ ++ /* Test whether the lengths match. */ + if (weights[idx] == len) + { + /* They do. New compare the bytes of +@@ -4389,7 +4389,7 @@ wcs_compile_range (CHAR_T range_start_char, const CHAR_T **p_ptr, + { + /* range_start is a collating symbol. */ + int32_t *wextra; +- /* Retreive the index and get collation sequence value. */ ++ /* Retrieve the index and get collation sequence value. */ + wextra = (int32_t*)(extra + char_set[-range_start_char]); + start_val = wextra[1 + *wextra]; + } +@@ -5502,7 +5502,7 @@ count_mbs_length(int *offset_buffer, int length) + return 0; + + /* If there are no multibyte character, offset_buffer[i] == i. +- Optmize for this case. */ ++ Optimize for this case. */ + if (offset_buffer[length] == length) + return length; + +@@ -5538,7 +5538,7 @@ wcs_re_match_2_internal (struct re_pattern_buffer *bufp, + struct re_registers *regs, + int stop, + /* string1 == string2 == NULL means string1/2, size1/2 and +- mbs_offset1/2 need seting up in this function. */ ++ mbs_offset1/2 need setting up in this function. */ + /* We need wchar_t* buffers correspond to cstring1, cstring2. */ + wchar_t *string1, int size1, + wchar_t *string2, int size2, +@@ -5739,7 +5739,7 @@ byte_re_match_2_internal (struct re_pattern_buffer *bufp, + fill them with converted string. */ + if (string1 == NULL && string2 == NULL) + { +- /* We need seting up buffers here. */ ++ /* We need setting up buffers here. */ + + /* We must free wcs buffers in this function. */ + cant_free_wcs_buf = 0; +diff --git a/libiberty/sigsetmask.c b/libiberty/sigsetmask.c +--- a/libiberty/sigsetmask.c ++++ b/libiberty/sigsetmask.c +@@ -1,7 +1,7 @@ + /* Version of sigsetmask.c + Written by Steve Chamberlain (sac@cygnus.com). + Contributed by Cygnus Support. +- This file is in the public doamin. */ ++ This file is in the public domain. */ + + /* + +diff --git a/libiberty/simple-object-elf.c b/libiberty/simple-object-elf.c +--- a/libiberty/simple-object-elf.c ++++ b/libiberty/simple-object-elf.c +@@ -198,7 +198,7 @@ typedef struct { + #define SHT_RELA 4 /* Relocation entries with addends */ + #define SHT_REL 9 /* Relocation entries, no addends */ + #define SHT_GROUP 17 /* Section contains a section group */ +-#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ ++#define SHT_SYMTAB_SHNDX 18 /* Extended section indices */ + + /* Values for sh_flags field. */ + +@@ -246,7 +246,7 @@ typedef struct + #define STB_WEAK 2 /* Weak global */ + + #define STV_DEFAULT 0 /* Visibility is specified by binding type */ +-#define STV_HIDDEN 2 /* Can only be seen inside currect component */ ++#define STV_HIDDEN 2 /* Can only be seen inside current component */ + + /* Functions to fetch and store different ELF types, depending on the + endianness and size. */ +@@ -1512,7 +1512,7 @@ simple_object_elf_copy_lto_debug_sections (simple_object_read *sobj, + of __gnu_lto_slim symbol. */ + if (st_shndx == SHN_COMMON) + discard = 1; +- /* We also need to remove symbols refering to sections ++ /* We also need to remove symbols referring to sections + we'll eventually remove as with fat LTO objects + we otherwise get duplicate symbols at final link + (with GNU ld, gold is fine and ignores symbols in +diff --git a/libiberty/simple-object-mach-o.c b/libiberty/simple-object-mach-o.c +--- a/libiberty/simple-object-mach-o.c ++++ b/libiberty/simple-object-mach-o.c +@@ -53,7 +53,7 @@ struct mach_o_header_32 + unsigned char filetype[4]; /* Type of file. */ + unsigned char ncmds[4]; /* Number of load commands. */ + unsigned char sizeofcmds[4]; /* Total size of load commands. */ +- unsigned char flags[4]; /* Flags for special featues. */ ++ unsigned char flags[4]; /* Flags for special features. */ + }; + + /* Mach-O header (64-bit version). */ +@@ -66,7 +66,7 @@ struct mach_o_header_64 + unsigned char filetype[4]; /* Type of file. */ + unsigned char ncmds[4]; /* Number of load commands. */ + unsigned char sizeofcmds[4]; /* Total size of load commands. */ +- unsigned char flags[4]; /* Flags for special featues. */ ++ unsigned char flags[4]; /* Flags for special features. */ + unsigned char reserved[4]; /* Reserved. Duh. */ + }; + +@@ -1204,8 +1204,8 @@ simple_object_mach_o_write_segment (simple_object_write *sobj, int descriptor, + unsigned int i; + + /* Write the section header for the wrapper. */ +- /* Account for any initial aligment - which becomes the alignment for this +- created section. */ ++ /* Account for any initial alignment - which becomes the alignment for ++ this created section. */ + + secsize = (offset - index[0]); + if (!simple_object_mach_o_write_section_header (sobj, descriptor, +@@ -1218,7 +1218,7 @@ simple_object_mach_o_write_segment (simple_object_write *sobj, int descriptor, + errmsg, err)) + return 0; + +- /* Subtract the wrapper section start from the begining of each sub ++ /* Subtract the wrapper section start from the beginning of each sub + section. */ + + for (i = 1; i < nsects_in; ++i) +diff --git a/libiberty/strsignal.c b/libiberty/strsignal.c +--- a/libiberty/strsignal.c ++++ b/libiberty/strsignal.c +@@ -152,7 +152,7 @@ static const struct signal_info signal_table[] = + #endif + #if defined (SIGIO) + /* "I/O pending" has also been suggested, but is misleading since the +- signal only happens when the process has asked for it, not everytime ++ signal only happens when the process has asked for it, not every time + I/O is pending. */ + ENTRY(SIGIO, "SIGIO", "I/O possible"), + #endif +@@ -208,7 +208,7 @@ static const struct signal_info signal_table[] = + ENTRY(SIGGRANT, "SIGGRANT", "Monitor mode granted"), + #endif + #if defined (SIGRETRACT) +- ENTRY(SIGRETRACT, "SIGRETRACT", "Need to relinguish monitor mode"), ++ ENTRY(SIGRETRACT, "SIGRETRACT", "Need to relinquish monitor mode"), + #endif + #if defined (SIGMSG) + ENTRY(SIGMSG, "SIGMSG", "Monitor mode data available"), +diff --git a/libiberty/testsuite/d-demangle-expected b/libiberty/testsuite/d-demangle-expected +--- a/libiberty/testsuite/d-demangle-expected ++++ b/libiberty/testsuite/d-demangle-expected +@@ -1475,3 +1475,15 @@ demangle.anonymous.foo + --format=auto + _D8demangle9anonymous03fooZ + demangle.anonymous.foo ++# ++# This symbol was seen in the wild. It has a pattern of back ++# references that result in exponential growth, with the innermost ++# type being referenced 10s of millions of times. We use the ++# demangler's recursion limit to place a hard cap on back reference ++# usage, which causes the demangler to safely bail out before ++# consuming an excessive amount of memory. Trying to demangle this ++# without the recursion limit in place will eventually cause the OOM ++# killer to kick in. ++--format=dlang ++_D3std6format5write__T14formattedWriteTDFAxaZvTaTS2ae5utils4text7functor__T13stringifiableTSQBqQBqQBi10primitives__TQCaSQCsQCsQCk11composition__T3seqTSQDxQDxQDpQCh__TQDySQEqQEqQEnQEl__T17formattingFunctorVAyaa2_2573Vii250TQrZ15__lambda_L40_C3TQBmZ7FunctorTSQHzQHzQHrQGj__TQIaSQIsQIsQIpQIn__TQEcVQDma2_2573Vii250TSQKdQKdQKaQJy__TQJtTSQKxQKxQKpQJh__TQKyS4btdu2ui6curses6Curses4Wand__T8withAttrTSQNfQNfQNcQNa__TQMvTSQNzQNzQNrQMj__TQOaSQOsQOsQOkQMa__T6selectTSQPqQPqQPiQOa__TQPrSQQjQQjQQbQOt__T12valueFunctorTbZQrFbZ17__lambda_L143_C57TbZQLbTSQSvQSvQSnQRf__TQSwSQToQToQTlQTj__T5fmtIfVQOlnTbTPFNaNbNiNfZQPdTSQVhQVhQUzQTr__TQViSQKkQKi7browser7Browser6updateMFZ__T17__lambda_L1185_C4TQSeTSQYiQYiQYfQYd__TQXyTSQZcQZcQYuQXm__TQZdSQZvQZvQZnQXd__TQLdTQKzTSQBAtQBAuQBAnQZg__TQBAxSQBBqQBBrQBBpQBBo__TQIgVQWonTbTSQBCvQBCwQBCpQBBi__TQBDaSQBDtQBDuQBDnQBCg__TQNoTSQBErQBEsQBEqQBEp__TQBElTSQBFqQBFrQBFkQBEd__TQBFvSQBGoQBGpQBGiQBDz__TQBDqTSQBHnQBHoQBHhQBGa__TQBHsSQBIlQBImQBIkQBIj__TQBDzVQBDka2_2573Vii250TSQBKcQBKdQBKbQBKa__TQBJwTSQBLbQBLcQBKvQBJo__TQBLgSQBAjQBAiQBAjQBAgQBAd__TQBAcTQBHcTSQBNhQBNiQBNgQBNf__TQBNbTSQBOgQBOhQBOaQBMt__TQBOlSQBDoQBDnQBDoQBDlQBDi__TQBDhTQBKhZQBDrMFEQBFcQBFbQBFcQBEzQBEw9AttributebKQBLzZ19__lambda_L503_C20_1TPSQBHlQBHkQBHlQBHiQBHfTQDfTbTSQBUdQBUeQBUcQBUb__TQBTxTSQBVcQBVdQBUwQBTp__TQBVhSQBWaQBWbQBVuQBTl__TQBTcTQBTcZ3funTQBTmZQBPyZQBWtFQCwZ13StringifiableZQBRcZQBXxFQKwZQBeTQBTkZQBMuMFQJdbQBTzQMwQBUgZ17__lambda_L503_C20TQIfTQKrTbTSQCBpQCBqQCBoQCBn__TQCBjTSQCCoQCCpQCCiQCBb__TQCCtSQCDmQCDnQCDgQCAx__TQCAoTQCAoTSQCEqQCErQCEkQCDd__TQCEvSQCFoQCFpQCFnQCFm__TQCBcVQCAna2_2573Vii250TQTyZ17__lambda_L40_C3_1TQUwZQCArTQCEpZQLnTQCEyTQEkTQCFhZQCBtZQCIoFQHfZQLvZQCClZQCJgFQZkZQMnZ17__lambda_L40_C3_3TQBBlZQCEcTQCIaTQBEpZQPdTQBEyTQCItTQBFiZQCFkZQCMfFQBHuZQPnZQBWuFQBJhZ19__lambda_L143_C57_1TQBKiZQCHoTSQCPjQCPkQCPdQCNw__TQCPoSQCQhQCQiQCQbQCOu__TQCAcTQBHeZQCAmFQBHoZ19__lambda_L143_C57_2TQBIpZQCLgZQBYxFNibQBQoQEeZQWnTQBRaZQCMkTSQCUfQCUgQCTzQCSs__TQCUkSQCVdQCVeQCVcQCVb__TQCBtVQCQcnTbTQBTpTQHgZQCCpFNibQBUgQHwZQBAfTQIfZQCQcZQBAtTQCImTQBXoTQEhZQCRaZQCXvFQBZyZQBBdZQCCaMFQCTqQCBmbSQCOjQCOiQCEbQCDx13ScrollContextPSQCPq5paths11BrowserPathZ20__lambda_L1195_C76_1TQBxZQCVqZQCJhFNibQCJbQCIqZQBGyTQCJpZQCWwTSQDErQDEsQDElQDDe__TQDEwSQDFpQDFqQDFoQDFn__TQCMfVQDAonTbTQCMeTQCLuZQCNcFNibQCMwQCMlZQBKtTQCMvZQDArZQBLiTQCTbTQCQbTQEkZQDBpZQDIkFQCVqZQBLsZQCXeMFQBTnbQCXfZ19__lambda_L503_C20_2TQBSlTQBUyTbTSQDLxQDLyQDLwQDLv__TQDLrTSQDMwQDMxQDMqQDLj__TQDNbSQDNuQDNvQDNoQDLf__TQDKwTSQDOtQDOuQDOnQDNg__TQDOySQDPrQDPsQDPqQDPp__TQDLfVQDKqa2_2573Vii250TQDEdZ17__lambda_L40_C3_7TQDFcZQDKwZQBVnTQEdZQDLkZQDSfFQGoZQBVmZQDMdZQDSyFQDJgZQBWgZ17__lambda_L40_C3_8TQDLeZQDNwZQBYnTQDRzTQDOcZQDOqZQDVlFQDVaZQBYtZQDYbFKQDXqMxAaQDXoZk ++_D3std6format5write__T14formattedWriteTDFAxaZvTaTS2ae5utils4text7functor__T13stringifiableTSQBqQBqQBi10primitives__TQCaSQCsQCsQCk11composition__T3seqTSQDxQDxQDpQCh__TQDySQEqQEqQEnQEl__T17formattingFunctorVAyaa2_2573Vii250TQrZ15__lambda_L40_C3TQBmZ7FunctorTSQHzQHzQHrQGj__TQIaSQIsQIsQIpQIn__TQEcVQDma2_2573Vii250TSQKdQKdQKaQJy__TQJtTSQKxQKxQKpQJh__TQKyS4btdu2ui6curses6Curses4Wand__T8withAttrTSQNfQNfQNcQNa__TQMvTSQNzQNzQNrQMj__TQOaSQOsQOsQOkQMa__T6selectTSQPqQPqQPiQOa__TQPrSQQjQQjQQbQOt__T12valueFunctorTbZQrFbZ17__lambda_L143_C57TbZQLbTSQSvQSvQSnQRf__TQSwSQToQToQTlQTj__T5fmtIfVQOlnTbTPFNaNbNiNfZQPdTSQVhQVhQUzQTr__TQViSQKkQKi7browser7Browser6updateMFZ__T17__lambda_L1185_C4TQSeTSQYiQYiQYfQYd__TQXyTSQZcQZcQYuQXm__TQZdSQZvQZvQZnQXd__TQLdTQKzTSQBAtQBAuQBAnQZg__TQBAxSQBBqQBBrQBBpQBBo__TQIgVQWonTbTSQBCvQBCwQBCpQBBi__TQBDaSQBDtQBDuQBDnQBCg__TQNoTSQBErQBEsQBEqQBEp__TQBElTSQBFqQBFrQBFkQBEd__TQBFvSQBGoQBGpQBGiQBDz__TQBDqTSQBHnQBHoQBHhQBGa__TQBHsSQBIlQBImQBIkQBIj__TQBDzVQBDka2_2573Vii250TSQBKcQBKdQBKbQBKa__TQBJwTSQBLbQBLcQBKvQBJo__TQBLgSQBAjQBAiQBAjQBAgQBAd__TQBAcTQBHcTSQBNhQBNiQBNgQBNf__TQBNbTSQBOgQBOhQBOaQBMt__TQBOlSQBDoQBDnQBDoQBDlQBDi__TQBDhTQBKhZQBDrMFEQBFcQBFbQBFcQBEzQBEw9AttributebKQBLzZ19__lambda_L503_C20_1TPSQBHlQBHkQBHlQBHiQBHfTQDfTbTSQBUdQBUeQBUcQBUb__TQBTxTSQBVcQBVdQBUwQBTp__TQBVhSQBWaQBWbQBVuQBTl__TQBTcTQBTcZ3funTQBTmZQBPyZQBWtFQCwZ13StringifiableZQBRcZQBXxFQKwZQBeTQBTkZQBMuMFQJdbQBTzQMwQBUgZ17__lambda_L503_C20TQIfTQKrTbTSQCBpQCBqQCBoQCBn__TQCBjTSQCCoQCCpQCCiQCBb__TQCCtSQCDmQCDnQCDgQCAx__TQCAoTQCAoTSQCEqQCErQCEkQCDd__TQCEvSQCFoQCFpQCFnQCFm__TQCBcVQCAna2_2573Vii250TQTyZ17__lambda_L40_C3_1TQUwZQCArTQCEpZQLnTQCEyTQEkTQCFhZQCBtZQCIoFQHfZQLvZQCClZQCJgFQZkZQMnZ17__lambda_L40_C3_3TQBBlZQCEcTQCIaTQBEpZQPdTQBEyTQCItTQBFiZQCFkZQCMfFQBHuZQPnZQBWuFQBJhZ19__lambda_L143_C57_1TQBKiZQCHoTSQCPjQCPkQCPdQCNw__TQCPoSQCQhQCQiQCQbQCOu__TQCAcTQBHeZQCAmFQBHoZ19__lambda_L143_C57_2TQBIpZQCLgZQBYxFNibQBQoQEeZQWnTQBRaZQCMkTSQCUfQCUgQCTzQCSs__TQCUkSQCVdQCVeQCVcQCVb__TQCBtVQCQcnTbTQBTpTQHgZQCCpFNibQBUgQHwZQBAfTQIfZQCQcZQBAtTQCImTQBXoTQEhZQCRaZQCXvFQBZyZQBBdZQCCaMFQCTqQCBmbSQCOjQCOiQCEbQCDx13ScrollContextPSQCPq5paths11BrowserPathZ20__lambda_L1195_C76_1TQBxZQCVqZQCJhFNibQCJbQCIqZQBGyTQCJpZQCWwTSQDErQDEsQDElQDDe__TQDEwSQDFpQDFqQDFoQDFn__TQCMfVQDAonTbTQCMeTQCLuZQCNcFNibQCMwQCMlZQBKtTQCMvZQDArZQBLiTQCTbTQCQbTQEkZQDBpZQDIkFQCVqZQBLsZQCXeMFQBTnbQCXfZ19__lambda_L503_C20_2TQBSlTQBUyTbTSQDLxQDLyQDLwQDLv__TQDLrTSQDMwQDMxQDMqQDLj__TQDNbSQDNuQDNvQDNoQDLf__TQDKwTSQDOtQDOuQDOnQDNg__TQDOySQDPrQDPsQDPqQDPp__TQDLfVQDKqa2_2573Vii250TQDEdZ17__lambda_L40_C3_7TQDFcZQDKwZQBVnTQEdZQDLkZQDSfFQGoZQBVmZQDMdZQDSyFQDJgZQBWgZ17__lambda_L40_C3_8TQDLeZQDNwZQBYnTQDRzTQDOcZQDOqZQDVlFQDVaZQBYtZQDYbFKQDXqMxAaQDXoZk diff --git a/gdb-fileio-test-fixes.patch b/gdb-fileio-test-fixes.patch new file mode 100644 index 0000000..045cb38 --- /dev/null +++ b/gdb-fileio-test-fixes.patch @@ -0,0 +1,89 @@ +From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 +From: Kevin Buettner +Date: Wed, 25 Feb 2026 20:02:22 -0700 +Subject: gdb-fileio-test-fixes.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 + +I'm experiencing intermittent FAILs in fileio.exp when running on (my) +CI: + + FAIL: gdb.base/fileio.exp: Open a file + FAIL: gdb.base/fileio.exp: Creating already existing file returns EEXIST + FAIL: gdb.base/fileio.exp: Open for write but no write permission returns EACCES + FAIL: gdb.base/fileio.exp: Writing to a file + ... + +The problem turned out to be the way the OUTDIR gets defined in fileio.c. +The path is passed down "naked" and turned into string by STRINGIFY macro. + +However, if the path happens to contain name of unrelated pre-existing +C macro, this macro gets expanded during the "stringification", resulting +in (likely) different path than used in fileio.exp and therefore causing +failures. + +For example, if the GDB is compiled and tested in directory + + /var/lib/jenkins/workspace/binutils-gdb/build/x86_64-linux-gnu + +then fileio.c is compiled with + + -DOUTDIR_=/var/lib/jenkins/workspace/binutils-gdb/build/x86_64-linux-gnu/gdb/testsuite/outputs/gdb.base/fileio + +But because there's also C macro named "linux" defined to 1, the resulting +OUTDIR is actually: + + /var/lib/jenkins/workspace/binutils-gdb/build/x86_64-1-gnu/gdb/testsuite/outputs/gdb.base/fileio + +This commit fixes this by defining the OUTDIR as string literal in first +place (similarly to how it was done prior commit cc91060) and updating +quote_for_host to handle strings that themselves contains quote ("). + +Tested on x86_64-linux by running all tests using quote_for_host with +both target board unix and host/target board local-remote-host-native. + +Approved-By: Tom Tromey + +diff --git a/gdb/testsuite/gdb.base/fileio.c b/gdb/testsuite/gdb.base/fileio.c +--- a/gdb/testsuite/gdb.base/fileio.c ++++ b/gdb/testsuite/gdb.base/fileio.c +@@ -73,10 +73,6 @@ static const char *strerrno (int err); + + #define STRING "Hello World" + +-#define STRINGIFY(s) STRINGIFY_(s) +-#define STRINGIFY_(s) #s +-#define OUTDIR STRINGIFY (OUTDIR_) +- + static void stop (void) {} + + /* A NULL string. We pass this to stat below instead of a NULL +diff --git a/gdb/testsuite/gdb.base/fileio.exp b/gdb/testsuite/gdb.base/fileio.exp +--- a/gdb/testsuite/gdb.base/fileio.exp ++++ b/gdb/testsuite/gdb.base/fileio.exp +@@ -27,7 +27,7 @@ if {[is_remote host]} { + + if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" \ + executable \ +- [list debug additional_flags=[quote_for_host -DOUTDIR_=$outdir/]]] != "" } { ++ [list debug additional_flags=[quote_for_host -DOUTDIR=\"$outdir/\"]]] != "" } { + untested "failed to compile" + return -1 + } +diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp +--- a/gdb/testsuite/lib/gdb.exp ++++ b/gdb/testsuite/lib/gdb.exp +@@ -6150,9 +6150,9 @@ proc escape_for_host { str } { + proc quote_for_host { args } { + set str [join $args] + if { [is_remote host] } { +- set str [join [list {\"} $str {\"}] ""] ++ set str [join [list {\"} [regsub -all {"} $str {\\\"}] {\"}] ""] + } else { +- set str [join [list {"} $str {"}] ""] ++ set str [join [list {"} [regsub -all {"} $str {\"}] {"}] ""] + } + return $str + } diff --git a/gdb-fix-testsuite-newer-tcl.patch b/gdb-fix-testsuite-newer-tcl.patch new file mode 100644 index 0000000..5e0e16c --- /dev/null +++ b/gdb-fix-testsuite-newer-tcl.patch @@ -0,0 +1,46 @@ +From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 +From: Tom de Vries +Date: Fri, 20 Mar 2026 10:13:27 +0100 +Subject: gdb-fix-testsuite-newer-tcl.patch + +;; Fix use of deprecated trace variable subcommand +;; (Tom de Vries) + +With Tcl 9.0, we get: +... +bad option "variable": must be add, info, or remove + while executing +"trace variable "boards_dir" w append_gdb_boards_dir" + (file "lib/append_gdb_boards_dir.exp" line 48) +... + +The trace subcommand "trace variable " [1]: +- is equivalent to "trace add variable " +- is for backwards compatibility, +- uses "an older syntax in which array, read, write, unset are replaced by a, + r, w and u respectively", +- has an ops argument which is "not a list, but simply a string concatenation + of the operations", and +- is "deprecated and will likely be removed in a future version of Tcl". + +Fix this by using "trace add variable": +... +-trace variable "boards_dir" w append_gdb_boards_dir ++trace add variable "boards_dir" {write} append_gdb_boards_dir +... + +Approved-By: Tom Tromey + +Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33948 + +[1] https://www.tcl-lang.org/man/tcl8.6/TclCmd/trace.htm#M27 + +diff --git a/gdb/testsuite/lib/append_gdb_boards_dir.exp b/gdb/testsuite/lib/append_gdb_boards_dir.exp +--- a/gdb/testsuite/lib/append_gdb_boards_dir.exp ++++ b/gdb/testsuite/lib/append_gdb_boards_dir.exp +@@ -45,4 +45,4 @@ proc append_gdb_boards_dir { name1 name2 op } { + } + lappend boards_dir "${gdb_boards_dir}" + } +-trace variable "boards_dir" w append_gdb_boards_dir ++trace add variable "boards_dir" {write} append_gdb_boards_dir diff --git a/gdb-rhbz-818343-set-solib-absolute-prefix-testcase.patch b/gdb-rhbz-818343-set-solib-absolute-prefix-testcase.patch deleted file mode 100644 index 20e5f75..0000000 --- a/gdb-rhbz-818343-set-solib-absolute-prefix-testcase.patch +++ /dev/null @@ -1,83 +0,0 @@ -From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 -From: Fedora GDB patches -Date: Fri, 27 Oct 2017 21:07:50 +0200 -Subject: gdb-rhbz-818343-set-solib-absolute-prefix-testcase.patch - -;; Testcase for `Setting solib-absolute-prefix breaks vDSO' (BZ 818343). -;;=fedoratest - -diff --git a/gdb/testsuite/gdb.base/set-solib-absolute-prefix.c b/gdb/testsuite/gdb.base/set-solib-absolute-prefix.c -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.base/set-solib-absolute-prefix.c -@@ -0,0 +1,26 @@ -+/* Copyright (C) 2012 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 . */ -+ -+#include -+#include -+ -+int -+main (int argc, char *argv[]) -+{ -+ printf ("Hello, World.\n"); -+ abort (); -+} -diff --git a/gdb/testsuite/gdb.base/set-solib-absolute-prefix.exp b/gdb/testsuite/gdb.base/set-solib-absolute-prefix.exp -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.base/set-solib-absolute-prefix.exp -@@ -0,0 +1,39 @@ -+# Copyright 2012 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 . -+ -+set testfile "set-solib-absolute-prefix" -+set srcfile ${testfile}.c -+ -+# It is necessary to verify if the binary is 32-bit, so that the system -+# call `__kernel_vsyscall' originates from vDSO. -+ -+if { ![is_ilp32_target] } { -+ return -1 -+} -+ -+if { [prepare_for_testing $testfile.exp $testfile $srcfile] } { -+ return -1 -+} -+ -+if { ![runto_main] } { -+ return -1 -+} -+ -+gdb_test "continue" "Program received signal SIGABRT, Aborted.*" \ -+ "continue until abort" -+gdb_test "set solib-absolute-prefix /BOGUS_DIRECT" \ -+ ".*warning: Unable to find dynamic linker breakpoint function.*" \ -+ "set solib-absolute-prefix" -+gdb_test "bt" "__kernel_vsyscall.*" "backtrace with __kernel_vsyscall" diff --git a/gdb-rhbz1084404-ppc64-s390x-wrong-prologue-skip-O2-g-3of3.patch b/gdb-rhbz1084404-ppc64-s390x-wrong-prologue-skip-O2-g-3of3.patch deleted file mode 100644 index d92bfc3..0000000 --- a/gdb-rhbz1084404-ppc64-s390x-wrong-prologue-skip-O2-g-3of3.patch +++ /dev/null @@ -1,235 +0,0 @@ -From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 -From: Fedora GDB patches -Date: Fri, 27 Oct 2017 21:07:50 +0200 -Subject: gdb-rhbz1084404-ppc64-s390x-wrong-prologue-skip-O2-g-3of3.patch - -;; Fix '[ppc64] and [s390x] wrong prologue skip on -O2 -g code' (Jan -;; Kratochvil, RH BZ 1084404). -;;=fedoratest - -These testcases have been created by compiling glibc-2.17-78 on -RHEL-7.1 s390x/ppc64 boxes, and then taking the "select.o" file -present at $builddir/misc/select.o. - -diff --git a/gdb/testsuite/gdb.arch/ppc64-prologue-skip.exp b/gdb/testsuite/gdb.arch/ppc64-prologue-skip.exp -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.arch/ppc64-prologue-skip.exp -@@ -0,0 +1,34 @@ -+# Copyright 2015 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 . -+ -+if { ![istarget powerpc64-*linux-*] || ![is_lp64_target] } { -+ verbose "Skipping ppc64-prologue-skip.exp" -+ return -+} -+ -+set testfile "ppc64-prologue-skip" -+set uufile "${srcdir}/${subdir}/${testfile}.o.uu" -+set ofile "${srcdir}/${subdir}/${testfile}.o" -+ -+if { [catch "system \"uudecode -o ${ofile} ${uufile}\"" ] != 0 } { -+ untested "failed uudecode" -+ return -1 -+} -+ -+gdb_exit -+gdb_start -+gdb_load $ofile -+ -+gdb_test "break ___newselect_nocancel" "Breakpoint $decimal at 0xc: file ../sysdeps/unix/syscall-template.S, line 81." "breakpoint on ___newselect_nocancel" -diff --git a/gdb/testsuite/gdb.arch/ppc64-prologue-skip.o.uu b/gdb/testsuite/gdb.arch/ppc64-prologue-skip.o.uu -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.arch/ppc64-prologue-skip.o.uu -@@ -0,0 +1,70 @@ -+begin 644 ppc64-skip-prologue.o.uu -+M?T5,1@("`0`````````````!`!4````!```````````````````````````` -+M``-(``````!```````!``!0`$8%-B-`L"@``0,(`-#@``(Y$```"3.,`('P( -+M`J;X`0`0^"'_D4@```%@````Z`$`@#@A`'!\"`.F3H``(/@A_X%]*`*F^2$` -+MD/CA`-#XP0#(^*$`P/B!`+CX80"P2````6````#X80!PZ.$`T.C!`,CHH0#` -+MZ($`N.AA`+`X``".1````GP``";X80!X^`$`B.AA`'!(```!8````.DA`)#H -+M`0"(Z&$`>'TH`Z9\#_$@."$`@$SC`"!+__]@```````,($``````````O``( -+M7U]S96QE8W0```````````````````````````!6``(````Y!`'[#@T``0$! -+M`0````$```$N+B]S>7-D97!S+W5N:7@``'-Y"]S>7-C86QL+71E;7!L871E -+M+E,`+W)O;W0O9VQI8F,O9VQI8F,M,BXQ-RTW."YE;#$$!&PP!```` -+M`#`````8`````````+P`20YP$4%^1`X`009!0@Z``4(107Y2$49_20X`!D$& -+M1@``````+G-Y;71A8@`N`````````!(````$@````$`````````"``````````8```` -+M)@````$``````````P```````````````````1@````````````````````` -+M``````````$``````````````"P````(``````````,````````````````` -+M``$8```````````````````````````````!```````````````V`````0`` -+M```````#```````````````````!&``````````0```````````````````` -+M"```````````````,0````0`````````````````````````````"L`````` -+M````,````!(````%``````````@`````````&````#L````!```````````` -+M``````````````````$H```````````````````````````````!```````` -+M``````!0`````0`````````````````````````````!*`````````!:```` -+M`````````````````0``````````````2P````0````````````````````` -+M````````"O``````````&````!(````(``````````@`````````&````&$` -+M```!``````````````````````````````&"`````````),````````````` -+M```````!``````````````!<````!``````````````````````````````+ -+M"`````````!@````$@````H`````````"``````````8````;0````$````` -+M`````````````````````````A4`````````%`````````````````````$` -+M`````````````(`````!``````````````````````````````(P```````` -+M`#`````````````````````0``````````````![````!``````````````` -+M```````````````+:``````````P````$@````T`````````"``````````8 -+M````E`````$``````````@```````````````````F``````````2``````` -+M``````````````@``````````````(\````$```````````````````````` -+M``````N8`````````!@````2````#P`````````(`````````!@````1```` -+M`P`````````````````````````````"J`````````">```````````````` -+M`````0```````````````0````(`````````````````````````````"$@` -+M```````!L````!,````+``````````@`````````&`````D````#```````` -+M``````````````````````GX`````````'H````````````````````!```` -+M`````````````````````````````````````````````P```0`````````` -+M`````````````````P```P```````````````````````````P``!``````` -+M`````````````````````P``!0```````````````````````````P``"@`` -+M`````````````````````````P``#````````````````````````````P`` -+M"````````````````````````````P``#0`````````````````````````` -+M`P``#P```````````````````````````P``!P`````````````````````` -+M```!$@``!0```````````````````-@````*$@```0`````````,```````` -+M`#`````@$``````````````````````````````P$``````````````````` -+M``````````!*$`````````````````````````````!E(@``!0`````````` -+M`````````-@```!S(@``!0```````````````````-@`7U]S96QE8W0`7U]? -+M;F5W6YC8V%N8V5L`%]?;&EB8U]D:7-A8FQE7V%S>6YC8V%N8V5L`%]? -+M;&EB8U]S96QE8W0`. -+ -+if { ![istarget s390x-*linux-*] || ![is_lp64_target] } { -+ verbose "Skipping s390x-prologue-skip.exp" -+ return -+} -+ -+set testfile "s390x-prologue-skip" -+set uufile "${srcdir}/${subdir}/${testfile}.o.uu" -+set ofile "${srcdir}/${subdir}/${testfile}.o" -+ -+if { [catch "system \"uudecode -o ${ofile} ${uufile}\"" ] != 0 } { -+ untested "failed uudecode" -+ return -1 -+} -+ -+gdb_exit -+gdb_start -+gdb_load $ofile -+ -+gdb_test "break select" "Breakpoint $decimal at 0x48: file ../sysdeps/unix/syscall-template.S, line 81." "breakpoint on select" -diff --git a/gdb/testsuite/gdb.arch/s390x-prologue-skip.o.uu b/gdb/testsuite/gdb.arch/s390x-prologue-skip.o.uu -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.arch/s390x-prologue-skip.o.uu -@@ -0,0 +1,64 @@ -+begin 644 s390x-prologue-skip.o.uu -+M?T5,1@("`0`````````````!`!8````!```````````````````````````` -+M``+```````!```````!``!(`#^LE\!``).O?\&@`)+D$`.^G^_]@X^#P```D -+MP.4`````N00``NLE\+``!`J.N00`TKD$`"#`Y0````"Y!``MZ]_Q"``$I_0` -+M"L`0`````+\/$`"G=/_7"HZG2?`!N2$`),"T``````?^````5@`"````.0$! -+M^PX-``$!`0$````!```!+BXO7-C86QL+71E;7!L -+M871E+E,``0`````)`@```````````]```0)F$P("``$!````CP`"``````@! -+M```````````````````````````N+B]S>7-D97!S+W5N:7@OE(``7@. -+M`1L,#Z`!````````&````!P`````````1`!,CP6.!HT'2`[``@```!`````X -+M`````````"```````"YS>6UT86(`+G-T``````````&````!`````&``````````@````````` -+M&````%<````!``````````````````````````````$"`````````),````` -+M```````````````!``````````````!2````!``````````````````````` -+M```````)^`````````!@````$`````@`````````"``````````8````8P`` -+M``$``````````````````````````````94`````````%``````````````` -+M``````$``````````````'8````!``````````````````````````````&P -+M`````````#`````````````````````0``````````````!Q````!``````` -+M```````````````````````*6``````````P````$`````L`````````"``` -+M```````8````B@````$``````````@```````````````````>`````````` -+M2`````````````````````@``````````````(4````$```````````````` -+M``````````````J(`````````#`````0````#0`````````(`````````!@` -+M```1`````P`````````````````````````````"*`````````"4```````` -+M`````````````0```````````````0````(````````````````````````` -+M````!T`````````!L````!$````*``````````@`````````&`````D````# -+M``````````````````````````````CP`````````(X````````````````` -+M```!`````````````````````````````````````````````````P```0`` -+M`````````````````````````P```P```````````````````````````P`` -+M!````````````````````````````P``"``````````````````````````` -+M`P``"@```````````````````````````P``!@`````````````````````` -+M`````P``"P```````````````````````````P``#0`````````````````` -+M`````````P``!0`````````````````````````!$``````````````````` -+M```````````;$``````````````````````````````V$@```0````````!( -+M`````````"`````_$`````````````````````````````!7$@```0`````` -+M``!6`````````!````!I$`````````````````````````````!Y(@```0`` -+M``````!(`````````"````"'(@```0````````!(`````````"``7U]L:6)C -+M7V5N86)L95]A -Date: Fri, 27 Oct 2017 21:07:50 +0200 -Subject: gdb-rhbz1149205-catch-syscall-after-fork-test.patch - -;; Fix '`catch syscall' doesn't work for parent after `fork' is called' -;; (Philippe Waroquiers, RH BZ 1149205). -;;=fedoratest - -URL: -Message-ID: <1368136582.30058.7.camel@soleil> - - From: Philippe Waroquiers - To: gdb-patches at sourceware dot org - Subject: RFA: fix gdb_assert caused by 'catch signal ...' and fork - Date: Thu, 09 May 2013 23:56:22 +0200 - - The attached patch fixes a gdb_assert caused by the combination of catch - signal and fork: - break-catch-sig.c:152: internal-error: signal_catchpoint_remove_location: Assertion `signal_catch_counts[iter] > 0' failed. - - The problem is that the signal_catch_counts is decremented by detach_breakpoints. - The fix consists in not detaching breakpoint locations of type bp_loc_other. - The patch introduces a new test. - -Comments by Sergio Durigan Junior: - - I addded a specific testcase for this patch, which tests exactly the - issue that the customer is facing. This patch does not solve the - whole problem of catching a syscall and forking (for more details, - see , - specifically comment #3), but it solves the issue reported by the - customer. - - I also removed the original testcase of this patch, because it - relied on "catch signal", which is a command that is not implemented - in this version of GDB. - -commit bd9673a4ded96ea5c108601501c8e59003ea1be6 -Author: Philippe Waroquiers -Date: Tue May 21 18:47:05 2013 +0000 - - Fix internal error caused by interaction between catch signal and fork - -diff --git a/gdb/testsuite/gdb.base/gdb-rhbz1149205-catch-syscall-fork.c b/gdb/testsuite/gdb.base/gdb-rhbz1149205-catch-syscall-fork.c -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.base/gdb-rhbz1149205-catch-syscall-fork.c -@@ -0,0 +1,11 @@ -+#include -+#include -+ -+int -+main (int argc, char **argv) -+{ -+ if (fork () == 0) -+ sleep (1); -+ chdir ("."); -+ return 0; -+} -diff --git a/gdb/testsuite/gdb.base/gdb-rhbz1149205-catch-syscall-fork.exp b/gdb/testsuite/gdb.base/gdb-rhbz1149205-catch-syscall-fork.exp -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.base/gdb-rhbz1149205-catch-syscall-fork.exp -@@ -0,0 +1,58 @@ -+# Copyright 2015 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 . -+ -+if { [is_remote target] || ![isnative] } then { -+ continue -+} -+ -+set testfile "gdb-rhbz1149205-catch-syscall-fork" -+set srcfile ${testfile}.c -+set binfile [standard_output_file ${testfile}] -+ -+# Until "catch syscall" is implemented on other targets... -+if {![istarget "hppa*-hp-hpux*"] && ![istarget "*-linux*"]} then { -+ continue -+} -+ -+# This shall be updated whenever 'catch syscall' is implemented -+# on some architecture. -+#if { ![istarget "i\[34567\]86-*-linux*"] -+if { ![istarget "x86_64-*-linux*"] && ![istarget "i\[34567\]86-*-linux*"] -+ && ![istarget "powerpc-*-linux*"] && ![istarget "powerpc64-*-linux*"] -+ && ![istarget "sparc-*-linux*"] && ![istarget "sparc64-*-linux*"] } { -+ continue -+} -+ -+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } { -+ untested ${testfile}.exp -+ return -1 -+} -+ -+gdb_exit -+gdb_start -+gdb_reinitialize_dir $srcdir/$subdir -+gdb_load $binfile -+ -+if { ![runto_main] } { -+ return -1 -+} -+ -+gdb_test "catch syscall chdir" \ -+ "Catchpoint $decimal \\\(syscall (.)?chdir(.)? \\\[$decimal\\\]\\\)" \ -+ "catch syscall chdir" -+ -+gdb_test "continue" \ -+ "Continuing\.\r\n.*\r\nCatchpoint $decimal \\\(call to syscall .?chdir.?.*" \ -+ "continue from catch syscall after fork" diff --git a/gdb-rhbz2366461-bad-solib-entry-addr.patch b/gdb-rhbz2366461-bad-solib-entry-addr.patch new file mode 100644 index 0000000..836ab5f --- /dev/null +++ b/gdb-rhbz2366461-bad-solib-entry-addr.patch @@ -0,0 +1,453 @@ +From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 +From: Andrew Burgess +Date: Wed, 15 Apr 2026 10:43:31 +0100 +Subject: gdb-rhbz2366461-bad-solib-entry-addr.patch + +;; Backport upstream commit cd289df068e to address rhbz2366461. This +;; commit will drop out with GDB 18. + +gdb: don't use .text as default entry point section + +We got a Fedora GDB bug report that a user tried to debug an Appimage, +and GDB would reliably crash like this: + + (gdb) run + Starting program: /tmp/build/gdb/testsuite/outputs/gdb.base/solib-bad-entry-addr/solib-bad-entry-addr + ../../src/gdb/symfile.c:843: internal-error: sect_index_text not initialized + A problem internal to GDB has been detected, + further debugging may prove unreliable. + ----- Backtrace ----- + ... etc ... + +The specific AppImage being debugged can be found here, I've modified +the URL with a warning marker. I make no claims about whether it is +safe to download the image, and running it is definitely at your own +risk. If you wish to, delete the warning marker to download: + + https://github.com/Murmele/Gittyup//releases/download/gittyup_v1.4.0/Gittyup-1.4.0-x86_64.AppImage + +At the point of the above crash GDB's stack is: + + #9 0x000000000190c6ed in internal_error_loc (file=0x1e4a94e "../../src/gdb/symfile.c", line=838, fmt=0x1e4aa50 "sect_index_text not initialized") at ../../src/gdbsupport/errors.cc:57 + #10 0x0000000000f5f5ac in init_entry_point_info (objfile=0x5a98e80) at ../../src/gdb/symfile.c:838 + #11 0x0000000000f5f943 in syms_from_objfile (objfile=0x5a98e80, addrs=0x7ffd78728490, add_flags=...) at ../../src/gdb/symfile.c:962 + #12 0x0000000000f5fe6d in symbol_file_add_with_addrs (abfd=..., name=0x3e76e50 "/tmp/.mount_GittyujmIBkD/usr/bin/../../home/runner/work/Gittyup/Qt/5.15.2/gcc_64/lib/./libicudata.so.56", add_flags=..., addrs=0x7ffd78728490, flags=..., parent=0x0) at ../../src/gdb/symfile.c:1071 + #13 0x0000000000f601aa in symbol_file_add_from_bfd (abfd=..., name=0x3e76e50 "/tmp/.mount_GittyujmIBkD/usr/bin/../../home/runner/work/Gittyup/Qt/5.15.2/gcc_64/lib/./libicudata.so.56", add_flags=..., addrs=0x7ffd78728490, flags=..., parent=0x0) at ../../src/gdb/symfile.c:1145 + #14 0x0000000000f0f2ad in solib_read_symbols (so=..., flags=...) at ../../src/gdb/solib.c:627 + #15 0x0000000000f10263 in solib_add (pattern=0x0, from_tty=0, readsyms=1) at ../../src/gdb/solib.c:960 + +From this we can see GDB is trying to add the shared library: + + /tmp/.mount_GittyujmIBkD/usr/bin/../../home/runner/work/Gittyup/Qt/5.15.2/gcc_64/lib/./libicudata.so.56 + +The internal error is triggered from these lines in +init_entry_point_info: + + if (!found) + ei->the_bfd_section_index = SECT_OFF_TEXT (objfile); + +Where SECT_OFF_TEXT is: + + #define SECT_OFF_TEXT(objfile) \ + ((objfile->sect_index_text == -1) \ + ? (internal_error (_("sect_index_text not initialized")), -1) \ + : objfile->sect_index_text) + +So we can see that objfile::sect_index_text is -1, which leads to the +internal error. + +Looking at the 'readelf -Wa ...' output for the shared library in +question we see this: + + ELF Header: + Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 + Class: ELF64 + Data: 2's complement, little endian + Version: 1 (current) + OS/ABI: UNIX - System V + ABI Version: 0 + Type: DYN (Shared object file) + Machine: Advanced Micro Devices X86-64 + Version: 0x1 + Entry point address: 0x2d7 + Start of program headers: 25051136 (bytes into file) + Start of section headers: 25047552 (bytes into file) + Flags: 0x0 + Size of this header: 64 (bytes) + Size of program headers: 56 (bytes) + Number of program headers: 7 + Size of section headers: 64 (bytes) + Number of section headers: 11 + Section header string table index: 7 + + Section Headers: + [Nr] Name Type Address Off Size ES Flg Lk Inf Al + [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 + [ 1] .note.gnu.build-id NOTE 0000000000000190 000190 000024 00 A 0 0 4 + [ 2] .gnu.hash GNU_HASH 00000000000001b8 0001b8 000034 00 A 3 0 8 + [ 3] .dynsym DYNSYM 00000000000001f0 0001f0 000090 18 A 10 2 8 + [ 4] .rodata PROGBITS 00000000000002e0 0002e0 17e27d0 00 A 0 0 16 + [ 5] .eh_frame PROGBITS 00000000017e2ab0 17e2ab0 000000 00 A 0 0 8 + [ 6] .dynamic DYNAMIC 00000000019e2f10 17e2f10 0000f0 10 WA 10 0 8 + [ 7] .shstrtab STRTAB 0000000000000000 17e3000 000063 00 0 0 1 + [ 8] .symtab SYMTAB 0000000000000000 17e3068 000150 18 9 10 8 + [ 9] .strtab STRTAB 0000000000000000 17e31b8 000044 00 0 0 1 + [10] .dynstr STRTAB 00000000019e3188 17e4188 000420 00 A 0 0 8 + Key to Flags: + W (write), A (alloc), X (execute), M (merge), S (strings), I (info), + L (link order), O (extra OS processing required), G (group), T (TLS), + C (compressed), x (unknown), o (OS specific), E (exclude), + D (mbind), l (large), p (processor specific) + + There are no section groups in this file. + + Program Headers: + Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align + LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x17e2ab0 0x17e2ab0 R 0x200000 + GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10 + NOTE 0x000190 0x0000000000000190 0x0000000000000190 0x000024 0x000024 R 0x4 + LOAD 0x17e2f10 0x00000000019e2f10 0x00000000019e2f10 0x0000f0 0x0000f0 RW 0x200000 + DYNAMIC 0x17e2f10 0x00000000019e2f10 0x00000000019e2f10 0x0000f0 0x0000f0 RW 0x8 + GNU_RELRO 0x17e2f10 0x00000000019e2f10 0x00000000019e2f10 0x0000f0 0x0000f0 R 0x1 + LOAD 0x17e4000 0x00000000019e3000 0x00000000019e3000 0x0005a8 0x0005a8 RW 0x1000 + +Things to note here are: + + 1. There really is no .text section, or any executable sections, + + 2. there are 3 LOAD segments. This will be important later, and + + 3. the "Entry point address" is outside all sections, and is + non-zero. + +Next we can investigate where objfile::sect_index_text is set to +something other than -1. Starting in init_objfile_sect_indices, if +the objfile has a ".text" section then sect_index_text can be set. +This case clearly doesn't apply. + +Next symfile_find_segment_sections is called. This tries to match a +common case where we have either 1 or 2 LOAD segments, and assumes a +default distribution of sections to segments. However, we have 3 LOAD +segments, so these lines: + + if (data->segments.size () != 1 && data->segments.size () != 2) + return; + +result in an early return from symfile_find_segment_sections without +sect_index_text being set. + +Back in init_objfile_sect_indices, if no sections have an offset then +we set any currently unset sect_index_* values, including +sect_index_text, to point at section 0. However, in our case the +objfile is a relocatable shared library, so the sections will have an +offset, and so this final fallback case doesn't apply. + +The result is that init_objfile_sect_indices never sets +sect_index_text. This worries me a little as +init_objfile_sect_indices contains this comment: + + /* This is where things get really weird... We MUST have valid + indices for the various sect_index_* members or gdb will abort. + So if for example, there is no ".text" section, we have to + accommodate that. First, check for a file with the standard + one or two segments. */ + +Notice the emphasis on MUST in that comment, and indeed, we exit this +function without setting sect_index_text, and GDB does indeed abort. +The comment seems to imply that the following code is going to try to +figure out a suitable stand-in sect_index_text for when there is no +".text" section, but clearly I've run into a case that isn't covered. + +All of this code relating to setting sect_index_text was introduced in +commit: + + commit 31d99776c73d6fca13163da59c852b0fa99f89b8 + Date: Mon Jun 18 15:46:38 2007 +0000 + +Which unfortunately is from a time where we didn't write useful commit +messages, so to understand the commit you need to go read the mailing +list archive, but they don't offer much more insight: + + https://sourceware.org/pipermail/gdb-patches/2007-May/050527.html + +Clearly the comment in init_objfile_sect_indices would suggest that +the fix here is to figure out some "fake" value for sect_index_text, +and that would certainly avoid the problem here. But, at least for +this problem, I think there's maybe a better solution. + +The original internal error is triggered by a use of SECT_OFF_TEXT in +init_entry_point_info. We have an entry point address, we try to find +the section index for the section containing the entry point, and +failing that, we assume the entry point is in the text section. This +fall-back assumption means that, if the text section has an offset +applied, then the entry point will also have that same offset +applied. But it's not clear to me why picking the text section is +going to be any more valid than any other section, especially in a +case like this where we don't even have a text section, so the +sect_index_text might itself point to some other arbitrary section. + +Earlier in init_entry_point_info we already have a fall-back case +where we set entry_info::entry_point_p to false to indicate that the +objfile has no entry point, so this is always a possibility. So I +wondered about writing something like: + + if (!found) + { + if (objfile->sect_index_text != -1) + ei->the_bfd_section_index = SECT_OFF_TEXT (objfile); + else + ei->entry_point_p = false; + } + +If we have no text section index then we just claim the objfile has no +entry point. But I didn't like this for two reasons, first, the +comment back in init_objfile_sect_indices saying that the index should +be set, this seems to indicate that we should not be making decisions +later within GDB based on whether the index is set or not. + +And second, using the text section as a fall back, when the entry +address is outside every section, just seems off. So I wondered, why +not just reject the entry point completely in this case? Which is how +I ended up with: + + if (!found) + ei->entry_point_p = false; + +With this patch in place I was able to start debugging the AppImage +linked above. + +I created a simple test case which reproduces this issue. It's a +little contrived because it has to hit all the points required to +trigger this bug: + + 1. No .text section, + + 2. more than 2 LOAD segments, and + + 3. entry address outside every section. + +I have no idea what caused the original shared library to take on +these characteristics, it might even be a tool issue building the +original shared library. I haven't investigated this, as I don't +think it really matters, GDB shouldn't be crashing just because the +incoming objects are a little weird. + +I've attached a link to the Fedora bug in the 'Bug:' tag, but it's a +little confusing. An automated system has merged together two bug +reports. As such the overall bug report linked too is for a +completely different issue, only comments 21, 22, 23, and 24 relate to +the bug being fixed here. + +Bug: https://bugzilla.redhat.com/show_bug.cgi?id=2366461#c21 + +Approved-By: Tom Tromey + +diff --git a/gdb/symfile.c b/gdb/symfile.c +--- a/gdb/symfile.c ++++ b/gdb/symfile.c +@@ -822,7 +822,6 @@ init_entry_point_info (struct objfile *objfile) + if (ei->entry_point_p) + { + CORE_ADDR entry_point = ei->entry_point; +- int found; + + /* Make certain that the address points at real code, and not a + function descriptor. */ +@@ -834,7 +833,7 @@ init_entry_point_info (struct objfile *objfile) + ei->entry_point + = gdbarch_addr_bits_remove (objfile->arch (), entry_point); + +- found = 0; ++ bool found = false; + for (obj_section &osect : objfile->sections ()) + { + struct bfd_section *sect = osect.the_bfd_section; +@@ -845,13 +844,17 @@ init_entry_point_info (struct objfile *objfile) + { + ei->the_bfd_section_index + = gdb_bfd_section_index (objfile->obfd.get (), sect); +- found = 1; ++ found = true; + break; + } + } + ++ /* We store the section index so that the entry address can be ++ relocated when used. If the entry address is outside of any ++ section then we cannot relocate it. Just claim that there is no ++ entry address in this case. */ + if (!found) +- ei->the_bfd_section_index = SECT_OFF_TEXT (objfile); ++ ei->entry_point_p = false; + } + } + +diff --git a/gdb/testsuite/gdb.base/solib-bad-entry-addr-lib.s b/gdb/testsuite/gdb.base/solib-bad-entry-addr-lib.s +new file mode 100644 +--- /dev/null ++++ b/gdb/testsuite/gdb.base/solib-bad-entry-addr-lib.s +@@ -0,0 +1,33 @@ ++/* 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 . */ ++ ++ .section .rodata ++ .globl lib_var ++ .type lib_var, @object ++lib_var: ++ .long 42 ++ .size lib_var, .-lib_var ++ ++ /* This executable section exists solely to force the linker ++ to create an additional LOAD segment (with R+X permissions), ++ giving the library 3+ LOAD segments instead of the default 2. ++ ++ This matters because GDB's symfile_find_segment_sections only ++ runs for objects with exactly 1 or 2 segments. When it runs, ++ it sets sect_index_text from the first section in segment 1, ++ which masks the bug we are testing for, see the .exp file for ++ details. */ ++ .section .not_text, "ax", @progbits ++ .byte 0 +diff --git a/gdb/testsuite/gdb.base/solib-bad-entry-addr.c b/gdb/testsuite/gdb.base/solib-bad-entry-addr.c +new file mode 100644 +--- /dev/null ++++ b/gdb/testsuite/gdb.base/solib-bad-entry-addr.c +@@ -0,0 +1,22 @@ ++/* 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 . */ ++ ++extern int lib_var; ++ ++int ++main () ++{ ++ return lib_var; ++} +diff --git a/gdb/testsuite/gdb.base/solib-bad-entry-addr.exp b/gdb/testsuite/gdb.base/solib-bad-entry-addr.exp +new file mode 100644 +--- /dev/null ++++ b/gdb/testsuite/gdb.base/solib-bad-entry-addr.exp +@@ -0,0 +1,99 @@ ++# 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 . ++ ++# This test aims to exercise a specific situation which was seen ++# causing GDB to crash. An application has a shared library that ++# meets the following conditions: ++# ++# 1. No ".text" section, ++# 2. at least 3 LOAD segments, and ++# 3. a non-zero entry address that is outside of every section. ++# ++# When these 3 conditions are met then GDB would run into a problem in ++# init_entry_point_info for the shared library. The non-zero entry ++# address means GDB would try to find the section corresponding to the ++# address. As the address is outside of all sections then GDB would ++# try to use the .text section as a fall-back. But there is no text ++# section, so an internal error would be triggered. ++# ++# The 3 LOAD segments is important because, if there are only 1 or 2 ++# segments GDB has a default in symfile_find_segment_sections where ++# it assumes the text section is the first section in the first ++# segment, which means init_entry_point_info will have a .text section ++# to use. ++# ++# This test has a very simple assembler file (which hopefully contains ++# no architecture specific content), which will compile to a couple of ++# sections, but no .text section. These sections force the creation ++# of more than 2 LOAD segments. The compiler flags then set the entry ++# address to 0x1, which (we hope) is outside all sections. This ++# assembler file is compiled into a shared library. ++ ++require allow_shlib_tests ++require !use_gdb_stub ++require {istarget *-linux*} ++ ++standard_testfile .c -lib.s ++ ++# Build shared library. ++set lib_testfile ${testfile}-lib.so ++set lib_srcfile ${srcfile2} ++set lib_binfile [standard_output_file ${lib_testfile}] ++set lib_flags {shlib \ ++ additional_flags=-nostartfiles \ ++ additional_flags=-Wl,-e,0x1 \ ++ additional_flags=-Wl,-z,separate-code} ++if { [build_executable "build solib" $lib_testfile $lib_srcfile \ ++ $lib_flags] == -1 } { ++ return ++} ++ ++# Build the test executable. ++if { [build_executable "build exec" $testfile $srcfile \ ++ [list debug pie shlib=${lib_binfile}]] == -1 } { ++ return ++} ++ ++# Confirm we have more than 2 LOAD segments in the shared library. ++set readelf_program [gdb_find_readelf] ++set command "exec $readelf_program -Wl $lib_binfile" ++verbose -log "command is $command" ++set result [catch {{*}$command} output] ++verbose -log "result is $result" ++verbose -log "output is $output" ++if {$result != 0} { ++ fail "read program headers from $lib_testfile" ++ return ++} ++if {![regexp {\nProgram Headers:\n *Type [^\n]* Align\n(.*?)\n\n} $output trash phdr]} { ++ fail "no Program Headers found" ++ return ++} ++set load_segment_count 0 ++foreach line [regexp -line -all -inline {^ *LOAD .* 0x[0-9]+$} $phdr] { ++ incr load_segment_count ++} ++if { $load_segment_count <= 2 } { ++ fail "not enough LOAD segments" ++ return ++} ++ ++# Start GDB and run to main. If the shared library is causing issues ++# then we will see an internal error once the inferior starts running. ++clean_restart $testfile ++ ++if {![runto_main message]} { ++ return 0 ++} diff --git a/gdb-rhbz2366461-missing-thread.patch b/gdb-rhbz2366461-missing-thread.patch new file mode 100644 index 0000000..9dacf97 --- /dev/null +++ b/gdb-rhbz2366461-missing-thread.patch @@ -0,0 +1,499 @@ +From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 +From: Andrew Burgess +Date: Fri, 16 May 2025 17:56:58 +0100 +Subject: gdb-rhbz2366461-missing-thread.patch + +;; Backport upstream commit 8bd08ee92c4 to address rhbz2366461. This +;; commit will drop out with GDB 18. + +gdb: crash if thread unexpectedly disappears from thread list + +A bug was reported to Red Hat where GDB was crashing with an assertion +failure, the assertion message is: + + ../../gdb/regcache.c:432: internal-error: get_thread_regcache: Assertion `thread->state != THREAD_EXITED' failed. + +The backtrace for the crash is: + + #5 0x000055a21da8a880 in internal_vproblem(internal_problem *, const char *, int, const char *, typedef __va_list_tag __va_list_tag *) (problem=problem@entry=0x55a21e289060 , file=, line=, fmt=, ap=ap@entry=0x7ffec7576be0) at ../../gdb/utils.c:477 + #6 0x000055a21da8aadf in internal_verror (file=, line=, fmt=, ap=ap@entry=0x7ffec7576be0) at ../../gdb/utils.c:503 + #7 0x000055a21dcbd055 in internal_error_loc (file=file@entry=0x55a21dd33b71 "../../gdb/regcache.c", line=line@entry=432, fmt=) at ../../gdbsupport/errors.cc:57 + #8 0x000055a21d8baaa9 in get_thread_regcache (thread=thread@entry=0x55a258de3a50) at ../../gdb/regcache.c:432 + #9 0x000055a21d74fa18 in print_signal_received_reason (uiout=0x55a258b649b0, siggnal=GDB_SIGNAL_TRAP) at ../../gdb/infrun.c:9287 + #10 0x000055a21d7daad9 in mi_interp::on_signal_received (this=0x55a258af5f60, siggnal=GDB_SIGNAL_TRAP) at ../../gdb/mi/mi-interp.c:372 + #11 0x000055a21d76ef99 in interps_notify (method=&virtual table offset 88, this adjustment 974682) at ../../gdb/interps.c:369 + #12 0x000055a21d76e58f in interps_notify_signal_received (sig=, sig@entry=GDB_SIGNAL_TRAP) at ../../gdb/interps.c:378 + #13 0x000055a21d75074d in notify_signal_received (sig=GDB_SIGNAL_TRAP) at ../../gdb/infrun.c:6818 + #14 0x000055a21d755af0 in normal_stop () at ../../gdb/gdbthread.h:432 + #15 0x000055a21d768331 in fetch_inferior_event () at ../../gdb/infrun.c:4753 + +The user is using a build of GDB with 32-bit ARM support included, and +they gave the following description for what they were doing at the +time of the crash: + + Suspended the execution of the firmware in Eclipse. The gdb was + connected to JLinkGDBServer with activated FreeRTOS awareness JLink + plugin. + +So they are remote debugging with a non-gdbserver target. + +Looking in normal_stop() we see this code: + + /* As we're presenting a stop, and potentially removing breakpoints, + update the thread list so we can tell whether there are threads + running on the target. With target remote, for example, we can + only learn about new threads when we explicitly update the thread + list. Do this before notifying the interpreters about signal + stops, end of stepping ranges, etc., so that the "new thread" + output is emitted before e.g., "Program received signal FOO", + instead of after. */ + update_thread_list (); + + if (last.kind () == TARGET_WAITKIND_STOPPED && stopped_by_random_signal) + notify_signal_received (inferior_thread ()->stop_signal ()); + +Which accounts for the transition from frame #14 to frame #13. But it +is the update_thread_list() call which interests me. This call asks +the target (remote target in this case) for the current thread list, +and then marks threads exited based on the answer. + +And so, if a (badly behaved) target (incorrectly) removes a thread +from the thread list, then the update_thread_list() call will mark the +impacted thread as exited, even if GDB is currently handling a signal +stop event for that target. + +My guess for what's going on here then is this: + + 1. Thread receives a signal. + 2. Remote target sends GDB a stop with signal packet. + 3. Remote decides that the thread is going away soon, and marks the + thread as exited. + 4. GDB asks for the thread list. + 5. Remote sends back the thread list, which doesn't include the + event thread, as the remote things this thread has exited. + 6. GDB marks the thread as exited, and then proceeds to try and + print the signal stop event for the event thread. + 7. Printing the signal stop requires reading registers, which + requires a regache. We can only get a regcache for a non-exited + thread, and so GDB raises an assertion. + +Using the gdbreplay test frame work I was able to reproduce this +failure using gdbserver. I create an inferior with two threads, the +main thread sends a signal to the second thread, GDB sees the signal +arrive and prints this information for the user. + +Having captured the trace of this activity, I then find the thread +list reply in the log file, and modify it to remove the second thread. + +Now, when I replay the modified log file I see the same assertion +complaining about an attempt to get a regcache for an exited thread. + +I'm not entirely sure the best way to fix this. Clearly the problem +here is a bad remote target. But, replies from a remote target +should (in my opinion) not be considered trusted, as a consequence, we +should not be asserting based on data coming from a remote. Instead, +we should be giving warnings or errors and have GDB handle the bad +data as best it can. + +This is the second attempt to fix this issue, my first patch can be +seen here: + + https://inbox.sourceware.org/gdb-patches/062e438c8677e2ab28fac6183d2ea6d444cb9121.1747567717.git.aburgess@redhat.com + +In the first patch I was to checking in normal_stop, immediately after +the call to update_thread_list, to see if the current thread was now +marked as exited. However CI testing showed an issue with this +approach; I was already checking for many different TARGET_WAITKIND_* +kinds where the "is the current thread exited" question didn't make +sense, and it turns out that the list of kinds in my first attempt was +already insufficient. + +Rather than trying to just adding to the list, in this revised patch +I'm proposing to move the "is this thread exited" check inside the +block which handles signal stop events. + +Right now, the only part of normal_stop which I know relies on the +current thread not being exited is the call to notify_signal_received, +so before calling notify_signal_received I check to see if the current +thread is now exited. If it is then I print a warning to indicate +that the thread has unexpectedly exited and that the current +command (continue/step/etc) has been cancelled, I then change the +current event type to TARGET_WAITKIND_SPURIOUS. + +GDB's output now looks like this in all-stop mode: + + (gdb) continue + Continuing. + [New Thread 3483690.3483693] + [Thread 3483690.3483693 exited] + warning: Thread 3483690.3483693 unexpectedly exited after non-exit event + [Switching to Thread 3483690.3483693] + (gdb) + +The non-stop output is identical, except we don't switch thread (stop +events never trigger a thread switch in non-stop mode). + +The include test makes use of the gdbreplay framework, and tests in +all-stop and non-stop modes. I would like to do more extensive +testing of GDB's state after the receiving the unexpected thread list, +but due to using gdbreplay for testing, this is quite hard. Many +commands, especially those looking at thread state, are likely to +trigger additional packets being sent to the remote, which causes +gdbreplay to bail out as the new packet doesn't match the original +recorded state. However, I really don't think it is a good idea to +change gdbserver in order to "fake" this error case, so for now, using +gdbreplay is the best idea I have. + +Bug: https://bugzilla.redhat.com/show_bug.cgi?id=2366461 + +diff --git a/gdb/infrun.c b/gdb/infrun.c +--- a/gdb/infrun.c ++++ b/gdb/infrun.c +@@ -9562,7 +9562,38 @@ normal_stop () + update_thread_list (); + + if (last.kind () == TARGET_WAITKIND_STOPPED && stopped_by_random_signal) +- notify_signal_received (inferior_thread ()->stop_signal ()); ++ { ++ gdb_assert (inferior_ptid != null_ptid); ++ ++ /* Calling update_thread_list pulls information from the target. For ++ native targets we can be (reasonably) sure that the information we ++ get back is sane, but for remote targets, we cannot reply on the ++ returned thread list to be correct. ++ ++ Specifically, a remote target (not gdbserver), has been seen to ++ prematurely remove threads from the thread list after sending a ++ signal stop event. The consequence of this, is that the thread ++ might now be exited. This is bad as, trying to calling ++ notify_signal_received will cause GDB to read registers for the ++ current thread, but requesting the regcache for an exited thread ++ will trigger an assertion. ++ ++ Check for the exited thread case here, and convert the stop reason ++ to a spurious stop event. The thread exiting will have already ++ been reported (when the thread list was parsed), so making this a ++ spurious stop will cause GDB to drop back to the prompt. */ ++ if (inferior_thread ()->state != THREAD_EXITED) ++ notify_signal_received (inferior_thread ()->stop_signal ()); ++ else ++ { ++ warning (_("command aborted, %s unexpectedly exited after signal stop event"), ++ target_pid_to_str (inferior_thread ()->ptid).c_str ()); ++ ++ /* Mark this as a spurious stop. GDB will return to the ++ prompt. The warning above tells the user why. */ ++ last.set_spurious (); ++ } ++ } + + /* As with the notification of thread events, we want to delay + notifying the user that we've switched thread context until +diff --git a/gdb/testsuite/gdb.replay/missing-thread.c b/gdb/testsuite/gdb.replay/missing-thread.c +new file mode 100644 +--- /dev/null ++++ b/gdb/testsuite/gdb.replay/missing-thread.c +@@ -0,0 +1,61 @@ ++/* This testcase is part of GDB, the GNU debugger. ++ ++ 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 . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER; ++pthread_cond_t g_condvar = PTHREAD_COND_INITIALIZER; ++ ++void * ++worker_function (void *arg) ++{ ++ printf ("In worker, about to notify\n"); ++ pthread_cond_signal (&g_condvar); ++ ++ while (true) ++ sleep(1); ++ ++ return NULL; ++} ++ ++int ++main() ++{ ++ pthread_t my_thread; ++ ++ int result = pthread_create (&my_thread, NULL, worker_function, NULL); ++ assert (result == 0); ++ ++ pthread_mutex_lock (&g_mutex); ++ pthread_cond_wait (&g_condvar, &g_mutex); ++ ++ printf ("In main, have been woken.\n"); ++ pthread_mutex_unlock (&g_mutex); ++ ++ result = pthread_kill (my_thread, SIGTRAP); ++ assert (result == 0); ++ ++ result = pthread_join (my_thread, NULL); ++ assert (result == 0); ++ ++ return 0; ++} +diff --git a/gdb/testsuite/gdb.replay/missing-thread.exp b/gdb/testsuite/gdb.replay/missing-thread.exp +new file mode 100644 +--- /dev/null ++++ b/gdb/testsuite/gdb.replay/missing-thread.exp +@@ -0,0 +1,237 @@ ++# 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 . */ ++ ++# This test confirms how GDB handles a badly behaving remote target. The ++# remote target reports a stop event (signal delivery), then, as GDB is ++# processing the stop it syncs the thread list with the remote. ++# ++# The badly behaving remote target was dropping the signaled thread from the ++# thread list at this point, that is, the thread appeared to exit before an ++# exit event had been sent to (and seen by) GDB. ++# ++# At one point this was causing an assertion failed. GDB would try to ++# process the signal stop event, and to do this would try to read some ++# registers. Reading registers requires a regcache, and GDB will only ++# create a regcache for a non-exited thread. ++ ++load_lib gdbserver-support.exp ++load_lib gdbreplay-support.exp ++ ++require allow_gdbserver_tests ++require has_gdbreplay ++ ++standard_testfile ++ ++if { [build_executable "failed to build exec" $testfile $srcfile {debug pthreads}] } { ++ return -1 ++} ++ ++# Start the inferior and record a remote log for our interaction with it. ++# All we do is start the inferior and wait for thread 2 to receive a signal. ++# Check that GDB correctly shows the signal as received. LOG_FILENAME is ++# where we should write the remote log. ++proc_with_prefix record_initial_logfile { log_filename } { ++ clean_restart $::testfile ++ ++ # Make sure we're disconnected, in case we're testing with an ++ # extended-remote board, therefore already connected. ++ gdb_test "disconnect" ".*" ++ ++ gdb_test_no_output "set sysroot" \ ++ "setting sysroot before starting gdbserver" ++ ++ # Start gdbserver like: ++ # gdbserver :PORT .... ++ set res [gdbserver_start "" $::binfile] ++ set gdbserver_protocol [lindex $res 0] ++ set gdbserver_gdbport [lindex $res 1] ++ ++ gdb_test_no_output "set remotelogfile $log_filename" \ ++ "setup remotelogfile" ++ ++ # Connect to gdbserver. ++ if {![gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport] == 0} { ++ unsupported "$testfile (couldn't start gdbserver)" ++ return ++ } ++ ++ gdb_breakpoint main ++ gdb_continue_to_breakpoint "continuing to main" ++ ++ gdb_test "continue" \ ++ "Thread $::decimal \[^\r\n\]+ received signal SIGTRAP, .*" ++ ++ gdb_test "disconnect" ".*" \ ++ "disconnect after seeing signal" ++} ++ ++# Copy the remote log from IN_FILENAME to OUT_FILENAME, but modify one ++# particular line. ++# ++# The line to be modified is the last ... line, this is ++# the reply from the remote that indicates the thread list. It is expected ++# that the thread list will contain two threads. ++# ++# Whn DROP_BOTH is true then both threads will be removed from the modified ++# line. Otherwise, only the second thread is removed. ++proc update_replay_log { in_filename out_filename drop_both } { ++ # Read IN_FILENAME into a list. ++ set fd [open $in_filename] ++ set data [read $fd] ++ close $fd ++ set lines [split $data "\n"] ++ ++ # Find the last line in LINES that contains the list. ++ set idx -1 ++ for { set i 0 } { $i < [llength $lines] } { incr i } { ++ if { [regexp "^r.*.*" [lindex $lines $i]] } { ++ set idx $i ++ } ++ } ++ ++ # Modify the line by dropping the second thread. This does assume ++ # the thread order as seen in the ... list, but ++ # this seems stable for now. ++ set line [lindex $lines $idx] ++ set fixed_log false ++ if {[regexp "^(r .*\\\\n)(\\\\n)(\\\\n)(.*)$" $line \ ++ match part1 part2 part3 part4]} { ++ if { $drop_both } { ++ set line $part1$part4 ++ } else { ++ set line $part1$part2$part4 ++ } ++ set lines [lreplace $lines $idx $idx $line] ++ set fixed_log true ++ } ++ ++ # Write all the lines to OUT_FILENAME ++ set fd [open $out_filename "w"] ++ foreach l $lines { ++ puts $fd $l ++ } ++ close $fd ++ ++ # Did we manage to update the log file? ++ return $fixed_log ++} ++ ++# Replay the test process using REMOTE_LOG as the logfile to replay. If ++# EXPECT_ERROR is true then after the final 'continue' we expect GDB to give ++# an error as the required thread is missing. When EXPECT_ERROR is false ++# then we expect the test to complete as normal. NON_STOP is eithe 'on' or ++# 'off' and indicates GDBs non-stop mode. ++proc_with_prefix replay_with_log { remote_log expect_error non_stop } { ++ clean_restart $::testfile ++ ++ # Make sure we're disconnected, in case we're testing with an ++ # extended-remote board, therefore already connected. ++ gdb_test "disconnect" ".*" ++ ++ gdb_test_no_output "set sysroot" ++ ++ set res [gdbreplay_start $remote_log] ++ set gdbserver_protocol [lindex $res 0] ++ set gdbserver_gdbport [lindex $res 1] ++ ++ # Connect to gdbserver. ++ if {![gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport] == 0} { ++ fail "couldn't connect to gdbreplay" ++ return ++ } ++ ++ gdb_breakpoint main ++ gdb_continue_to_breakpoint "continuing to main" ++ ++ if { $expect_error } { ++ set expected_output \ ++ [list \ ++ "\\\[Thread \[^\r\n\]+ exited\\\]" \ ++ "warning: command aborted, Thread \[^\r\n\]+ unexpectedly exited after signal stop event"] ++ ++ if { !$non_stop } { ++ lappend expected_output "\\\[Switching to Thread \[^\r\n\]+\\\]" ++ } ++ ++ gdb_test "continue" [multi_line {*}$expected_output] ++ } else { ++ # This is the original behaviour, we see this when running ++ # with the unmodified log. ++ gdb_test "continue" \ ++ "Thread ${::decimal}(?: \[^\r\n\]+)? received signal SIGTRAP, .*" ++ } ++ ++ gdb_test "disconnect" ".*" \ ++ "disconnect after seeing signal" ++} ++ ++# Run the complete test cycle; generate an initial log file, modify the log ++# file, then check that GDB correctly handles replaying the modified log ++# file. ++# ++# NON_STOP is either 'on' or 'off' and indicates GDB's non-stop mode. ++proc run_test { non_stop } { ++ if { $non_stop } { ++ set suffix "-ns" ++ } else { ++ set suffix "" ++ } ++ ++ # The replay log is placed in 'replay.log'. ++ set remote_log [standard_output_file replay${suffix}.log] ++ set missing_1_log [standard_output_file replay-missing-1${suffix}.log] ++ set missing_2_log [standard_output_file replay-missing-2${suffix}.log] ++ ++ record_initial_logfile $remote_log ++ ++ if { ![update_replay_log $remote_log $missing_1_log false] } { ++ fail "couldn't update remote replay log (drop 1 case)" ++ } ++ ++ if { ![update_replay_log $remote_log $missing_2_log true] } { ++ fail "couldn't update remote replay log (drop 2 case)" ++ } ++ ++ with_test_prefix "with unmodified log" { ++ # Replay with the unmodified log. This confirms that we can replay this ++ # scenario correctly. ++ replay_with_log $remote_log false $non_stop ++ } ++ ++ with_test_prefix "missing 1 thread log" { ++ # Now replay with the modified log, this time the thread that receives ++ # the event should be missing from the thread list, GDB will give an ++ # error when the inferior stops. ++ replay_with_log $missing_1_log true $non_stop ++ } ++ ++ with_test_prefix "missing 2 threads log" { ++ # When we drop both threads from the reply, GDB doesn't ++ # actually remove both threads from the inferior; an inferior must ++ # always have at least one thread. So in this case, as the primary ++ # thread is first, GDB drops this, then retains the second thread, which ++ # is the one we're stopping in, and so, we don't expect to see the error ++ # in this case. ++ replay_with_log $missing_2_log false $non_stop ++ } ++} ++ ++# Run the test twice, with non-stop on and off. ++foreach_with_prefix non_stop { on off } { ++ save_vars { ::GDBFLAGS } { ++ append ::GDBFLAGS " -ex \"set non-stop $non_stop\"" ++ run_test $non_stop ++ } ++} diff --git a/gdb-rhbz2403580-misplaced-symtabs.patch b/gdb-rhbz2403580-misplaced-symtabs.patch new file mode 100644 index 0000000..35fdaad --- /dev/null +++ b/gdb-rhbz2403580-misplaced-symtabs.patch @@ -0,0 +1,151 @@ +From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 +From: Andrew Burgess +Date: Thu, 11 Dec 2025 10:33:15 +0000 +Subject: gdb-rhbz2403580-misplaced-symtabs.patch + +;; Backport of upstream commit 70b66cf338b14336 to address RHBZ +;; 2402580. This backport can be dropped when rebasing to GDB 18. +;; There were some moderate merge conflicts which needed resolving +;; when backporting this fix. + +diff --git a/gdb/dwarf2/line-header.c b/gdb/dwarf2/line-header.c +--- a/gdb/dwarf2/line-header.c ++++ b/gdb/dwarf2/line-header.c +@@ -417,3 +417,46 @@ dwarf_decode_line_header (sect_offset sect_off, bool is_dwz, + + return lh; + } ++ ++/* See dwarf2/line-header.h. */ ++ ++struct symtab * ++file_entry::symtab (dwarf2_cu &cu) ++{ ++ if (m_symtab == nullptr) ++ { ++ buildsym_compunit *builder = cu.get_builder (); ++ compunit_symtab *cust = builder->get_compunit_symtab (); ++ ++ { ++ std::string filename_holder; ++ const char *filename = this->name; ++ const char *dirname = cu.line_header->include_dir_at (this->d_index); ++ ++ /* In order not to lose the line information directory, ++ we concatenate it to the filename when it makes sense. ++ Note that the Dwarf3 standard says (speaking of filenames in line ++ information): ``The directory index is ignored for file names ++ that represent full path names''. Thus ignoring dirname in the ++ `else' branch below isn't an issue. */ ++ ++ if (!IS_ABSOLUTE_PATH (filename) && dirname != NULL) ++ { ++ filename_holder = path_join (dirname, filename); ++ filename = filename_holder.c_str (); ++ } ++ ++ std::string filename_for_id = cu.line_header->file_file_name (*this); ++ cu.get_builder ()->start_subfile (filename, filename_for_id.c_str ()); ++ } ++ ++ subfile *sf = builder->get_current_subfile (); ++ if (sf->symtab == nullptr) ++ sf->symtab = allocate_symtab (cust, sf->name.c_str (), ++ sf->name_for_id.c_str ()); ++ ++ m_symtab = sf->symtab; ++ } ++ ++ return m_symtab; ++} +diff --git a/gdb/dwarf2/line-header.h b/gdb/dwarf2/line-header.h +--- a/gdb/dwarf2/line-header.h ++++ b/gdb/dwarf2/line-header.h +@@ -23,6 +23,7 @@ + #include "dwarf2/types.h" + + struct dwarf2_per_objfile; ++struct dwarf2_cu; + + /* dir_index is 1-based in DWARF 4 and before, and is 0-based in DWARF 5 and + later. */ +@@ -65,8 +66,18 @@ struct file_entry + + unsigned int length {}; + ++ /* Get the symtab for this file_entry. If no symtab has yet been created ++ or set (see set_symtab) for this file_entry then a new one will be ++ created. */ ++ struct symtab *symtab (struct dwarf2_cu &cu); ++ ++ /* Set the symtab for this file_entry. */ ++ void set_symtab (struct symtab *s) ++ { m_symtab = s; } ++ ++private: + /* The associated symbol table, if any. */ +- struct symtab *symtab {}; ++ struct symtab *m_symtab {}; + }; + + /* The line number information for a compilation unit (found in the +diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c +--- a/gdb/dwarf2/read.c ++++ b/gdb/dwarf2/read.c +@@ -6227,8 +6227,8 @@ dwarf2_cu::setup_type_unit_groups (struct die_info *die) + sf->symtab = allocate_symtab (cust, name, name_for_id); + } + +- fe.symtab = b->get_current_subfile ()->symtab; +- tug_unshare->symtabs[i] = fe.symtab; ++ fe.set_symtab (b->get_current_subfile ()->symtab); ++ tug_unshare->symtabs[i] = fe.symtab (*this); + } + } + else +@@ -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]; +- fe.symtab = tug_unshare->symtabs[i]; ++ fe.set_symtab (tug_unshare->symtabs[i]); + } + } + +@@ -11589,7 +11589,7 @@ process_structure_scope (struct die_info *die, struct dwarf2_cu *cu) + { + /* Any related symtab will do. */ + symtab +- = cu->line_header->file_names ()[0].symtab; ++ = cu->line_header->file_names ()[0].symtab (*cu); + } + else + { +@@ -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); + ++ if (cu->per_cu->is_dwz) ++ return; ++ + /* Make sure a symtab is created for every file, even files + which contain only variables (i.e. no code with associated + line numbers). */ +@@ -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 ()); + +- fe.symtab = sf->symtab; ++ fe.set_symtab (sf->symtab); + } + } + +@@ -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 +- sym->set_symtab (fe->symtab); ++ sym->set_symtab (fe->symtab (*file_cu)); + } + } + diff --git a/gdb-rhbz2413405-gcore-unreadable-pages.patch b/gdb-rhbz2413405-gcore-unreadable-pages.patch new file mode 100644 index 0000000..9dd5847 --- /dev/null +++ b/gdb-rhbz2413405-gcore-unreadable-pages.patch @@ -0,0 +1,188 @@ +From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 +From: Kevin Buettner +Date: Wed, 25 Feb 2026 19:58:50 -0700 +Subject: gdb-rhbz2413405-gcore-unreadable-pages.patch + +;; Backport of upstream commit c1da013915e from Kevin Buettner +;; (RHBZ 2413405). + +gcore: Handle unreadable pages within readable memory regions + +GLIBC 2.42 changed how thread stack guard pages are implemented [2]. +In GLIBC 2.41 and earlier, guard pages were set up using mprotect() to +mark guard regions with no permissions. Once configured, guard pages +were visible as separate entries in /proc/PID/maps with no permissions +(i.e. they're inaccessible). In GLIBC 2.42, guard pages are +installed using the kernel's MADV_GUARD_INSTALL mechanism [1], which +marks them at the page table entry (PTE) level within the existing +mapping. + +As a consequence, guard pages do not appear as separate entries in +/proc/PID/maps, but remain as part of the containing mapping. Moreover, +thread stacks from multiple mmap() calls may be merged into a single +virtual memory area (VMA) with read and write permissions since there's +no guard page VMA to separate them. These guard pages cannot be +distinguished by examining VMA listings but do return EIO when read +from /proc/PID/mem. + +GDB's gcore code reads /proc/PID/smaps to discover memory regions and +creates one BFD section per mapping. (On linux, this is performed in +linux_find_memory_regions_full in linux-tdep.c.) With the old layout, +memory areas with guard pages appeared separately with no permissions, +which were filtered out. Each thread stack became its own section +containing only readable data. With the new layout, using +MADV_GUARD_INSTALL instead of the older mechanism, it's often the case +that thread stacks created with multiple calls to mmap() are exposed +as a single mapping appearing in /proc/PID/smaps with read and write +permissions. Should that happen, GDB's code creates a single section +covering all thread stacks and their guard pages. (Even if each +thread stack appears in its own mapping, the fact remains that there +will be an inaccessible portion of the mapping. When one or more +thread stacks are coalesced into a single mapping, there will be +several inaccessible "holes" representing the guard pages.) + +When gcore_copy_callback copies section contents, it reads memory in +1MB (MAX_COPY_BYTES) chunks. If any page in the chunk is a guard page, +the call to target_read_memory() fails. The old code responded by +breaking out of the copy loop, abandoning the entire section. This +prevents correct copying of thread stack data, resulting in core files +with zero-filled thread stacks, resulting in nearly empty backtraces. + +Fix this by falling back to page-by-page reading when a 1MB chunk read +fails. Individual pages that cannot be read are filled with zeros, +allowing the remaining readable memory to be captured. + +I also considered a simpler change using the value of +FALLBACK_PAGE_SIZE (4096) as the read size instead of MAX_COPY_BYTES +(1MB). This would avoid the fallback logic but would cause up to 256x +more syscalls. The proposed approach also allows meaningful warnings: +we warn only if an entire region is unreadable (indicating a real +problem), whereas per-page reads would make it harder to distinguish +guard page failures from actual errors. Since guard pages are at +offset 0 for downward-growing stacks, a large target_read_memory() +fails early at the first unreadable byte anyway. + +With this fix, I see 16 failures resolved in the following test cases: + + gdb.ada/task_switch_in_core.exp + gdb.arch/i386-tls-regs.exp + gdb.threads/threadcrash.exp + gdb.threads/tls-core.exp + +Looking at just one of these, from gdb.log without the fix, I see: + + thread apply 5 backtrace + + Thread 5 (LWP 3414829): + #0 0x00007ffff7d1d982 in __syscall_cancel_arch () from /lib64/libc.so.6 + #1 0x0000000000000000 in ?? () + (gdb) FAIL: gdb.threads/threadcrash.exp: test_gcore: thread apply 5 backtrace + +And this is what it looks like with the fix in place (some paths have +been shortened): + + thread apply 5 backtrace + + Thread 5 (Thread 0x7fffeffff6c0 (LWP 1282651) "threadcrash"): + #0 0x00007ffff7d1d982 in __syscall_cancel_arch () from /lib64/libc.so.6 + #1 0x00007ffff7d11c3c in __internal_syscall_cancel () from /lib64/libc.so.6 + #2 0x00007ffff7d61b62 in clock_nanosleep@GLIBC_2.2.5 () from /lib64/libc.so.6 + #3 0x00007ffff7d6db37 in nanosleep () from /lib64/libc.so.6 + #4 0x00007ffff7d8008e in sleep () from /lib64/libc.so.6 + #5 0x00000000004006a8 in do_syscall_task (location=NORMAL) at threadcrash.c:158 + #6 0x0000000000400885 in thread_function (arg=0x404340) at threadcrash.c:277 + #7 0x00007ffff7d15464 in start_thread () from /lib64/libc.so.6 + #8 0x00007ffff7d985ac in __clone3 () from /lib64/libc.so.6 + (gdb) PASS: gdb.threads/threadcrash.exp: test_live_inferior: thread apply 5 backtrace + +Regression testing on Fedora 42 (glibc 2.41) shows no new failures. + +The v1 patch used SPARSE_BLOCK_SIZE as the fallback size. While it +was the correct size, it's used for an entirely different purpose +elsewhere in this file. This v2 commit introduces the constant +FALLBACK_PAGE_SIZE instead. + +References: + +[1] Linux commit 662df3e5c376 ("mm: madvise: implement lightweight + guard page mechanism") + https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=662df3e5c37666d6ed75c88098699e070a4b35b5 +[2] glibc commit a6fbe36b7f31 ("nptl: Add support for setup guard + pages with MADV_GUARD_INSTALL") + https://sourceware.org/git/?p=glibc.git;a=commit;h=a6fbe36b7f31292981422692236465ab56670ea9 + +Claude Opus 4.5 and GLM 4.7 assisted with the development of this commit. + +Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33855 +Approved-By: Tom de Vries + +diff --git a/gdb/gcore.c b/gdb/gcore.c +--- a/gdb/gcore.c ++++ b/gdb/gcore.c +@@ -744,6 +744,12 @@ sparse_bfd_set_section_contents (bfd *obfd, asection *osec, + return true; + } + ++/* Fallback page size to use when target_read_memory fails when attempting ++ to read MAX_COPY_BYTES in gcore_copy_callback. 4KB is the correct size ++ to use for x86 and most other architectures. Some may have larger pages, ++ but this size will still work at the cost of more syscalls. */ ++#define FALLBACK_PAGE_SIZE 0x1000 ++ + static void + gcore_copy_callback (bfd *obfd, asection *osec) + { +@@ -766,15 +772,45 @@ gcore_copy_callback (bfd *obfd, asection *osec) + if (size > total_size) + size = total_size; + +- if (target_read_memory (bfd_section_vma (osec) + offset, +- memhunk.data (), size) != 0) ++ CORE_ADDR vma = bfd_section_vma (osec) + offset; ++ ++ if (target_read_memory (vma, memhunk.data (), size) != 0) + { +- warning (_("Memory read failed for corefile " +- "section, %s bytes at %s."), +- plongest (size), +- paddress (current_inferior ()->arch (), +- bfd_section_vma (osec))); +- break; ++ /* Large read failed. This can happen when the memory region ++ contains unreadable pages (such as guard pages embedded within ++ a larger mapping). Fall back to reading page by page, filling ++ unreadable pages with zeros. */ ++ gdb_byte *p = memhunk.data (); ++ bfd_size_type remaining = size; ++ CORE_ADDR addr = vma; ++ bool at_least_one_page_read = false; ++ ++ while (remaining > 0) ++ { ++ bfd_size_type chunk_size ++ = std::min (remaining, (bfd_size_type) FALLBACK_PAGE_SIZE); ++ ++ if (target_read_memory (addr, p, chunk_size) != 0) ++ { ++ /* Failed to read this page. Fill with zeros. This ++ handles guard pages and other unreadable regions ++ that may exist within a larger readable mapping. */ ++ memset (p, 0, chunk_size); ++ } ++ else ++ at_least_one_page_read = true; ++ ++ p += chunk_size; ++ addr += chunk_size; ++ remaining -= chunk_size; ++ } ++ /* Warn only if the entire region was unreadable - this ++ indicates a real problem, not just embedded guard pages. */ ++ if (!at_least_one_page_read) ++ warning (_("Memory read failed for corefile " ++ "section, %s bytes at %s."), ++ plongest (size), ++ paddress (current_inferior ()->arch (), vma)); + } + + if (!sparse_bfd_set_section_contents (obfd, osec, memhunk.data (), diff --git a/gdb-rhbz2424325-c++20-implicit-lambda-capture.patch b/gdb-rhbz2424325-c++20-implicit-lambda-capture.patch new file mode 100644 index 0000000..90473a1 --- /dev/null +++ b/gdb-rhbz2424325-c++20-implicit-lambda-capture.patch @@ -0,0 +1,27 @@ +From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 +From: Kevin Buettner +Date: Wed, 7 Jan 2026 23:51:38 -0700 +Subject: gdb-rhbz2424325-c++20-implicit-lambda-capture.patch + +;; Backport Tom de Vries fix regarding implicit lambda captures +;; (RH BZ 2424325). + +[gdb/build, c++20] Fix deprecated implicit capture in cooked_index::set_contents + +Fix deprecated implicit capture of this in cooked_index::set_contents, by +removing the capture default, and explicitly listing all captures. + +Tested on x86_64-linux. + +diff --git a/gdb/dwarf2/cooked-index.c b/gdb/dwarf2/cooked-index.c +--- a/gdb/dwarf2/cooked-index.c ++++ b/gdb/dwarf2/cooked-index.c +@@ -102,7 +102,7 @@ cooked_index::set_contents () + { + auto this_shard = shard.get (); + const parent_map_map *parent_maps = m_state->get_parent_map_map (); +- finalizers.add_task ([=] () ++ finalizers.add_task ([this, this_shard, parent_maps] () + { + scoped_time_it time_it ("DWARF finalize worker", + m_state->m_per_command_time); diff --git a/gdb-rhbz2424325-c23-const-build-warnings.patch b/gdb-rhbz2424325-c23-const-build-warnings.patch new file mode 100644 index 0000000..517ee6e --- /dev/null +++ b/gdb-rhbz2424325-c23-const-build-warnings.patch @@ -0,0 +1,209 @@ +From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 +From: Kevin Buettner +Date: Wed, 7 Jan 2026 22:12:42 -0700 +Subject: gdb-rhbz2424325-c23-const-build-warnings.patch + +;; Backport Guinevere Larsen's build warning fixes (RH BZ 2424325). + +bfd: fix build with C23 + +Starting in C23, strchr and strrchr will return const char *, if fed a +const char *. This means that several files in the BFD directory will +fail to build as they are assigning the return of those functions to a +char *. + +Fix this by const-ifying several variables. The only place where that +wasn't just that was in targets.c, where a variable was being used in +subsequent strrchr invocations to change the underlying string, so a new +variable had to be introduced. + +No user-visible change should happen after this commit. + +diff --git a/bfd/bfd.c b/bfd/bfd.c +--- a/bfd/bfd.c ++++ b/bfd/bfd.c +@@ -1060,7 +1060,7 @@ _bfd_doprnt (bfd_print_callback print, void *stream, const char *format, + if (*ptr != '%') + { + /* While we have regular characters, print them. */ +- char *end = strchr (ptr, '%'); ++ const char *end = strchr (ptr, '%'); + if (end != NULL) + result = print (stream, "%.*s", (int) (end - ptr), ptr); + else +diff --git a/bfd/elflink.c b/bfd/elflink.c +--- a/bfd/elflink.c ++++ b/bfd/elflink.c +@@ -148,7 +148,7 @@ _bfd_elf_section_for_symbol (struct elf_reloc_cookie *cookie, + struct elf_link_hash_entry *h; + + h = get_ext_sym_hash_from_cookie (cookie, r_symndx); +- ++ + if (h != NULL) + { + if ((h->root.type == bfd_link_hash_defined +@@ -588,7 +588,7 @@ bfd_elf_link_record_dynamic_symbol (struct bfd_link_info *info, + if (h->dynindx == -1) + { + struct elf_strtab_hash *dynstr; +- char *p; ++ const char *p; + const char *name; + size_t indx; + +@@ -718,7 +718,7 @@ bfd_elf_record_link_assignment (bfd *output_bfd, + if (h->versioned == unknown) + { + /* Set versioned if symbol version is unknown. */ +- char *version = strrchr (name, ELF_VER_CHR); ++ const char *version = strrchr (name, ELF_VER_CHR); + if (version) + { + if (version > name && version[-1] != ELF_VER_CHR) +@@ -1152,7 +1152,7 @@ _bfd_elf_merge_symbol (bfd *abfd, + bool newdyn, olddyn, olddef, newdef, newdyncommon, olddyncommon; + bool newweak, oldweak, newfunc, oldfunc; + const struct elf_backend_data *bed; +- char *new_version; ++ const char *new_version; + bool default_sym = *matched; + struct elf_link_hash_table *htab; + +@@ -1224,7 +1224,7 @@ _bfd_elf_merge_symbol (bfd *abfd, + { + /* OLD_VERSION is the symbol version of the existing + symbol. */ +- char *old_version; ++ const char *old_version; + + if (h->versioned >= versioned) + old_version = strrchr (h->root.root.string, +@@ -1950,7 +1950,7 @@ _bfd_elf_add_default_symbol (bfd *abfd, + bool collect; + bool dynamic; + bfd *override; +- char *p; ++ const char *p; + size_t len, shortlen; + asection *tmp_sec; + bool matched; +@@ -2642,7 +2642,7 @@ _bfd_elf_link_assign_sym_version (struct elf_link_hash_entry *h, void *data) + struct bfd_link_info *info; + const struct elf_backend_data *bed; + struct elf_info_failed eif; +- char *p; ++ const char *p; + bool hide; + + sinfo = (struct elf_info_failed *) data; +@@ -5641,7 +5641,7 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info) + && !dynamic + && (abfd->flags & BFD_PLUGIN) == 0) + { +- char *p = strchr (name, ELF_VER_CHR); ++ const char *p = strchr (name, ELF_VER_CHR); + if (p != NULL && p[1] != ELF_VER_CHR) + { + /* Queue non-default versions so that .symver x, x@FOO +@@ -5891,7 +5891,8 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info) + for (cnt = 0; cnt < nondeflt_vers_cnt; ++cnt) + { + struct elf_link_hash_entry *h = nondeflt_vers[cnt], *hi; +- char *shortname, *p; ++ char *shortname; ++ const char *p; + size_t amt; + + p = strchr (h->root.root.string, ELF_VER_CHR); +@@ -6171,7 +6172,8 @@ _bfd_elf_archive_symbol_lookup (bfd *abfd, + const char *name) + { + struct bfd_link_hash_entry *h; +- char *p, *copy; ++ const char *p; ++ char *copy; + size_t len, first; + + h = bfd_link_hash_lookup (info->hash, name, false, false, true); +@@ -6466,7 +6468,7 @@ elf_collect_hash_codes (struct elf_link_hash_entry *h, void *data) + name = h->root.root.string; + if (h->versioned >= versioned) + { +- char *p = strchr (name, ELF_VER_CHR); ++ const char *p = strchr (name, ELF_VER_CHR); + if (p != NULL) + { + alc = (char *) bfd_malloc (p - name + 1); +@@ -6539,7 +6541,7 @@ elf_collect_gnu_hash_codes (struct elf_link_hash_entry *h, void *data) + name = h->root.root.string; + if (h->versioned >= versioned) + { +- char *p = strchr (name, ELF_VER_CHR); ++ const char *p = strchr (name, ELF_VER_CHR); + if (p != NULL) + { + alc = (char *) bfd_malloc (p - name + 1); +@@ -10378,8 +10380,8 @@ elf_link_output_symstrtab (void *finf, + { + /* Keep only one '@' for versioned symbols defined in + shared objects. */ +- char *version = strrchr (name, ELF_VER_CHR); +- char *base_end = strchr (name, ELF_VER_CHR); ++ const char *version = strrchr (name, ELF_VER_CHR); ++ const char *base_end = strchr (name, ELF_VER_CHR); + if (version != base_end) + { + size_t base_len; +@@ -11118,7 +11120,7 @@ elf_link_output_extsym (struct bfd_hash_entry *bh, void *data) + || h->ref_dynamic + || !h->def_regular)) + { +- char *p = strrchr (h->root.root.string, ELF_VER_CHR); ++ const char *p = strrchr (h->root.root.string, ELF_VER_CHR); + + if (p && p [1] != '\0') + { +@@ -11670,7 +11672,7 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd) + input_bfd); + bfd_set_error (bfd_error_bad_value); + return false; +- } ++ } + + /* Arrange for symbol to be output. */ + h->indx = -2; +@@ -15142,7 +15144,7 @@ bfd_elf_reloc_symbol_deleted_p (bfd_vma offset, void *cookie) + struct elf_link_hash_entry *h; + + h = get_ext_sym_hash_from_cookie (rcookie, r_symndx); +- ++ + if (h != NULL) + { + if ((h->root.type == bfd_link_hash_defined +diff --git a/bfd/targets.c b/bfd/targets.c +--- a/bfd/targets.c ++++ b/bfd/targets.c +@@ -1660,7 +1660,7 @@ bfd_get_target_info (const char *target_name, bfd *abfd, + + if (arches && tname) + { +- char *hyp = strchr (tname, '-'); ++ const char *hyp = strchr (tname, '-'); + + if (hyp != NULL) + { +@@ -1673,9 +1673,10 @@ bfd_get_target_info (const char *target_name, bfd *abfd, + char new_tname[50]; + + strcpy (new_tname, hyp); +- while ((hyp = strrchr (new_tname, '-')) != NULL) ++ char *new_hyp; ++ while ((new_hyp = strrchr (new_tname, '-')) != NULL) + { +- *hyp = 0; ++ *new_hyp = 0; + if (_bfd_find_arch_match (new_tname, arches, + def_target_arch)) + break; diff --git a/gdb-rhbz2424325-c23-more-const-fixes.patch b/gdb-rhbz2424325-c23-more-const-fixes.patch new file mode 100644 index 0000000..6655e73 --- /dev/null +++ b/gdb-rhbz2424325-c23-more-const-fixes.patch @@ -0,0 +1,175 @@ +From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 +From: Keith Seitz +Date: Wed, 7 Jan 2026 09:36:28 -0800 +Subject: gdb-rhbz2424325-c23-more-const-fixes.patch +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +;; Backport Keith Seitz's C23 const-correctness fixes (pending upstream review). +;; Mailing list: https://sourceware.org/pipermail/binutils/2026-January/... +;; Posted 2026-01-07, not yet approved. + +Fix some more C23 const-correctness issues + +Fedora Rawhide is failing to build due to new glibc header changes +enforcing const-correctness in functions like strchr and memchr. +For example: + +../../opcodes/aarch64-dis.c: In function ‘remove_dot_suffix’: +../../opcodes/aarch64-dis.c:4027:7: error: assignment discards ‘const’ qualifier from po +inter target type [-Werror=discarded-qualifiers] + 4027 | ptr = strchr (inst->opcode->name, '.'); + | ^ +cc1: all warnings being treated as errors + +This patch addresses all the discovered issues with --enable-targets=all +and regenerates a few cgen files along the way. + +diff --git a/bfd/elf32-arc.c b/bfd/elf32-arc.c +--- a/bfd/elf32-arc.c ++++ b/bfd/elf32-arc.c +@@ -550,7 +550,7 @@ arc_extract_features (const char *p) + + for (i = 0; i < ARRAY_SIZE (bfd_feature_list); i++) + { +- char *t = strstr (p, bfd_feature_list[i].attr); ++ const char *t = strstr (p, bfd_feature_list[i].attr); + unsigned l = strlen (bfd_feature_list[i].attr); + if ((t != NULL) + && (t[l] == ',' +diff --git a/bfd/vms-misc.c b/bfd/vms-misc.c +--- a/bfd/vms-misc.c ++++ b/bfd/vms-misc.c +@@ -492,14 +492,14 @@ get_vms_time_string (unsigned char *tbuf) + } + + /* Create module name from filename (ie, extract the basename and convert it +- in upper cases). Works on both VMS and UNIX pathes. ++ in upper cases). Works on both VMS and UNIX paths. + The result has to be free(). */ + + char * + vms_get_module_name (const char *filename, bool upcase) + { + char *fname, *fptr; +- const char *fout; ++ const char *fout, *p; + + /* Strip VMS path. */ + fout = strrchr (filename, ']'); +@@ -511,9 +511,9 @@ vms_get_module_name (const char *filename, bool upcase) + fout = filename; + + /* Strip UNIX path. */ +- fptr = strrchr (fout, '/'); +- if (fptr != NULL) +- fout = fptr + 1; ++ p = strrchr (fout, '/'); ++ if (p != NULL) ++ fout = p + 1; + + fname = strdup (fout); + +diff --git a/cpu/ip2k.opc b/cpu/ip2k.opc +--- a/cpu/ip2k.opc ++++ b/cpu/ip2k.opc +@@ -94,7 +94,7 @@ parse_fr (CGEN_CPU_DESC cd, + { + const char *errmsg; + const char *old_strp; +- char *afteroffset; ++ const char *afteroffset; + enum cgen_parse_operand_result result_type; + bfd_vma value; + extern CGEN_KEYWORD ip2k_cgen_opval_register_names; +diff --git a/opcodes/aarch64-dis.c b/opcodes/aarch64-dis.c +--- a/opcodes/aarch64-dis.c ++++ b/opcodes/aarch64-dis.c +@@ -4061,7 +4061,7 @@ print_operands (bfd_vma pc, const aarch64_opcode *opcode, + static void + remove_dot_suffix (char *name, const aarch64_inst *inst) + { +- char *ptr; ++ const char *ptr; + size_t len; + + ptr = strchr (inst->opcode->name, '.'); +diff --git a/opcodes/ia64-opc.c b/opcodes/ia64-opc.c +--- a/opcodes/ia64-opc.c ++++ b/opcodes/ia64-opc.c +@@ -66,7 +66,7 @@ const struct ia64_templ_desc ia64_templ_desc[16] = + static void + get_opc_prefix (const char **ptr, char *dest) + { +- char *c = strchr (*ptr, '.'); ++ const char *c = strchr (*ptr, '.'); + if (c != NULL) + { + memcpy (dest, *ptr, c - *ptr); +diff --git a/opcodes/ip2k-asm.c b/opcodes/ip2k-asm.c +--- a/opcodes/ip2k-asm.c ++++ b/opcodes/ip2k-asm.c +@@ -59,7 +59,7 @@ parse_fr (CGEN_CPU_DESC cd, + { + const char *errmsg; + const char *old_strp; +- char *afteroffset; ++ const char *afteroffset; + enum cgen_parse_operand_result result_type; + bfd_vma value; + extern CGEN_KEYWORD ip2k_cgen_opval_register_names; +diff --git a/opcodes/riscv-dis.c b/opcodes/riscv-dis.c +--- a/opcodes/riscv-dis.c ++++ b/opcodes/riscv-dis.c +@@ -110,7 +110,7 @@ parse_riscv_dis_option_without_args (const char *option, + /* Parse RISC-V disassembler option (possibly with arguments). */ + + static void +-parse_riscv_dis_option (const char *option, struct disassemble_info *info) ++parse_riscv_dis_option (char *option, struct disassemble_info *info) + { + char *equal, *value; + +@@ -1140,7 +1140,7 @@ riscv_update_map_state (int n, + + /* ISA mapping string may be numbered, suffixed with '.n'. Do not + consider this as part of the ISA string. */ +- char *suffix = strchr (name, '.'); ++ const char *suffix = strchr (name, '.'); + if (suffix) + { + int suffix_index = (int)(suffix - name); +diff --git a/opcodes/tilegx-opc.c b/opcodes/tilegx-opc.c +--- a/opcodes/tilegx-opc.c ++++ b/opcodes/tilegx-opc.c +@@ -8003,7 +8003,7 @@ tilegx_spr_compare (const void *a_ptr, const void *b_ptr) + const char * + get_tilegx_spr_name (int num) + { +- void *result; ++ const void *result; + struct tilegx_spr key; + + key.number = num; +diff --git a/opcodes/tilepro-opc.c b/opcodes/tilepro-opc.c +--- a/opcodes/tilepro-opc.c ++++ b/opcodes/tilepro-opc.c +@@ -10119,7 +10119,7 @@ tilepro_spr_compare (const void *a_ptr, const void *b_ptr) + const char * + get_tilepro_spr_name (int num) + { +- void *result; ++ const void *result; + struct tilepro_spr key; + + key.number = num; +@@ -10131,7 +10131,7 @@ get_tilepro_spr_name (int num) + return NULL; + + { +- struct tilepro_spr *result_ptr = (struct tilepro_spr *) result; ++ const struct tilepro_spr *result_ptr = (const struct tilepro_spr *) result; + + return result_ptr->name; + } diff --git a/gdb-rhbz2467251-computed-location-synthetic-pointers.patch b/gdb-rhbz2467251-computed-location-synthetic-pointers.patch new file mode 100644 index 0000000..f2c1e0a --- /dev/null +++ b/gdb-rhbz2467251-computed-location-synthetic-pointers.patch @@ -0,0 +1,1229 @@ +From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 +From: Andrew Burgess +Date: Fri, 8 May 2026 21:44:12 +0100 +Subject: gdb-rhbz2467251-computed-location-synthetic-pointers.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. + +*** Commit d980317c7f1: gdb: int to bool conversion in valprint.{c,}h + +gdb: int to bool conversion in valprint.{c,h} + +Some int to bool conversion in valprint.c and valprint.h. I also +moved the header comment on val_print_scalar_type_p into the header. + +There should be no user visible changes after this commit. + +Approved-By: Tom Tromey + +Commit 958d06262a7: gdb: fix coerce_pieced_ref for multi-piece values + +gdb: fix coerce_pieced_ref for multi-piece values + +Bug PR gdb/30693 describes a case where the following assertion can be +triggered: + + ../../gdb/dwarf2/loc.c:2213: internal-error: value* coerce_pieced_ref(const value*): Assertion `closure->pieces.size () == 1' failed. + +The problem is that coerce_pieced_ref makes the following claim: + + /* gdb represents synthetic pointers as pieced values with a single + piece. */ + gdb_assert (closure != NULL); + gdb_assert (closure->pieces.size () == 1); + +But this is not really true. If an aggregate type contains a synthetic +pointer, then it is possible that the aggregate type will have a +computed location consisting of multiple pieces. When GDB prints the +fields of that aggregate type these fields are extracted by calling +value::primitive_field. Within value::primitive_field the location of +the field is set by calling value::set_component_location. + +When the parent value that holds the field has a computed location, the +field value gains a reference to the parent value's closure, this can be +seen in copy_pieced_value_closure in dwarf2/expr.c. + +What this means is that, if the aggregate value has a multi-piece +computed location, then the synthetic pointer field will also have a +reference to that same multi-piece computed location, even if there is +really only a single piece that describes the synthetic pointer itself. + +Some parts of GDB are already aware of this. If we look at +check_pieced_synthetic_pointer which implements the +value::bits_synthetic_pointer function, you'll see that this function +searches through all of the pieces to find the piece that covers the +value we are looking for, it then checks if that piece is an implicit +pointer location. But back in coerce_pieced_ref, after calling +value::bits_synthetic_pointer, we still make the assertion that there +will be just a single piece. + +Fix this by copying the search through all pieces logic into +coerce_pieced_ref (see note on efficiency below). We now search through +all the pieces looking for a piece that describes the location of the +synthetic pointer, and we then use that piece to form the pointer's +value. + +There are some assertions in the new code, these align with how +check_pieced_synthetic_pointer operates. + +In addition, there is an error for the case where multiple pieces are +used to describe the location of a synthetic pointer. This case is +technically allowed by check_pieced_synthetic_pointer, but supporting +this would require changes to indirect_synthetic_pointer, so I propose +leaving that until we see such a case in the wild. + +On efficiency, you'll notice that check_pieced_synthetic_pointer +performs a search through all the location pieces, and +coerce_pieced_ref also has to search through the pieces. It would be +nice if this could be avoided in order to avoid multiple searches. +Currently though coerce_pieced_ref calls +value->bits_synthetic_pointer, which is an API that should be agnostic +to the underlying implementation, i.e. shouldn't need to know that the +implementation is computed, so passing pieces back would be harder. + +Maybe coerce_pieced_ref could avoid the value::bits_synthetic_pointer +call, and instead call check_pieced_synthetic_pointer directly, or +some related helper function, and could get the pieces back that way. +But this breaks the cleanly structured API that we currently have. + +For now I'm leaving things as they are. My assumption is that the +number of pieces used to represent a value is pretty low, so the +search is actually pretty cheap. + +There's a new test that uses the DWARF assembler to create a +representative example of a multi-piece aggregate that contains a +synthetic pointer member variable. This test triggers the assertion +before this commit. + +Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30693 +Bug: https://bugzilla.redhat.com/show_bug.cgi?id=2467251 + +Approved-By: Tom Tromey + +Commit 7d1d7386561: gdb: rename argument in valprint_check_validity + +gdb: rename argument in valprint_check_validity + +To understand the motivation for this commit you'll need to look ahead +two commits in this series for an upcoming bug fix. + +Look at coerce_pieced_ref in dwarf2/expr.c, and this call: + + if (value->bits_synthetic_pointer (value->embedded_offset (), + TARGET_CHAR_BIT * type->length ())) + { ... } + +This passes the result from value::embedded_offset as the first argument. +Though it's not clear from value.h, the number returned by +value::embedded_offset is a byte offset. + +The value::bits_synthetic_pointer call can end up calling +check_pieced_synthetic_pointer, with the first argument to +value::bits_synthetic_pointer becoming the second argument to +check_pieced_synthetic_pointer. The second argument to +check_pieced_synthetic_pointer is called BIT_OFFSET, a value in bits. +Looking at how this argument is used confirms that it is expected to be +a value in bits, not bytes. + +The fix should be easy, multiply the embedded offset by +TARGET_CHAR_BIT, but I think things are a little more complex than +this. + +I started looking at how value::bits_synthetic_pointer is used, there +are only 7 uses, and they can be grouped as follows: + +gdb/dwarf2/expr.c +gdb/valops.c + + These call value::embedded_offset directly within the call to + value::bits_synthetic_pointer. + +gdb/opencl-lang.c + + This function is used as a wrapper that implements a + lval_funcs::check_synthetic_pointer callback, it adjusts the + arguments and then calls value::bits_synthetic_pointer. As such we'd + not expect to see embedded offset mentioned here as that would (we + assume) be handled by the caller. + +gdb/valprint.c (in generic_val_print_ref) + + This call passes an argument called 'embedded_offset', but the + function generic_val_print_ref is only used in one place, and the + embedded_offset is always passed as zero. + +gdb/valprint.c (in valprint_check_validity) + + This call also passes an argument called 'embedded_offset'. This + function is used in two places. In common_val_print the + embedded_offset is always passed as zero, but in + valprint_check_validity (in cp-valprint.c) the embedded_offset being + passed is the offset of a field within the value, not the value's + embedded_offset within some larger value. + +gdb/cp-valprint.c +gdb/p-valprint.c + + As with the call to valprint_check_validity in cp-valprint.c, these + two direct calls to value::bits_synthetic_pointer pass the offset of a + field within the value, rather than the value's embedded_offset. + +What I see in the above is some confusion. In some places we are +passing the value::embedded_offset, while in other places we are +passing the offset of a field within the value itself. + +If we consider the direct call to value::bits_synthetic_pointer in +gdb/cp-valprint.c, where a field offset is passed, then it should be +possible, that if we can create an object with a non-zero +embedded_offset (which isn't accounted for in this code path), then we +should see some bugs in GDB, and indeed, this is what I do see. + +My plan for fixing this is to have the offset passed to +value::bits_synthetic_pointer always be a field offset within the value, +the value::embedded_offset will then be handled directly within the +various value::bits_synthetic_pointer implementations. + +This commit is a small refactor in preparation for this fix. + +I believe part of the confusion here is that we have functions that +take arguments called embedded_offset, when the value they should +accept is no longer the embedded offset. + +So in this commit I propose renaming the embedded_offset argument to +field_byte_offset in valprint_check_validity. This is purely a +mechanical rename, there should be no user visible changes after this +commit. + +Approved-By: Tom Tromey + +Commit 8915de0883c: gdb: remove embedded_offset argument that is always 0 + +gdb: remove embedded_offset argument that is always 0 + +For the background and motivation for this patch you should read the +previous commit. + +The goal of this commit started as similar to the previous one; rename +the embedded_offset argument to generic_val_print_ref. To do this I +started tracing back which values are passed into this function so I +could make sure the new argument name matched the usage. + +But it turns out that the only value that is ever passed to this +function is zero. + +I believe that, like the last commit, the embedded_offset is actually +representing the offset of a field within a value. However, in all of +the use cases, the "field" being accessed is the entire value, hence +why we always pass 0, we are asking about the whole value starting +from the very beginning. + +Given this, I couldn't bring myself to rename the argument. Let's just +remove it. It turns out that there's a bunch of functions in +valprint.c that take an argument called embedded_offset, which are +always zero. + +This commit removes the argument, and updates the code to assume zero. + +There should be no user visible changes after this commit. + +Approved-By: Tom Tromey + +Commit 8f65ab7b71f: gdb: use value::embedded_offset in check_pieced_synthetic_pointer + +gdb: use value::embedded_offset in check_pieced_synthetic_pointer + +This commit builds on the previous two commits. You should go and +read them both for context. + +Looking at the users of value::bits_synthetic_pointer, all callers but +two are now passing either the offset of a field within the value, or +hard-coded zero as they want to ask about the entire value. + +The two exceptions are in coerce_pieced_ref (in dwarf2/expr.c) and in +value_addr (in valops.c) where we still pass value::embedded_offset. + +The problem is that some of the other callers, where we currently pass +just a field offset, might also have a non-zero embedded offset, +specifically, the call in cp_print_value_fields is in this category. +In cp_print_value_fields we are printing fields from a value which is +potentially a base class contained within an instance of a derived +class, this will be represented by a non-zero embedded_offset, which +is currently not taken into account. + +Additionally, the two callers that currently pass the +value::embedded_offset don't scale the embedded offset from bytes to +bits. + +The failure to scale from bytes to bits is interesting, I originally +tried to create some tests that exposed this, but constantly failed. +It turns out, that in both these locations, in all code paths that I +could find, the value::embedded_offset will always be zero. For +example in coerce_pieced_ref, a reference cannot be a base class, and +so we never expect to see a non-zero embedded_offset in this case. + +Similarly, in value_addr, the use of value::bits_synthetic_pointer is +within a block that only applies to reference types, which means the +embedded_offset will be zero. + +Now within check_pieced_synthetic_pointer we already add the +value::offset into the bit_offset which is passed in. Remember, after +the previous two commits, the incoming bit_offset is (almost) always +the offset of a field within the value. The almost here is the two +cases mentioned above. + +I propose that within check_pieced_synthetic_pointer we should take +into account both value::offset and value::embedded_offset. Then the +two cases that currently pass value::embedded_offset can be changed to +just pass zero. + +With this done, and the previous two commits, we now have a consistent +model. value::bits_synthetic_pointer expects the (bit) offset of a +field within the value to check. In many cases this will be zero +meaning we want to check from the start of the value, but in some +cases it can be non-zero. + +Then within value::bits_synthetic_pointer implementations, like +check_pieced_synthetic_pointer, we will take the value::offset and +value::embedded_offset into account, remembering to scale them from +bytes to bits. + +I have also changed indirect_pieced_value to also take the +embedded_offset into account. I have done this for consistency rather +than necessity. I believe that the embedded_offset will always be +zero within indirect_pieced_value. The indirect_pieced_value function +is only called from value_ind (in valops.c), and only operates on +TYPE_CODE_PTR types (checked for in indirect_pieced_value). As a +TYPE_CODE_PTR cannot be the base class for a derived type, then we +don't expect to ever see a TYPE_CODE_PTR value with a non-zero +embedded_offset. But, having indirect_pieced_value take the embedded +offset into account is simple enough, and future proofs the code. + +In both check_pieced_synthetic_pointer and indirect_pieced_value I +have changed uses of '8' to 'TARGET_CHAR_BIT', I was already touching +some of these lines, and I think TARGET_CHAR_BIT is clearer, but one +line in indirect_pieced_value was just updated to use TARGET_CHAR_BIT, +this is done for consistency. + +The gdb.dwarf2/multi-piece-inherited-bitfield.exp test fails without +this patch, this exposes the case where the embedded_offset is +non-zero and we were previously failing to take this into account. + +The gdb.dwarf2/multi-piece-primitive-field.exp test was something I +wrote while trying to exercise the coerce_pieced_ref code path some +more. It is an inheritance based version of the existing test, I was +wondering if this would result in a value with a non-zero +embedded_offset, but due to how the fields are extracted from the +aggregate prior to calling coerce_pieced_ref the embedded_offset is +always zero in this function. + +Approved-By: Tom Tromey + +diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c +--- a/gdb/dwarf2/expr.c ++++ b/gdb/dwarf2/expr.c +@@ -492,7 +492,8 @@ check_pieced_synthetic_pointer (const value *value, LONGEST bit_offset, + piece_closure *c = (piece_closure *) value->computed_closure (); + int i; + +- bit_offset += 8 * value->offset (); ++ bit_offset += (TARGET_CHAR_BIT ++ * (value->offset () + value->embedded_offset ())); + if (value->bitsize ()) + bit_offset += value->bitpos (); + +@@ -537,8 +538,9 @@ indirect_pieced_value (value *value) + if (type->code () != TYPE_CODE_PTR) + return NULL; + +- int bit_length = 8 * type->length (); +- LONGEST bit_offset = 8 * value->offset (); ++ int bit_length = TARGET_CHAR_BIT * type->length (); ++ LONGEST bit_offset ++ = (TARGET_CHAR_BIT * (value->offset () + value->embedded_offset ())); + if (value->bitsize ()) + bit_offset += value->bitpos (); + +@@ -602,22 +604,62 @@ coerce_pieced_ref (const value *value) + { + struct type *type = check_typedef (value->type ()); + +- if (value->bits_synthetic_pointer (value->embedded_offset (), +- TARGET_CHAR_BIT * type->length ())) ++ if (value->bits_synthetic_pointer (0, TARGET_CHAR_BIT * type->length ())) + { + const piece_closure *closure + = (piece_closure *) value->computed_closure (); + frame_info_ptr frame + = get_selected_frame (_("No frame selected.")); + +- /* gdb represents synthetic pointers as pieced values with a single +- piece. */ +- gdb_assert (closure != NULL); +- gdb_assert (closure->pieces.size () == 1); ++ gdb_assert (closure != nullptr); ++ ++ /* The value::bits_synthetic_pointer will return true if multiple ++ pieces are used to cover VALUE, so long as each piece is ++ DWARF_VALUE_IMPLICIT_POINTER. I guess maybe this is possible, but ++ we've not seen such a case in the wild yet. For now then we look ++ in CLOSURE for a single DWARF_VALUE_IMPLICIT_POINTER piece that ++ covers VALUE. */ ++ LONGEST bit_offset = TARGET_CHAR_BIT * (value->embedded_offset () ++ + value->offset ()); ++ if (value->bitsize ()) ++ bit_offset += value->bitpos (); ++ int bit_length = TARGET_CHAR_BIT * type->length (); ++ ++ const dwarf_expr_piece *piece = nullptr; ++ for (size_t i = 0; i < closure->pieces.size (); i++) ++ { ++ const dwarf_expr_piece *p = &closure->pieces[i]; ++ size_t this_size_bits = p->size; ++ ++ if (bit_offset >= this_size_bits) ++ { ++ bit_offset -= this_size_bits; ++ continue; ++ } ++ ++ /* value::bits_synthetic_pointer does allow for multiple ++ pieces to describe the location of a single synthetic ++ pointer, or for a synthetic pointer to not start at the ++ exact start of a piece, however, we don't currently ++ support this case. */ ++ if (bit_offset != 0 || bit_length != this_size_bits) ++ error (_("unsupported value-piece configuration " ++ "bit_offset = %s, bit_length = %d, this_size_bits = %s"), ++ plongest (bit_offset), bit_length, ++ pulongest (this_size_bits)); ++ ++ piece = p; ++ break; ++ } ++ ++ /* If value::bits_synthetic_pointer returned true then we should have ++ found a suitable piece. */ ++ gdb_assert (piece != nullptr); ++ gdb_assert (piece->location == DWARF_VALUE_IMPLICIT_POINTER); + + return indirect_synthetic_pointer +- (closure->pieces[0].v.ptr.die_sect_off, +- closure->pieces[0].v.ptr.offset, ++ (piece->v.ptr.die_sect_off, ++ piece->v.ptr.offset, + closure->per_cu, closure->per_objfile, frame, type); + } + else +diff --git a/gdb/testsuite/gdb.dwarf2/multi-piece-inherited-bitfield.c b/gdb/testsuite/gdb.dwarf2/multi-piece-inherited-bitfield.c +new file mode 100644 +--- /dev/null ++++ b/gdb/testsuite/gdb.dwarf2/multi-piece-inherited-bitfield.c +@@ -0,0 +1,25 @@ ++/* Copyright (C) 2026 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 . */ ++ ++int target_var = 42; ++ ++int ++main (void) ++{ ++ asm ("main_label: .globl main_label"); ++ return 0; ++} +diff --git a/gdb/testsuite/gdb.dwarf2/multi-piece-inherited-bitfield.exp b/gdb/testsuite/gdb.dwarf2/multi-piece-inherited-bitfield.exp +new file mode 100644 +--- /dev/null ++++ b/gdb/testsuite/gdb.dwarf2/multi-piece-inherited-bitfield.exp +@@ -0,0 +1,151 @@ ++# 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 . ++ ++# Setup a derived struct that uses multiple inheritance and is described ++# by a multi-piece DWARF location. The second base class contains a ++# bitfield member. At one time, printing such a struct would incorrectly ++# display the bitfield as . ++# ++# The cause was that check_pieced_synthetic_pointer (dwarf2/expr.c) ++# computes the bit offset as 8 * value->offset() but does not add ++# 8 * value->embedded_offset(). When a base subobject is extracted via ++# primitive_field, it gets a non-zero embedded_offset but offset remains ++# zero. check_pieced_synthetic_pointer then checks piece 0 instead of ++# the correct piece, and if piece 0 happens to be an implicit pointer, ++# it incorrectly reports the bitfield as a synthetic pointer. ++ ++require allow_cplus_tests ++ ++load_lib dwarf.exp ++ ++# This test can only be run on targets which support DWARF-2 and use gas. ++require dwarf2_support ++ ++standard_testfile .c .S ++ ++set asm_file [standard_output_file ${srcfile2}] ++ ++# First compile the C file only, so we can query some type sizes. ++if {[prepare_for_testing "failed to prepare" ${testfile} ${srcfile}]} { ++ return ++} ++ ++Dwarf::assemble ${asm_file} { ++ cu {} { ++ DW_TAG_compile_unit { ++ {DW_AT_language @DW_LANG_C_plus_plus} ++ } { ++ declare_labels char_label int_label first_label second_label \ ++ derived_label target_label ++ set int_size [get_sizeof "int" -1] ++ ++ char_label: DW_TAG_base_type { ++ {DW_AT_byte_size 1 DW_FORM_udata} ++ {DW_AT_encoding @DW_ATE_unsigned_char} ++ {DW_AT_name "char"} ++ } ++ ++ int_label: DW_TAG_base_type { ++ {DW_AT_byte_size ${int_size} DW_FORM_udata} ++ {DW_AT_encoding @DW_ATE_signed} ++ {DW_AT_name "int"} ++ } ++ ++ first_label: DW_TAG_structure_type { ++ {DW_AT_name "First"} ++ {DW_AT_byte_size 1 DW_FORM_udata} ++ } { ++ DW_TAG_member { ++ {DW_AT_name "a"} ++ {DW_AT_type :${char_label}} ++ {DW_AT_data_member_location 0 DW_FORM_udata} ++ } ++ } ++ ++ second_label: DW_TAG_structure_type { ++ {DW_AT_name "Second"} ++ {DW_AT_byte_size 1 DW_FORM_udata} ++ } { ++ DW_TAG_member { ++ {DW_AT_name "bf"} ++ {DW_AT_type :${int_label}} ++ {DW_AT_data_member_location 0 DW_FORM_udata} ++ {DW_AT_bit_size 8 DW_FORM_udata} ++ } ++ } ++ ++ derived_label: DW_TAG_structure_type { ++ {DW_AT_name "Derived"} ++ {DW_AT_byte_size 2 DW_FORM_udata} ++ } { ++ DW_TAG_inheritance { ++ {DW_AT_type :${first_label}} ++ {DW_AT_data_member_location 0 DW_FORM_udata} ++ } ++ ++ DW_TAG_inheritance { ++ {DW_AT_type :${second_label}} ++ {DW_AT_data_member_location 1 DW_FORM_udata} ++ } ++ } ++ ++ target_label: DW_TAG_variable { ++ {DW_AT_name "target_var"} ++ {DW_AT_type :${int_label}} ++ {DW_AT_external 1 DW_FORM_flag} ++ {DW_AT_location { ++ DW_OP_addr [gdb_target_symbol "target_var"] ++ } SPECIAL_expr} ++ } ++ ++ DW_TAG_subprogram { ++ {MACRO_AT_func { "main" }} ++ {DW_AT_type :${int_label}} ++ {DW_AT_external 1 DW_FORM_flag} ++ } { ++ DW_TAG_variable { ++ {DW_AT_name "d"} ++ {DW_AT_type :${derived_label}} ++ {DW_AT_location { ++ DW_OP_GNU_implicit_pointer $target_label 0 ++ DW_OP_piece 1 ++ DW_OP_const1u 42 ++ DW_OP_stack_value ++ DW_OP_piece 1 ++ } SPECIAL_expr} ++ } ++ } ++ } ++ } ++} ++ ++# Now compile both C source and generated DWARF assembly. ++if {[prepare_for_testing "failed to prepare" ${testfile} \ ++ [list ${asm_file} ${srcfile}] {}]} { ++ return ++} ++ ++# Start the inferior so that 'd' is in scope. ++if {![runto_main]} { ++ return ++} ++ ++# With the bug in check_pieced_synthetic_pointer, the bitfield bf in the ++# Second base subobject is incorrectly reported as . ++# This happens because check_pieced_synthetic_pointer does not account for ++# embedded_offset, so it checks piece 0 (the implicit pointer covering ++# First) instead of piece 1 (the stack value covering Second). ++gdb_test "print d" " = { = {a = }, = {bf = 42}, }" \ ++ "bitfield in base subobject is not synthetic pointer" +diff --git a/gdb/testsuite/gdb.dwarf2/multi-piece-primitive-field.c b/gdb/testsuite/gdb.dwarf2/multi-piece-primitive-field.c +new file mode 100644 +--- /dev/null ++++ b/gdb/testsuite/gdb.dwarf2/multi-piece-primitive-field.c +@@ -0,0 +1,25 @@ ++/* Copyright (C) 2026 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 . */ ++ ++int target_var = 42; ++ ++int ++main (void) ++{ ++ asm ("main_label: .globl main_label"); ++ return 0; ++} +diff --git a/gdb/testsuite/gdb.dwarf2/multi-piece-primitive-field.exp b/gdb/testsuite/gdb.dwarf2/multi-piece-primitive-field.exp +new file mode 100644 +--- /dev/null ++++ b/gdb/testsuite/gdb.dwarf2/multi-piece-primitive-field.exp +@@ -0,0 +1,190 @@ ++# 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 . ++ ++# Setup a struct variable that is described by a multi-piece DWARF location. ++# One of the struct's fields is a C++ reference type implemented via ++# DW_OP_GNU_implicit_pointer. At one time, attempting to print such a field ++# would trigger an assertion in GDB. ++# ++# The cause was that primitive_field extracts the reference field and ++# set_component_location copies the parent's entire piece_closure (just ++# incrementing its refcount). Then coerce_pieced_ref would assert ++# closure->pieces.size() == 1, which failed because the closure still had ++# all the parent's pieces. ++# ++# This test confirms that this issue has now been fixed. ++ ++require allow_cplus_tests ++ ++load_lib dwarf.exp ++ ++# This test can only be run on targets which support DWARF-2 and use gas. ++require dwarf2_support ++ ++standard_testfile .c .S ++ ++set asm_file [standard_output_file ${srcfile2}] ++ ++# First compile the C file only, so we can query some type sizes. ++if {[prepare_for_testing "failed to prepare" ${testfile} ${srcfile}]} { ++ return ++} ++ ++Dwarf::assemble ${asm_file} { ++ cu {} { ++ DW_TAG_compile_unit { ++ {DW_AT_language @DW_LANG_C_plus_plus} ++ } { ++ declare_labels int_label struct_label ref_type_label target_label \ ++ first_label second_label derived_label ++ set int_size [get_sizeof "int" -1] ++ set addr_size [get_sizeof "void *" -1] ++ set struct_size [expr {$int_size + $addr_size}] ++ ++ int_label: DW_TAG_base_type { ++ {DW_AT_byte_size ${int_size} DW_FORM_udata} ++ {DW_AT_encoding @DW_ATE_signed} ++ {DW_AT_name "int"} ++ } ++ ++ ref_type_label: DW_TAG_reference_type { ++ {DW_AT_byte_size ${addr_size} DW_FORM_udata} ++ {DW_AT_type :${int_label}} ++ } ++ ++ struct_label: DW_TAG_structure_type { ++ {DW_AT_name "S"} ++ {DW_AT_byte_size ${struct_size} DW_FORM_udata} ++ } { ++ DW_TAG_member { ++ {DW_AT_name "x"} ++ {DW_AT_type :${int_label}} ++ {DW_AT_data_member_location 0 DW_FORM_udata} ++ } ++ ++ DW_TAG_member { ++ {DW_AT_name "ref"} ++ {DW_AT_type :${ref_type_label}} ++ {DW_AT_data_member_location ${int_size} DW_FORM_udata} ++ } ++ } ++ ++ first_label: DW_TAG_structure_type { ++ {DW_AT_name "first"} ++ {DW_AT_byte_size ${int_size} DW_FORM_udata} ++ } { ++ DW_TAG_member { ++ {DW_AT_name "x"} ++ {DW_AT_type :${int_label}} ++ {DW_AT_data_member_location 0 DW_FORM_udata} ++ } ++ } ++ ++ second_label: DW_TAG_structure_type { ++ {DW_AT_name "second"} ++ {DW_AT_byte_size ${addr_size} DW_FORM_udata} ++ } { ++ DW_TAG_member { ++ {DW_AT_name "ref"} ++ {DW_AT_type :${ref_type_label}} ++ {DW_AT_data_member_location 0 DW_FORM_udata} ++ } ++ } ++ ++ derived_label: DW_TAG_structure_type { ++ {DW_AT_name "derived"} ++ {DW_AT_byte_size ${struct_size} DW_FORM_udata} ++ } { ++ DW_TAG_inheritance { ++ {DW_AT_type :${first_label}} ++ {DW_AT_data_member_location 0 DW_FORM_udata} ++ } ++ ++ DW_TAG_inheritance { ++ {DW_AT_type :${second_label}} ++ {DW_AT_data_member_location ${int_size} DW_FORM_udata} ++ } ++ } ++ ++ target_label: DW_TAG_variable { ++ {DW_AT_name "target_var"} ++ {DW_AT_type :${int_label}} ++ {DW_AT_external 1 DW_FORM_flag} ++ {DW_AT_location { ++ DW_OP_addr [gdb_target_symbol "target_var"] ++ } SPECIAL_expr} ++ } ++ ++ DW_TAG_subprogram { ++ {MACRO_AT_func { "main" }} ++ {DW_AT_type :${int_label}} ++ {DW_AT_external 1 DW_FORM_flag} ++ } { ++ DW_TAG_variable { ++ {DW_AT_name "s"} ++ {DW_AT_type :${struct_label}} ++ {DW_AT_location { ++ DW_OP_const4u 123 ++ DW_OP_stack_value ++ DW_OP_piece $int_size ++ DW_OP_GNU_implicit_pointer $target_label 0 ++ DW_OP_piece $addr_size ++ } SPECIAL_expr} ++ } ++ ++ DW_TAG_variable { ++ {DW_AT_name "d"} ++ {DW_AT_type :${derived_label}} ++ {DW_AT_location { ++ DW_OP_const4u 456 ++ DW_OP_stack_value ++ DW_OP_piece $int_size ++ DW_OP_GNU_implicit_pointer $target_label 0 ++ DW_OP_piece $addr_size ++ } SPECIAL_expr} ++ } ++ } ++ } ++ } ++} ++ ++# Now compile both C source and generated DWARF assembly. ++if {[prepare_for_testing "failed to prepare" ${testfile} \ ++ [list ${asm_file} ${srcfile}] {}]} { ++ return ++} ++ ++# Start the inferior so that 's' is in scope. ++if {![runto_main]} { ++ return ++} ++ ++# Printing `s` used to crash with an assertion failure in coerce_pieced_ref ++# because the ref field inherited the parent struct's multi-piece closure. ++gdb_test "print s" " = {x = 123, ref = @$hex}" \ ++ "print multi-piece struct with implicit pointer reference field" ++ ++gdb_test "print s.ref" " = \\(int &\\) @$hex: 42" \ ++ "print ref member of multi-piece struct" ++ ++# Same test but with inheritance. The reference field is in the ++# second base class, so it reaches coerce_pieced_ref through a ++# different path to the above case. ++gdb_test "print d" \ ++ " = { = {x = 456}, = {ref = @$hex}, }" \ ++ "print inherited multi-piece struct with implicit pointer ref" ++ ++gdb_test "print d.ref" " = \\(int &\\) @$hex: 42" \ ++ "print ref member of inherited multi-piece struct" +diff --git a/gdb/valops.c b/gdb/valops.c +--- a/gdb/valops.c ++++ b/gdb/valops.c +@@ -1545,8 +1545,7 @@ value_addr (struct value *arg1) + + if (TYPE_IS_REFERENCE (type)) + { +- if (arg1->bits_synthetic_pointer (arg1->embedded_offset (), +- TARGET_CHAR_BIT * type->length ())) ++ if (arg1->bits_synthetic_pointer (0, TARGET_CHAR_BIT * type->length ())) + arg1 = coerce_ref (arg1); + else + { +diff --git a/gdb/valprint.c b/gdb/valprint.c +--- a/gdb/valprint.c ++++ b/gdb/valprint.c +@@ -92,7 +92,6 @@ static void set_output_radix_1 (int, unsigned); + + static void val_print_type_code_flags (struct type *type, + struct value *original_value, +- int embedded_offset, + struct ui_file *stream); + + /* Start print_max at this value. */ +@@ -310,11 +309,9 @@ show_symbol_print (struct ui_file *file, int from_tty, + + + +-/* A helper function for val_print. When printing in "summary" mode, +- we want to print scalar arguments, but not aggregate arguments. +- This function distinguishes between the two. */ ++/* See valprint.h. */ + +-int ++bool + val_print_scalar_type_p (struct type *type) + { + type = check_typedef (type); +@@ -330,9 +327,9 @@ val_print_scalar_type_p (struct type *type) + case TYPE_CODE_UNION: + case TYPE_CODE_SET: + case TYPE_CODE_STRING: +- return 0; ++ return false; + default: +- return 1; ++ return true; + } + } + +@@ -350,10 +347,10 @@ val_print_scalar_or_string_type_p (struct type *type, + + /* See valprint.h. */ + +-int ++bool + valprint_check_validity (struct ui_file *stream, + struct type *type, +- LONGEST embedded_offset, ++ LONGEST field_byte_offset, + const struct value *val) + { + type = check_typedef (type); +@@ -361,31 +358,31 @@ valprint_check_validity (struct ui_file *stream, + if (type_not_associated (type)) + { + val_print_not_associated (stream); +- return 0; ++ return false; + } + + if (type_not_allocated (type)) + { + val_print_not_allocated (stream); +- return 0; ++ return false; + } + + if (type->code () != TYPE_CODE_UNION + && type->code () != TYPE_CODE_STRUCT + && type->code () != TYPE_CODE_ARRAY) + { +- if (val->bits_any_optimized_out (TARGET_CHAR_BIT * embedded_offset, ++ if (val->bits_any_optimized_out (TARGET_CHAR_BIT * field_byte_offset, + TARGET_CHAR_BIT * type->length ())) + { + val_print_optimized_out (val, stream); +- return 0; ++ return false; + } + +- if (val->bits_synthetic_pointer (TARGET_CHAR_BIT * embedded_offset, ++ if (val->bits_synthetic_pointer (TARGET_CHAR_BIT * field_byte_offset, + TARGET_CHAR_BIT * type->length ())) + { +- const int is_ref = type->code () == TYPE_CODE_REF; +- int ref_is_addressable = 0; ++ const bool is_ref = type->code () == TYPE_CODE_REF; ++ bool ref_is_addressable = false; + + if (is_ref) + { +@@ -403,14 +400,14 @@ valprint_check_validity (struct ui_file *stream, + return is_ref; + } + +- if (!val->bytes_available (embedded_offset, type->length ())) ++ if (!val->bytes_available (field_byte_offset, type->length ())) + { + val_print_unavailable (stream); +- return 0; ++ return false; + } + } + +- return 1; ++ return true; + } + + void +@@ -595,14 +592,13 @@ generic_value_print_ptr (struct value *val, struct ui_file *stream, + + static void + print_ref_address (struct type *type, const gdb_byte *address_buffer, +- int embedded_offset, struct ui_file *stream) ++ struct ui_file *stream) + { + struct gdbarch *gdbarch = type->arch (); + + if (address_buffer != NULL) + { +- CORE_ADDR address +- = extract_typed_address (address_buffer + embedded_offset, type); ++ CORE_ADDR address = extract_typed_address (address_buffer, type); + + gdb_printf (stream, "@"); + gdb_puts (paddress (gdbarch, address), stream); +@@ -631,32 +627,27 @@ get_value_addr_contents (struct value *deref_val) + + static void + generic_val_print_ref (struct type *type, +- int embedded_offset, struct ui_file *stream, int recurse, ++ struct ui_file *stream, int recurse, + struct value *original_value, + const struct value_print_options *options) + { + struct type *elttype = check_typedef (type->target_type ()); + struct value *deref_val = NULL; + const bool value_is_synthetic +- = original_value->bits_synthetic_pointer (TARGET_CHAR_BIT * embedded_offset, +- TARGET_CHAR_BIT * type->length ()); +- const int must_coerce_ref = ((options->addressprint && value_is_synthetic) +- || options->deref_ref); +- const int type_is_defined = elttype->code () != TYPE_CODE_UNDEF; ++ = original_value->bits_synthetic_pointer (0, (TARGET_CHAR_BIT ++ * type->length ())); ++ const bool must_coerce_ref = ((options->addressprint && value_is_synthetic) ++ || options->deref_ref); ++ const bool type_is_defined = elttype->code () != TYPE_CODE_UNDEF; + const gdb_byte *valaddr = original_value->contents_for_printing ().data (); + + if (must_coerce_ref && type_is_defined) + { + deref_val = coerce_ref_if_computed (original_value); + +- if (deref_val != NULL) +- { +- /* More complicated computed references are not supported. */ +- gdb_assert (embedded_offset == 0); +- } +- else ++ if (deref_val == nullptr) + deref_val = value_at (type->target_type (), +- unpack_pointer (type, valaddr + embedded_offset)); ++ unpack_pointer (type, valaddr)); + } + /* Else, original_value isn't a synthetic reference or we don't have to print + the reference's contents. +@@ -676,7 +667,7 @@ generic_val_print_ref (struct type *type, + ? get_value_addr_contents (deref_val) + : valaddr); + +- print_ref_address (type, address, embedded_offset, stream); ++ print_ref_address (type, address, stream); + + if (options->deref_ref) + gdb_puts (": ", stream); +@@ -718,7 +709,7 @@ generic_val_print_enum_1 (struct type *type, LONGEST val, + } + else if (type->is_flag_enum ()) + { +- int first = 1; ++ bool first = true; + + /* We have a "flag" enum, so we try to decompose it into pieces as + appropriate. The enum may have multiple enumerators representing +@@ -738,7 +729,7 @@ generic_val_print_enum_1 (struct type *type, LONGEST val, + if (first) + { + gdb_puts ("(", stream); +- first = 0; ++ first = false; + } + else + gdb_puts (" | ", stream); +@@ -780,20 +771,15 @@ generic_val_print_enum_1 (struct type *type, LONGEST val, + /* generic_val_print helper for TYPE_CODE_ENUM. */ + + static void +-generic_val_print_enum (struct type *type, +- int embedded_offset, struct ui_file *stream, ++generic_val_print_enum (struct type *type, struct ui_file *stream, + struct value *original_value, + const struct value_print_options *options) + { +- LONGEST val; +- struct gdbarch *gdbarch = type->arch (); +- int unit_size = gdbarch_addressable_memory_unit_size (gdbarch); +- + gdb_assert (!options->format); + + const gdb_byte *valaddr = original_value->contents_for_printing ().data (); + +- val = unpack_long (type, valaddr + embedded_offset * unit_size); ++ LONGEST val = unpack_long (type, valaddr); + + generic_val_print_enum_1 (type, val, stream); + } +@@ -801,8 +787,7 @@ generic_val_print_enum (struct type *type, + /* generic_val_print helper for TYPE_CODE_FUNC and TYPE_CODE_METHOD. */ + + static void +-generic_val_print_func (struct type *type, +- int embedded_offset, CORE_ADDR address, ++generic_val_print_func (struct type *type, CORE_ADDR address, + struct ui_file *stream, + struct value *original_value, + const struct value_print_options *options) +@@ -1015,7 +1000,7 @@ generic_value_print (struct value *val, struct ui_file *stream, int recurse, + + case TYPE_CODE_REF: + case TYPE_CODE_RVALUE_REF: +- generic_val_print_ref (type, 0, stream, recurse, ++ generic_val_print_ref (type, stream, recurse, + val, options); + break; + +@@ -1023,14 +1008,14 @@ generic_value_print (struct value *val, struct ui_file *stream, int recurse, + if (options->format) + value_print_scalar_formatted (val, options, 0, stream); + else +- generic_val_print_enum (type, 0, stream, val, options); ++ generic_val_print_enum (type, stream, val, options); + break; + + case TYPE_CODE_FLAGS: + if (options->format) + value_print_scalar_formatted (val, options, 0, stream); + else +- val_print_type_code_flags (type, val, 0, stream); ++ val_print_type_code_flags (type, val, stream); + break; + + case TYPE_CODE_FUNC: +@@ -1038,7 +1023,7 @@ generic_value_print (struct value *val, struct ui_file *stream, int recurse, + if (options->format) + value_print_scalar_formatted (val, options, 0, stream); + else +- generic_val_print_func (type, 0, val->address (), stream, ++ generic_val_print_func (type, val->address (), stream, + val, options); + break; + +@@ -1190,11 +1175,11 @@ val_print_check_max_depth (struct ui_file *stream, int recurse, + return false; + } + +-/* Check whether the value VAL is printable. Return 1 if it is; +- return 0 and print an appropriate error message to STREAM according to +- OPTIONS if it is not. */ ++/* Check whether the value VAL is printable. Return true if it is; ++ return false and print an appropriate error message to STREAM ++ according to OPTIONS if it is not. */ + +-static int ++static bool + value_check_printable (struct value *val, struct ui_file *stream, + const struct value_print_options *options) + { +@@ -1202,7 +1187,7 @@ value_check_printable (struct value *val, struct ui_file *stream, + { + fprintf_styled (stream, metadata_style.style (), + _("
")); +- return 0; ++ return false; + } + + if (val->entirely_optimized_out ()) +@@ -1211,7 +1196,7 @@ value_check_printable (struct value *val, struct ui_file *stream, + gdb_printf (stream, "..."); + else + val_print_optimized_out (val, stream); +- return 0; ++ return false; + } + + if (val->entirely_unavailable ()) +@@ -1220,7 +1205,7 @@ value_check_printable (struct value *val, struct ui_file *stream, + gdb_printf (stream, "..."); + else + val_print_unavailable (stream); +- return 0; ++ return false; + } + + if (val->type ()->code () == TYPE_CODE_INTERNAL_FUNCTION) +@@ -1228,16 +1213,16 @@ value_check_printable (struct value *val, struct ui_file *stream, + fprintf_styled (stream, metadata_style.style (), + _(""), + value_internal_function_name (val)); +- return 0; ++ return false; + } + + if (type_not_allocated (val->type ())) + { + val_print_not_allocated (stream); +- return 0; ++ return false; + } + +- return 1; ++ return true; + } + + /* See valprint.h. */ +@@ -1292,10 +1277,9 @@ debug_val (struct value *val) + + static void + val_print_type_code_flags (struct type *type, struct value *original_value, +- int embedded_offset, struct ui_file *stream) ++ struct ui_file *stream) + { +- const gdb_byte *valaddr = (original_value->contents_for_printing ().data () +- + embedded_offset); ++ const gdb_byte *valaddr = (original_value->contents_for_printing ().data ()); + ULONGEST val = unpack_long (type, valaddr); + int field, nfields = type->num_fields (); + struct gdbarch *gdbarch = type->arch (); +@@ -3188,7 +3172,7 @@ test_print_flags (gdbarch *arch) + store_unsigned_integer (contents, 4, gdbarch_byte_order (arch), 0xaa); + + string_file out; +- val_print_type_code_flags (flags_type, val, 0, &out); ++ val_print_type_code_flags (flags_type, val, &out); + SELF_CHECK (out.string () == "[ A=2 B=1 C=5 ]"); + } + +diff --git a/gdb/valprint.h b/gdb/valprint.h +--- a/gdb/valprint.h ++++ b/gdb/valprint.h +@@ -197,17 +197,15 @@ extern void print_function_pointer_address (const struct value_print_options *op + + /* Helper function to check the validity of some bits of a value. + +- If TYPE represents some aggregate type (e.g., a structure), return 1. ++ If TYPE represents some aggregate type (e.g., a structure), return true. + +- Otherwise, any of the bytes starting at OFFSET and extending for +- TYPE->length () bytes are invalid, print a message to STREAM and +- return 0. The checking is done using FUNCS. ++ For non-aggregate TYPEs, if any of the bytes starting at FIELD_BYTE_OFFSET ++ and extending for TYPE->length () bytes are invalid, print a message to ++ STREAM and return false. Otherwise, return true. */ + +- Otherwise, return 1. */ +- +-extern int valprint_check_validity (struct ui_file *stream, struct type *type, +- LONGEST embedded_offset, +- const struct value *val); ++extern bool valprint_check_validity (struct ui_file *stream, struct type *type, ++ LONGEST field_byte_offset, ++ const struct value *val); + + extern void val_print_optimized_out (const struct value *val, + struct ui_file *stream); +@@ -272,7 +270,11 @@ extern void generic_printstr (struct ui_file *stream, struct type *type, + + extern void output_command (const char *args, int from_tty); + +-extern int val_print_scalar_type_p (struct type *type); ++/* When printing in "summary" mode we want to print scalar arguments ++ but not aggregate arguments. Return true if TYPE is a scalar type, ++ false if it is an aggregate. */ ++ ++extern bool val_print_scalar_type_p (struct type *type); + + struct format_data + { diff --git a/gdb-rhel-150992-index-generation-fixes.patch b/gdb-rhel-150992-index-generation-fixes.patch deleted file mode 100644 index 9ff6853..0000000 --- a/gdb-rhel-150992-index-generation-fixes.patch +++ /dev/null @@ -1,1272 +0,0 @@ -From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 -From: Keith Seitz -Date: Thu, 19 Feb 2026 10:14:13 -0800 -Subject: gdb-rhel-150992-index-generation-fixes.patch - -;; Backport of three upstream patches. The first two relate to index -;; generation, while the third relates to symbol lookup, but is needed -;; so that the tests from the earlier two patches will pass. -;; -;; (Tom Tromey and Andrew Burgess, RHEL-150992) - -Upstream commit: 0fa4f62568b1deeb071c4a6ae8a1671c8ec7ba6e -========================================================= - -Emit some type declarations in .gdb_index - -If you run struct-decl.exp with the .gdb_index board, you will see -that "the_type" is not emitted in the index. This would cause a -failure in this series. The fix is to ensure that certain necessary -type declarations are emitted. - -However, a naive fix here will regress stub-array-size.exp, where a -type declaration and a type definition are both seen -- but the -declaration is seen first and causes a failure. This is handled by -adding some code (including a mild hack) to filter out type -declarations when a corresponding type definition is seen. - -Acked-By: Simon Marchi - -Upstream commit: ccd56e22703c094e5b6d0025e28059277ed8d8c5 -========================================================= - -gdb: remove attempted type de-duplication when building gdb-index - -This commit removes the attempted de-duplication of types when -building the gdb-index. This commit is the natural extension of this -earlier commit: - - commit aef36dee93bf194cb0b976a4ae49a79a736a188d - Date: Sun Aug 13 14:08:06 2023 +0200 - - [gdb/symtab] Don't deduplicate variables in gdb-index - -Which removed the de-duplication of variables. It is worth reading -the earlier commit as all the justifications for that patch also -apply to this one. - -Currently, when building the gdb-index we sort the type entries, -moving declarations to the end of the entry list, and non-declarations -to the front. Then within each group, declarations, and -non-declarations, the index entries are sorted by CU offset. - -We then emit the first entry for any given type name. - -There are two problems with this. - -First, a non-declaration entry could be a definition, but it could -also be a typedef. Now sure, a typedef is a type definition, but not -necessarily a useful one. - -If we have a header file that contains: - - typedef struct foo_t foo_t; - -And a CU which makes use of 'foo_t', then the CU will include both a -typedef and a type declaration. The target of the typedef will be the -declaration. But notice, the CU will not include a type definition. - -If we have two CUs, one which only sees the above typedef and -declaration, and another which sees the typedef and an actual type -definition, then the final list of entries for this type's name will -be: - - 1. A typedef entry that points at the declaration. - 2. A typedef entry that points at the definition. - 3. A definition. - 4. A declaration. - -Now (4) will get sorted to the end of the entry list. But the order -of (1), (2), and (3) will depend on the CU offset. If the CU which -containing the typedef and declaration has the smallest offset, -then (1) will be sorted to the front of the list of entries for this -type name. Due to the de-duplication code this means that only (1) -will be added to the gdb-index. - -After GDB starts and parses the index, if a user references 'foo_t' -GDB will look in the index and find just (1). GDB loads the CU -containing (1) and finds both the typedef and the declaration. But -GDB does not find the full type definition. As a result GDB will -display 'foo_t' as an incomplete type. - -This differs from the behaviour when no index is used. With no index -GDB expands the first CU containing 'foo_t', finds the typedef and -type declaration, decides that this is not good enough and carries on. -GDB will then expand the second CU and find the type's definition, GDB -now has a full understanding of the type, and can print the type -correctly. - -We could solve this problem by marking typedefs as a distinct -sub-category of types, just as we do with declarations. Then we could -sort definitions to the front of the list, then typedefs, and finally, -declarations after that. This would, I think, mean that we always -prefer emitting a definition for a type, which would resolve this -first problem, or at least, it would resolve it well enough, but it -wouldn't fix the second problem. - -The second problem is that the Python API and the 'info types' command -can be used to query all type symbols. As such, GDB needs to be able -to find all the CUs which contain a given type. Especially as it is -possible that a type might be defined differently within different -CUs. - -NOTE: Obviously a program doing this (defining a type differently in - different CUs) would need to be mindful of the One Definition Rule, - but so long as the type doesn't escape outside of a single CU then - reusing a type name isn't, as I understand it, wrong. And even if - it is, the fact that it compiles, and could be a source of bugs, - means (in my opinion) that GDB should handle this case to enable - debugging of it. - -Even something as simple as 'info types ....' relies on GDB being able -to find multiple entries for a given type in different CUs. If the -index only contains a single type entry, then this means GDB will see -different things depending on which CUs happen to have been expanded. - -Given all of the above, I think that any attempt to remove type -entries from the gdb-index is unsafe and can result in GDB behaving -differently when using the gdb-index compared to using no index. - -The solution is to remove the de-duplication code, which is what this -patch does. - -Now that we no longer need to sort declarations to the end of the -entry list, I've removed all the code related to the special use of -GDB_INDEX_SYMBOL_KIND_UNUSED5 (which is how we marked declarations), -this cleans things up a little bit. - -I've also renamed some of the functions away from minimize, now that -there's no minimization being done. - -A problem was revealed by this change. When running the test -gdb.cp/stub-array-size.exp with the --target_board=cc-with-gdb-index, -I was seeing a failure using gcc 15.1.0. - -This test has two CUs, and a type 'A'. The test description says: - - Test size of arrays of stubbed types (structures where the full - definition is not immediately available). - -Which I don't really understand given the test's source code. The -type 'A' is defined in a header, which is included in both CUs. -However, the test description does seem to be accurate; in one CU the -type looks like this: - - <1><4a>: Abbrev Number: 8 (DW_TAG_structure_type) - <4b> DW_AT_name : A - <4d> DW_AT_declaration : 1 - <4d> DW_AT_sibling : <0x6d> - <2><51>: Abbrev Number: 9 (DW_TAG_subprogram) - <52> DW_AT_external : 1 - <52> DW_AT_name : ~A - <55> DW_AT_decl_file : 2 - <56> DW_AT_decl_line : 20 - <57> DW_AT_decl_column : 11 - <58> DW_AT_linkage_name: (indirect string, offset: 0x103): _ZN1AD4Ev - <5c> DW_AT_virtuality : 1 (virtual) - <5d> DW_AT_containing_type: <0x4a> - <61> DW_AT_declaration : 1 - <61> DW_AT_object_pointer: <0x66> - <65> DW_AT_inline : 0 (not inlined) - <3><66>: Abbrev Number: 10 (DW_TAG_formal_parameter) - <67> DW_AT_type : <0x8c> - <6b> DW_AT_artificial : 1 - <3><6b>: Abbrev Number: 0 - <2><6c>: Abbrev Number: 0 - -while in the second CU, the type looks like this: - - <1><178>: Abbrev Number: 4 (DW_TAG_structure_type) - <179> DW_AT_name : A - <17b> DW_AT_byte_size : 8 - <17c> DW_AT_decl_file : 2 - <17d> DW_AT_decl_line : 18 - <17e> DW_AT_decl_column : 8 - <17f> DW_AT_containing_type: <0x178> - <183> DW_AT_sibling : <0x1ac> - <2><187>: Abbrev Number: 5 (DW_TAG_member) - <188> DW_AT_name : (indirect string, offset: 0x19e): _vptr.A - <18c> DW_AT_type : <0x1be> - <190> DW_AT_data_member_location: 0 - <191> DW_AT_artificial : 1 - <2><191>: Abbrev Number: 6 (DW_TAG_subprogram) - <192> DW_AT_external : 1 - <192> DW_AT_name : ~A - <195> DW_AT_decl_file : 1 - <196> DW_AT_decl_line : 20 - <197> DW_AT_decl_column : 1 - <198> DW_AT_linkage_name: (indirect string, offset: 0x103): _ZN1AD4Ev - <19c> DW_AT_virtuality : 1 (virtual) - <19d> DW_AT_containing_type: <0x178> - <1a1> DW_AT_declaration : 1 - <1a1> DW_AT_object_pointer: <0x1a5> - <3><1a5>: Abbrev Number: 7 (DW_TAG_formal_parameter) - <1a6> DW_AT_type : <0x1cd> - <1aa> DW_AT_artificial : 1 - <3><1aa>: Abbrev Number: 0 - <2><1ab>: Abbrev Number: 0 - -So, for reasons that I don't understand, the type, despite (as far as -I can see) having its full definition available, is recorded only as -declared in one CU. - -The test then performs some actions that rely on 'sizeof(A)' and -expects GDB to correctly figure out the size. This requires GDB to -find, and expand the CU containing the real definition of 'A'. - -Prior to this patch GDB would sort the two type entries for 'A', -placing the declaration second, and then record only one entry, the -definition. When it came to expansion there was only one thing to -expand, and this is the declaration we needed. It happens that in -this test the definition is in the second CU, that is, the CU with the -biggest offset. This means that, if all index entries were considered -equal, the definition entry would be second. However, currently, due -to the way GDB forces definitions to the front, the entry for the -second CU, the definition, is placed first in the index, and with -de-duplication, this is the only entry added to the index. - -After this patch, both the declaration and the definition are placed -in the index, and as the declaration is in the CU at offset 0, the -declaration is added first to the index. - -This should be fine. When looking for 'A' GDB should expand the CU -containing the declaration, see that all we have is a declaration, and -so continue, next expanding the definition, at which point we're done. - -However, in read-gdb-index.c, in the function -mapped_gdb_index::build_name_components, there is a work around for -gold bug PR gold/15646. Ironically, the bug here is that gold was not -removing duplicate index entries, and it is noted that this has a -performance impact on GDB. A work around for this was added to GDB in -commit: - - commit 8943b874760d9cf35b71890a70af9866e4fab2a6 - Date: Tue Nov 12 09:43:17 2013 -0800 - - Work around gold/15646. - -A test for this was added in: - - commit 40d22035a7fc239ac1e944b75a2e3ee9029d1b76 - Date: Tue May 26 11:35:32 2020 +0200 - - [gdb/testsuite] Add test-case gold-gdb-index.exp - -And the fix was tweaked in commit: - - commit f030440daa989ae3dadc1fa4342cfa16d690db3c - Date: Thu May 28 17:26:22 2020 +0200 - - [gdb/symtab] Make gold index workaround more precise - -The problem specifically called out in the bug report is that -namespaces can appear in multiple CUs, and that trying to complete -'ns::misspelled' would expand every CU containing namespace 'ns' due -to the duplicate 'ns' type symbols. - -The work around that was added in 8943b874760d9cf3 was to ignore -duplicate global symbols when expanding entries from the index. In -commit f030440daa989ae3 this work around was restricted to only ignore -duplicate type entries. This restriction was required to allow the -earlier de-duplication patch aef36dee93bf194c to function correctly. - -Now that I'm taking the work started in aef36dee93bf194c to its -logical conclusion, and allowing duplicate type entries, the work -around of ignoring duplicate global type symbols is no longer needed, -and can be removed. - -The associated test for this, added in 40d22035a7fc239a, is also -removed in this commit. - -To be clear; the performance issue mentioned in PR gold/15646 is now -back again. But my claim is that gold was right all along to include -the duplicate index entries, and any performance hit we see as a -result, though unfortunate, is just a consequence of doing it right. - -That doesn't mean there's not room for optimisation and improvement in -the future, though I don't have any immediate ideas, or plans in this -area. It's just we can't throw out a bunch of index entries that are -critical, and claim this as a performance optimisation. - -I am seeing some failure with this patch when using the board file -dwarf5-fission-debug-types. These failures all have the error: - - DWARF Error: wrong unit_type in unit header (is DW_UT_skeleton, should be DW_UT_type) [in module ....] - -However, I ran the whole testsuite with this board, and this error -crops up often, so I don't think this is something specific to my -patch, so I'm choosing to ignore this. - -Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=15646 -Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=15035 - -Approved-By: Tom Tromey - -Upstream commit: c09ebee0d3ae9148e33aa861cbd857bf2d4100ba -========================================================= - -gdb: symbol_search objects of different types are not the same - -Consider the C construct: - - typedef struct foo - { - int a; - int b; - } foo; - -GDB will see two types here, 'struct foo' and the typedef 'foo'. -However, if we use 'info types foo' we will see this: - - File test.c: - 18: struct foo; - -At least that's what I see with current HEAD of master. However, it -is really just luck that we see the 'struct' here. See more below. - -When searching for symbols matching 'foo' GDB ends up in the function -global_symbol_searcher::add_matching_symbols, where we consider all -possible matching symbols. This will include the 'struct foo' and the -typedef 'foo'. However, before a new symbols is added to the results, -we attempt to remove duplicates with this code: - - /* Match, insert if not already in the results. */ - symbol_search ss (block, sym); - if (result_set->find (ss) == result_set->end ()) - result_set->insert (ss); - -If a symbol is already present in result_set then it will not be added -a second time. - -The symbol_search equality check is done using the function -symbol_search::compare_search_syms, this function does a number of -checks, but at the end, any two symbols that are in the same block -within the same file, with the same name, are considered the same, -even if the types of those symbols are different. - -This makes sense in most cases, it usually wouldn't make sense to have -two symbols within a single block with different types. But the -'struct foo' and typedef 'foo' case is a bit of a strange one. Within -DWARF and GDB we consider both of these as just types. But in C -types and structure names live in different namespaces, and so we can -have both in the same block. I don't think that GDB should consider -these two as the same, especially if we consider something really -ill-advised like this: - - struct foo - { - int a; - int b; - }; - - typedef int foo; - -This is perfectly valid C code, 'struct foo' and the typedef 'foo' are -in different namespaces, and can be used within the same block. But -please, never write C code like this. - -Given the above, I think, when asked about 'foo', GDB should, report -both 'struct foo' and the typedef 'foo'. - -To do this I propose extending symbol_search::compare_search_syms such -that if two symbol_search objects are in the same block, within the -same file, and they have the same name, then if just one of them is a -typedef, the two objects will not be considered equal. The results -will be sorted by line number if the line numbers are different, or, -if the line numbers are the same, the non-typedef will be sorted -first. This means that for something like this: - - typedef struct foo { int a; } foo; - -We'll get an 'info types foo' result like: - - File test.c: - 18: struct foo; - 18: typedef struct foo foo; - -I mentioned earlier that it is really just luck that we see 'struct -foo'. I ran into this problem while working on another patch. When -testing with the 'debug-types' board file I was seeing the typedef -being reported rather than the struct. In "normal" DWARF given the -'typedef struct foo { ...} foo;' construct, the compiler will usually -emit the struct definition first, and then the typedef definition. So -when GDB parses the DWARF it sees the struct first. It is the typedef -that becomes the duplicate which is not added to the results list. - -But with the 'debug-types' board the compiler moves the struct -definition out to the .debug_types section. And GDB now parses the CU -containing the typedef first, and then expands the structure -definition from the separate section afterwards. As a result, it is -the structure that is now considered the duplicate, and the typedef is -the result that gets reported. - -I think this is yet another motivation for this patch. Changes like -this (the use of .debug_types section) shouldn't impact what results -GDB shows to the user. - -There is an interesting update to the gdb.base/info-types.exp.tcl test -script. In this case the C results only needed to change to include -the typedef. The C++ results already included both the struct and the -typedef in the expected results. The reason for this is that C places -both the struct baz_t and the typedef for baz_t into the global block, -while C++ places the struct in the global block, and the typedef into -the static block. I have no idea why there's a difference in the -placement, but I'm choosing to believe the difference is correct. But -this explains why only the C results needed to change. If anything -this (I think) is yet another justification for this change; having C -not show the typedef in this case seems weird when the same source -code compiled as C++ does show the typedef. - -Approved-By: Tom Tromey - -diff --git a/gdb/dwarf2/index-write.c b/gdb/dwarf2/index-write.c ---- a/gdb/dwarf2/index-write.c -+++ b/gdb/dwarf2/index-write.c -@@ -58,7 +58,7 @@ - #define DW2_GDB_INDEX_SYMBOL_KIND_SET_VALUE(cu_index, value) \ - do { \ - gdb_assert ((value) >= GDB_INDEX_SYMBOL_KIND_TYPE \ -- && (value) <= GDB_INDEX_SYMBOL_KIND_OTHER); \ -+ && (value) < GDB_INDEX_SYMBOL_KIND_UNUSED5); \ - GDB_INDEX_SYMBOL_KIND_SET_VALUE((cu_index), (value)); \ - } while (0) - -@@ -178,9 +178,8 @@ struct symtab_index_entry - of this name. */ - std::vector cu_indices; - -- /* Minimize CU_INDICES, sorting them and removing duplicates as -- appropriate. */ -- void minimize (); -+ /* Sort CU_INDICES. */ -+ void sort (); - }; - - /* The symbol table. This is a power-of-2-sized hash table. */ -@@ -192,16 +191,16 @@ struct mapped_symtab - } - - /* If there are no elements in the symbol table, then reduce the table -- size to zero. Otherwise call symtab_index_entry::minimize each entry -+ size to zero. Otherwise call symtab_index_entry::sort each entry - in the symbol table. */ - -- void minimize () -+ void minimize_and_sort () - { - if (m_element_count == 0) - m_data.resize (0); - - for (symtab_index_entry &item : m_data) -- item.minimize (); -+ item.sort (); - } - - /* Add an entry to SYMTAB. NAME is the name of the symbol. CU_INDEX is -@@ -410,32 +409,19 @@ mapped_symtab::add_index_entry (const char *name, int is_static, - /* See symtab_index_entry. */ - - void --symtab_index_entry::minimize () -+symtab_index_entry::sort () - { - if (name == nullptr || cu_indices.empty ()) - return; - -- std::sort (cu_indices.begin (), cu_indices.end ()); -+ /* Sort the entries based on the CU offset. */ -+ std::sort (cu_indices.begin (), cu_indices.end (), -+ [] (offset_type vala, offset_type valb) -+ { -+ return vala < valb; -+ }); - auto from = std::unique (cu_indices.begin (), cu_indices.end ()); - cu_indices.erase (from, cu_indices.end ()); -- -- /* We don't want to enter a type more than once, so -- remove any such duplicates from the list as well. When doing -- this, we want to keep the entry from the first CU -- but this is -- implicit due to the sort. This choice is done because it's -- similar to what gdb historically did for partial symbols. */ -- std::unordered_set seen; -- from = std::remove_if (cu_indices.begin (), cu_indices.end (), -- [&] (offset_type val) -- { -- gdb_index_symbol_kind kind = GDB_INDEX_SYMBOL_KIND_VALUE (val); -- if (kind != GDB_INDEX_SYMBOL_KIND_TYPE) -- return false; -- -- val &= ~GDB_INDEX_CU_MASK; -- return !seen.insert (val).second; -- }); -- cu_indices.erase (from, cu_indices.end ()); - } - - /* A form of 'const char *' suitable for container keys. Only the -@@ -1193,6 +1179,21 @@ write_cooked_index (cooked_index *table, - const cu_index_map &cu_index_htab, - struct mapped_symtab *symtab) - { -+ gdb::unordered_set required_decl_entries; -+ for (const cooked_index_entry *entry : table->all_entries ()) -+ { -+ /* Any type declaration that is used as a (non-trivial) parent -+ entry must be written out. */ -+ if ((entry->flags & IS_TYPE_DECLARATION) == 0) -+ { -+ for (const cooked_index_entry *parent = entry->get_parent (); -+ parent != nullptr; -+ parent = parent->get_parent ()) -+ if ((parent->flags & IS_TYPE_DECLARATION) != 0) -+ required_decl_entries.insert (parent); -+ } -+ } -+ - for (const cooked_index_entry *entry : table->all_entries ()) - { - const auto it = cu_index_htab.find (entry->per_cu); -@@ -1218,11 +1219,10 @@ write_cooked_index (cooked_index *table, - be redundant are rare and not worth supporting. */ - continue; - } -- else if ((entry->flags & IS_TYPE_DECLARATION) != 0) -- { -- /* Don't add type declarations to the index. */ -- continue; -- } -+ /* Don't add most type declarations to the index. */ -+ else if ((entry->flags & IS_TYPE_DECLARATION) != 0 -+ && !required_decl_entries.contains (entry)) -+ continue; - - gdb_index_symbol_kind kind; - if (entry->tag == DW_TAG_subprogram -@@ -1341,7 +1341,7 @@ write_gdbindex (dwarf2_per_bfd *per_bfd, cooked_index *table, - - /* Now that we've processed all symbols we can shrink their cu_indices - lists. */ -- symtab.minimize (); -+ symtab.minimize_and_sort (); - - data_buf symtab_vec, constant_pool; - -diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c ---- a/gdb/dwarf2/read-gdb-index.c -+++ b/gdb/dwarf2/read-gdb-index.c -@@ -194,7 +194,6 @@ dw2_expand_marked_cus - gdb::function_view lang_matcher) - { - offset_type vec_len, vec_idx; -- bool global_seen = false; - mapped_gdb_index &index - = *(gdb::checked_static_cast - (per_objfile->per_bfd->index_table.get ())); -@@ -217,17 +216,6 @@ dw2_expand_marked_cus - (index.version >= 7 - && symbol_kind != GDB_INDEX_SYMBOL_KIND_NONE); - -- /* Work around gold/15646. */ -- if (attrs_valid -- && !is_static -- && symbol_kind == GDB_INDEX_SYMBOL_KIND_TYPE) -- { -- if (global_seen) -- continue; -- -- global_seen = true; -- } -- - /* Only check the symbol's kind if it has one. */ - if (attrs_valid) - { -diff --git a/gdb/symtab.c b/gdb/symtab.c ---- a/gdb/symtab.c -+++ b/gdb/symtab.c -@@ -4832,7 +4832,50 @@ symbol_search::compare_search_syms (const symbol_search &sym_a, - if (sym_a.block != sym_b.block) - return sym_a.block - sym_b.block; - -- return strcmp (sym_a.symbol->print_name (), sym_b.symbol->print_name ()); -+ c = strcmp (sym_a.symbol->print_name (), sym_b.symbol->print_name ()); -+ -+ if (c != 0) -+ return c; -+ -+ /* These two symbols have the same name. It is possible, with types, -+ that we can see two symbols with the same name, but different types, -+ consider in C: 'typedef struct foo { ... } foo;' which creates a -+ 'struct foo' type and a 'foo' typedef type. For now this is the only -+ case we handle. In all other cases, we treat symbols with the same -+ name as being the same. -+ -+ -+ First, check the types, if they are the same, then consider these -+ symbols as the same. */ -+ if (sym_a.symbol->type ()->code () == sym_b.symbol->type ()->code ()) -+ return 0; -+ -+ /* The types are different, but if neither is a typedef then we still -+ consider these symbols as the same. */ -+ if (sym_a.symbol->type ()->code () != TYPE_CODE_TYPEDEF -+ && sym_b.symbol->type ()->code () != TYPE_CODE_TYPEDEF) -+ return 0; -+ -+ /* The symbols have different types, and one is a typedef. They cannot -+ both be typedefs or we'd have taken the "types are the same" exit path -+ above. If the two types are defined on different lines then order by -+ line number. As line numbers are unsigned, don't subtract one from -+ the other in order to avoid underflow. */ -+ if (sym_a.symbol->line () != sym_b.symbol->line ()) -+ return (sym_a.symbol->line () > sym_b.symbol->line () ? 1 : -1); -+ -+ /* The symbols have different types, and one is a typedef, but both -+ symbols are defined on the same line. For example: -+ -+ typedef struct foo { int a; } foo; -+ -+ In this case we sort the typedef after the non-typedef. This is an -+ arbitrary decision, but I think looks slightly nicer in the 'info -+ types' output; first we get the type, then the typedef. */ -+ if (sym_a.symbol->type ()->code () == TYPE_CODE_TYPEDEF) -+ return 1; -+ else -+ return -1; - } - - /* Returns true if the type_name of symbol_type of SYM matches TREG. -diff --git a/gdb/testsuite/gdb.base/gold-gdb-index.c b/gdb/testsuite/gdb.base/gdb-index-many-types-1.c -similarity index 68% -rename from gdb/testsuite/gdb.base/gold-gdb-index.c -rename to gdb/testsuite/gdb.base/gdb-index-many-types-1.c ---- a/gdb/testsuite/gdb.base/gold-gdb-index.c -+++ b/gdb/testsuite/gdb.base/gdb-index-many-types-1.c -@@ -1,6 +1,6 @@ - /* This testcase is part of GDB, the GNU debugger. - -- Copyright 2020-2024 Free Software Foundation, Inc. -+ 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 -@@ -15,15 +15,34 @@ - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - --#include "gold-gdb-index.h" -- --namespace N1 --{ -- void foo () { C1::baz (); } --} -+#include "gdb-index-many-types.h" - - int - main () - { -+ foo_func_a (0); -+ foo_func_b (0); -+ bar_func_a (0); -+ bar_func_b (0); -+ baz_func_a (0); -+ baz_func_b (0); - return 0; - } -+ -+void -+foo_func_c (foo_t *obj) -+{ -+ (void) obj; -+} -+ -+void -+bar_func_c (bar_t *obj) -+{ -+ (void) obj; -+} -+ -+void -+baz_func_c (baz_t *obj) -+{ -+ (void) obj; -+} -diff --git a/gdb/testsuite/gdb.base/gold-gdb-index-2.c b/gdb/testsuite/gdb.base/gdb-index-many-types-2.c -similarity index 61% -rename from gdb/testsuite/gdb.base/gold-gdb-index-2.c -rename to gdb/testsuite/gdb.base/gdb-index-many-types-2.c ---- a/gdb/testsuite/gdb.base/gold-gdb-index-2.c -+++ b/gdb/testsuite/gdb.base/gdb-index-many-types-2.c -@@ -1,6 +1,6 @@ - /* This testcase is part of GDB, the GNU debugger. - -- Copyright 2020-2024 Free Software Foundation, Inc. -+ 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 -@@ -15,9 +15,42 @@ - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - --#include "gold-gdb-index.h" -+#include "gdb-index-many-types.h" - --namespace N1 -+typedef struct foo_t - { -- void bar () { C1::baz (); } -+ int foo_t_1; -+ int foo_t_2; -+} foo_t; -+ -+typedef struct woof_t -+{ -+ int woof_t_1; -+ int woof_t_2; -+} woof_t; -+ -+static void -+woof_func (woof_t *obj) -+{ -+ (void) obj; -+} -+ -+void -+foo_func_a (foo_t *obj) -+{ -+ woof_func (0); -+ (void) obj; -+} -+ -+void -+baz_func_a (baz_t *obj) -+{ -+ (void) obj; -+} -+ -+void -+bar_func_a (bar_t *obj) -+{ -+ woof_func (0); -+ (void) obj; - } -diff --git a/gdb/testsuite/gdb.base/gold-gdb-index.h b/gdb/testsuite/gdb.base/gdb-index-many-types-3.c -similarity index 61% -rename from gdb/testsuite/gdb.base/gold-gdb-index.h -rename to gdb/testsuite/gdb.base/gdb-index-many-types-3.c ---- a/gdb/testsuite/gdb.base/gold-gdb-index.h -+++ b/gdb/testsuite/gdb.base/gdb-index-many-types-3.c -@@ -1,6 +1,6 @@ - /* This testcase is part of GDB, the GNU debugger. - -- Copyright 2020-2024 Free Software Foundation, Inc. -+ 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 -@@ -15,11 +15,42 @@ - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - --namespace N1 -+#include "gdb-index-many-types.h" -+ -+typedef struct woof_t -+{ -+ double woof_t_3; -+ double woof_t_4; -+} woof_t; -+ -+static void -+woof_func (woof_t *obj) -+{ -+ (void) obj; -+} -+ -+typedef struct bar_t -+{ -+ int bar_t_1; -+ int bar_t_2; -+} bar_t; -+ -+void -+bar_func_b (bar_t *obj) -+{ -+ woof_func (0); -+ (void) obj; -+} -+ -+void -+baz_func_b (baz_t *obj) -+{ -+ (void) obj; -+} -+ -+void -+foo_func_b (foo_t *obj) - { -- class C1 -- { -- public: -- static void baz () {} -- }; -+ woof_func (0); -+ (void) obj; - } -diff --git a/gdb/testsuite/gdb.base/gdb-index-many-types.exp b/gdb/testsuite/gdb.base/gdb-index-many-types.exp -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.base/gdb-index-many-types.exp -@@ -0,0 +1,157 @@ -+# 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 . -+ -+# Check that adding an index to an executable (both a gdb index and a -+# dwarf-5 index are tested), doesn't prevent GDB from seeing the -+# expected types. -+ -+standard_testfile -1.c -2.c -3.c .h -+ -+# One of the tests uses this Python file. The test_* proc checks that -+# GDB supports Python tests. Some of the other procs don't use this -+# Python file. -+set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py] -+ -+if {[build_executable "building" $testfile \ -+ [list $srcfile $srcfile2 $srcfile3]] == -1} { -+ return -+} -+ -+# Run 'info types TYPENAME', expect to see an entry from FILENAME for -+# the line matching PATTERN. -+proc check_info_types { testfile typename filename pattern } { -+ with_test_prefix "$filename '$pattern'" { -+ clean_restart $testfile -+ -+ set line_num [gdb_get_line_number $pattern $filename] -+ gdb_test "info types $typename" \ -+ "File \[^\r\n\]+/${filename}:(?:\r\n${::decimal}:\[^\r\n\]+)*\r\n${line_num}:\[^\r\n\]+.*" -+ } -+} -+ -+# Start GDB with FILENAME, and examine some of the types. This proc -+# might seem to be using clean_restart a little too much, but we need -+# to be really careful here. As we examine one type, e.g. foo_t, this -+# might cause GDB to fully parse a CU, which then means examining -+# bar_t gives the expected result. When, if we'd first looked for -+# bar_t, then (due to an index bug) we might not have found the -+# correct type definition. -+# -+# The only way we can be sure that an earlier test isn't going to -+# trigger CU expansion is to restart GDB before every query. -+proc run_test { filename } { -+ # Print all the types for which there is only one representation. -+ foreach type { foo_t bar_t baz_t } { -+ clean_restart $filename -+ gdb_test "ptype $type" \ -+ [multi_line \ -+ "type = struct $type {" \ -+ "\\s+int ${type}_1;" \ -+ "\\s+int ${type}_2;" \ -+ "}"] -+ } -+ -+ # There are two different versions of woof_t. For now, when using -+ # `ptype` GDB will just display the first one it finds, which could -+ # legitimately be either. -+ set woof_int_re [multi_line \ -+ "type = struct woof_t {" \ -+ "\\s+int woof_t_1;" \ -+ "\\s+int woof_t_2;" \ -+ "}"] -+ set woof_double_re [multi_line \ -+ "type = struct woof_t {" \ -+ "\\s+double woof_t_3;" \ -+ "\\s+double woof_t_4;" \ -+ "}"] -+ clean_restart $filename -+ gdb_test_multiple "ptype woof_t" "" { -+ -re -wrap $woof_int_re { -+ pass $gdb_test_name -+ } -+ -re -wrap $woof_double_re { -+ pass $gdb_test_name -+ } -+ } -+ -+ # Check for declarations and definitions of some types. -+ check_info_types $filename foo_t $::srcfile2 "typedef struct foo_t" -+ check_info_types $filename foo_t $::srcfile4 "typedef struct foo_t foo_t;" -+ check_info_types $filename bar_t $::srcfile3 "typedef struct bar_t" -+ check_info_types $filename bar_t $::srcfile4 "typedef struct bar_t bar_t;" -+ check_info_types $filename baz_t $::srcfile4 "typedef struct baz_t" -+ check_info_types $filename baz_t $::srcfile4 "\} baz_t;" -+ check_info_types $filename woof_t $::srcfile2 "typedef struct woof_t" -+ check_info_types $filename woof_t $::srcfile3 "typedef struct woof_t" -+ -+ # Use Python to look for type symbols. -+ if { [allow_python_tests] } { -+ foreach_with_prefix type { foo_t bar_t baz_t } { -+ clean_restart $filename -+ gdb_test_no_output "source $::pyfile" "import python scripts" -+ gdb_test "py-show-type $type" \ -+ [multi_line \ -+ "Looking for type '$type':" \ -+ " Found 3 type symbols" \ -+ " 1: struct $type \\{ int ${type}_1; int ${type}_2; \\}" \ -+ " 2: struct $type \\{ int ${type}_1; int ${type}_2; \\}" \ -+ " 3: struct $type \\{ int ${type}_1; int ${type}_2; \\}"] -+ } -+ -+ clean_restart $filename -+ gdb_test_no_output "source $::pyfile" "import python scripts" -+ gdb_test "py-show-type woof_t" \ -+ [multi_line \ -+ "Looking for type 'woof_t':" \ -+ " Found 2 type symbols" \ -+ " 1: struct woof_t \\{ (?:int|double) woof_t_(?:1|3); (?:int|double) woof_t_(?:2|4); \\}" \ -+ " 2: struct woof_t \\{ (?:int|double) woof_t_(?:1|3); (?:int|double) woof_t_(?:2|4); \\}"] -+ -+ } -+} -+ -+with_test_prefix "no index" { -+ run_test $testfile -+} -+ -+# The previous call to 'run_test' will have left GDB active. Check if -+# BINFILE already has an index. If it does then we must be running -+# with one of the boardfiles that adds an index. We could possibly -+# try to remove the index, but for now, just don't run the following -+# parts which rely on adding an index. -+set index_type [get_index_type $binfile "check debug style"] -+if { $index_type ne "cooked" } { -+ unsupported "cannot test without a cooked index" -+ return -+} -+ -+foreach_with_prefix index_type { gdb dwarf5 } { -+ set binfile_with_index ${binfile}-idx-${index_type} -+ -+ remote_exec build "cp $binfile $binfile_with_index" -+ -+ if { $index_type eq "gdb" } { -+ set style "" -+ } else { -+ set style "-dwarf-5" -+ } -+ -+ if {[ensure_gdb_index $binfile_with_index $style] != 1} { -+ unsupported "couldn't add $index_type index" -+ return -+ } -+ -+ run_test [file tail $binfile_with_index] -+} -diff --git a/gdb/testsuite/gdb.base/gdb-index-many-types.h b/gdb/testsuite/gdb.base/gdb-index-many-types.h -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.base/gdb-index-many-types.h -@@ -0,0 +1,42 @@ -+/* This testcase is part of GDB, the GNU debugger. -+ -+ 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 . */ -+ -+#ifndef GDB_INDEX_MANY_TYPES_H -+#define GDB_INDEX_MANY_TYPES_H -+ -+typedef struct foo_t foo_t; -+typedef struct bar_t bar_t; -+ -+extern void foo_func_a (foo_t *obj); -+extern void foo_func_b (foo_t *obj); -+extern void foo_func_c (foo_t *obj); -+ -+extern void bar_func_a (bar_t *obj); -+extern void bar_func_b (bar_t *obj); -+extern void bar_func_c (bar_t *obj); -+ -+typedef struct baz_t -+{ -+ int baz_t_1; -+ int baz_t_2; -+} baz_t; -+ -+extern void baz_func_a (baz_t *obj); -+extern void baz_func_b (baz_t *obj); -+extern void baz_func_c (baz_t *obj); -+ -+#endif /* GDB_INDEX_MANY_TYPES_H */ -diff --git a/gdb/testsuite/gdb.base/gdb-index-many-types.py b/gdb/testsuite/gdb.base/gdb-index-many-types.py -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.base/gdb-index-many-types.py -@@ -0,0 +1,54 @@ -+# 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 . -+ -+ -+class TypeViewer(gdb.Command): -+ """A command which takes a string and looks up types with that name. -+ -+ The types are expected to all be structs. This command prints a -+ basic representation of the struct. This is only going to work when -+ used with the types defined in the gdb-index-many-types test source -+ files.""" -+ -+ def __init__(self): -+ super().__init__("py-show-type", gdb.COMMAND_USER) -+ -+ def invoke(self, args, from_tty): -+ argv = gdb.string_to_argv(args) -+ if argv[0] == "": -+ raise gdb.GdbError("missing argument") -+ print("Looking for type '" + argv[0] + "':") -+ syms = gdb.lookup_static_symbols(argv[0], gdb.SYMBOL_TYPE_DOMAIN) -+ count = len(syms) -+ print(" Found %d type symbol%s" % (count, "" if count == 1 else "s")) -+ for i, s in enumerate(syms, start=1): -+ t = s.type -+ if t is None: -+ print(" %d: No type." % i) -+ else: -+ fields = "struct " + argv[0] + " {" -+ try: -+ for f in t.fields(): -+ if len(fields) > 0: -+ fields = fields + " " -+ fields = fields + "%s %s;" % (str(f.type), f.name) -+ except: -+ pass -+ fields = fields + " }" -+ -+ print(" %d: %s" % (i, fields)) -+ -+ -+TypeViewer() -diff --git a/gdb/testsuite/gdb.base/gold-gdb-index.exp b/gdb/testsuite/gdb.base/gold-gdb-index.exp -deleted file mode 100644 ---- a/gdb/testsuite/gdb.base/gold-gdb-index.exp -+++ /dev/null -@@ -1,51 +0,0 @@ --# Copyright 2020-2024 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 . */ -- --# This tests the gdb workaround for PR binutils/15646. -- --standard_testfile .c gold-gdb-index-2.c -- --if { [have_fuse_ld_gold] == 0} { -- return -1 --} -- --if {[prepare_for_testing "failed to prepare" $testfile "$srcfile $srcfile2" \ -- {debug c++ additional_flags=-fuse-ld=gold \ -- ldflags=-Wl,--gdb-index \ -- additional_flags=-ggnu-pubnames}]} { -- return -1 --} -- --if { [have_index $binfile] != "gdb_index" } { -- return -1 --} -- --if {![runto_main]} { -- return 0 --} -- --gdb_test_no_output "nosharedlibrary" -- --gdb_test_no_output "set breakpoint pending off" --gdb_test "break N1::misspelled" "Function \"N1::misspelled\" not defined\." -- --gdb_test_multiple "maint info symtabs" "" { -- -re -wrap "\{ symtab \[^\r\n\]*gold-gdb-index-2.c.*" { -- fail $gdb_test_name -- } -- -re -wrap "" { -- pass $gdb_test_name -- } --} -diff --git a/gdb/testsuite/gdb.base/info-types.exp.tcl b/gdb/testsuite/gdb.base/info-types.exp.tcl ---- a/gdb/testsuite/gdb.base/info-types.exp.tcl -+++ b/gdb/testsuite/gdb.base/info-types.exp.tcl -@@ -90,6 +90,7 @@ proc run_test { lang } { - "28:\[\t \]+typedef struct baz_t baz;" \ - "31:\[\t \]+typedef struct baz_t \\* baz_ptr;" \ - "21:\[\t \]+struct baz_t;" \ -+ "27:\[\t \]+typedef struct baz_t baz_t;" \ - "\[\t \]+double" \ - "33:\[\t \]+enum enum_t;" \ - "\[\t \]+float" \ -diff --git a/gdb/testsuite/gdb.base/type-and-typedef.c b/gdb/testsuite/gdb.base/type-and-typedef.c -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.base/type-and-typedef.c -@@ -0,0 +1,48 @@ -+/* This testcase is part of GDB, the GNU debugger. -+ -+ 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 . */ -+ -+typedef struct foo /* struct foo defined here. */ -+{ -+ int a; -+ int b; -+} foo; /* typedef foo defined here. */ -+ -+struct bar /* struct bar defined here. */ -+{ -+ int a; -+ int b; -+}; -+ -+/* Yes, this really is typedef-ing 'bar' to something other than 'struct -+ bar'. Just testing that GDB handles this. This is not good code. */ -+typedef struct foo bar; /* typedef bar defined here. */ -+ -+/* The following must be a single line. This tests the 'struct baz' and -+ the 'typedef ... baz;' being on the same line. */ -+typedef struct baz { int a; int b; } baz; /* baz defined here. */ -+ -+volatile struct foo obj1 = { 1, 2 }; -+volatile foo obj2 = { 1, 2 }; -+volatile struct bar obj3 = { 1, 2 }; -+volatile bar obj4 = { 1, 2 }; -+volatile baz obj5 = { 1, 2 }; -+ -+int -+main () -+{ -+ return obj1.b + obj2.b + obj3.b + obj4.b + obj5.b; -+} -diff --git a/gdb/testsuite/gdb.base/type-and-typedef.exp b/gdb/testsuite/gdb.base/type-and-typedef.exp -new file mode 100644 ---- /dev/null -+++ b/gdb/testsuite/gdb.base/type-and-typedef.exp -@@ -0,0 +1,65 @@ -+# 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 . -+ -+# Compile a single CU containing a type and a typedef with the same -+# name (think C's "typedef struct foo { ... } foo;"). Check that -+# 'info types' shows both types. -+ -+standard_testfile -+ -+if { [prepare_for_testing "failed to prepare" $testfile $srcfile] } { -+ return -+} -+ -+# Line numbers needed by the test. -+set struct_foo_lineno [gdb_get_line_number "struct foo defined here"] -+set typedef_foo_lineno [gdb_get_line_number "typedef foo defined here"] -+set struct_bar_lineno [gdb_get_line_number "struct bar defined here"] -+set typedef_bar_lineno [gdb_get_line_number "typedef bar defined here"] -+set baz_lineno [gdb_get_line_number "baz defined here"] -+ -+# Check that the expected two types show up in global SRCFILE. -+proc check_types { testname } { -+ gdb_test "info types foo" \ -+ [multi_line \ -+ "File (?:\[^\r\n\]+/)?[string_to_regexp $::srcfile]:" \ -+ "${::struct_foo_lineno}:\\s+struct foo;" \ -+ "${::typedef_foo_lineno}:\\s+typedef struct foo foo;"] \ -+ "$testname, check foo" -+ -+ gdb_test "info types bar" \ -+ [multi_line \ -+ "File (?:\[^\r\n\]+/)?[string_to_regexp $::srcfile]:" \ -+ "${::struct_bar_lineno}:\\s+struct bar;" \ -+ "${::typedef_bar_lineno}:\\s+typedef struct foo bar;"] \ -+ "$testname, check bar" -+ -+ gdb_test "info types baz" \ -+ [multi_line \ -+ "File (?:\[^\r\n\]+/)?[string_to_regexp $::srcfile]:" \ -+ "${::baz_lineno}:\\s+struct baz;" \ -+ "${::baz_lineno}:\\s+typedef struct baz baz;"] \ -+ "$testname, check baz" -+} -+ -+check_types "before inferior is started" -+ -+clean_restart $testfile -+ -+if {![runto_main]} { -+ return -+} -+ -+check_types "after starting the inferior" diff --git a/gdb-rhel-50069-support-z17.patch b/gdb-rhel-50069-support-z17.patch deleted file mode 100644 index 7c3504e..0000000 --- a/gdb-rhel-50069-support-z17.patch +++ /dev/null @@ -1,38 +0,0 @@ -From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 -From: Jens Remus -Date: Wed, 9 Apr 2025 08:59:24 +0200 -Subject: gdb-rhel-50069-support-z17.patch - -;; Backport "s390: Add support for z17 as CPU name" -;; (Jens Remus, RHEL-50069) - -So far IBM z17 was identified as arch15. Add the real name, as it has -been announced. [1] - -[1]: IBM z17 announcement letter, AD25-0015, - https://www.ibm.com/docs/en/announcements/z17-makes-more-possible - -gas/ - * config/tc-s390.c (s390_parse_cpu): Add z17 as alternate CPU - name for arch15. - * doc/c-s390.texi: Likewise. - * doc/as.texi: Likewise. - -opcodes/ - * s390-mkopc.c (main): Add z17 as alternate CPU name for arch15. - -Signed-off-by: Jens Remus - -diff --git a/opcodes/s390-mkopc.c b/opcodes/s390-mkopc.c ---- a/opcodes/s390-mkopc.c -+++ b/opcodes/s390-mkopc.c -@@ -443,7 +443,8 @@ main (void) - else if (strcmp (cpu_string, "z16") == 0 - || strcmp (cpu_string, "arch14") == 0) - min_cpu = S390_OPCODE_ARCH14; -- else if (strcmp (cpu_string, "arch15") == 0) -+ else if (strcmp (cpu_string, "z17") == 0 -+ || strcmp (cpu_string, "arch15") == 0) - min_cpu = S390_OPCODE_ARCH15; - else { - print_error ("Mnemonic \"%s\": Couldn't parse CPU string: %s\n", diff --git a/gdb-rhel-7329-vandps-clobbers-registers-1-of-13.patch b/gdb-rhel-7329-vandps-clobbers-registers-1-of-13.patch deleted file mode 100644 index f3e9017..0000000 --- a/gdb-rhel-7329-vandps-clobbers-registers-1-of-13.patch +++ /dev/null @@ -1,115 +0,0 @@ -From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 -From: Tom de Vries -Date: Fri, 7 Mar 2025 09:25:33 +0100 -Subject: gdb-rhel-7329-vandps-clobbers-registers-1-of-13.patch - -;; [gdb/tdep] Refactor amd64_get_unused_input_int_reg, part 1 -;; (Tom de Vries, RHEL-7329) - -While reading amd64_get_unused_input_int_reg, I noticed that it first asserts, -then throws an internal_error if no unused register can be found. - -Looking at the documentation of gdbarch_displaced_step_copy_insn, it seems -that a failure can be indicated less abruptly, by returning a nullptr. - -Fix this by: -- returning -1 in case of failure to find an unused register in - amd64_get_unused_input_int_reg, and -- propagating this to amd64_displaced_step_copy_insn. - -Tested on x86_64-linux. - -diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c ---- a/gdb/amd64-tdep.c -+++ b/gdb/amd64-tdep.c -@@ -1211,7 +1211,7 @@ amd64_skip_prefixes (gdb_byte *insn) - In order to not require adding a rex prefix if the insn doesn't already - have one, the result is restricted to RAX ... RDI, sans RSP. - The register numbering of the result follows architecture ordering, -- e.g. RDI = 7. */ -+ e.g. RDI = 7. Return -1 if no register can be found. */ - - static int - amd64_get_unused_input_int_reg (const struct amd64_insn *details) -@@ -1263,7 +1263,6 @@ amd64_get_unused_input_int_reg (const struct amd64_insn *details) - } - - gdb_assert (used_regs_mask < 256); -- gdb_assert (used_regs_mask != 255); - - /* Finally, find a free reg. */ - { -@@ -1274,10 +1273,9 @@ amd64_get_unused_input_int_reg (const struct amd64_insn *details) - if (! (used_regs_mask & (1 << i))) - return i; - } -- -- /* We shouldn't get here. */ -- internal_error (_("unable to find free reg")); - } -+ -+ return -1; - } - - /* Extract the details of INSN that we need. */ -@@ -1361,9 +1359,10 @@ amd64_get_insn_details (gdb_byte *insn, struct amd64_insn *details) - 32 bits is not enough to be guaranteed to cover the distance between where - the real instruction is and where its copy is. - Convert the insn to use base+disp addressing. -- We set base = pc + insn_length so we can leave disp unchanged. */ -+ We set base = pc + insn_length so we can leave disp unchanged. -+ Return true if successful, false otherwise. */ - --static void -+static bool - fixup_riprel (struct gdbarch *gdbarch, - amd64_displaced_step_copy_insn_closure *dsc, - CORE_ADDR from, CORE_ADDR to, struct regcache *regs) -@@ -1384,6 +1383,9 @@ fixup_riprel (struct gdbarch *gdbarch, - Pick one not used in the insn. - NOTE: arch_tmp_regno uses architecture ordering, e.g. RDI = 7. */ - arch_tmp_regno = amd64_get_unused_input_int_reg (insn_details); -+ if (arch_tmp_regno == -1) -+ return false; -+ - tmp_regno = amd64_arch_reg_to_regnum (arch_tmp_regno); - - /* Position of the not-B bit in the 3-byte VEX prefix (in byte 1). */ -@@ -1418,9 +1420,13 @@ fixup_riprel (struct gdbarch *gdbarch, - displaced_debug_printf ("using temp reg %d, old value %s, new value %s", - dsc->tmp_regno, paddress (gdbarch, dsc->tmp_save), - paddress (gdbarch, rip_base)); -+ return true; - } - --static void -+/* Fixup the insn in DSC->insn_buf, which was copied from address FROM to TO. -+ Return true if successful, false otherwise. */ -+ -+static bool - fixup_displaced_copy (struct gdbarch *gdbarch, - amd64_displaced_step_copy_insn_closure *dsc, - CORE_ADDR from, CORE_ADDR to, struct regcache *regs) -@@ -1435,9 +1441,11 @@ fixup_displaced_copy (struct gdbarch *gdbarch, - { - /* The insn uses rip-relative addressing. - Deal with it. */ -- fixup_riprel (gdbarch, dsc, from, to, regs); -+ return fixup_riprel (gdbarch, dsc, from, to, regs); - } - } -+ -+ return true; - } - - displaced_step_copy_insn_closure_up -@@ -1475,7 +1483,8 @@ amd64_displaced_step_copy_insn (struct gdbarch *gdbarch, - - /* Modify the insn to cope with the address where it will be executed from. - In particular, handle any rip-relative addressing. */ -- fixup_displaced_copy (gdbarch, dsc.get (), from, to, regs); -+ if (!fixup_displaced_copy (gdbarch, dsc.get (), from, to, regs)) -+ return nullptr; - - write_memory (to, buf, len); - diff --git a/gdb-rhel-7329-vandps-clobbers-registers-10-of-13.patch b/gdb-rhel-7329-vandps-clobbers-registers-10-of-13.patch deleted file mode 100644 index 97e5f47..0000000 --- a/gdb-rhel-7329-vandps-clobbers-registers-10-of-13.patch +++ /dev/null @@ -1,61 +0,0 @@ -From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 -From: Tom de Vries -Date: Fri, 7 Mar 2025 09:25:33 +0100 -Subject: gdb-rhel-7329-vandps-clobbers-registers-10-of-13.patch - -;; [gdb/tdep] Add vzeroupper and vzeroall in amd64-insn-decode selftest -;; (Tom de Vries, RHEL-7329) - -After I posted a tentative patch for PR31952, Alexander Monakov pointed out -that the patch broke instruction decoding for instructions vzeroall and -vzeroupper. - -Add selftests for these two instructions in amd64-insn-decode, both using -vex2 and vex3 prefixes. - -Tested on x86_64-linux. - -diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c ---- a/gdb/amd64-tdep.c -+++ b/gdb/amd64-tdep.c -@@ -3512,6 +3512,40 @@ test_amd64_get_insn_details (void) - gdb::byte_vector updated_insn = { 0x48, 0x8d, 0xb9, 0x1e, 0x00, 0x00, 0x00 }; - fixup_riprel (details, insn.data (), ECX_REG_NUM); - SELF_CHECK (insn == updated_insn); -+ -+ gdb::byte_vector vex2, vex3; -+ -+ /* INSN: vzeroall, vex2 prefix. */ -+ vex2 = { 0xc5, 0xfc, 0x77 }; -+ amd64_get_insn_details (vex2.data (), &details); -+ SELF_CHECK (details.opcode_len == 1); -+ SELF_CHECK (details.enc_prefix_offset == -1); -+ SELF_CHECK (details.opcode_offset == 2); -+ SELF_CHECK (details.modrm_offset == -1); -+ -+ /* INSN: vzeroall, vex3 prefix. */ -+ vex2_to_vex3 (vex2, vex3); -+ amd64_get_insn_details (vex3.data (), &details); -+ SELF_CHECK (details.opcode_len == 1); -+ SELF_CHECK (details.enc_prefix_offset == 0); -+ SELF_CHECK (details.opcode_offset == 3); -+ SELF_CHECK (details.modrm_offset == -1); -+ -+ /* INSN: vzeroupper, vex2 prefix. */ -+ vex2 = { 0xc5, 0xf8, 0x77 }; -+ amd64_get_insn_details (vex2.data (), &details); -+ SELF_CHECK (details.opcode_len == 1); -+ SELF_CHECK (details.enc_prefix_offset == -1); -+ SELF_CHECK (details.opcode_offset == 2); -+ SELF_CHECK (details.modrm_offset == -1); -+ -+ /* INSN: vzeroupper, vex3 prefix. */ -+ vex2_to_vex3 (vex2, vex3); -+ amd64_get_insn_details (vex3.data (), &details); -+ SELF_CHECK (details.opcode_len == 1); -+ SELF_CHECK (details.enc_prefix_offset == 0); -+ SELF_CHECK (details.opcode_offset == 3); -+ SELF_CHECK (details.modrm_offset == -1); - } - - static void diff --git a/gdb-rhel-7329-vandps-clobbers-registers-11-of-13.patch b/gdb-rhel-7329-vandps-clobbers-registers-11-of-13.patch deleted file mode 100644 index 55d3b91..0000000 --- a/gdb-rhel-7329-vandps-clobbers-registers-11-of-13.patch +++ /dev/null @@ -1,86 +0,0 @@ -From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 -From: Tom de Vries -Date: Fri, 7 Mar 2025 09:25:33 +0100 -Subject: gdb-rhel-7329-vandps-clobbers-registers-11-of-13.patch - -;; [gdb/tdep] Make amd64_get_insn_details more regular -;; (Tom de Vries, RHEL-7329) - -In amd64_get_insn_details, I found this code with a comment explaining why -enc_prefix_offset is not set: -... - else if (vex2_prefix_p (*insn)) - { - /* Don't record the offset in this case because this prefix has - no REX.B equivalent. */ - insn += 2; - } -... -which I didn't understand until I looked at the only use of enc_prefix_offset, -in fixup_riprel: -... - /* REX.B should be unset (VEX.!B set) as we were using rip-relative - addressing, but ensure it's unset (set for VEX) anyway, tmp_regno - is not r8-r15. */ - if (insn_details->enc_prefix_offset != -1) - { - gdb_byte *pfx = &dsc->insn_buf[insn_details->enc_prefix_offset]; - if (rex_prefix_p (pfx[0])) - pfx[0] &= ~REX_B; - else if (vex3_prefix_p (pfx[0])) - pfx[1] |= VEX3_NOT_B; - else - gdb_assert_not_reached ("unhandled prefix"); - } -... - -Fix this by: -- setting enc_prefix_offset for the vex2 case in amd64_get_insn_details, - making the function more regular and easier to understand, and -- handling the vex2 case in the "enc_prefix_offset != -1" clause in - fixup_riprel. - -Tested on x86_64-linux. - -diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c ---- a/gdb/amd64-tdep.c -+++ b/gdb/amd64-tdep.c -@@ -1328,8 +1328,7 @@ amd64_get_insn_details (gdb_byte *insn, struct amd64_insn *details) - } - else if (vex2_prefix_p (*insn)) - { -- /* Don't record the offset in this case because this prefix has -- no REX.B equivalent. */ -+ details->enc_prefix_offset = insn - start; - insn += 2; - } - else if (vex3_prefix_p (*insn)) -@@ -1395,6 +1394,10 @@ fixup_riprel (const struct amd64_insn &details, gdb_byte *insn, - gdb_byte *pfx = &insn[details.enc_prefix_offset]; - if (rex_prefix_p (pfx[0])) - pfx[0] &= ~REX_B; -+ else if (vex2_prefix_p (pfx[0])) -+ { -+ /* VEX.!B is set implicitly. */ -+ } - else if (vex3_prefix_p (pfx[0])) - pfx[1] |= VEX3_NOT_B; - else -@@ -3519,7 +3522,7 @@ test_amd64_get_insn_details (void) - vex2 = { 0xc5, 0xfc, 0x77 }; - amd64_get_insn_details (vex2.data (), &details); - SELF_CHECK (details.opcode_len == 1); -- SELF_CHECK (details.enc_prefix_offset == -1); -+ SELF_CHECK (details.enc_prefix_offset == 0); - SELF_CHECK (details.opcode_offset == 2); - SELF_CHECK (details.modrm_offset == -1); - -@@ -3535,7 +3538,7 @@ test_amd64_get_insn_details (void) - vex2 = { 0xc5, 0xf8, 0x77 }; - amd64_get_insn_details (vex2.data (), &details); - SELF_CHECK (details.opcode_len == 1); -- SELF_CHECK (details.enc_prefix_offset == -1); -+ SELF_CHECK (details.enc_prefix_offset == 0); - SELF_CHECK (details.opcode_offset == 2); - SELF_CHECK (details.modrm_offset == -1); - diff --git a/gdb-rhel-7329-vandps-clobbers-registers-12-of-13.patch b/gdb-rhel-7329-vandps-clobbers-registers-12-of-13.patch deleted file mode 100644 index 90498bb..0000000 --- a/gdb-rhel-7329-vandps-clobbers-registers-12-of-13.patch +++ /dev/null @@ -1,183 +0,0 @@ -From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 -From: Tom de Vries -Date: Fri, 7 Mar 2025 09:25:33 +0100 -Subject: gdb-rhel-7329-vandps-clobbers-registers-12-of-13.patch - -;; [gdb/tdep] Fix vmovdqu decoding -;; (Tom de Vries, RHEL-7329) - -PR tdep/31952 reports that displaced stepping over an instruction pointer -relative insn "vmovdqu 0x20(%rip),%ymm1" gives the wrong results. - -This is caused by misclassification of the insn in amd64_get_insn_details, -which results in details.modrm_offset == -1, while the instruction in fact -does have a modrm byte. - -The instruction is encoded as follows: -... - 400557: c5 fe 6f 0d 20 00 00 00 vmovdqu 0x20(%rip),%ymm1 -... -where: -- "0xc5 0xfe" is the vex2 prefix, -- "0x6f" is the opcode, -- "0x0d" is the modrm byte, and -- "0x20 0x00 0x00 0x00" is a 32-bit displacement. - -The problem is related to details.opcode_len, which is 1. - -While it is true that the length of the opcode in the insn (0x6f) is 1 byte, -the vex2 prefix implies that we're encoding an 2-byte opcode beginnning -with 0x0f [1]. - -Consequently, we should be using the twobyte_has_modrm map rather than the -onebyte_has_modrm map. - -Fix this in amd64_get_insn_details, and add a selftest to check this. - -Tested on x86_64-linux. - -Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31952 - -[1] https://en.wikipedia.org/wiki/VEX_prefix - -diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c ---- a/gdb/amd64-tdep.c -+++ b/gdb/amd64-tdep.c -@@ -1336,10 +1336,49 @@ amd64_get_insn_details (gdb_byte *insn, struct amd64_insn *details) - details->enc_prefix_offset = insn - start; - insn += 3; - } -+ gdb_byte *prefix = (details->enc_prefix_offset == -1 -+ ? nullptr -+ : &start[details->enc_prefix_offset]); - - details->opcode_offset = insn - start; - -- if (*insn == TWO_BYTE_OPCODE_ESCAPE) -+ if (prefix != nullptr && vex2_prefix_p (*prefix)) -+ { -+ need_modrm = twobyte_has_modrm[*insn]; -+ details->opcode_len = 2; -+ } -+ else if (prefix != nullptr && vex3_prefix_p (*prefix)) -+ { -+ need_modrm = twobyte_has_modrm[*insn]; -+ -+ gdb_byte m = prefix[1] & 0x1f; -+ if (m == 0) -+ { -+ /* Todo: Xeon Phi-specific JKZD/JKNZD. */ -+ return; -+ } -+ else if (m == 1) -+ { -+ /* Escape 0x0f. */ -+ details->opcode_len = 2; -+ } -+ else if (m == 2 || m == 3) -+ { -+ /* Escape 0x0f 0x38 or 0x0f 0x3a. */ -+ details->opcode_len = 3; -+ } -+ else if (m == 7) -+ { -+ /* Todo: URDMSR/UWRMSR instructions. */ -+ return; -+ } -+ else -+ { -+ /* Unknown opcode map. */ -+ return; -+ } -+ } -+ else if (*insn == TWO_BYTE_OPCODE_ESCAPE) - { - /* Two or three-byte opcode. */ - ++insn; -@@ -1513,6 +1552,8 @@ amd64_displaced_step_copy_insn (struct gdbarch *gdbarch, - memset (buf + len, 0, fixup_sentinel_space); - - amd64_get_insn_details (buf, details); -+ if (details->opcode_len == -1) -+ return nullptr; - - /* GDB may get control back after the insn after the syscall. - Presumably this is a kernel bug. -@@ -3475,7 +3516,7 @@ static void - test_amd64_get_insn_details (void) - { - struct amd64_insn details; -- gdb::byte_vector insn; -+ gdb::byte_vector insn, tmp; - - /* INSN: add %eax,(%rcx). */ - insn = { 0x01, 0x01 }; -@@ -3521,7 +3562,7 @@ test_amd64_get_insn_details (void) - /* INSN: vzeroall, vex2 prefix. */ - vex2 = { 0xc5, 0xfc, 0x77 }; - amd64_get_insn_details (vex2.data (), &details); -- SELF_CHECK (details.opcode_len == 1); -+ SELF_CHECK (details.opcode_len == 2); - SELF_CHECK (details.enc_prefix_offset == 0); - SELF_CHECK (details.opcode_offset == 2); - SELF_CHECK (details.modrm_offset == -1); -@@ -3529,7 +3570,7 @@ test_amd64_get_insn_details (void) - /* INSN: vzeroall, vex3 prefix. */ - vex2_to_vex3 (vex2, vex3); - amd64_get_insn_details (vex3.data (), &details); -- SELF_CHECK (details.opcode_len == 1); -+ SELF_CHECK (details.opcode_len == 2); - SELF_CHECK (details.enc_prefix_offset == 0); - SELF_CHECK (details.opcode_offset == 3); - SELF_CHECK (details.modrm_offset == -1); -@@ -3537,7 +3578,7 @@ test_amd64_get_insn_details (void) - /* INSN: vzeroupper, vex2 prefix. */ - vex2 = { 0xc5, 0xf8, 0x77 }; - amd64_get_insn_details (vex2.data (), &details); -- SELF_CHECK (details.opcode_len == 1); -+ SELF_CHECK (details.opcode_len == 2); - SELF_CHECK (details.enc_prefix_offset == 0); - SELF_CHECK (details.opcode_offset == 2); - SELF_CHECK (details.modrm_offset == -1); -@@ -3545,10 +3586,40 @@ test_amd64_get_insn_details (void) - /* INSN: vzeroupper, vex3 prefix. */ - vex2_to_vex3 (vex2, vex3); - amd64_get_insn_details (vex3.data (), &details); -- SELF_CHECK (details.opcode_len == 1); -+ SELF_CHECK (details.opcode_len == 2); - SELF_CHECK (details.enc_prefix_offset == 0); - SELF_CHECK (details.opcode_offset == 3); - SELF_CHECK (details.modrm_offset == -1); -+ -+ /* INSN: vmovdqu 0x9(%rip),%ymm3, vex2 prefix. */ -+ vex2 = { 0xc5, 0xfe, 0x6f, 0x1d, 0x09, 0x00, 0x00, 0x00 }; -+ amd64_get_insn_details (vex2.data (), &details); -+ SELF_CHECK (details.opcode_len == 2); -+ SELF_CHECK (details.enc_prefix_offset == 0); -+ SELF_CHECK (details.opcode_offset == 2); -+ SELF_CHECK (details.modrm_offset == 3); -+ -+ /* INSN: vmovdqu 0x9(%rcx),%ymm3, vex2 prefix. */ -+ gdb::byte_vector updated_vex2 -+ = { 0xc5, 0xfe, 0x6f, 0x99, 0x09, 0x00, 0x00, 0x00 }; -+ tmp = vex2; -+ fixup_riprel (details, tmp.data (), ECX_REG_NUM); -+ SELF_CHECK (tmp == updated_vex2); -+ -+ /* INSN: vmovdqu 0x9(%rip),%ymm3, vex3 prefix. */ -+ vex2_to_vex3 (vex2, vex3); -+ amd64_get_insn_details (vex3.data (), &details); -+ SELF_CHECK (details.opcode_len == 2); -+ SELF_CHECK (details.enc_prefix_offset == 0); -+ SELF_CHECK (details.opcode_offset == 3); -+ SELF_CHECK (details.modrm_offset == 4); -+ -+ /* INSN: vmovdqu 0x9(%rcx),%ymm3, vex3 prefix. */ -+ gdb::byte_vector updated_vex3; -+ vex2_to_vex3 (updated_vex2, updated_vex3); -+ tmp = vex3; -+ fixup_riprel (details, tmp.data (), ECX_REG_NUM); -+ SELF_CHECK (tmp == updated_vex3); - } - - static void diff --git a/gdb-rhel-7329-vandps-clobbers-registers-13-of-13.patch b/gdb-rhel-7329-vandps-clobbers-registers-13-of-13.patch deleted file mode 100644 index 0d1fc91..0000000 --- a/gdb-rhel-7329-vandps-clobbers-registers-13-of-13.patch +++ /dev/null @@ -1,212 +0,0 @@ -From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 -From: Tom de Vries -Date: Fri, 7 Mar 2025 09:25:33 +0100 -Subject: gdb-rhel-7329-vandps-clobbers-registers-13-of-13.patch - -;; [gdb/tdep] Support REX2 and EVEX prefix -;; (Tom de Vries, RHEL-7329) - -The following amd64 insn: -... - 0: 67 d5 44 8d 3d 00 00 00 00 lea 0x0(%eip),%r31d -... -uses the REX2 prefix [1], which is currently not supported in -amd64_get_insn_details. - -Add the missing support in amd64_get_insn_details, as well as a corresponding -unit test. - -Likewise for an amd64 insn using an EVEX prefix [2]: -... - 0: 62 f1 7c 48 28 05 00 fc ff ff vmovaps -0x400(%rip),%zmm0 -... - -Tested on x86_64-linux. - -PR tdep/32725 -Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32725 - -[1] https://en.wikipedia.org/wiki/VEX_prefix -[2] https://en.wikipedia.org/wiki/EVEX_prefix - -diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c ---- a/gdb/amd64-tdep.c -+++ b/gdb/amd64-tdep.c -@@ -1158,6 +1158,14 @@ rex_prefix_p (gdb_byte pfx) - return REX_PREFIX_P (pfx); - } - -+/* True if PFX is the start of the 2-byte REX2 prefix. */ -+ -+static bool -+rex2_prefix_p (gdb_byte pfx) -+{ -+ return pfx == REX2_OPCODE; -+} -+ - /* True if PFX is the start of the 2-byte VEX prefix. */ - - static bool -@@ -1174,6 +1182,14 @@ vex3_prefix_p (gdb_byte pfx) - return pfx == 0xc4; - } - -+/* Return true if PFX is the start of the 4-byte EVEX prefix. */ -+ -+static bool -+evex_prefix_p (gdb_byte pfx) -+{ -+ return pfx == 0x62; -+} -+ - /* Skip the legacy instruction prefixes in INSN. - We assume INSN is properly sentineled so we don't have to worry - about falling off the end of the buffer. */ -@@ -1326,6 +1342,11 @@ amd64_get_insn_details (gdb_byte *insn, struct amd64_insn *details) - details->enc_prefix_offset = insn - start; - ++insn; - } -+ else if (rex2_prefix_p (*insn)) -+ { -+ details->enc_prefix_offset = insn - start; -+ insn += 2; -+ } - else if (vex2_prefix_p (*insn)) - { - details->enc_prefix_offset = insn - start; -@@ -1336,13 +1357,32 @@ amd64_get_insn_details (gdb_byte *insn, struct amd64_insn *details) - details->enc_prefix_offset = insn - start; - insn += 3; - } -+ else if (evex_prefix_p (*insn)) -+ { -+ details->enc_prefix_offset = insn - start; -+ insn += 4; -+ } - gdb_byte *prefix = (details->enc_prefix_offset == -1 - ? nullptr - : &start[details->enc_prefix_offset]); - - details->opcode_offset = insn - start; - -- if (prefix != nullptr && vex2_prefix_p (*prefix)) -+ if (prefix != nullptr && rex2_prefix_p (*prefix)) -+ { -+ bool m = (prefix[1] & (REX2_M << 4)) != 0; -+ if (!m) -+ { -+ need_modrm = onebyte_has_modrm[*insn]; -+ details->opcode_len = 1; -+ } -+ else -+ { -+ need_modrm = twobyte_has_modrm[*insn]; -+ details->opcode_len = 2; -+ } -+ } -+ else if (prefix != nullptr && vex2_prefix_p (*prefix)) - { - need_modrm = twobyte_has_modrm[*insn]; - details->opcode_len = 2; -@@ -1378,6 +1418,27 @@ amd64_get_insn_details (gdb_byte *insn, struct amd64_insn *details) - return; - } - } -+ else if (prefix != nullptr && evex_prefix_p (*prefix)) -+ { -+ need_modrm = twobyte_has_modrm[*insn]; -+ -+ gdb_byte m = prefix[1] & 0x7; -+ if (m == 1) -+ { -+ /* Escape 0x0f. */ -+ details->opcode_len = 2; -+ } -+ else if (m == 2 || m == 3) -+ { -+ /* Escape 0x0f 0x38 or 0x0f 0x3a. */ -+ details->opcode_len = 3; -+ } -+ else -+ { -+ /* Unknown opcode map. */ -+ return; -+ } -+ } - else if (*insn == TWO_BYTE_OPCODE_ESCAPE) - { - /* Two or three-byte opcode. */ -@@ -1425,6 +1486,15 @@ fixup_riprel (const struct amd64_insn &details, gdb_byte *insn, - /* Position of the not-B bit in the 3-byte VEX prefix (in byte 1). */ - static constexpr gdb_byte VEX3_NOT_B = 0x20; - -+ /* Position of the B3 and B4 bits in the REX2 prefix (in byte 1). */ -+ static constexpr gdb_byte REX2_B = 0x11; -+ -+ /* Position of the not-B3 bit in the EVEX prefix (in byte 1). */ -+ static constexpr gdb_byte EVEX_NOT_B = VEX3_NOT_B; -+ -+ /* Position of the B4 bit in the EVEX prefix (in byte 1). */ -+ static constexpr gdb_byte EVEX_B = 0x08; -+ - /* REX.B should be unset (VEX.!B set) as we were using rip-relative - addressing, but ensure it's unset (set for VEX) anyway, tmp_regno - is not r8-r15. */ -@@ -1433,12 +1503,19 @@ fixup_riprel (const struct amd64_insn &details, gdb_byte *insn, - gdb_byte *pfx = &insn[details.enc_prefix_offset]; - if (rex_prefix_p (pfx[0])) - pfx[0] &= ~REX_B; -+ else if (rex2_prefix_p (pfx[0])) -+ pfx[1] &= ~REX2_B; - else if (vex2_prefix_p (pfx[0])) - { - /* VEX.!B is set implicitly. */ - } - else if (vex3_prefix_p (pfx[0])) - pfx[1] |= VEX3_NOT_B; -+ else if (evex_prefix_p (pfx[0])) -+ { -+ pfx[1] |= EVEX_NOT_B; -+ pfx[1] &= ~EVEX_B; -+ } - else - gdb_assert_not_reached ("unhandled prefix"); - } -@@ -3620,6 +3697,37 @@ test_amd64_get_insn_details (void) - tmp = vex3; - fixup_riprel (details, tmp.data (), ECX_REG_NUM); - SELF_CHECK (tmp == updated_vex3); -+ -+ /* INSN: lea 0x0(%eip),%r31d, rex2 prefix. */ -+ insn = { 0x67, 0xd5, 0x44, 0x8d, 0x3d, 0x00, 0x00, 0x00, 0x00 }; -+ amd64_get_insn_details (insn.data (), &details); -+ SELF_CHECK (details.opcode_len == 1); -+ SELF_CHECK (details.enc_prefix_offset == 1); -+ SELF_CHECK (details.opcode_offset == 3); -+ SELF_CHECK (details.modrm_offset == 4); -+ /* This is incorrect, r31 is used instead of rdi, but currently that doesn't -+ matter. */ -+ SELF_CHECK (amd64_get_used_input_int_regs (&details, false) -+ == (1 << EDI_REG_NUM)); -+ -+ /* INSN: lea 0x0(%ecx),%r31d, rex2 prefix. */ -+ updated_insn = { 0x67, 0xd5, 0x44, 0x8d, 0xb9, 0x00, 0x00, 0x00, 0x00 }; -+ fixup_riprel (details, insn.data (), ECX_REG_NUM); -+ SELF_CHECK (insn == updated_insn); -+ -+ /* INSN: vmovaps -0x400(%rip),%zmm0, evex prefix. */ -+ insn = { 0x62, 0xf1, 0x7c, 0x48, 0x28, 0x05, 0x00, 0xfc, 0xff, 0xff }; -+ amd64_get_insn_details (insn.data (), &details); -+ SELF_CHECK (details.opcode_len == 2); -+ SELF_CHECK (details.enc_prefix_offset == 0); -+ SELF_CHECK (details.opcode_offset == 4); -+ SELF_CHECK (details.modrm_offset == 5); -+ -+ /* INSN: vmovaps -0x400(%rcx),%zmm0, evex prefix. */ -+ updated_insn -+ = { 0x62, 0xf1, 0x7c, 0x48, 0x28, 0x81, 0x00, 0xfc, 0xff, 0xff }; -+ fixup_riprel (details, insn.data (), ECX_REG_NUM); -+ SELF_CHECK (insn == updated_insn); - } - - static void diff --git a/gdb-rhel-7329-vandps-clobbers-registers-2-of-13.patch b/gdb-rhel-7329-vandps-clobbers-registers-2-of-13.patch deleted file mode 100644 index d9037d3..0000000 --- a/gdb-rhel-7329-vandps-clobbers-registers-2-of-13.patch +++ /dev/null @@ -1,66 +0,0 @@ -From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 -From: Tom de Vries -Date: Fri, 7 Mar 2025 09:25:33 +0100 -Subject: gdb-rhel-7329-vandps-clobbers-registers-2-of-13.patch - -;; [gdb/tdep] Refactor amd64_get_unused_input_int_reg, part 2 -;; (Tom de Vries, RHEL-7329) - -I noticed that amd64_get_unused_input_int_reg uses a signed int for a bit -mask: -... - /* 1 bit for each reg */ - int used_regs_mask = 0; -... - -There's an assert: -... - gdb_assert (used_regs_mask < 256); -... -which is meant to assert on register numbers >= 8, but if for instance -sizeof (used_regs_mask) == 4 and used_regs_mask == (1 << 31), then that is not -caught because of the signedness. - -We could fix this by changing the type to unsigned int, but that only -guarantees 16 bits in the reg mask. Intel CPUs with the APX extension support -32 int registers. - -The implementation of amd64_get_unused_input_int_reg doesn't support analyzing -registers with register number >= 8 yet, but now that we're changing the type, -it seems like a good idea to anticipate this. - -Fix this by using uint32_t. - -Likewise, update the loop over the reg mask: -... - for (i = 0; i < 8; ++i) - { - if (! (used_regs_mask & (1 << i))) - return i; -... -to handle any used_regs_mask value rather than just those for -register number < 8. - -Tested on x86_64-linux. - -diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c ---- a/gdb/amd64-tdep.c -+++ b/gdb/amd64-tdep.c -@@ -1217,7 +1217,7 @@ static int - amd64_get_unused_input_int_reg (const struct amd64_insn *details) - { - /* 1 bit for each reg */ -- int used_regs_mask = 0; -+ uint32_t used_regs_mask = 0; - - /* There can be at most 3 int regs used as inputs in an insn, and we have - 7 to choose from (RAX ... RDI, sans RSP). -@@ -1268,7 +1268,7 @@ amd64_get_unused_input_int_reg (const struct amd64_insn *details) - { - int i; - -- for (i = 0; i < 8; ++i) -+ for (i = 0; i < 32; ++i) - { - if (! (used_regs_mask & (1 << i))) - return i; diff --git a/gdb-rhel-7329-vandps-clobbers-registers-3-of-13.patch b/gdb-rhel-7329-vandps-clobbers-registers-3-of-13.patch deleted file mode 100644 index a28e03a..0000000 --- a/gdb-rhel-7329-vandps-clobbers-registers-3-of-13.patch +++ /dev/null @@ -1,100 +0,0 @@ -From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 -From: Tom de Vries -Date: Fri, 7 Mar 2025 09:25:33 +0100 -Subject: gdb-rhel-7329-vandps-clobbers-registers-3-of-13.patch - -;; [gdb/tdep] Refactor amd64_get_unused_input_int_reg, part 3 -;; (Tom de Vries, RHEL-7329) - -While reading amd64_get_unused_input_int_reg, I noticed that it avoids picking -RSP, which has to do with how the result of the only call to it is going to be -used. - -Likewise for picking a register in the RAX ... RDI range. - -Fix this by: -- adding an allowed_regs_mask parameter to amd64_get_unused_input_int_reg, and -- properly documenting the value of the corresponding argument in fixup_riprel. - -No functional changes. - -Tested on x86_64-linux. - -diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c ---- a/gdb/amd64-tdep.c -+++ b/gdb/amd64-tdep.c -@@ -1206,31 +1206,23 @@ amd64_skip_prefixes (gdb_byte *insn) - return insn; - } - --/* Return an integer register (other than RSP) that is unused as an input -- operand in INSN. -- In order to not require adding a rex prefix if the insn doesn't already -- have one, the result is restricted to RAX ... RDI, sans RSP. -- The register numbering of the result follows architecture ordering, -- e.g. RDI = 7. Return -1 if no register can be found. */ -+/* Return an integer register in ALLOWED_REGS_MASK that is unused as an input -+ operand in INSN. The register numbering of the result follows architecture -+ ordering, e.g. RDI = 7. Return -1 if no register can be found. */ - - static int --amd64_get_unused_input_int_reg (const struct amd64_insn *details) -+amd64_get_unused_input_int_reg (const struct amd64_insn *details, -+ uint32_t allowed_regs_mask) - { - /* 1 bit for each reg */ - uint32_t used_regs_mask = 0; - -- /* There can be at most 3 int regs used as inputs in an insn, and we have -- 7 to choose from (RAX ... RDI, sans RSP). -- This allows us to take a conservative approach and keep things simple. -- E.g. By avoiding RAX, we don't have to specifically watch for opcodes -- that implicitly specify RAX. */ -- -- /* Avoid RAX. */ -+ /* Assume RAX is used. If not, we'd have to detect opcodes that implicitly -+ use RAX. */ - used_regs_mask |= 1 << EAX_REG_NUM; -- /* Similarly avoid RDX, implicit operand in divides. */ -+ /* Assume RDX is used. If not, we'd have to detect opcodes that implicitly -+ use RDX, like divides. */ - used_regs_mask |= 1 << EDX_REG_NUM; -- /* Avoid RSP. */ -- used_regs_mask |= 1 << ESP_REG_NUM; - - /* If the opcode is one byte long and there's no ModRM byte, - assume the opcode specifies a register. */ -@@ -1270,6 +1262,9 @@ amd64_get_unused_input_int_reg (const struct amd64_insn *details) - - for (i = 0; i < 32; ++i) - { -+ if (! (allowed_regs_mask & (1 << i))) -+ continue; -+ - if (! (used_regs_mask & (1 << i))) - return i; - } -@@ -1379,13 +1374,19 @@ fixup_riprel (struct gdbarch *gdbarch, - dsc->insn_buf.size (), from); - rip_base = from + insn_length; - -- /* We need a register to hold the address. -- Pick one not used in the insn. -- NOTE: arch_tmp_regno uses architecture ordering, e.g. RDI = 7. */ -- arch_tmp_regno = amd64_get_unused_input_int_reg (insn_details); -+ /* We need a register to hold the address. Pick one not used in the insn. -+ In order to not require adding a rex prefix if the insn doesn't already -+ have one, the range is restricted to RAX ... RDI, without RSP. -+ We avoid RSP, because when patched into in the modrm byte, it doesn't -+ indicate the use of the register, but instead the use of a SIB byte. */ -+ uint32_t allowed_regs_mask = 0xff & ~(1 << ESP_REG_NUM); -+ arch_tmp_regno -+ = amd64_get_unused_input_int_reg (insn_details, allowed_regs_mask); - if (arch_tmp_regno == -1) - return false; - -+ /* Convert arch_tmp_regno, which uses architecture ordering (e.g. RDI = 7), -+ to GDB regnum. */ - tmp_regno = amd64_arch_reg_to_regnum (arch_tmp_regno); - - /* Position of the not-B bit in the 3-byte VEX prefix (in byte 1). */ diff --git a/gdb-rhel-7329-vandps-clobbers-registers-4-of-13.patch b/gdb-rhel-7329-vandps-clobbers-registers-4-of-13.patch deleted file mode 100644 index 37159e1..0000000 --- a/gdb-rhel-7329-vandps-clobbers-registers-4-of-13.patch +++ /dev/null @@ -1,59 +0,0 @@ -From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 -From: Tom de Vries -Date: Fri, 7 Mar 2025 09:25:33 +0100 -Subject: gdb-rhel-7329-vandps-clobbers-registers-4-of-13.patch - -;; [gdb/tdep] Factor out amd64_get_used_input_int_regs -;; (Tom de Vries, RHEL-7329) - -The function amd64_get_unused_input_int_reg consists of two parts: -- finding the used int registers in an insn, and -- picking an unused int register. - -Factor out the first part as new function amd64_get_used_input_int_regs. - -No functional changes. - -Tested on x86_64-linux. - -diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c ---- a/gdb/amd64-tdep.c -+++ b/gdb/amd64-tdep.c -@@ -1206,13 +1206,11 @@ amd64_skip_prefixes (gdb_byte *insn) - return insn; - } - --/* Return an integer register in ALLOWED_REGS_MASK that is unused as an input -- operand in INSN. The register numbering of the result follows architecture -- ordering, e.g. RDI = 7. Return -1 if no register can be found. */ -+/* Return a register mask for the integer registers that are used as an input -+ operand in INSN. */ - --static int --amd64_get_unused_input_int_reg (const struct amd64_insn *details, -- uint32_t allowed_regs_mask) -+static uint32_t -+amd64_get_used_input_int_regs (const struct amd64_insn *details) - { - /* 1 bit for each reg */ - uint32_t used_regs_mask = 0; -@@ -1255,6 +1253,19 @@ amd64_get_unused_input_int_reg (const struct amd64_insn *details, - } - - gdb_assert (used_regs_mask < 256); -+ return used_regs_mask; -+} -+ -+/* Return an integer register in ALLOWED_REGS_MASK that is unused as an input -+ operand in INSN. The register numbering of the result follows architecture -+ ordering, e.g. RDI = 7. Return -1 if no register can be found. */ -+ -+static int -+amd64_get_unused_input_int_reg (const struct amd64_insn *details, -+ uint32_t allowed_regs_mask) -+{ -+ /* 1 bit for each reg */ -+ uint32_t used_regs_mask = amd64_get_used_input_int_regs (details); - - /* Finally, find a free reg. */ - { diff --git a/gdb-rhel-7329-vandps-clobbers-registers-5-of-13.patch b/gdb-rhel-7329-vandps-clobbers-registers-5-of-13.patch deleted file mode 100644 index 10f8046..0000000 --- a/gdb-rhel-7329-vandps-clobbers-registers-5-of-13.patch +++ /dev/null @@ -1,137 +0,0 @@ -From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 -From: Tom de Vries -Date: Fri, 7 Mar 2025 09:25:33 +0100 -Subject: gdb-rhel-7329-vandps-clobbers-registers-5-of-13.patch - -;; [gdb/tdep] Add amd64-insn-decode selftest -;; (Tom de Vries, RHEL-7329) - -Add a selftest that checks the results of amd64_get_insn_details and related -functions for two basic instructions. - -Add a parameter assumptions to amd64_get_used_input_int_regs, to make sure -that this selftest: -... - /* INSN: add %eax,(%rcx). */ - ... - SELF_CHECK (amd64_get_used_input_int_regs (&details, false) - == ((1 << EAX_REG_NUM) | (1 << ECX_REG_NUM))); -... -passes because it found the "%eax" in the insn, rather than passing because of -this assumption: -... - /* Assume RAX is used. If not, we'd have to detect opcodes that implicitly - use RAX. */ - used_regs_mask |= 1 << EAX_REG_NUM; -... - -Tested on x86_64-linux. - -diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c ---- a/gdb/amd64-tdep.c -+++ b/gdb/amd64-tdep.c -@@ -50,6 +50,7 @@ - #include "osabi.h" - #include "x86-tdep.h" - #include "amd64-ravenscar-thread.h" -+#include "gdbsupport/selftest.h" - - /* Note that the AMD64 architecture was previously known as x86-64. - The latter is (forever) engraved into the canonical system name as -@@ -1207,20 +1208,25 @@ amd64_skip_prefixes (gdb_byte *insn) - } - - /* Return a register mask for the integer registers that are used as an input -- operand in INSN. */ -+ operand in INSN. If !ASSUMPTIONS, only return the registers we actually -+ found, for the benefit of self tests. */ - - static uint32_t --amd64_get_used_input_int_regs (const struct amd64_insn *details) -+amd64_get_used_input_int_regs (const struct amd64_insn *details, -+ bool assumptions = true) - { - /* 1 bit for each reg */ - uint32_t used_regs_mask = 0; - -- /* Assume RAX is used. If not, we'd have to detect opcodes that implicitly -- use RAX. */ -- used_regs_mask |= 1 << EAX_REG_NUM; -- /* Assume RDX is used. If not, we'd have to detect opcodes that implicitly -- use RDX, like divides. */ -- used_regs_mask |= 1 << EDX_REG_NUM; -+ if (assumptions) -+ { -+ /* Assume RAX is used. If not, we'd have to detect opcodes that implicitly -+ use RAX. */ -+ used_regs_mask |= 1 << EAX_REG_NUM; -+ /* Assume RDX is used. If not, we'd have to detect opcodes that implicitly -+ use RDX, like divides. */ -+ used_regs_mask |= 1 << EDX_REG_NUM; -+ } - - /* If the opcode is one byte long and there's no ModRM byte, - assume the opcode specifies a register. */ -@@ -3395,6 +3401,51 @@ amd64_target_description (uint64_t xcr0, bool segments) - return *tdesc; - } - -+#if GDB_SELF_TEST -+ -+namespace selftests { -+ -+/* Test amd64_get_insn_details. */ -+ -+static void -+test_amd64_get_insn_details (void) -+{ -+ struct amd64_insn details; -+ gdb::byte_vector insn; -+ -+ /* INSN: add %eax,(%rcx). */ -+ insn = { 0x01, 0x01 }; -+ amd64_get_insn_details (insn.data (), &details); -+ SELF_CHECK (details.opcode_len == 1); -+ SELF_CHECK (details.enc_prefix_offset == -1); -+ SELF_CHECK (details.opcode_offset == 0); -+ SELF_CHECK (details.modrm_offset == 1); -+ SELF_CHECK (amd64_get_used_input_int_regs (&details, false) -+ == ((1 << EAX_REG_NUM) | (1 << ECX_REG_NUM))); -+ SELF_CHECK (rip_relative_offset (&details) == 0); -+ -+ /* INSN: push %rax. This exercises the "opcode specifies register" case in -+ amd64_get_used_input_int_regs. */ -+ insn = { 0x50 }; -+ amd64_get_insn_details (insn.data (), &details); -+ SELF_CHECK (details.opcode_len == 1); -+ SELF_CHECK (details.enc_prefix_offset == -1); -+ SELF_CHECK (details.opcode_offset == 0); -+ SELF_CHECK (details.modrm_offset == -1); -+ SELF_CHECK (amd64_get_used_input_int_regs (&details, false) -+ == ((1 << EAX_REG_NUM))); -+ SELF_CHECK (rip_relative_offset (&details) == 0); -+} -+ -+static void -+amd64_insn_decode (void) -+{ -+ test_amd64_get_insn_details (); -+} -+ -+} // namespace selftests -+#endif /* GDB_SELF_TEST */ -+ - void _initialize_amd64_tdep (); - void - _initialize_amd64_tdep () -@@ -3403,6 +3454,10 @@ _initialize_amd64_tdep () - amd64_none_init_abi); - gdbarch_register_osabi (bfd_arch_i386, bfd_mach_x64_32, GDB_OSABI_NONE, - amd64_x32_none_init_abi); -+#if GDB_SELF_TEST -+ selftests::register_test ("amd64-insn-decode", -+ selftests::amd64_insn_decode); -+#endif - } - - diff --git a/gdb-rhel-7329-vandps-clobbers-registers-6-of-13.patch b/gdb-rhel-7329-vandps-clobbers-registers-6-of-13.patch deleted file mode 100644 index 9bb4b87..0000000 --- a/gdb-rhel-7329-vandps-clobbers-registers-6-of-13.patch +++ /dev/null @@ -1,55 +0,0 @@ -From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 -From: Tom de Vries -Date: Fri, 7 Mar 2025 09:25:33 +0100 -Subject: gdb-rhel-7329-vandps-clobbers-registers-6-of-13.patch - -;; [gdb/tdep] Factor out rip_relative_p -;; (Tom de Vries, RHEL-7329) - -Factor out rip_relative_p, and rewrite it to use MODRM_MOD_FIELD and -MODRM_RM_FIELD. - -No functional changes. - -Tested on x86_64-linux. - -diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c ---- a/gdb/amd64-tdep.c -+++ b/gdb/amd64-tdep.c -@@ -1207,6 +1207,18 @@ amd64_skip_prefixes (gdb_byte *insn) - return insn; - } - -+/* Return true if the MODRM byte of an insn indicates that the insn is -+ rip-relative. */ -+ -+static bool -+rip_relative_p (gdb_byte modrm) -+{ -+ gdb_byte mod = MODRM_MOD_FIELD (modrm); -+ gdb_byte rm = MODRM_RM_FIELD (modrm); -+ -+ return mod == 0 && rm == 0x05; -+} -+ - /* Return a register mask for the integer registers that are used as an input - operand in INSN. If !ASSUMPTIONS, only return the registers we actually - found, for the benefit of self tests. */ -@@ -1455,7 +1467,7 @@ fixup_displaced_copy (struct gdbarch *gdbarch, - { - gdb_byte modrm = details->raw_insn[details->modrm_offset]; - -- if ((modrm & 0xc7) == 0x05) -+ if (rip_relative_p (modrm)) - { - /* The insn uses rip-relative addressing. - Deal with it. */ -@@ -1789,7 +1801,7 @@ rip_relative_offset (struct amd64_insn *insn) - { - gdb_byte modrm = insn->raw_insn[insn->modrm_offset]; - -- if ((modrm & 0xc7) == 0x05) -+ if (rip_relative_p (modrm)) - { - /* The displacement is found right after the ModRM byte. */ - return insn->modrm_offset + 1; diff --git a/gdb-rhel-7329-vandps-clobbers-registers-7-of-13.patch b/gdb-rhel-7329-vandps-clobbers-registers-7-of-13.patch deleted file mode 100644 index 9a04e2e..0000000 --- a/gdb-rhel-7329-vandps-clobbers-registers-7-of-13.patch +++ /dev/null @@ -1,55 +0,0 @@ -From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 -From: Tom de Vries -Date: Fri, 7 Mar 2025 09:25:33 +0100 -Subject: gdb-rhel-7329-vandps-clobbers-registers-7-of-13.patch - -;; [gdb/tdep] Fix rip-relative insn handling in amd64_get_used_input_int_reg -;; (Tom de Vries, RHEL-7329) - -I wanted to add a unit test for an an rip-relative amd64 insn, so I did: -... -$ gcc -fPIE hello.c -... -and used an rip-relative insn from main: -... - 4005db: 48 8d 3d 1e 00 00 00 lea 0x1e(%rip),%rdi -... - -While writing the unit test, I found that amd64_get_used_input_int_reg returns -rbp as input register. - -Fix this by using rip_relative_p in amd64_get_used_input_int_reg to handle -this case. - -Tested on x86_64-linux. - -diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c ---- a/gdb/amd64-tdep.c -+++ b/gdb/amd64-tdep.c -@@ -1264,7 +1264,7 @@ amd64_get_used_input_int_regs (const struct amd64_insn *details, - used_regs_mask |= 1 << base; - used_regs_mask |= 1 << idx; - } -- else -+ else if (!rip_relative_p (modrm)) - { - used_regs_mask |= 1 << rm; - } -@@ -3447,6 +3447,17 @@ test_amd64_get_insn_details (void) - SELF_CHECK (amd64_get_used_input_int_regs (&details, false) - == ((1 << EAX_REG_NUM))); - SELF_CHECK (rip_relative_offset (&details) == 0); -+ -+ /* INSN: lea 0x1e(%rip),%rdi, rex prefix. */ -+ insn = { 0x48, 0x8d, 0x3d, 0x1e, 0x00, 0x00, 0x00 }; -+ amd64_get_insn_details (insn.data (), &details); -+ SELF_CHECK (details.opcode_len == 1); -+ SELF_CHECK (details.enc_prefix_offset == 0); -+ SELF_CHECK (details.opcode_offset == 1); -+ SELF_CHECK (details.modrm_offset == 2); -+ SELF_CHECK (amd64_get_used_input_int_regs (&details, false) -+ == (1 << EDI_REG_NUM)); -+ SELF_CHECK (rip_relative_offset (&details) == 3); - } - - static void diff --git a/gdb-rhel-7329-vandps-clobbers-registers-8-of-13.patch b/gdb-rhel-7329-vandps-clobbers-registers-8-of-13.patch deleted file mode 100644 index 4b75b0e..0000000 --- a/gdb-rhel-7329-vandps-clobbers-registers-8-of-13.patch +++ /dev/null @@ -1,112 +0,0 @@ -From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 -From: Tom de Vries -Date: Fri, 7 Mar 2025 09:25:33 +0100 -Subject: gdb-rhel-7329-vandps-clobbers-registers-8-of-13.patch - -;; [gdb/tdep] Factor out part of fixup_riprel -;; (Tom de Vries, RHEL-7329) - -Factor out the part of fixup_riprel that patches the insn, and use it in a -unit test. - -Tested on x86_64-linux. - -diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c ---- a/gdb/amd64-tdep.c -+++ b/gdb/amd64-tdep.c -@@ -1377,6 +1377,36 @@ amd64_get_insn_details (gdb_byte *insn, struct amd64_insn *details) - } - } - -+/* Convert a %rip-relative INSN to use BASEREG+disp addressing, leaving -+ displacement unchanged. */ -+ -+static void -+fixup_riprel (const struct amd64_insn &details, gdb_byte *insn, -+ int basereg) -+{ -+ /* Position of the not-B bit in the 3-byte VEX prefix (in byte 1). */ -+ static constexpr gdb_byte VEX3_NOT_B = 0x20; -+ -+ /* REX.B should be unset (VEX.!B set) as we were using rip-relative -+ addressing, but ensure it's unset (set for VEX) anyway, tmp_regno -+ is not r8-r15. */ -+ if (details.enc_prefix_offset != -1) -+ { -+ gdb_byte *pfx = &insn[details.enc_prefix_offset]; -+ if (rex_prefix_p (pfx[0])) -+ pfx[0] &= ~REX_B; -+ else if (vex3_prefix_p (pfx[0])) -+ pfx[1] |= VEX3_NOT_B; -+ else -+ gdb_assert_not_reached ("unhandled prefix"); -+ } -+ -+ int modrm_offset = details.modrm_offset; -+ /* Convert the ModRM field to be base+disp. */ -+ insn[modrm_offset] &= ~0xc7; -+ insn[modrm_offset] |= 0x80 + basereg; -+} -+ - /* Update %rip-relative addressing in INSN. - - %rip-relative addressing only uses a 32-bit displacement. -@@ -1392,7 +1422,6 @@ fixup_riprel (struct gdbarch *gdbarch, - CORE_ADDR from, CORE_ADDR to, struct regcache *regs) - { - const struct amd64_insn *insn_details = &dsc->insn_details; -- int modrm_offset = insn_details->modrm_offset; - CORE_ADDR rip_base; - int insn_length; - int arch_tmp_regno, tmp_regno; -@@ -1414,36 +1443,17 @@ fixup_riprel (struct gdbarch *gdbarch, - if (arch_tmp_regno == -1) - return false; - -+ fixup_riprel (dsc->insn_details, dsc->insn_buf.data (), arch_tmp_regno); -+ - /* Convert arch_tmp_regno, which uses architecture ordering (e.g. RDI = 7), - to GDB regnum. */ - tmp_regno = amd64_arch_reg_to_regnum (arch_tmp_regno); - -- /* Position of the not-B bit in the 3-byte VEX prefix (in byte 1). */ -- static constexpr gdb_byte VEX3_NOT_B = 0x20; -- -- /* REX.B should be unset (VEX.!B set) as we were using rip-relative -- addressing, but ensure it's unset (set for VEX) anyway, tmp_regno -- is not r8-r15. */ -- if (insn_details->enc_prefix_offset != -1) -- { -- gdb_byte *pfx = &dsc->insn_buf[insn_details->enc_prefix_offset]; -- if (rex_prefix_p (pfx[0])) -- pfx[0] &= ~REX_B; -- else if (vex3_prefix_p (pfx[0])) -- pfx[1] |= VEX3_NOT_B; -- else -- gdb_assert_not_reached ("unhandled prefix"); -- } -- - regcache_cooked_read_unsigned (regs, tmp_regno, &orig_value); - dsc->tmp_regno = tmp_regno; - dsc->tmp_save = orig_value; - dsc->tmp_used = 1; - -- /* Convert the ModRM field to be base+disp. */ -- dsc->insn_buf[modrm_offset] &= ~0xc7; -- dsc->insn_buf[modrm_offset] |= 0x80 + arch_tmp_regno; -- - regcache_cooked_write_unsigned (regs, tmp_regno, rip_base); - - displaced_debug_printf ("%%rip-relative addressing used."); -@@ -3458,6 +3468,11 @@ test_amd64_get_insn_details (void) - SELF_CHECK (amd64_get_used_input_int_regs (&details, false) - == (1 << EDI_REG_NUM)); - SELF_CHECK (rip_relative_offset (&details) == 3); -+ -+ /* INSN: lea 0x1e(%ecx),%rdi, rex prefix. */ -+ gdb::byte_vector updated_insn = { 0x48, 0x8d, 0xb9, 0x1e, 0x00, 0x00, 0x00 }; -+ fixup_riprel (details, insn.data (), ECX_REG_NUM); -+ SELF_CHECK (insn == updated_insn); - } - - static void diff --git a/gdb-rhel-7329-vandps-clobbers-registers-9-of-13.patch b/gdb-rhel-7329-vandps-clobbers-registers-9-of-13.patch deleted file mode 100644 index 3c61905..0000000 --- a/gdb-rhel-7329-vandps-clobbers-registers-9-of-13.patch +++ /dev/null @@ -1,78 +0,0 @@ -From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 -From: Tom de Vries -Date: Fri, 7 Mar 2025 09:25:33 +0100 -Subject: gdb-rhel-7329-vandps-clobbers-registers-9-of-13.patch - -;; [gdb/tdep] Add vex2_to_vex3 -;; (Tom de Vries, RHEL-7329) - -I noticed here [1] that the vex2 prefix is essentially a special case of the -vex3 prefix, meaning it's possible to rewrite any insn with a vex2 prefix into -an equivalent one with a vex3 prefix. - -Add function vex2_to_vex3 that does precisely that, in the selftests -namespace. - -Add a selftest that exercises this function. - -Tested on x86_64-linux. - -[1] https://en.wikipedia.org/wiki/VEX_prefix - -diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c ---- a/gdb/amd64-tdep.c -+++ b/gdb/amd64-tdep.c -@@ -3427,6 +3427,45 @@ amd64_target_description (uint64_t xcr0, bool segments) - - namespace selftests { - -+/* Recode a vex2 instruction into a vex3 instruction. */ -+ -+static void -+vex2_to_vex3 (gdb::byte_vector &vex2, gdb::byte_vector &vex3) -+{ -+ gdb_assert (vex2.size () >= 2); -+ gdb_assert (vex2[0] == 0xc5); -+ -+ unsigned char r = vex2[1] >> 7; -+ unsigned char b = 0x1; -+ unsigned char x = 0x1; -+ unsigned char m = 0x1; -+ unsigned char w = 0x0; -+ -+ vex3.resize (3); -+ vex3[0] = 0xc4; -+ vex3[1] = (r << 7) | (x << 6) | (b << 5) | m; -+ vex3[2] = (vex2[1] & ~0x80) | (w << 7); -+ -+ std::copy (vex2.begin () + 2, vex2.end (), -+ std::back_inserter (vex3)); -+} -+ -+/* Test vex2 to vex3. */ -+ -+static void -+test_vex2_to_vex3 (void) -+{ -+ /* INSN: vzeroall, vex2 prefix. */ -+ gdb::byte_vector vex2 = { 0xc5, 0xfc, 0x77 }; -+ -+ gdb::byte_vector vex3; -+ vex2_to_vex3 (vex2, vex3); -+ -+ /* INSN: vzeroall, vex3 prefix. */ -+ gdb::byte_vector vex3_ref = { 0xc4, 0xe1, 0x7c, 0x77 }; -+ SELF_CHECK (vex3 == vex3_ref); -+} -+ - /* Test amd64_get_insn_details. */ - - static void -@@ -3478,6 +3517,7 @@ test_amd64_get_insn_details (void) - static void - amd64_insn_decode (void) - { -+ test_vex2_to_vex3 (); - test_amd64_get_insn_details (); - } - diff --git a/gdb-add-rpm-suggestion-script.patch b/gdb-rpm-suggestion-script.patch similarity index 99% rename from gdb-add-rpm-suggestion-script.patch rename to gdb-rpm-suggestion-script.patch index 1eccb30..28df14b 100644 --- a/gdb-add-rpm-suggestion-script.patch +++ b/gdb-rpm-suggestion-script.patch @@ -1,7 +1,7 @@ From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 From: Andrew Burgess Date: Thu, 7 Mar 2024 15:14:23 +0000 -Subject: gdb-add-rpm-suggestion-script.patch +Subject: gdb-rpm-suggestion-script.patch ;; Not a backport. Add a new script which hooks into GDB and suggests ;; RPMs to install when GDB finds an objfile with no debug info. @@ -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} -@@ -50588,6 +50589,111 @@ Show the current verbosity setting. +@@ -50986,6 +50987,111 @@ Show the current verbosity setting. @end table @@ -149,7 +149,7 @@ diff --git a/gdb/python/lib/gdb/command/rpm-suggestions.py b/gdb/python/lib/gdb/ new file mode 100644 --- /dev/null +++ b/gdb/python/lib/gdb/command/rpm-suggestions.py -@@ -0,0 +1,525 @@ +@@ -0,0 +1,527 @@ +# Copyright 2023 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify @@ -655,7 +655,9 @@ new file mode 100644 + +# The 'info rpm-suggestions' command. +class rpm_suggestion_info(gdb.Command): -+ """Relist any RPM installation suggestions that have been made ++ """Relist RPM suggestions. ++ ++ Relist any RPM installation suggestions that have been made + since the executable was last changed.""" + def __init__(self): + super().__init__("info rpm-suggestions", gdb.COMMAND_NONE, gdb.COMPLETE_NONE) @@ -967,7 +969,7 @@ diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp # If DEBUGINFOD_URLS is set, gdb will try to download sources and # debug info for f.i. system libraries. Prevent this. -@@ -2610,6 +2611,18 @@ proc default_gdb_start { } { +@@ -2850,6 +2851,18 @@ proc default_gdb_start { } { } } diff --git a/gdb-tcl9-utf8-encoding-fix.patch b/gdb-tcl9-utf8-encoding-fix.patch new file mode 100644 index 0000000..9383e17 --- /dev/null +++ b/gdb-tcl9-utf8-encoding-fix.patch @@ -0,0 +1,157 @@ +From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 +From: Kevin Buettner +Date: Sun, 24 May 2026 21:02:34 -0700 +Subject: gdb-tcl9-utf8-encoding-fix.patch + +;; Fix EILSEQ problems for UTF8 related tests when using expect +;; enabled with Tcl 9 and full set of Tcl 9 compatibility fixes +;; from upstream PR80674 branch. Required for testing GDB on +;; Fedora 44 and rawhide (Fedora 45). +;; +;; At the time of this Fedora commit, this patch was not upstream yet. +;; For its status, see: +;; +;; https://inbox.sourceware.org/gdb-patches/20260526192701.3835262-2-kevinb@redhat.com/T/#u + +Fix EILSEQ problems for UTF8 related tests + +On Fedora 44 and Rawhide (Fedora 45), these tests... + + gdb.ada/non-ascii-utf-8.exp + gdb.base/utf8-identifiers.exp + gdb.rust/unicode.exp + +...all die due to these errors: + + Running ...gdb/testsuite/gdb.base/utf8-identifiers.exp ... + ERROR: tcl error sourcing .../gdb/testsuite/gdb.base/utf8-identifiers.exp. + ERROR: tcl error code POSIX EILSEQ {invalid or incomplete multibyte or wide character} + error writing "file6": invalid or incomplete multibyte or wide character + ... + +(I've shortened some of the pathnames for brevity.) + +These Fedora systems are using Tcl 9 and also an updated version of +dejagnu with this change applied: + + * Thu Apr 16 2026 Jakub Jelinek - 1:1.6.3-17 + - Apply full set of Tcl 9 compatibility fixes from upstream PR80674 branch + (#2448542) + +That runtest change is responsible for the POSIX EILSEQ errors on +machines with that change. The change to runtest causing the change +in behavior for GDB is the addition of these lines near the top of +the runtest script: + + # Ensure that DejaGnu will be run in the POSIX locale + LC_ALL=C + export LC_ALL + +Tcl 8 used a permissive encoding strategy: bytes that could not be +represented in the current encoding were silently mangled or +substituted. Tcl 9 changed this default to a strict profile, which +means that any attempt to write a character that cannot be expressed +in the channel's encoding raises a POSIX EILSEQ error ("invalid or +incomplete multibyte or wide character"). + +So, together, this Tcl 9 behavior combined with the dejagnu change +to runtest causes the EILSEQ error for the tests mentioned earlier. + +Fix it by using "fconfigure $handle -encoding utf-8 -profile replace" +in proc spawn_capture_tty_name, and proc gdb_stdin_log_init. Also, +the open_logs wrapper has been changed to invoke fconfigure using only +"-encoding utf-8". Testing showed that "-profile replace" wasn't +necessary there. + +Tested on Fedora 28 (Tcl 8.6.8), Fedora 43 (Tcl 9.0.2 / 8.6.16; expect +uses 8.6.16), Fedora 44 (Tcl 9.0.2 / 8.6.17; expect uses 9.0.2), and +Rawhide / Fedora 45 (Tcl 9.0.3 / 8.6.18; expect uses 9.0.3). + +With regard to the Bug noted below, I haven't been able to reproduce +the failures in gdb.ada/lazy-string.exp, but I've been informed that +this commit fixes it. + +Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=34146 +Reviewed-By: Tom de Vries + +diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp +--- a/gdb/testsuite/lib/gdb.exp ++++ b/gdb/testsuite/lib/gdb.exp +@@ -152,6 +152,35 @@ proc load_lib { file } { + return $result + } + ++# Tcl 9.0 changed the default channel encoding profile to "strict". ++# When runtest sets LC_ALL=C the system encoding is iso8859-1, so file ++# channels opened by DejaGNU (gdb.sum, gdb.log) and spawn channels ++# (for GDB and subprocesses) default to iso8859-1 with strict profile. ++# Writing non-Latin-1 characters in test names then raises EILSEQ, and ++# sending them to GDB truncates the command at the unrepresentable ++# character. ++# ++# Fix this by: ++# 1. Overriding open_logs to reconfigure gdb.sum to UTF-8 after ++# DejaGNU opens it with the system (iso8859-1) encoding. Only ++# "-encoding utf-8" is needed here, not "-profile replace". The ++# test names written to gdb.sum are Unicode strings, and since ++# UTF-8 can represent every Unicode character, the encode ++# operation cannot fail. This use of "fconfigure" lacks a Tcl ++# version guard since "-encoding utf-8" works in both Tcl 8 and ++# Tcl 9. (Use of "-profile replace" requires a guard, as that ++# option did not exist before Tcl 9.) ++# 2. Reconfiguring each new spawn channel to UTF-8 and to also use ++# "-profile replace" in spawn_capture_tty_name, which wraps every ++# spawn call. ++# 3. Likewise for gdb_stdin_log_init. ++ ++rename open_logs saved_open_logs ++proc open_logs {} { ++ saved_open_logs ++ fconfigure $::sum_file -encoding utf-8 ++} ++ + load_lib libgloss.exp + load_lib cache.exp + load_lib gdb-utils.exp +@@ -2685,6 +2714,7 @@ proc gdb_file_cmd { arg {kill_flag 1} } { + proc spawn_capture_tty_name { args } { + set result [uplevel builtin_spawn $args] + upvar spawn_out spawn_out ++ upvar spawn_id spawn_id + if { [info exists spawn_out(slave,name)] } { + set ::last_spawn_tty_name $spawn_out(slave,name) + } else { +@@ -2700,6 +2730,21 @@ proc spawn_capture_tty_name { args } { + # use -nocomplain here we would otherwise get an error. + unset -nocomplain ::last_spawn_tty_name + } ++ # Tcl 9.0 defaults spawn channels to iso8859-1/strict, which ++ # raises EILSEQ when non-Latin-1 characters (e.g. identifiers ++ # with UTF-8 letters) are written to or read from the channel. ++ # Use utf-8 instead. ++ # ++ # "catch" is used here because, unlike the other sites in this ++ # file where fconfigure is used, this use of fconfigure could ++ # attempt to modify channels which do not support these options. ++ # Those other sites use "fconfigure" on recently opened files ++ # where it will almost certainly work. (And, for those other sites, ++ # if it doesn't work, we want to be notified of that fact via the ++ # normal Tcl error reporting mechanisms.) ++ if {[tcl_version_at_least 9 0 0]} { ++ catch {fconfigure $spawn_id -encoding utf-8 -profile replace} ++ } + return $result + } + +@@ -10387,6 +10432,11 @@ proc gdb_stdin_log_init { } { + set logfile [standard_output_file_with_gdb_instance gdb.in] + set in_file [open $logfile w] + ++ if {[tcl_version_at_least 9 0 0]} { ++ # Tcl 9 strict profile: gdb.in must accept UTF-8 command strings. ++ fconfigure $in_file -encoding utf-8 -profile replace ++ } ++ + verbose -log "" + verbose -log "Starting logfile: $logfile" + verbose -log "" diff --git a/gdb-6.3-rh-testversion-20041202.patch b/gdb-test-show-version.patch similarity index 96% rename from gdb-6.3-rh-testversion-20041202.patch rename to gdb-test-show-version.patch index 59132bf..feee38e 100644 --- a/gdb-6.3-rh-testversion-20041202.patch +++ b/gdb-test-show-version.patch @@ -1,7 +1,7 @@ From FEDORA_PATCHES Mon Sep 17 00:00:00 2001 From: Andrew Burgess Date: Fri, 27 Oct 2017 21:07:50 +0200 -Subject: gdb-6.3-rh-testversion-20041202.patch +Subject: gdb-test-show-version.patch ;; Check distro name is included in the version output. diff --git a/gdb.spec b/gdb.spec index 64c477d..80770cb 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: 16.3 +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: 3%{?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. @@ -135,10 +135,6 @@ BuildRequires: gcc-c++ #=push+jan Source2: gdb-orphanripper.c -# Man page for gstack(1). -#=push+jan -Source3: gdb-gstack.man - # /etc/gdbinit (from Debian but with Fedora compliant location). #=fedora Source4: gdbinit @@ -146,7 +142,6 @@ Source4: gdbinit # Include the auto-generated file containing the "Patch:" directives. # See README.local-patches for more details. Source9998: _gdb.spec.Patch.include -Source9999: _gdb.spec.patch.include %include %{SOURCE9998} BuildRequires: readline-devel%{buildisa} >= 7.0 @@ -224,11 +219,6 @@ BuildRequires: %{?scl_testing_prefix}gcc %{?scl_testing_prefix}gcc-c++ %{?scl_te BuildRequires: gcc-objc %endif -# We don't support gcc-gdb-plugin on RHEL anymore. -%if 0%{!?rhel:1} -BuildRequires: gcc-gdb-plugin%{?_isa} -%endif - BuildRequires: systemtap-sdt-devel BuildRequires: opencl-headers ocl-icd-devel%{bits_local} ocl-icd-devel%{bits_other} @@ -313,7 +303,7 @@ machine than the one which is running the program being debugged. %package doc Summary: Documentation for GDB (the GNU source-level debugger) -License: GFDL +License: GFDL-1.3-or-later BuildArch: noarch %if 0%{?scl:1} # As of F-28, packages won't need to call /sbin/install-info by hand @@ -331,7 +321,7 @@ and printing their data. This package provides INFO, HTML and PDF user manual for GDB. %prep -%setup -q -n %{gdb_src} +%autosetup -p1 -n %{gdb_src} # Files have `# ' statements breaking VPATH / find-debuginfo.sh . (cd gdb;rm -fv $(perl -pe 's/\\\n/ /' - 16.3-3.el10 -- Include gdb-index-generation-fixes.patch (renamed). - (Tom Tromey and Andrew Burgess, RHEL-150992) +* Mon Jul 13 2026 Guinevere Larsen 17.2-1 +- Rebased to Fedora's GDB 17.2. + Resolves: RHEL-126216 -* Mon Jun 02 2025 Guinevere Larsen - 16.3-2.el10 -- Backport fixes to decoding AMD64 instructions - Resolves: RHEL-93581 +* Wed Jul 8 2026 Andrew Burgess +- Backport upstream commit 93f536d813c41527 to fix RHBZ 2498034. This + commit will drop out when we rebase to GDB 18. -* Fri May 30 2025 Guinevere Larsen - 16.3-1.el10 -- Rebase RHEL10 GDB to 16.3 - Resolves: RHEL-56897 - Resolves: RHEL-91382 +* Wed Jul 8 2026 Andrew Burgess +- Backport upstream commit be3a9405ceb4d6d6 to fix and issue with the + D demangler (RHBZ 2368350). This commit will drop out when we + rebase to GDB 18. + +* Wed Jul 8 2026 Andrew Burgess +- Adjust commit message in gdb-backport-dap-core-file-support.patch to + remove '---' string which confuses git. + +* Wed Jun 24 2026 Michal Kolar +- 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 +- Applied proposed patch "[gdb/testsuite, Tcl 9] Fix EILSEQ problems for + UTF8 related tests". This is required in order to complete a build + with testing. + +* Sat May 16 2026 Andrew Burgess +- 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. + +* Tue May 5 2026 Keith Seitz +- Backport upstream commit ef7727ae from Tom de Vries to + fix the test suite for newer Tcl releases. + +* Tue Apr 21 2026 Andrew Burgess +- Backport upstream commits 8bd08ee92c4 and cd289df068e to address + rhbz2366461. These backports will not be needed once we rebase to + GDB 18. + +* Wed Feb 25 2026 Kevin Buettner +- Backport upstream commit c1da013915e from Kevin Buettner to fix + gcore failures caused by glibc 2.42 guard page changes (RHBZ 2413405). +- Backport upstream commit d2cc16cd7fc from Jan Vrany to fix + FAILs in gdb.base/fileio.exp caused by macro expansion of + path components in OUTDIR. + +* Mon Feb 16 2026 Guinevere Larsen +- Backport upstream commit f08ffbbf269 to fix RHBZ 2435950 + This reverts a new feature that was never properly approved + for merging in upstream GDB. + +* Wed Feb 4 2026 Andrew Burgess +- Backport upstream commit 70b66cf338b14336 to fix RH BZ 2402580. + This backport will not be needed once we rebase to GDB 18. + +* Tue Feb 3 2026 Kevin Buettner +- Remove gcc-gdb-plugin BuildRequires and build with --disable-gdb-compile + due to this feature's brokenness. Tests using this feature skip cleanly + without timeouts. Reduces binary size and attack surface. + +* Fri Jan 16 2026 Fedora Release Engineering +- Rebuilt for https://fedoraproject.org/wiki/Fedora_44_Mass_Rebuild + +* Thu Jan 8 2026 Kevin Buettner - 17.1-1 +- Rebase to FSF GDB 17.1. + Deleted: gdb-fix-bg-execution-repeat.patch + Backport 919ba8ce158e, "bfd: fix build with C23", from Guinevere Larsen, + fixing RH BZ 2424325. + Backport "Fix some more C23 const-correctness issues", from Keith Seitz, + fixing same class of problem described by RH BZ 2424325. + Backport 777079c67c81, "[gdb/build, c++20] Fix deprecated implicit + capture in cooked_index::set_contents", from Tom de Vries, fixing more + FTBS problems. + +* Fri Sep 19 2025 Python Maint +- Rebuilt for Python 3.14.0rc3 bytecode + +* Fri Aug 15 2025 Python Maint +- Rebuilt for Python 3.14.0rc2 bytecode + +* Wed Jul 23 2025 Fedora Release Engineering +- Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild + +* Tue Jun 03 2025 Andrew Burgess +- Rename 'gdb-add-rpm-suggestion-script.patch' to + 'gdb-rpm-suggestion-script.patch' and + 'gdb-6.3-rh-testversion-20041202.patch' to + 'gdb-test-show-version.patch'. The new names better reflect the + patch contents. + +* Tue Jun 03 2025 Andrew Burgess +- Remove gdb-archer-next-over-throw-cxx-exec.patch, we now have + upstream commit d462550c91c. + +* Tue Jun 03 2025 Andrew Burgess +- Remove gdb-6.3-mapping-zero-inode-test.patch, we now have upstream + commit fcfd8a4f239. + +* Tue Jun 03 2025 Python Maint +- Rebuilt for Python 3.14 + +* Mon Jun 02 2025 Python Maint - 16.3-2 +- Bootstrap for Python 3.14 * Wed May 28 2025 Andrew Burgess - Remove 32-bit ARM support for RHEL 10+. This is not a RHEL supported target, so lets drop this target. Retain for older RHEL versions, as well as Fedora. -* Tue May 13 2025 Alexandra Hájková - 16.3-1 +* Mon May 12 2025 Keith Seitz +- Remove gdb-rhbz1149205-catch-syscall-after-fork-test.patch + in favor of upstream commit. + +* Wed May 07 2025 Andrew Burgess +- Remove gdb-6.8-bz466901-backtrace-full-prelinked.patch. Prelinking + is no longer supported, there seems little point keeping this test. + +* Fri Apr 25 2025 Alexandra Hájková +- Backport "Fix another timeout in gdb.base/bg-execution-repeat.exp" + (Tom de Vries) + +* Wed Apr 23 2025 Alexandra Hájková - 16.3-1 - Rebase to FSF GDB 16.3. - Deleted: gdb-rhbz2354997-gstack-drop-readnever.patch + Deleted: core-target-open-segfault.patch + gdb-rhbz2354997-gstack-drop-readnever.patch + tui-wrefresh-issue.patch + +* Wed Apr 02 2025 Alexandra Hájková +- Remove upstreamed gdb-rhbz-818343-set-solib-absolute-prefix-testcase.patch. * Fri Mar 28 2025 Keith Seitz - 16.2-3 - Backport "Fix gstack issues" from upstream. (Keith Seitz, RH BZ 2354997) -* Thu Feb 13 2025 Alexandra Hájková - 16.2-2 -- Bump the release number. +* Thu Mar 06 2025 Kevin Buettner +- Remove gdb-rhbz1084404-ppc64-s390x-wrong-prologue-skip-O2-g-3of3.patch. + The upstream test 'gdb.arch/amd64-prologue-skip.exp' should be + sufficient for testing an upstream fix which was pushed in 2015. -* Tue Feb 11 2025 Alexandra Hájková - 16.2-1 +* Wed Mar 05 2025 Guinevere Larsen +- Remove gdb-6.5-gcore-buffer-limit-test.patch. A similar test has been + added upstream, which will trickle down soon. + +* Thu Feb 27 2025 Keith Seitz +- Remove gdb-6.6-bz237572-ppc-atomic-sequence-test.patch. This is covered + by gdb.arch/ppc64-isa207-atomic-inst.exp. + +* Mon Feb 10 2025 Andrew Burgess +- Add core-target-open-segfault.patch and tui-wrefresh-issue.patch, + these backport upstream commits 2fc56106422 and 2b646bb8767 + respectively, both fix regressiosn in GDB 16 vs GDB 15. + +* Fri Feb 07 2025 Andrew Burgess +- Remove gdb-6.5-bz218379-ppc-solib-trampoline-test.patch. There must + be upstream tests that 'next' over a libc call. Additionally, it is + not clear to me exactly what bug is being tested for here. + +* Fri Feb 07 2025 Andrew Burgess +- Remove gdb-gstack.man file. This is no longer used. There is an + upstream version of gstack which provides its own man page. + +* Tue Feb 04 2025 Alexandra Hájková - 16.2-1 - Rebase to FSF GDB 16.2. - Dropped: gdb-backport-buildid-related-changes.patch - gdb-catchpoint-re-set.patch - gdb-remove-qnx-neutrino-support.patch Modified: gdb-add-rpm-suggestion-script.patch +* Thu Jan 23 2025 Alexandra Hájková - 16.1-1 +- Rebase to FSF GDB 16.1. + Dropped: + gdb-backport-buildid-related-changes.patch + gdb-catchpoint-re-set.patch + gdb-remove-qnx-neutrino-support.patch + * Thu Jan 23 2025 Alexandra Hájková - Remove upstreamed gdb-6.3-gstack-20050411.patch. diff --git a/gdbtui b/gdbtui deleted file mode 100755 index 88f9d55..0000000 --- a/gdbtui +++ /dev/null @@ -1,2 +0,0 @@ -#! /bin/sh -exec gdb -tui "$@" diff --git a/generate-patches-from-git-repo.sh b/generate-patches-from-git-repo.sh index 1937a3c..6003008 100755 --- a/generate-patches-from-git-repo.sh +++ b/generate-patches-from-git-repo.sh @@ -69,10 +69,9 @@ common_ancestor=`git merge-base HEAD $commit_or_tag` test -z "$common_ancestor" && die "Could not find common ancestor between HEAD and $commit_or_tag." temp_PATCH_file=/tmp/_gdb.spec.Patch.include -temp_patch_file=/tmp/_gdb.spec.patch.include temp_patch_order_file=/tmp/_patch_order -rm -f $temp_PATCH_file $temp_patch_file $temp_patch_order_file +rm -f $temp_PATCH_file $temp_patch_order_file for c in `git rev-list --reverse ${common_ancestor}..HEAD` ; do fname=`git log -1 --pretty='format:%s' $c` @@ -100,13 +99,11 @@ for c in `git rev-list --reverse ${common_ancestor}..HEAD` ; do `git log -1 --pretty='format:%b' $c | sed -n 's/^;;/#/p'` EOF printf "Patch%03d: %s\n\n" $idx $fname >> $temp_PATCH_file - printf "%%patch -p1 -P%03d\n" $idx >> $temp_patch_file echo $fname >> $temp_patch_order_file idx=`expr $idx + 1` done cd $orig_dir mv $temp_PATCH_file _gdb.spec.Patch.include -mv $temp_patch_file _gdb.spec.patch.include mv $temp_patch_order_file _patch_order echo "$common_ancestor" > _git_upstream_commit diff --git a/gnu-keyring.gpg b/gnu-keyring.gpg new file mode 100644 index 0000000..8997652 Binary files /dev/null and b/gnu-keyring.gpg differ diff --git a/rpminspect.yaml b/rpminspect.yaml new file mode 100644 index 0000000..84451eb --- /dev/null +++ b/rpminspect.yaml @@ -0,0 +1,7 @@ +--- + +# Disable Link-Time Optimization checks +# Upstream GDB is not fully compatible with LTO +annocheck: + jobs: + - hardened: --skip-lto diff --git a/sources b/sources index a86cf9b..f76e77c 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (gdb-16.3.tar.xz) = fffd6689c3405466a179670b04720dc825e4f210a761f63dd2b33027432f8cd5d1c059c431a5ec9e165eedd1901220b5329d73c522f9a444788888c731b29e9c +SHA512 (gdb-17.2.tar.xz) = 7794c5a185be7ed5e7ad1000c4ff7d8497c80425a1bc108aab8fd3dd8ecdde034e294dfd65b25c6b0dcd8ed2a240caf07293f3e73791b6cfc890d580d0af4581 diff --git a/tests/debug-toolset-binary/Makefile b/tests/debug-toolset-binary/Makefile index 1453d76..9a7ecfd 100644 --- a/tests/debug-toolset-binary/Makefile +++ b/tests/debug-toolset-binary/Makefile @@ -54,7 +54,7 @@ $(METADATA): Makefile @echo "Type: Sanity" >> $(METADATA) @echo "TestTime: 5m" >> $(METADATA) @echo "RunFor: gdb" >> $(METADATA) - @echo "Requires: gdb coreutils binutils binutils-debuginfo glibc glibc-debuginfo gcc gcc-debuginfo zlib zlib-debuginfo" >> $(METADATA) + @echo "Requires: gdb coreutils binutils binutils-debuginfo glibc glibc-debuginfo gcc gcc-debuginfo zlib-ng zlib-ng-debuginfo" >> $(METADATA) @echo "Priority: Normal" >> $(METADATA) @echo "License: GPLv2" >> $(METADATA) @echo "Confidential: no" >> $(METADATA) diff --git a/tests/debug-toolset-binary/main.fmf b/tests/debug-toolset-binary/main.fmf index 04b7702..eb6d8e7 100644 --- a/tests/debug-toolset-binary/main.fmf +++ b/tests/debug-toolset-binary/main.fmf @@ -15,8 +15,8 @@ recommend: - glibc-debuginfo - gcc - gcc-debuginfo -- zlib -- zlib-debuginfo +- zlib-ng +- zlib-ng-debuginfo duration: 5m extra-summary: /tools/gdb/Sanity/debug-toolset-binary extra-task: /tools/gdb/Sanity/debug-toolset-binary diff --git a/tests/debug-toolset-binary/runtest.sh b/tests/debug-toolset-binary/runtest.sh index 65cc2c7..f2a339f 100755 --- a/tests/debug-toolset-binary/runtest.sh +++ b/tests/debug-toolset-binary/runtest.sh @@ -38,7 +38,7 @@ CMD="gdb" BIN="$(which $CMD)" export PACKAGE="${PACKAGE:-$(rpm -qf --qf='%{name}\n' $BIN)}" -PACKAGES="coreutils binutils binutils-debuginfo glibc glibc-debuginfo gcc gcc-debuginfo zlib zlib-debuginfo" +PACKAGES="coreutils binutils binutils-debuginfo glibc glibc-debuginfo gcc gcc-debuginfo zlib-ng zlib-ng-debuginfo" rlJournalStart rlPhaseStartSetup