public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
From: Andrew Burgess <aburgess@redhat.com>
To: git-commits@fedoraproject.org
Subject: [rpms/gdb] gdb-17.2-rebase-f44: Convert RPM suggestion feature into a Python extension
Date: Sun, 28 Jun 2026 00:01:56 GMT [thread overview]
Message-ID: <178260491640.1.18352866082687228845.rpms-gdb-8d5adc72d172@fedoraproject.org> (raw)
A new commit has been pushed.
Repo : rpms/gdb
Branch : gdb-17.2-rebase-f44
Commit : 8d5adc72d172051b5eae90a9ab35956c020bd7a0
Author : Andrew Burgess <aburgess@redhat.com>
Date : 2024-12-10T16:55:20+00:00
Stats : +10731/-1709 in 13 file(s)
URL : https://src.fedoraproject.org/rpms/gdb/c/8d5adc72d172051b5eae90a9ab35956c020bd7a0?branch=gdb-17.2-rebase-f44
Log:
Convert RPM suggestion feature into a Python extension
This commit converts the RPM suggestion feature into a Python
extension. The patches which changed GDB itself are all gone now.
One of the patches appears in the 'git diff' as a rename, I've
retained only the tests within that patch.
The old RPM suggestion feature didn't have many tests so it's hard to
know for sure if the new Python script covers all of the same cases,
but hopefully I got most of them, and we can always improve the Python
script later if I've missed some cases.
I've added a bunch of new GDB commands to control RPM suggestions,
these are:
set rpm-suggestion enabled on|off
- Allows RPM suggestion to be turned on/off.
show rpm-suggestion enabled
- Is RPM currently turned on or off?
set rpm-suggestion build-id-mode fast|slow
- How to suggestion RPMs based on a build-id. Fast mode gives a
command based on the build-id itself, slow mode tries to
resolve the build-id to an actual package. The 'fast' mode is
the default as this matches the existing behaviour.
show rpm-suggestion build-id-mode
- How are we handling build-id based RPM suggestions?
info rpm-suggestions
- Show all RPM suggestions that have been made in this session.
Useful if you might have missed one.
These are all documented in the GDB manual.
There are rather a lot of back-ports required to support this change,
but these should all drop out when we rebase to GDB 16. I've made
this change now though so that the RPM suggestion script can be ready,
and even tested ahead of the GDB 16 release and rebase. We might want
to port the RPM suggestion Python script to other Fedora branches when
that happens as the old C++ based RPM suggestion changes are going to
conflict horribly when we try to rebase to GDB 16.
---
diff --git a/_gdb.spec.Patch.include b/_gdb.spec.Patch.include
index 0f6cac1..a88f36d 100644
--- a/_gdb.spec.Patch.include
+++ b/_gdb.spec.Patch.include
@@ -14,63 +14,48 @@ Patch003: gdb-6.5-bz218379-ppc-solib-trampoline-test.patch
#=fedoratest
Patch004: gdb-6.6-bz237572-ppc-atomic-sequence-test.patch
-# New locating of the matching binaries from the pure core file (build-id).
-#=push+jan
-Patch005: gdb-6.6-buildid-locate.patch
-
-# Fix loading of core files without build-ids but with build-ids in executables.
-# Load strictly build-id-checked core files only if no executable is specified
-# (Jan Kratochvil, RH BZ 1339862).
-#=push+jan
-Patch006: gdb-6.6-buildid-locate-solib-missing-ids.patch
-
# Test gcore memory and time requirements for large inferiors.
#=fedoratest
-Patch007: gdb-6.5-gcore-buffer-limit-test.patch
+Patch005: 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.
-Patch008: gdb-6.3-mapping-zero-inode-test.patch
+Patch006: gdb-6.3-mapping-zero-inode-test.patch
# Fix resolving of variables at locations lists in prelinked libs (BZ 466901).
#=fedoratest
-Patch009: gdb-6.8-bz466901-backtrace-full-prelinked.patch
+Patch007: gdb-6.8-bz466901-backtrace-full-prelinked.patch
# Fix follow-exec for C++ programs (bugreported by Martin Stransky).
#=fedoratest
-Patch010: gdb-archer-next-over-throw-cxx-exec.patch
+Patch008: gdb-archer-next-over-throw-cxx-exec.patch
# [delayed-symfile] Test a backtrace regression on CFIs without DIE (BZ 614604).
#=fedoratest
-Patch011: gdb-test-bt-cfi-without-die.patch
+Patch009: gdb-test-bt-cfi-without-die.patch
# Testcase for `Setting solib-absolute-prefix breaks vDSO' (BZ 818343).
#=fedoratest
-Patch012: gdb-rhbz-818343-set-solib-absolute-prefix-testcase.patch
-
-# Fix 'gdb gives highly misleading error when debuginfo pkg is present,
-# but not corresponding binary pkg' (RH BZ 981154).
-#=push+jan
-Patch013: gdb-6.6-buildid-locate-misleading-warning-missing-debuginfo-rhbz981154.patch
+Patch010: gdb-rhbz-818343-set-solib-absolute-prefix-testcase.patch
# Testcase for '[SAP] Recursive dlopen causes SAP HANA installer to
# crash.' (RH BZ 1156192).
#=fedoratest
-Patch014: gdb-rhbz1156192-recursive-dlopen-test.patch
+Patch011: gdb-rhbz1156192-recursive-dlopen-test.patch
# Fix '`catch syscall' doesn't work for parent after `fork' is called'
# (Philippe Waroquiers, RH BZ 1149205).
#=fedoratest
-Patch015: gdb-rhbz1149205-catch-syscall-after-fork-test.patch
+Patch012: 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
-Patch016: gdb-rhbz1084404-ppc64-s390x-wrong-prologue-skip-O2-g-3of3.patch
+Patch013: gdb-rhbz1084404-ppc64-s390x-wrong-prologue-skip-O2-g-3of3.patch
# [aarch64] Fix hardware watchpoints (RH BZ 1261564).
#=fedoratest
-Patch017: gdb-rhbz1261564-aarch64-hw-watchpoint-test.patch
+Patch014: gdb-rhbz1261564-aarch64-hw-watchpoint-test.patch
# Update gdb-add-index.sh such that, when the GDB environment
# variable is not set, the script is smarter than just looking for
@@ -84,14 +69,47 @@ Patch017: gdb-rhbz1261564-aarch64-hw-watchpoint-test.patch
# https://fedoraproject.org/wiki/Changes/Minimal_GDB_in_buildroot
#
#=fedora
-Patch018: gdb-add-index.patch
+Patch015: gdb-add-index.patch
-# Backport upstream commit 7db795bc67a.
-Patch019: gdb-remove-use-of-py-isascii
+# Backport of upstream commit 36fb20fa93484b104d. This is not really
+# relevant for our branch, but later commits that are important, and
+# which we want to backport, conflict unless the cleanup in this
+# patch is in place.
+#
+# This commit will be part of GDB 16, so this back-port should drop
+# out when we rebase onto GDB 16.
+#
+# gdb: remove QNX Neutrino support
+#
+# Remove the support for the QNX Neutrino OS (tdep and native bits). This
+# has been unmaintained for years, and we don't have a way to see if it
+# works (or even builds, for the native parts). Without somebody actively
+# maintaining it, this is just a burden for developers, especially that
+# this port does a few weird unique things that require reasoning about
+# when doing big change.
+#
+# Support for GDBserver was removed in 2020, commit 613f149a90d6
+# ("gdbserver: remove support for Neutrino").
+#
+# Change-Id: I4e25ec26ab06636629adebd02ceb161ee31c232d
+# Approved-by: Kevin Buettner <kevinb@redhat.com>
+Patch016: gdb-remove-qnx-neutrino-support.patch
+
+# Backport patches related to build-id usage when opening a core
+# file, and also improving general build-id based lookup. These will
+# all drop out when rebasing to GDB 16.
+Patch017: gdb-backport-buildid-related-changes.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.
-Patch020: gdb-add-rpm-suggestion-script.patch
+Patch018: gdb-add-rpm-suggestion-script.patch
+
+# Not a backport. Add a new script which defines a setting which is
+# deprecated, but we don't want to remove it from Fedora GDB just yet.
+Patch019: gdb-add-deprecated-settings-py-script.patch
+
+# Tests and test updates related to the rpm suggestion feature.
+Patch020: gdb-6.6-buildid-locate-tests.patch
Patch021: gdb-catchpoint-re-set.patch
diff --git a/_patch_order b/_patch_order
index d2afeab..12e002d 100644
--- a/_patch_order
+++ b/_patch_order
@@ -2,20 +2,20 @@ gdb-6.3-rh-testversion-20041202.patch
gdb-6.3-gstack-20050411.patch
gdb-6.5-bz218379-ppc-solib-trampoline-test.patch
gdb-6.6-bz237572-ppc-atomic-sequence-test.patch
-gdb-6.6-buildid-locate.patch
-gdb-6.6-buildid-locate-solib-missing-ids.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-test-bt-cfi-without-die.patch
gdb-rhbz-818343-set-solib-absolute-prefix-testcase.patch
-gdb-6.6-buildid-locate-misleading-warning-missing-debuginfo-rhbz981154.patch
gdb-rhbz1156192-recursive-dlopen-test.patch
gdb-rhbz1149205-catch-syscall-after-fork-test.patch
gdb-rhbz1084404-ppc64-s390x-wrong-prologue-skip-O2-g-3of3.patch
gdb-rhbz1261564-aarch64-hw-watchpoint-test.patch
gdb-add-index.patch
-gdb-remove-use-of-py-isascii
+gdb-remove-qnx-neutrino-support.patch
+gdb-backport-buildid-related-changes.patch
gdb-add-rpm-suggestion-script.patch
+gdb-add-deprecated-settings-py-script.patch
+gdb-6.6-buildid-locate-tests.patch
gdb-catchpoint-re-set.patch
diff --git a/gdb-6.6-buildid-locate-misleading-warning-missing-debuginfo-rhbz981154.patch b/gdb-6.6-buildid-locate-misleading-warning-missing-debuginfo-rhbz981154.patch
deleted file mode 100644
index 985cb6a..0000000
--- a/gdb-6.6-buildid-locate-misleading-warning-missing-debuginfo-rhbz981154.patch
+++ /dev/null
@@ -1,129 +0,0 @@
-From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
-From: Fedora GDB patches <invalid@email.com>
-Date: Fri, 27 Oct 2017 21:07:50 +0200
-Subject:
- gdb-6.6-buildid-locate-misleading-warning-missing-debuginfo-rhbz981154.patch
-
-;; Fix 'gdb gives highly misleading error when debuginfo pkg is present,
-;; but not corresponding binary pkg' (RH BZ 981154).
-;;=push+jan
-
-Comments by Sergio Durigan Junior <sergiodj@redhat.com>:
-
- This is the fix for RH BZ #981154
-
- It is mainly a testcase addition, but a minor fix in the gdb/build-id.c
- file was also needed.
-
- gdb/build-id.c was added by:
-
- commit dc294be54c96414035eed7d53dafdea0a6f31a72
- Author: Tom Tromey <tromey@redhat.com>
- Date: Tue Oct 8 19:56:15 2013 +0000
-
- and had a little thinko there. The variable 'filename' needs to be set to
- NULL after it is free'd, otherwise the code below thinks that it is still
- valid and doesn't print the necessary warning ("Try: yum install ...").
-
-diff --git a/gdb/testsuite/gdb.base/rhbz981154-misleading-yum-install-warning.exp b/gdb/testsuite/gdb.base/rhbz981154-misleading-yum-install-warning.exp
-new file mode 100644
---- /dev/null
-+++ b/gdb/testsuite/gdb.base/rhbz981154-misleading-yum-install-warning.exp
-@@ -0,0 +1,97 @@
-+# Copyright (C) 2014 Free Software Foundation, Inc.
-+
-+# This program is free software; you can redistribute it and/or modify
-+# it under the terms of the GNU General Public License as published by
-+# the Free Software Foundation; either version 3 of the License, or
-+# (at your option) any later version.
-+#
-+# This program is distributed in the hope that it will be useful,
-+# but WITHOUT ANY WARRANTY; without even the implied warranty of
-+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+# GNU General Public License for more details.
-+#
-+# You should have received a copy of the GNU General Public License
-+# along with this program. If not, see <http://www.gnu.org/licenses/>.
-+
-+standard_testfile "normal.c"
-+
-+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
-+ return -1
-+}
-+
-+# Get the build-id of the file
-+set build_id_debug_file [build_id_debug_filename_get $binfile]
-+regsub -all ".debug$" $build_id_debug_file "" build_id_without_debug
-+
-+# Run to main
-+if { ![runto_main] } {
-+ return -1
-+}
-+
-+# We first need to generate a corefile
-+set escapedfilename [string_to_regexp [standard_output_file gcore.test]]
-+set core_supported 0
-+gdb_test_multiple "gcore [standard_output_file gcore.test]" \
-+ "save a corefile" \
-+{
-+ -re "Saved corefile ${escapedfilename}\[\r\n\]+$gdb_prompt $" {
-+ pass "save a corefile"
-+ global core_supported
-+ set core_supported 1
-+ }
-+ -re "Can't create a corefile\[\r\n\]+$gdb_prompt $" {
-+ unsupported "save a corefile"
-+ global core_supported
-+ set core_supported 0
-+ }
-+}
-+
-+if {!$core_supported} {
-+ return -1
-+}
-+
-+# Move the binfile to a temporary name
-+remote_exec build "mv $binfile ${binfile}.old"
-+
-+# Reinitialize GDB and see if we get a yum/dnf warning
-+gdb_exit
-+gdb_start
-+gdb_reinitialize_dir $srcdir/$subdir
-+
-+with_test_prefix "first run:" {
-+ gdb_test "set build-id-verbose 1" "" \
-+ "set build-id-verbose"
-+
-+ gdb_test "set debug-file-directory [file dirname [standard_output_file gcore.test]]" "" \
-+ "set debug-file-directory"
-+
-+ gdb_test "core-file [standard_output_file gcore.test]" \
-+ "Missing separate debuginfo for the main executable file\r\nTry: (yum|dnf) --enablerepo='\\*debug\\*' install [standard_output_file $build_id_without_debug]\r\n.*" \
-+ "test first yum/dnf warning"
-+}
-+
-+# Now we define and create our .build-id
-+file mkdir [file dirname [standard_output_file ${build_id_without_debug}]]
-+# Cannot use "file link" (from TCL) because it requires the target file to
-+# exist.
-+remote_exec build "ln -s $binfile [standard_output_file ${build_id_without_debug}]"
-+
-+# Reinitialize GDB to get the second yum/dnf warning
-+gdb_exit
-+gdb_start
-+gdb_reinitialize_dir $srcdir/$subdir
-+
-+with_test_prefix "second run:" {
-+ gdb_test "set build-id-verbose 1" "" \
-+ "set build-id-verbose"
-+
-+ gdb_test "set debug-file-directory [file dirname [standard_output_file gcore.test]]" "" \
-+ "set debug-file-directory"
-+
-+ gdb_test "core-file [standard_output_file gcore.test]" \
-+ "Missing separate debuginfo for the main executable file\r\nTry: (yum|dnf) --enablerepo='\\*debug\\*' install $binfile\r\n.*" \
-+ "test second yum/dnf warning"
-+}
-+
-+# Leaving the link there will cause breakage in the next run.
-+remote_exec build "rm -f [standard_output_file ${build_id_without_debug}]"
diff --git a/gdb-6.6-buildid-locate-solib-missing-ids.patch b/gdb-6.6-buildid-locate-solib-missing-ids.patch
deleted file mode 100644
index 897f691..0000000
--- a/gdb-6.6-buildid-locate-solib-missing-ids.patch
+++ /dev/null
@@ -1,262 +0,0 @@
-From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
-From: Fedora GDB patches <invalid@email.com>
-Date: Fri, 27 Oct 2017 21:07:50 +0200
-Subject: gdb-6.6-buildid-locate-solib-missing-ids.patch
-
-;; Fix loading of core files without build-ids but with build-ids in executables.
-;; Load strictly build-id-checked core files only if no executable is specified
-;; (Jan Kratochvil, RH BZ 1339862).
-;;=push+jan
-
-gdb returns an incorrect back trace when applying a debuginfo
-https://bugzilla.redhat.com/show_bug.cgi?id=1339862
-
-diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c
---- a/gdb/solib-svr4.c
-+++ b/gdb/solib-svr4.c
-@@ -347,11 +347,13 @@ lm_addr_check (const solib &so, bfd *abfd)
-
- struct svr4_so
- {
-- svr4_so (const char *name, lm_info_svr4_up lm_info)
-- : name (name), lm_info (std::move (lm_info))
-+ svr4_so (const char *name, lm_info_svr4_up lm_info, const char *orig_name = nullptr)
-+ : name (name), original_name (orig_name == nullptr ? name : orig_name),
-+ lm_info (std::move (lm_info))
- {}
-
- std::string name;
-+ std::string original_name;
- lm_info_svr4_up lm_info;
- };
-
-@@ -1002,6 +1004,7 @@ so_list_from_svr4_sos (const std::vector<svr4_so> &sos)
-
- newobj->so_name = so.name;
- newobj->so_original_name = so.name;
-+ newobj->so_original_name = so.original_name;
- newobj->lm_info = std::make_unique<lm_info_svr4> (*so.lm_info);
-
- dst.push_back (*newobj);
-@@ -1263,11 +1266,28 @@ svr4_read_so_list (svr4_info *info, CORE_ADDR lm, CORE_ADDR prev_lm,
- continue;
- }
-
-+ /* Preserve original name since name may be changed below. */
-+ gdb::unique_xmalloc_ptr<char> original_name = make_unique_xstrdup (name.get ());
- {
-- struct bfd_build_id *build_id;
-+ struct bfd_build_id *build_id = nullptr;
-
-- build_id = build_id_addr_get (li->l_ld);
-- if (build_id != NULL)
-+ /* In the case the main executable was found according to its build-id
-+ (from a core file) prevent loading a different build of a library
-+ with accidentally the same SO_NAME.
-+
-+ It suppresses bogus backtraces (and prints "??" there instead) if
-+ the on-disk files no longer match the running program version.
-+ If the main executable was not loaded according to its build-id do
-+ not do any build-id checking of the libraries. There may be missing
-+ build-ids dumped in the core file and we would map all the libraries
-+ to the only existing file loaded that time - the executable. */
-+ if (current_program_space->symfile_object_file != NULL
-+ && (current_program_space->symfile_object_file->flags
-+ & OBJF_BUILD_ID_CORE_LOADED) != 0)
-+ build_id = build_id_addr_get (li->l_ld);
-+
-+
-+ if (build_id != nullptr)
- {
- char *bid_name, *build_id_filename;
-
-@@ -1280,23 +1300,7 @@ svr4_read_so_list (svr4_info *info, CORE_ADDR lm, CORE_ADDR prev_lm,
- xfree (bid_name);
- }
- else
-- {
-- debug_print_missing (name.get (), build_id_filename);
--
-- /* In the case the main executable was found according to
-- its build-id (from a core file) prevent loading
-- a different build of a library with accidentally the
-- same SO_NAME.
--
-- It suppresses bogus backtraces (and prints "??" there
-- instead) if the on-disk files no longer match the
-- running program version. */
--
-- if (current_program_space->symfile_object_file != NULL
-- && (current_program_space->symfile_object_file->flags
-- & OBJF_BUILD_ID_CORE_LOADED) != 0)
-- name = make_unique_xstrdup ("");
-- }
-+ debug_print_missing (name.get (), build_id_filename);
-
- xfree (build_id_filename);
- xfree (build_id);
-diff --git a/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib-lib.c b/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib-lib.c
-new file mode 100644
---- /dev/null
-+++ b/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib-lib.c
-@@ -0,0 +1,21 @@
-+/* Copyright 2010 Free Software Foundation, Inc.
-+
-+ This file is part of GDB.
-+
-+ This program is free software; you can redistribute it and/or modify
-+ it under the terms of the GNU General Public License as published by
-+ the Free Software Foundation; either version 3 of the License, or
-+ (at your option) any later version.
-+
-+ This program is distributed in the hope that it will be useful,
-+ but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ GNU General Public License for more details.
-+
-+ You should have received a copy of the GNU General Public License
-+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
-+
-+void
-+lib (void)
-+{
-+}
-diff --git a/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib-main.c b/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib-main.c
-new file mode 100644
---- /dev/null
-+++ b/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib-main.c
-@@ -0,0 +1,25 @@
-+/* Copyright 2010 Free Software Foundation, Inc.
-+
-+ This file is part of GDB.
-+
-+ This program is free software; you can redistribute it and/or modify
-+ it under the terms of the GNU General Public License as published by
-+ the Free Software Foundation; either version 3 of the License, or
-+ (at your option) any later version.
-+
-+ This program is distributed in the hope that it will be useful,
-+ but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+ GNU General Public License for more details.
-+
-+ You should have received a copy of the GNU General Public License
-+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
-+
-+extern void lib (void);
-+
-+int
-+main (void)
-+{
-+ lib ();
-+ return 0;
-+}
-diff --git a/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib.exp b/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib.exp
-new file mode 100644
---- /dev/null
-+++ b/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib.exp
-@@ -0,0 +1,104 @@
-+# Copyright 2016 Free Software Foundation, Inc.
-+
-+# This program is free software; you can redistribute it and/or modify
-+# it under the terms of the GNU General Public License as published by
-+# the Free Software Foundation; either version 3 of the License, or
-+# (at your option) any later version.
-+#
-+# This program is distributed in the hope that it will be useful,
-+# but WITHOUT ANY WARRANTY; without even the implied warranty of
-+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-+# GNU General Public License for more details.
-+#
-+# You should have received a copy of the GNU General Public License
-+# along with this program. If not, see <http://www.gnu.org/licenses/>.
-+
-+require allow_shlib_tests
-+
-+set testfile "gcore-buildid-exec-but-not-solib"
-+set srcmainfile ${testfile}-main.c
-+set srclibfile ${testfile}-lib.c
-+set libfile [standard_output_file ${testfile}-lib.so]
-+set objfile [standard_output_file ${testfile}-main.o]
-+set executable ${testfile}-main
-+set binfile [standard_output_file ${executable}]
-+set gcorefile [standard_output_file ${executable}.gcore]
-+set outdir [file dirname $binfile]
-+
-+if { [gdb_compile_shlib ${srcdir}/${subdir}/${srclibfile} ${libfile} "debug additional_flags=-Wl,--build-id"] != ""
-+ || [gdb_compile ${srcdir}/${subdir}/${srcmainfile} ${objfile} object {debug}] != "" } {
-+ unsupported "-Wl,--build-id compilation failed"
-+ return -1
-+}
-+set opts [list debug shlib=${libfile} "additional_flags=-Wl,--build-id"]
-+if { [gdb_compile ${objfile} ${binfile} executable $opts] != "" } {
-+ unsupported "-Wl,--build-id compilation failed"
-+ return -1
-+}
-+
-+clean_restart $executable
-+gdb_load_shlib $libfile
-+
-+# Does this gdb support gcore?
-+set test "help gcore"
-+gdb_test_multiple $test $test {
-+ -re "Undefined command: .gcore.*\r\n$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 .*\r\n$gdb_prompt $" {
-+ pass $test
-+ }
-+}
-+
-+if { ![runto lib] } then {
-+ return -1
-+}
-+
-+set escapedfilename [string_to_regexp ${gcorefile}]
-+
-+set test "save a corefile"
-+gdb_test_multiple "gcore ${gcorefile}" $test {
-+ -re "Saved corefile ${escapedfilename}\r\n$gdb_prompt $" {
-+ pass $test
-+ }
-+ -re "Can't create a corefile\r\n$gdb_prompt $" {
-+ unsupported $test
-+ return -1
-+ }
-+}
-+
-+# Now restart gdb and load the corefile.
-+
-+clean_restart $executable
-+gdb_load_shlib $libfile
-+
-+set buildid [build_id_debug_filename_get $libfile]
-+
-+regsub {\.debug$} $buildid {} buildid
-+
-+set debugdir [standard_output_file ${testfile}-debugdir]
-+file delete -force -- $debugdir
-+
-+file mkdir $debugdir/[file dirname $libfile]
-+file copy $libfile $debugdir/${libfile}
-+
-+file mkdir $debugdir/[file dirname $buildid]
-+file copy $libfile $debugdir/${buildid}
-+
-+remote_exec build "ln -s /lib ${debugdir}/"
-+remote_exec build "ln -s /lib64 ${debugdir}/"
-+# /usr is not needed, all the libs are in /lib64: libm.so.6 libc.so.6 ld-linux-x86-64.so.2
-+
-+gdb_test_no_output "set solib-absolute-prefix $debugdir" \
-+ "set solib-absolute-prefix"
-+
-+gdb_test_no_output "set debug-file-directory $debugdir" "set debug-file-directory"
-+
-+gdb_test "core ${gcorefile}" "Core was generated by .*" "re-load generated corefile"
-+
-+gdb_test "frame" "#0 \[^\r\n\]* lib .*" "library got loaded"
-+
-+gdb_test "bt"
-+gdb_test "info shared"
diff --git a/gdb-6.6-buildid-locate-tests.patch b/gdb-6.6-buildid-locate-tests.patch
new file mode 100644
index 0000000..2cae49f
--- /dev/null
+++ b/gdb-6.6-buildid-locate-tests.patch
@@ -0,0 +1,237 @@
+From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
+From: Fedora GDB patches <invalid@email.com>
+Date: Fri, 27 Oct 2017 21:07:50 +0200
+Subject: gdb-6.6-buildid-locate-tests.patch
+
+;; Tests and test updates related to the rpm suggestion feature.
+
+diff --git a/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib-lib.c b/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib-lib.c
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib-lib.c
+@@ -0,0 +1,21 @@
++/* Copyright 2010 Free Software Foundation, Inc.
++
++ This file is part of GDB.
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <http://www.gnu.org/licenses/>. */
++
++void
++lib (void)
++{
++}
+diff --git a/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib-main.c b/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib-main.c
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib-main.c
+@@ -0,0 +1,25 @@
++/* Copyright 2010 Free Software Foundation, Inc.
++
++ This file is part of GDB.
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <http://www.gnu.org/licenses/>. */
++
++extern void lib (void);
++
++int
++main (void)
++{
++ lib ();
++ return 0;
++}
+diff --git a/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib.exp b/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib.exp
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.base/gcore-buildid-exec-but-not-solib.exp
+@@ -0,0 +1,104 @@
++# Copyright 2016 Free Software Foundation, Inc.
++
++# This program is free software; you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation; either version 3 of the License, or
++# (at your option) any later version.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program. If not, see <http://www.gnu.org/licenses/>.
++
++require allow_shlib_tests
++
++set testfile "gcore-buildid-exec-but-not-solib"
++set srcmainfile ${testfile}-main.c
++set srclibfile ${testfile}-lib.c
++set libfile [standard_output_file ${testfile}-lib.so]
++set objfile [standard_output_file ${testfile}-main.o]
++set executable ${testfile}-main
++set binfile [standard_output_file ${executable}]
++set gcorefile [standard_output_file ${executable}.gcore]
++set outdir [file dirname $binfile]
++
++if { [gdb_compile_shlib ${srcdir}/${subdir}/${srclibfile} ${libfile} "debug additional_flags=-Wl,--build-id"] != ""
++ || [gdb_compile ${srcdir}/${subdir}/${srcmainfile} ${objfile} object {debug}] != "" } {
++ unsupported "-Wl,--build-id compilation failed"
++ return -1
++}
++set opts [list debug shlib=${libfile} "additional_flags=-Wl,--build-id"]
++if { [gdb_compile ${objfile} ${binfile} executable $opts] != "" } {
++ unsupported "-Wl,--build-id compilation failed"
++ return -1
++}
++
++clean_restart $executable
++gdb_load_shlib $libfile
++
++# Does this gdb support gcore?
++set test "help gcore"
++gdb_test_multiple $test $test {
++ -re "Undefined command: .gcore.*\r\n$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 .*\r\n$gdb_prompt $" {
++ pass $test
++ }
++}
++
++if { ![runto lib] } then {
++ return -1
++}
++
++set escapedfilename [string_to_regexp ${gcorefile}]
++
++set test "save a corefile"
++gdb_test_multiple "gcore ${gcorefile}" $test {
++ -re "Saved corefile ${escapedfilename}\r\n$gdb_prompt $" {
++ pass $test
++ }
++ -re "Can't create a corefile\r\n$gdb_prompt $" {
++ unsupported $test
++ return -1
++ }
++}
++
++# Now restart gdb and load the corefile.
++
++clean_restart $executable
++gdb_load_shlib $libfile
++
++set buildid [build_id_debug_filename_get $libfile]
++
++regsub {\.debug$} $buildid {} buildid
++
++set debugdir [standard_output_file ${testfile}-debugdir]
++file delete -force -- $debugdir
++
++file mkdir $debugdir/[file dirname $libfile]
++file copy $libfile $debugdir/${libfile}
++
++file mkdir $debugdir/[file dirname $buildid]
++file copy $libfile $debugdir/${buildid}
++
++remote_exec build "ln -s /lib ${debugdir}/"
++remote_exec build "ln -s /lib64 ${debugdir}/"
++# /usr is not needed, all the libs are in /lib64: libm.so.6 libc.so.6 ld-linux-x86-64.so.2
++
++gdb_test_no_output "set solib-absolute-prefix $debugdir" \
++ "set solib-absolute-prefix"
++
++gdb_test_no_output "set debug-file-directory $debugdir" "set debug-file-directory"
++
++gdb_test "core ${gcorefile}" "Core was generated by .*" "re-load generated corefile"
++
++gdb_test "frame" "#0 \[^\r\n\]* lib .*" "library got loaded"
++
++gdb_test "bt"
++gdb_test "info shared"
+diff --git a/gdb/testsuite/gdb.base/rhbz981154-misleading-yum-install-warning.exp b/gdb/testsuite/gdb.base/rhbz981154-misleading-yum-install-warning.exp
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.base/rhbz981154-misleading-yum-install-warning.exp
+@@ -0,0 +1,60 @@
++# Copyright (C) 2014 Free Software Foundation, Inc.
++
++# This program is free software; you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation; either version 3 of the License, or
++# (at your option) any later version.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program. If not, see <http://www.gnu.org/licenses/>.
++
++# Create a core file, then hide the executable. Restart GDB and load
++# the core file. Check GDB gives a message suggesting a 'dnf' command
++# to try and install the executable based on its build-id.
++
++standard_testfile "normal.c"
++
++if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
++ return -1
++}
++
++# Get the build-id of the file.
++set build_id_debug_file [build_id_debug_filename_get $binfile]
++regsub -all ".debug$" $build_id_debug_file "" build_id_without_debug
++
++# Run to main.
++if { ![runto_main] } {
++ return -1
++}
++
++# We first need to generate a corefile.
++set corefilename "[standard_output_file gcore.test]"
++if { ![gdb_gcore_cmd "$corefilename" "save corefile"] } {
++ untested "could not generate a corefile"
++ return -1
++}
++
++# Move the binfile to a temporary name.
++remote_exec build "mv $binfile ${binfile}.old"
++
++# Reinitialize GDB and see if we get a dnf suggestion.
++clean_restart
++
++gdb_test "set build-id-verbose 1" "" \
++ "set build-id-verbose"
++
++# GDB only makes build-id based RPM suggestions if /usr/lib is in
++# the debug-file-directory list, the reason being that system RPMs
++# will always install under this location. If GDB is not looking
++# here then there's no point making suggestions.
++gdb_test "set debug-file-directory /usr/lib/" "" \
++ "set debug-file-directory"
++
++gdb_test "core-file [standard_output_file gcore.test]" \
++ "Missing file\\(s\\), try: dnf --enablerepo='\\*debug\\*' install [string_to_regexp /usr/lib/$build_id_without_debug] [string_to_regexp /usr/lib/debug/$build_id_debug_file]" \
++ "test first yum/dnf warning"
diff --git a/gdb-6.6-buildid-locate.patch b/gdb-6.6-buildid-locate.patch
deleted file mode 100644
index d407b43..0000000
--- a/gdb-6.6-buildid-locate.patch
+++ /dev/null
@@ -1,1194 +0,0 @@
-From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
-From: Fedora GDB patches <invalid@email.com>
-Date: Fri, 27 Oct 2017 21:07:50 +0200
-Subject: gdb-6.6-buildid-locate.patch
-
-;; New locating of the matching binaries from the pure core file (build-id).
-;;=push+jan
-
-diff --git a/gdb/build-id.c b/gdb/build-id.c
---- a/gdb/build-id.c
-+++ b/gdb/build-id.c
-@@ -23,9 +23,67 @@
- #include "gdbsupport/gdb_vecs.h"
- #include "symfile.h"
- #include "objfiles.h"
-+#include <sys/stat.h>
-+#include "elf-bfd.h"
-+#include "elf/common.h"
-+#include "elf/external.h"
-+#include "elf/internal.h"
- #include "filenames.h"
-+#include "gdb_bfd.h"
- #include "gdbcore.h"
-+#include "cli/cli-cmds.h"
- #include "cli/cli-style.h"
-+#include "inferior.h"
-+#include "objfiles.h"
-+#include "observable.h"
-+#include "symfile.h"
-+
-+#define BUILD_ID_VERBOSE_NONE 0
-+#define BUILD_ID_VERBOSE_FILENAMES 1
-+#define BUILD_ID_VERBOSE_BINARY_PARSE 2
-+static int build_id_verbose = BUILD_ID_VERBOSE_FILENAMES;
-+static void
-+show_build_id_verbose (struct ui_file *file, int from_tty,
-+ struct cmd_list_element *c, const char *value)
-+{
-+ gdb_printf (file, _("Verbosity level of the build-id locator is %s.\n"),
-+ value);
-+}
-+/* Locate NT_GNU_BUILD_ID and return its matching debug filename.
-+ FIXME: NOTE decoding should be unified with the BFD core notes decoding. */
-+
-+static struct bfd_build_id *
-+build_id_buf_get (bfd *templ, gdb_byte *buf, bfd_size_type size)
-+{
-+ bfd_byte *p;
-+
-+ p = buf;
-+ while (p < buf + size)
-+ {
-+ /* FIXME: bad alignment assumption. */
-+ Elf_External_Note *xnp = (Elf_External_Note *) p;
-+ size_t namesz = H_GET_32 (templ, xnp->namesz);
-+ size_t descsz = H_GET_32 (templ, xnp->descsz);
-+ bfd_byte *descdata = (gdb_byte *) xnp->name + BFD_ALIGN (namesz, 4);
-+
-+ if (H_GET_32 (templ, xnp->type) == NT_GNU_BUILD_ID
-+ && namesz == sizeof "GNU"
-+ && memcmp (xnp->name, "GNU", sizeof "GNU") == 0)
-+ {
-+ size_t sz = descsz;
-+ gdb_byte *data = (gdb_byte *) descdata;
-+ struct bfd_build_id *retval;
-+
-+ retval = (struct bfd_build_id *) xmalloc (sizeof *retval - 1 + sz);
-+ retval->size = sz;
-+ memcpy (retval->data, data, sz);
-+
-+ return retval;
-+ }
-+ p = descdata + BFD_ALIGN (descsz, 4);
-+ }
-+ return NULL;
-+}
-
- /* See build-id.h. */
-
-@@ -49,6 +107,349 @@ build_id_bfd_get (bfd *abfd)
- return NULL;
- }
-
-+/* Core files may have missing (corrupt) SHDR but PDHR is correct there.
-+ bfd_elf_bfd_from_remote_memory () has too much overhead by
-+ allocating/reading all the available ELF PT_LOADs. */
-+
-+static struct bfd_build_id *
-+build_id_phdr_get (bfd *templ, bfd_vma loadbase, unsigned e_phnum,
-+ Elf_Internal_Phdr *i_phdr)
-+{
-+ int i;
-+ struct bfd_build_id *retval = NULL;
-+
-+ for (i = 0; i < e_phnum; i++)
-+ if (i_phdr[i].p_type == PT_NOTE && i_phdr[i].p_filesz > 0)
-+ {
-+ Elf_Internal_Phdr *hdr = &i_phdr[i];
-+ gdb_byte *buf;
-+ int err;
-+
-+ buf = (gdb_byte *) xmalloc (hdr->p_filesz);
-+ err = target_read_memory (loadbase + i_phdr[i].p_vaddr, buf,
-+ hdr->p_filesz);
-+ if (err == 0)
-+ retval = build_id_buf_get (templ, buf, hdr->p_filesz);
-+ else
-+ retval = NULL;
-+ xfree (buf);
-+ if (retval != NULL)
-+ break;
-+ }
-+ return retval;
-+}
-+
-+/* First we validate the file by reading in the ELF header and checking
-+ the magic number. */
-+
-+static inline bfd_boolean
-+elf_file_p (Elf64_External_Ehdr *x_ehdrp64)
-+{
-+ gdb_assert (sizeof (Elf64_External_Ehdr) >= sizeof (Elf32_External_Ehdr));
-+ gdb_assert (offsetof (Elf64_External_Ehdr, e_ident)
-+ == offsetof (Elf32_External_Ehdr, e_ident));
-+ gdb_assert (sizeof (((Elf64_External_Ehdr *) 0)->e_ident)
-+ == sizeof (((Elf32_External_Ehdr *) 0)->e_ident));
-+
-+ return ((x_ehdrp64->e_ident[EI_MAG0] == ELFMAG0)
-+ && (x_ehdrp64->e_ident[EI_MAG1] == ELFMAG1)
-+ && (x_ehdrp64->e_ident[EI_MAG2] == ELFMAG2)
-+ && (x_ehdrp64->e_ident[EI_MAG3] == ELFMAG3));
-+}
-+
-+/* Translate an ELF file header in external format into an ELF file header in
-+ internal format. */
-+
-+#define H_GET_WORD(bfd, ptr) (is64 ? H_GET_64 (bfd, (ptr)) \
-+ : H_GET_32 (bfd, (ptr)))
-+#define H_GET_SIGNED_WORD(bfd, ptr) (is64 ? H_GET_S64 (bfd, (ptr)) \
-+ : H_GET_S32 (bfd, (ptr)))
-+
-+static void
-+elf_swap_ehdr_in (bfd *abfd,
-+ const Elf64_External_Ehdr *src64,
-+ Elf_Internal_Ehdr *dst)
-+{
-+ int is64 = bfd_get_arch_size (abfd) == 64;
-+#define SRC(field) (is64 ? src64->field \
-+ : ((const Elf32_External_Ehdr *) src64)->field)
-+
-+ int signed_vma = get_elf_backend_data (abfd)->sign_extend_vma;
-+ memcpy (dst->e_ident, SRC (e_ident), EI_NIDENT);
-+ dst->e_type = H_GET_16 (abfd, SRC (e_type));
-+ dst->e_machine = H_GET_16 (abfd, SRC (e_machine));
-+ dst->e_version = H_GET_32 (abfd, SRC (e_version));
-+ if (signed_vma)
-+ dst->e_entry = H_GET_SIGNED_WORD (abfd, SRC (e_entry));
-+ else
-+ dst->e_entry = H_GET_WORD (abfd, SRC (e_entry));
-+ dst->e_phoff = H_GET_WORD (abfd, SRC (e_phoff));
-+ dst->e_shoff = H_GET_WORD (abfd, SRC (e_shoff));
-+ dst->e_flags = H_GET_32 (abfd, SRC (e_flags));
-+ dst->e_ehsize = H_GET_16 (abfd, SRC (e_ehsize));
-+ dst->e_phentsize = H_GET_16 (abfd, SRC (e_phentsize));
-+ dst->e_phnum = H_GET_16 (abfd, SRC (e_phnum));
-+ dst->e_shentsize = H_GET_16 (abfd, SRC (e_shentsize));
-+ dst->e_shnum = H_GET_16 (abfd, SRC (e_shnum));
-+ dst->e_shstrndx = H_GET_16 (abfd, SRC (e_shstrndx));
-+
-+#undef SRC
-+}
-+
-+/* Translate an ELF program header table entry in external format into an
-+ ELF program header table entry in internal format. */
-+
-+static void
-+elf_swap_phdr_in (bfd *abfd,
-+ const Elf64_External_Phdr *src64,
-+ Elf_Internal_Phdr *dst)
-+{
-+ int is64 = bfd_get_arch_size (abfd) == 64;
-+#define SRC(field) (is64 ? src64->field \
-+ : ((const Elf32_External_Phdr *) src64)->field)
-+
-+ int signed_vma = get_elf_backend_data (abfd)->sign_extend_vma;
-+
-+ dst->p_type = H_GET_32 (abfd, SRC (p_type));
-+ dst->p_flags = H_GET_32 (abfd, SRC (p_flags));
-+ dst->p_offset = H_GET_WORD (abfd, SRC (p_offset));
-+ if (signed_vma)
-+ {
-+ dst->p_vaddr = H_GET_SIGNED_WORD (abfd, SRC (p_vaddr));
-+ dst->p_paddr = H_GET_SIGNED_WORD (abfd, SRC (p_paddr));
-+ }
-+ else
-+ {
-+ dst->p_vaddr = H_GET_WORD (abfd, SRC (p_vaddr));
-+ dst->p_paddr = H_GET_WORD (abfd, SRC (p_paddr));
-+ }
-+ dst->p_filesz = H_GET_WORD (abfd, SRC (p_filesz));
-+ dst->p_memsz = H_GET_WORD (abfd, SRC (p_memsz));
-+ dst->p_align = H_GET_WORD (abfd, SRC (p_align));
-+
-+#undef SRC
-+}
-+
-+#undef H_GET_SIGNED_WORD
-+#undef H_GET_WORD
-+
-+static Elf_Internal_Phdr *
-+elf_get_phdr (bfd *templ, bfd_vma ehdr_vma, unsigned *e_phnum_pointer,
-+ bfd_vma *loadbase_pointer)
-+{
-+ /* sizeof (Elf64_External_Ehdr) >= sizeof (Elf32_External_Ehdr) */
-+ Elf64_External_Ehdr x_ehdr64; /* Elf file header, external form */
-+ Elf_Internal_Ehdr i_ehdr; /* Elf file header, internal form */
-+ bfd_size_type x_phdrs_size;
-+ gdb_byte *x_phdrs_ptr;
-+ Elf_Internal_Phdr *i_phdrs;
-+ int err;
-+ unsigned int i;
-+ bfd_vma loadbase;
-+ int loadbase_set;
-+
-+ gdb_assert (templ != NULL);
-+ gdb_assert (sizeof (Elf64_External_Ehdr) >= sizeof (Elf32_External_Ehdr));
-+
-+ /* Read in the ELF header in external format. */
-+ err = target_read_memory (ehdr_vma, (bfd_byte *) &x_ehdr64, sizeof x_ehdr64);
-+ if (err)
-+ {
-+ if (build_id_verbose >= BUILD_ID_VERBOSE_BINARY_PARSE)
-+ warning (_("build-id: Error reading ELF header at address 0x%lx"),
-+ (unsigned long) ehdr_vma);
-+ return NULL;
-+ }
-+
-+ /* Now check to see if we have a valid ELF file, and one that BFD can
-+ make use of. The magic number must match, the address size ('class')
-+ and byte-swapping must match our XVEC entry. */
-+
-+ if (! elf_file_p (&x_ehdr64)
-+ || x_ehdr64.e_ident[EI_VERSION] != EV_CURRENT
-+ || !((bfd_get_arch_size (templ) == 64
-+ && x_ehdr64.e_ident[EI_CLASS] == ELFCLASS64)
-+ || (bfd_get_arch_size (templ) == 32
-+ && x_ehdr64.e_ident[EI_CLASS] == ELFCLASS32)))
-+ {
-+ if (build_id_verbose >= BUILD_ID_VERBOSE_BINARY_PARSE)
-+ warning (_("build-id: Unrecognized ELF header at address 0x%lx"),
-+ (unsigned long) ehdr_vma);
-+ return NULL;
-+ }
-+
-+ /* Check that file's byte order matches xvec's */
-+ switch (x_ehdr64.e_ident[EI_DATA])
-+ {
-+ case ELFDATA2MSB: /* Big-endian */
-+ if (! bfd_header_big_endian (templ))
-+ {
-+ if (build_id_verbose >= BUILD_ID_VERBOSE_BINARY_PARSE)
-+ warning (_("build-id: Unrecognized "
-+ "big-endian ELF header at address 0x%lx"),
-+ (unsigned long) ehdr_vma);
-+ return NULL;
-+ }
-+ break;
-+ case ELFDATA2LSB: /* Little-endian */
-+ if (! bfd_header_little_endian (templ))
-+ {
-+ if (build_id_verbose >= BUILD_ID_VERBOSE_BINARY_PARSE)
-+ warning (_("build-id: Unrecognized "
-+ "little-endian ELF header at address 0x%lx"),
-+ (unsigned long) ehdr_vma);
-+ return NULL;
-+ }
-+ break;
-+ case ELFDATANONE: /* No data encoding specified */
-+ default: /* Unknown data encoding specified */
-+ if (build_id_verbose >= BUILD_ID_VERBOSE_BINARY_PARSE)
-+ warning (_("build-id: Unrecognized "
-+ "ELF header endianity at address 0x%lx"),
-+ (unsigned long) ehdr_vma);
-+ return NULL;
-+ }
-+
-+ elf_swap_ehdr_in (templ, &x_ehdr64, &i_ehdr);
-+
-+ /* The file header tells where to find the program headers.
-+ These are what we use to actually choose what to read. */
-+
-+ if (i_ehdr.e_phentsize != (bfd_get_arch_size (templ) == 64
-+ ? sizeof (Elf64_External_Phdr)
-+ : sizeof (Elf32_External_Phdr))
-+ || i_ehdr.e_phnum == 0)
-+ {
-+ if (build_id_verbose >= BUILD_ID_VERBOSE_BINARY_PARSE)
-+ warning (_("build-id: Invalid ELF program headers from the ELF header "
-+ "at address 0x%lx"), (unsigned long) ehdr_vma);
-+ return NULL;
-+ }
-+
-+ x_phdrs_size = (bfd_get_arch_size (templ) == 64 ? sizeof (Elf64_External_Phdr)
-+ : sizeof (Elf32_External_Phdr));
-+
-+ i_phdrs = (Elf_Internal_Phdr *) xmalloc (i_ehdr.e_phnum * (sizeof *i_phdrs + x_phdrs_size));
-+ x_phdrs_ptr = (gdb_byte *) &i_phdrs[i_ehdr.e_phnum];
-+ err = target_read_memory (ehdr_vma + i_ehdr.e_phoff, (bfd_byte *) x_phdrs_ptr,
-+ i_ehdr.e_phnum * x_phdrs_size);
-+ if (err)
-+ {
-+ free (i_phdrs);
-+ if (build_id_verbose >= BUILD_ID_VERBOSE_BINARY_PARSE)
-+ warning (_("build-id: Error reading "
-+ "ELF program headers at address 0x%lx"),
-+ (unsigned long) (ehdr_vma + i_ehdr.e_phoff));
-+ return NULL;
-+ }
-+
-+ loadbase = ehdr_vma;
-+ loadbase_set = 0;
-+ for (i = 0; i < i_ehdr.e_phnum; ++i)
-+ {
-+ elf_swap_phdr_in (templ, (Elf64_External_Phdr *)
-+ (x_phdrs_ptr + i * x_phdrs_size), &i_phdrs[i]);
-+ /* IA-64 vDSO may have two mappings for one segment, where one mapping
-+ is executable only, and one is read only. We must not use the
-+ executable one (PF_R is the first one, PF_X the second one). */
-+ if (i_phdrs[i].p_type == PT_LOAD && (i_phdrs[i].p_flags & PF_R))
-+ {
-+ /* Only the first PT_LOAD segment indicates the file bias.
-+ Next segments may have P_VADDR arbitrarily higher.
-+ If the first segment has P_VADDR zero any next segment must not
-+ confuse us, the first one sets LOADBASE certainly enough. */
-+ if (!loadbase_set && i_phdrs[i].p_offset == 0)
-+ {
-+ loadbase = ehdr_vma - i_phdrs[i].p_vaddr;
-+ loadbase_set = 1;
-+ }
-+ }
-+ }
-+
-+ if (build_id_verbose >= BUILD_ID_VERBOSE_BINARY_PARSE)
-+ warning (_("build-id: Found ELF header at address 0x%lx, loadbase 0x%lx"),
-+ (unsigned long) ehdr_vma, (unsigned long) loadbase);
-+
-+ *e_phnum_pointer = i_ehdr.e_phnum;
-+ *loadbase_pointer = loadbase;
-+ return i_phdrs;
-+}
-+
-+/* BUILD_ID_ADDR_GET gets ADDR located somewhere in the object.
-+ Find the first section before ADDR containing an ELF header.
-+ We rely on the fact the sections from multiple files do not mix.
-+ FIXME: We should check ADDR is contained _inside_ the section with possibly
-+ missing content (P_FILESZ < P_MEMSZ). These omitted sections are currently
-+ hidden by _BFD_ELF_MAKE_SECTION_FROM_PHDR. */
-+
-+static CORE_ADDR build_id_addr;
-+struct build_id_addr_sect
-+ {
-+ struct build_id_addr_sect *next;
-+ asection *sect;
-+ };
-+static struct build_id_addr_sect *build_id_addr_sect;
-+
-+static void build_id_addr_candidate (bfd *abfd, asection *sect, void *obj)
-+{
-+ if (build_id_addr >= bfd_section_vma (sect))
-+ {
-+ struct build_id_addr_sect *candidate;
-+
-+ candidate = (struct build_id_addr_sect *) xmalloc (sizeof *candidate);
-+ candidate->next = build_id_addr_sect;
-+ build_id_addr_sect = candidate;
-+ candidate->sect = sect;
-+ }
-+}
-+
-+struct bfd_build_id *
-+build_id_addr_get (CORE_ADDR addr)
-+{
-+ struct build_id_addr_sect *candidate;
-+ struct bfd_build_id *retval = NULL;
-+ Elf_Internal_Phdr *i_phdr = NULL;
-+ bfd_vma loadbase = 0;
-+ unsigned e_phnum = 0;
-+
-+ if (current_program_space->core_bfd () == NULL)
-+ return NULL;
-+
-+ build_id_addr = addr;
-+ gdb_assert (build_id_addr_sect == NULL);
-+ bfd_map_over_sections (current_program_space->core_bfd (),
-+ build_id_addr_candidate, NULL);
-+
-+ /* Sections are sorted in the high-to-low VMAs order.
-+ Stop the search on the first ELF header we find.
-+ Do not continue the search even if it does not contain NT_GNU_BUILD_ID. */
-+
-+ for (candidate = build_id_addr_sect; candidate != NULL;
-+ candidate = candidate->next)
-+ {
-+ i_phdr = elf_get_phdr (current_program_space->core_bfd (),
-+ bfd_section_vma (candidate->sect),
-+ &e_phnum, &loadbase);
-+ if (i_phdr != NULL)
-+ break;
-+ }
-+
-+ if (i_phdr != NULL)
-+ {
-+ retval = build_id_phdr_get (current_program_space->core_bfd (), loadbase, e_phnum, i_phdr);
-+ xfree (i_phdr);
-+ }
-+
-+ while (build_id_addr_sect != NULL)
-+ {
-+ candidate = build_id_addr_sect;
-+ build_id_addr_sect = candidate->next;
-+ xfree (candidate);
-+ }
-+
-+ return retval;
-+}
-+
- /* See build-id.h. */
-
- int
-@@ -73,63 +474,166 @@ build_id_verify (bfd *abfd, size_t check_len, const bfd_byte *check)
- return retval;
- }
-
-+static char *
-+link_resolve (const char *symlink, int level)
-+{
-+ char buf[PATH_MAX + 1], *retval;
-+ gdb::unique_xmalloc_ptr<char> target;
-+ ssize_t got;
-+
-+ if (level > 10)
-+ return xstrdup (symlink);
-+
-+ got = readlink (symlink, buf, sizeof (buf));
-+ if (got < 0 || got >= sizeof (buf))
-+ return xstrdup (symlink);
-+ buf[got] = '\0';
-+
-+ if (IS_ABSOLUTE_PATH (buf))
-+ target = make_unique_xstrdup (buf);
-+ else
-+ {
-+ const std::string dir (ldirname (symlink));
-+
-+ target = xstrprintf ("%s"
-+#ifndef HAVE_DOS_BASED_FILE_SYSTEM
-+ "/"
-+#else /* HAVE_DOS_BASED_FILE_SYSTEM */
-+ "\\"
-+#endif /* HAVE_DOS_BASED_FILE_SYSTEM */
-+ "%s", dir.c_str(), buf);
-+ }
-+
-+ retval = link_resolve (target.get (), level + 1);
-+ return retval;
-+}
-+
- /* Helper for build_id_to_debug_bfd. LINK is a path to a potential
- build-id-based separate debug file, potentially a symlink to the real file.
- If the file exists and matches BUILD_ID, return a BFD reference to it. */
-
- static gdb_bfd_ref_ptr
--build_id_to_debug_bfd_1 (const std::string &link, size_t build_id_len,
-- const bfd_byte *build_id)
-+build_id_to_debug_bfd_1 (const std::string &orig_link, size_t build_id_len,
-+ const bfd_byte *build_id, char **link_return)
- {
-+ gdb_bfd_ref_ptr ret_bfd = {};
-+ std::string ret_link;
-+
- if (separate_debug_file_debug)
- {
-- gdb_printf (gdb_stdlog, _(" Trying %s..."), link.c_str ());
-- gdb_flush (gdb_stdlog);
-+ gdb_printf (gdb_stdlog, _(" Trying %s..."), orig_link.c_str ());
-+ gdb_flush (gdb_stdout);
- }
-
-- /* lrealpath() is expensive even for the usually non-existent files. */
-- gdb::unique_xmalloc_ptr<char> filename_holder;
-- const char *filename = nullptr;
-- if (is_target_filename (link))
-- filename = link.c_str ();
-- else if (access (link.c_str (), F_OK) == 0)
-+ for (unsigned seqno = 0;; seqno++)
- {
-- filename_holder.reset (lrealpath (link.c_str ()));
-- filename = filename_holder.get ();
-- }
-+ std::string link = orig_link;
-
-- if (filename == NULL)
-- {
-- if (separate_debug_file_debug)
-- gdb_printf (gdb_stdlog,
-- _(" no, unable to compute real path\n"));
-+ if (seqno > 0)
-+ {
-+ /* There can be multiple build-id symlinks pointing to real files
-+ with the same build-id (such as hard links). Some of the real
-+ files may not be installed. */
-
-- return {};
-- }
-+ string_appendf (link, ".%u", seqno);
-+ }
-
-- /* We expect to be silent on the non-existing files. */
-- gdb_bfd_ref_ptr debug_bfd = gdb_bfd_open (filename, gnutarget);
-+ ret_link = link;
-
-- if (debug_bfd == NULL)
-- {
-- if (separate_debug_file_debug)
-- gdb_printf (gdb_stdlog, _(" no, unable to open.\n"));
-+ struct stat statbuf_trash;
-+
-+ /* `access' automatically dereferences LINK. */
-+ if (lstat (link.c_str (), &statbuf_trash) != 0)
-+ {
-+ /* Stop increasing SEQNO. */
-+ break;
-+ }
-+
-+ /* lrealpath() is expensive even for the usually non-existent files. */
-+ gdb::unique_xmalloc_ptr<char> filename_holder;
-+ const char *filename = nullptr;
-+ if (startswith (link, TARGET_SYSROOT_PREFIX))
-+ filename = link.c_str ();
-+ else if (access (link.c_str (), F_OK) == 0)
-+ {
-+ filename_holder.reset (lrealpath (link.c_str ()));
-+ filename = filename_holder.get ();
-+ }
-+
-+ if (filename == NULL)
-+ {
-+ if (separate_debug_file_debug)
-+ gdb_printf (gdb_stdlog,
-+ _(" no, unable to compute real path\n"));
-+
-+ continue;
-+ }
-+
-+ /* We expect to be silent on the non-existing files. */
-+ gdb_bfd_ref_ptr debug_bfd = gdb_bfd_open (filename, gnutarget);
-
-- return {};
-+ if (debug_bfd == NULL)
-+ {
-+ if (separate_debug_file_debug)
-+ gdb_printf (gdb_stdlog, _(" no, unable to open.\n"));
-+
-+ continue;
-+ }
-+
-+ if (!build_id_verify (debug_bfd.get(), build_id_len, build_id))
-+ {
-+ if (separate_debug_file_debug)
-+ gdb_printf (gdb_stdlog,
-+ _(" no, build-id does not match.\n"));
-+
-+ continue;
-+ }
-+
-+ ret_bfd = debug_bfd;
-+ break;
- }
-
-- if (!build_id_verify (debug_bfd.get(), build_id_len, build_id))
-+ std::string link_all;
-+
-+ if (ret_bfd != NULL)
- {
- if (separate_debug_file_debug)
-- gdb_printf (gdb_stdlog, _(" no, build-id does not match.\n"));
--
-- return {};
-+ gdb_printf (gdb_stdlog, _(" yes!\n"));
-+ }
-+ else
-+ {
-+ /* If none of the real files is found report as missing file
-+ always the non-.%u-suffixed file. */
-+ std::string link0 = orig_link;
-+
-+ /* If the symlink has target request to install the target.
-+ BASE-debuginfo.rpm contains the symlink but BASE.rpm may be missing.
-+ https://bugzilla.redhat.com/show_bug.cgi?id=981154 */
-+ std::string link0_resolved (link_resolve (link0.c_str (), 0));
-+
-+ if (link_all.empty ())
-+ link_all = link0_resolved;
-+ else
-+ {
-+ /* Use whitespace instead of DIRNAME_SEPARATOR to be compatible with
-+ its possible use as an argument for installation command. */
-+ link_all += " " + link0_resolved;
-+ }
- }
-
-- if (separate_debug_file_debug)
-- gdb_printf (gdb_stdlog, _(" yes!\n"));
-+ if (link_return != NULL)
-+ {
-+ if (ret_bfd != NULL)
-+ {
-+ *link_return = xstrdup (ret_link.c_str ());
-+ }
-+ else
-+ {
-+ *link_return = xstrdup (link_all.c_str ());
-+ }
-+ }
-
-- return debug_bfd;
-+ return ret_bfd;
- }
-
- /* Common code for finding BFDs of a given build-id. This function
-@@ -138,7 +642,7 @@ build_id_to_debug_bfd_1 (const std::string &link, size_t build_id_len,
-
- static gdb_bfd_ref_ptr
- build_id_to_bfd_suffix (size_t build_id_len, const bfd_byte *build_id,
-- const char *suffix)
-+ const char *suffix, char **link_return)
- {
- /* Keep backward compatibility so that DEBUG_FILE_DIRECTORY being "" will
- cause "/.build-id/..." lookups. */
-@@ -161,16 +665,17 @@ build_id_to_bfd_suffix (size_t build_id_len, const bfd_byte *build_id,
- if (size > 0)
- {
- size--;
-- string_appendf (link, "%02x/", (unsigned) *data++);
-+ string_appendf (link, "%02x", (unsigned) *data++);
- }
--
-+ if (size > 0)
-+ link += "/";
- while (size-- > 0)
- string_appendf (link, "%02x", (unsigned) *data++);
-
- link += suffix;
-
- gdb_bfd_ref_ptr debug_bfd
-- = build_id_to_debug_bfd_1 (link, build_id_len, build_id);
-+ = build_id_to_debug_bfd_1 (link, build_id_len, build_id, link_return);
- if (debug_bfd != NULL)
- return debug_bfd;
-
-@@ -181,7 +686,7 @@ build_id_to_bfd_suffix (size_t build_id_len, const bfd_byte *build_id,
- if (!gdb_sysroot.empty ())
- {
- link = gdb_sysroot + link;
-- debug_bfd = build_id_to_debug_bfd_1 (link, build_id_len, build_id);
-+ debug_bfd = build_id_to_debug_bfd_1 (link, build_id_len, build_id, NULL);
- if (debug_bfd != NULL)
- return debug_bfd;
- }
-@@ -190,20 +695,178 @@ build_id_to_bfd_suffix (size_t build_id_len, const bfd_byte *build_id,
- return {};
- }
-
-+char *
-+build_id_to_filename (const struct bfd_build_id *build_id, char **link_return)
-+{
-+ gdb_bfd_ref_ptr abfd;
-+ char *result;
-+
-+ abfd = build_id_to_exec_bfd (build_id->size, build_id->data, link_return);
-+ if (abfd == NULL)
-+ return NULL;
-+
-+ result = xstrdup (bfd_get_filename (abfd.get ()));
-+ return result;
-+}
-+
-+/* This MISSING_FILEPAIR_HASH tracker is used only for the duplicite messages
-+ yum --enablerepo='*debug*' install ...
-+ avoidance. */
-+
-+struct missing_filepair
-+ {
-+ char *binary;
-+ char *debug;
-+ char data[1];
-+ };
-+
-+static struct htab *missing_filepair_hash;
-+static struct obstack missing_filepair_obstack;
-+
-+static void *
-+missing_filepair_xcalloc (size_t nmemb, size_t nmemb_size)
-+{
-+ void *retval;
-+ size_t size = nmemb * nmemb_size;
-+
-+ retval = obstack_alloc (&missing_filepair_obstack, size);
-+ memset (retval, 0, size);
-+ return retval;
-+}
-+
-+static hashval_t
-+missing_filepair_hash_func (const struct missing_filepair *elem)
-+{
-+ hashval_t retval = 0;
-+
-+ retval ^= htab_hash_string (elem->binary);
-+ if (elem->debug != NULL)
-+ retval ^= htab_hash_string (elem->debug);
-+
-+ return retval;
-+}
-+
-+static int
-+missing_filepair_eq (const struct missing_filepair *elem1,
-+ const struct missing_filepair *elem2)
-+{
-+ return strcmp (elem1->binary, elem2->binary) == 0
-+ && ((elem1->debug == NULL) == (elem2->debug == NULL))
-+ && (elem1->debug == NULL || strcmp (elem1->debug, elem2->debug) == 0);
-+}
-+
-+static void
-+missing_filepair_change (void)
-+{
-+ if (missing_filepair_hash != NULL)
-+ {
-+ obstack_free (&missing_filepair_obstack, NULL);
-+ /* All their memory came just from missing_filepair_OBSTACK. */
-+ missing_filepair_hash = NULL;
-+ }
-+}
-+
-+static void
-+debug_print_executable_changed (struct program_space *pspace, bool reload_p)
-+{
-+ missing_filepair_change ();
-+}
-+
-+/* Notify user the file BINARY with (possibly NULL) associated separate debug
-+ information file DEBUG is missing. DEBUG may or may not be the build-id
-+ file such as would be:
-+ /usr/lib/debug/.build-id/dd/b1d2ce632721c47bb9e8679f369e2295ce71be.debug
-+ */
-+
-+void
-+debug_print_missing (const char *binary, const char *debug)
-+{
-+ size_t binary_len0 = strlen (binary) + 1;
-+ size_t debug_len0 = debug ? strlen (debug) + 1 : 0;
-+ struct missing_filepair missing_filepair_find;
-+ struct missing_filepair *missing_filepair;
-+ struct missing_filepair **slot;
-+
-+ if (build_id_verbose < BUILD_ID_VERBOSE_FILENAMES)
-+ return;
-+
-+ if (missing_filepair_hash == NULL)
-+ {
-+ obstack_init (&missing_filepair_obstack);
-+ missing_filepair_hash = htab_create_alloc (64,
-+ (hashval_t (*) (const void *)) missing_filepair_hash_func,
-+ (int (*) (const void *, const void *)) missing_filepair_eq, NULL,
-+ missing_filepair_xcalloc, NULL);
-+ }
-+
-+ /* Use MISSING_FILEPAIR_FIND first instead of calling obstack_alloc with
-+ obstack_free in the case of a (rare) match. The problem is ALLOC_F for
-+ MISSING_FILEPAIR_HASH allocates from MISSING_FILEPAIR_OBSTACK maintenance
-+ structures for MISSING_FILEPAIR_HASH. Calling obstack_free would possibly
-+ not to free only MISSING_FILEPAIR but also some such structures (allocated
-+ during the htab_find_slot call). */
-+
-+ missing_filepair_find.binary = (char *) binary;
-+ missing_filepair_find.debug = (char *) debug;
-+ slot = (struct missing_filepair **) htab_find_slot (missing_filepair_hash,
-+ &missing_filepair_find,
-+ INSERT);
-+
-+ /* While it may be still printed duplicitely with the missing debuginfo file
-+ * it is due to once printing about the binary file build-id link and once
-+ * about the .debug file build-id link as both the build-id symlinks are
-+ * located in the debuginfo package. */
-+
-+ if (*slot != NULL)
-+ return;
-+
-+ missing_filepair = (struct missing_filepair *) obstack_alloc (&missing_filepair_obstack,
-+ sizeof (*missing_filepair) - 1
-+ + binary_len0 + debug_len0);
-+ missing_filepair->binary = missing_filepair->data;
-+ memcpy (missing_filepair->binary, binary, binary_len0);
-+ if (debug != NULL)
-+ {
-+ missing_filepair->debug = missing_filepair->binary + binary_len0;
-+ memcpy (missing_filepair->debug, debug, debug_len0);
-+ }
-+ else
-+ missing_filepair->debug = NULL;
-+
-+ *slot = missing_filepair;
-+
-+ /* We do not collect and flush these messages as each such message
-+ already requires its own separate lines. */
-+
-+ gdb_printf (gdb_stdlog,
-+ _("Missing separate debuginfo for %s.\n"), binary);
-+ if (debug != NULL)
-+ {
-+ gdb_printf (gdb_stdlog, _("Try: %s %s\n"),
-+#ifdef DNF_DEBUGINFO_INSTALL
-+ "dnf"
-+#else
-+ "yum"
-+#endif
-+ " --enablerepo='*debug*' install", debug);
-+ }
-+}
-+
- /* See build-id.h. */
-
- gdb_bfd_ref_ptr
- build_id_to_debug_bfd (size_t build_id_len, const bfd_byte *build_id)
- {
-- return build_id_to_bfd_suffix (build_id_len, build_id, ".debug");
-+ return build_id_to_bfd_suffix (build_id_len, build_id, ".debug", nullptr);
- }
-
- /* See build-id.h. */
-
- gdb_bfd_ref_ptr
--build_id_to_exec_bfd (size_t build_id_len, const bfd_byte *build_id)
-+build_id_to_exec_bfd (size_t build_id_len, const bfd_byte *build_id,
-+ char **link_return)
- {
-- return build_id_to_bfd_suffix (build_id_len, build_id, "");
-+ return build_id_to_bfd_suffix (build_id_len, build_id, "", link_return);
- }
-
- /* See build-id.h. */
-@@ -224,6 +887,7 @@ find_separate_debug_file_by_buildid (struct objfile *objfile,
-
- gdb_bfd_ref_ptr abfd (build_id_to_debug_bfd (build_id->size,
- build_id->data));
-+
- /* Prevent looping on a stripped .debug file. */
- if (abfd != NULL
- && filename_cmp (bfd_get_filename (abfd.get ()),
-@@ -243,3 +907,22 @@ find_separate_debug_file_by_buildid (struct objfile *objfile,
-
- return std::string ();
- }
-+
-+void _initialize_build_id ();
-+
-+void
-+_initialize_build_id ()
-+{
-+ add_setshow_zinteger_cmd ("build-id-verbose", no_class, &build_id_verbose,
-+ _("\
-+Set debugging level of the build-id locator."), _("\
-+Show debugging level of the build-id locator."), _("\
-+Level 1 (default) enables printing the missing debug filenames,\n\
-+level 2 also prints the parsing of binaries to find the identificators."),
-+ NULL,
-+ show_build_id_verbose,
-+ &setlist, &showlist);
-+
-+ gdb::observers::executable_changed.attach (debug_print_executable_changed,
-+ "build-id");
-+}
-diff --git a/gdb/build-id.h b/gdb/build-id.h
---- a/gdb/build-id.h
-+++ b/gdb/build-id.h
-@@ -40,12 +40,16 @@ extern int build_id_verify (bfd *abfd,
- extern gdb_bfd_ref_ptr build_id_to_debug_bfd (size_t build_id_len,
- const bfd_byte *build_id);
-
-+extern char *build_id_to_filename (const struct bfd_build_id *build_id,
-+ char **link_return);
-+
- /* Find and open a BFD for an executable file given a build-id. If no BFD
- can be found, return NULL. The returned reference to the BFD must be
- released by the caller. */
-
- extern gdb_bfd_ref_ptr build_id_to_exec_bfd (size_t build_id_len,
-- const bfd_byte *build_id);
-+ const bfd_byte *build_id,
-+ char **link_return);
-
- /* Find the separate debug file for OBJFILE, by using the build-id
- associated with OBJFILE's BFD. If successful, returns the file name for the
-diff --git a/gdb/corelow.c b/gdb/corelow.c
---- a/gdb/corelow.c
-+++ b/gdb/corelow.c
-@@ -21,6 +21,9 @@
- #include <signal.h>
- #include <fcntl.h>
- #include "frame.h"
-+#include "auxv.h"
-+#include "build-id.h"
-+#include "elf/common.h"
- #include "inferior.h"
- #include "infrun.h"
- #include "symtab.h"
-@@ -383,6 +386,8 @@ add_to_thread_list (asection *asect, asection *reg_sect, inferior *inf)
- switch_to_thread (thr); /* Yes, make it current. */
- }
-
-+static bool build_id_core_loads = true;
-+
- /* Issue a message saying we have no core to debug, if FROM_TTY. */
-
- static void
-@@ -570,8 +575,10 @@ locate_exec_from_corefile_build_id (bfd *abfd, int from_tty)
- if (build_id == nullptr)
- return;
-
-+ char *build_id_filename;
- gdb_bfd_ref_ptr execbfd
-- = build_id_to_exec_bfd (build_id->size, build_id->data);
-+ = build_id_to_exec_bfd (build_id->size, build_id->data,
-+ &build_id_filename);
-
- if (execbfd == nullptr)
- {
-@@ -599,7 +606,12 @@ locate_exec_from_corefile_build_id (bfd *abfd, int from_tty)
- exec_file_attach (bfd_get_filename (execbfd.get ()), from_tty);
- symbol_file_add_main (bfd_get_filename (execbfd.get ()),
- symfile_add_flag (from_tty ? SYMFILE_VERBOSE : 0));
-+ if (current_program_space->symfile_object_file != NULL)
-+ current_program_space->symfile_object_file->flags |=
-+ OBJF_BUILD_ID_CORE_LOADED;
- }
-+ else
-+ debug_print_missing (BUILD_ID_MAIN_EXECUTABLE_FILENAME, build_id_filename);
- }
-
- /* See gdbcore.h. */
-@@ -1524,4 +1536,11 @@ _initialize_corelow ()
- maintenance_print_core_file_backed_mappings,
- _("Print core file's file-backed mappings."),
- &maintenanceprintlist);
-+
-+ add_setshow_boolean_cmd ("build-id-core-loads", class_files,
-+ &build_id_core_loads, _("\
-+Set whether CORE-FILE loads the build-id associated files automatically."), _("\
-+Show whether CORE-FILE loads the build-id associated files automatically."),
-+ NULL, NULL, NULL,
-+ &setlist, &showlist);
- }
-diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
---- a/gdb/doc/gdb.texinfo
-+++ b/gdb/doc/gdb.texinfo
-@@ -22487,6 +22487,27 @@ information files.
-
- @end table
-
-+You can also adjust the current verbosity of the @dfn{build id} locating.
-+
-+@table @code
-+
-+@kindex set build-id-verbose
-+@item set build-id-verbose 0
-+No additional messages are printed.
-+
-+@item set build-id-verbose 1
-+Missing separate debug filenames are printed.
-+
-+@item set build-id-verbose 2
-+Missing separate debug filenames are printed and also all the parsing of the
-+binaries to find their @dfn{build id} content is printed.
-+
-+@kindex show build-id-verbose
-+@item show build-id-verbose
-+Show the current verbosity value for the @dfn{build id} content locating.
-+
-+@end table
-+
- @cindex @code{.gnu_debuglink} sections
- @cindex debug link sections
- A debug link is a special section of the executable file named
-diff --git a/gdb/objfiles.h b/gdb/objfiles.h
---- a/gdb/objfiles.h
-+++ b/gdb/objfiles.h
-@@ -877,6 +877,10 @@ struct objfile
- bool object_format_has_copy_relocs = false;
- };
-
-+/* This file was loaded according to the BUILD_ID_CORE_LOADS rules. */
-+
-+#define OBJF_BUILD_ID_CORE_LOADED static_cast<enum objfile_flag>(1 << 12)
-+
- /* A deleter for objfile. */
-
- struct objfile_deleter
-diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c
---- a/gdb/solib-svr4.c
-+++ b/gdb/solib-svr4.c
-@@ -44,6 +44,7 @@
- #include "auxv.h"
- #include "gdb_bfd.h"
- #include "probe.h"
-+#include "build-id.h"
-
- #include <map>
-
-@@ -1262,6 +1263,46 @@ svr4_read_so_list (svr4_info *info, CORE_ADDR lm, CORE_ADDR prev_lm,
- continue;
- }
-
-+ {
-+ struct bfd_build_id *build_id;
-+
-+ build_id = build_id_addr_get (li->l_ld);
-+ if (build_id != NULL)
-+ {
-+ char *bid_name, *build_id_filename;
-+
-+ /* Missing the build-id matching separate debug info file
-+ would be handled while SO_NAME gets loaded. */
-+ bid_name = build_id_to_filename (build_id, &build_id_filename);
-+ if (bid_name != NULL)
-+ {
-+ name = make_unique_xstrdup (bid_name);
-+ xfree (bid_name);
-+ }
-+ else
-+ {
-+ debug_print_missing (name.get (), build_id_filename);
-+
-+ /* In the case the main executable was found according to
-+ its build-id (from a core file) prevent loading
-+ a different build of a library with accidentally the
-+ same SO_NAME.
-+
-+ It suppresses bogus backtraces (and prints "??" there
-+ instead) if the on-disk files no longer match the
-+ running program version. */
-+
-+ if (current_program_space->symfile_object_file != NULL
-+ && (current_program_space->symfile_object_file->flags
-+ & OBJF_BUILD_ID_CORE_LOADED) != 0)
-+ name = make_unique_xstrdup ("");
-+ }
-+
-+ xfree (build_id_filename);
-+ xfree (build_id);
-+ }
-+ }
-+
- /* If this entry has no name, or its name matches the name
- for the main executable, don't include it in the list. */
- if (*name == '\0' || match_main (name.get ()))
-diff --git a/gdb/symfile.h b/gdb/symfile.h
---- a/gdb/symfile.h
-+++ b/gdb/symfile.h
-@@ -363,6 +363,11 @@ void map_symbol_filenames (gdb::function_view<symbol_filename_ftype> fun,
- optional offset to apply to each section. */
- extern void generic_load (const char *args, int from_tty);
-
-+/* build-id support. */
-+extern struct bfd_build_id *build_id_addr_get (CORE_ADDR addr);
-+extern void debug_print_missing (const char *binary, const char *debug);
-+#define BUILD_ID_MAIN_EXECUTABLE_FILENAME _("the main executable file")
-+
- /* From minidebug.c. */
-
- extern gdb_bfd_ref_ptr find_separate_debug_file_in_section (struct objfile *);
-diff --git a/gdb/testsuite/gdb.base/corefile.exp b/gdb/testsuite/gdb.base/corefile.exp
---- a/gdb/testsuite/gdb.base/corefile.exp
-+++ b/gdb/testsuite/gdb.base/corefile.exp
-@@ -347,3 +347,33 @@ gdb_test_multiple "core-file $corefile" $test {
- pass $test
- }
- }
-+
-+
-+# Test auto-loading of binary files through build-id from the core file.
-+set buildid [build_id_debug_filename_get $binfile]
-+set wholetest "binfile found by build-id"
-+if {$buildid == ""} {
-+ untested "$wholetest (binary has no build-id)"
-+} else {
-+ gdb_exit
-+ gdb_start
-+
-+ regsub {\.debug$} $buildid {} buildid
-+ set debugdir [standard_output_file ${testfile}-debugdir]
-+ file delete -force -- $debugdir
-+ file mkdir $debugdir/[file dirname $buildid]
-+ file copy $binfile $debugdir/$buildid
-+
-+ set test "show debug-file-directory"
-+ gdb_test_multiple $test $test {
-+ -re "The directory where separate debug symbols are searched for is \"(.*)\"\\.\r\n$gdb_prompt $" {
-+ set debugdir_orig $expect_out(1,string)
-+ pass $test
-+ }
-+ }
-+ gdb_test_no_output "set debug-file-directory $debugdir:$debugdir_orig" "set debug-file-directory"
-+ gdb_test "show build-id-core-loads" {Whether CORE-FILE loads the build-id associated files automatically is on\.}
-+ gdb_test "core-file $corefile" "\r\nProgram terminated with .*" "core-file without executable"
-+ gdb_test "info files" "Local exec file:\r\n\[ \t\]*`[string_to_regexp $debugdir/$buildid]', file type .*"
-+ pass $wholetest
-+}
-diff --git a/gdb/testsuite/gdb.base/gdbinit-history.exp b/gdb/testsuite/gdb.base/gdbinit-history.exp
---- a/gdb/testsuite/gdb.base/gdbinit-history.exp
-+++ b/gdb/testsuite/gdb.base/gdbinit-history.exp
-@@ -179,7 +179,8 @@ proc test_empty_history_filename { } {
- global env
- global gdb_prompt
-
-- set common_history [list "set height 0" "set width 0"]
-+ set common_history [list "set height 0" "set width 0" \
-+ "set build-id-verbose 0"]
-
- set test_dir [standard_output_file history_test]
- remote_exec host "mkdir -p $test_dir"
-diff --git a/gdb/testsuite/gdb.base/new-ui-pending-input.exp b/gdb/testsuite/gdb.base/new-ui-pending-input.exp
---- a/gdb/testsuite/gdb.base/new-ui-pending-input.exp
-+++ b/gdb/testsuite/gdb.base/new-ui-pending-input.exp
-@@ -60,6 +60,9 @@ proc test_command_line_new_ui_pending_input {} {
- set bpline [gdb_get_line_number "set breakpoint here"]
-
- set options ""
-+ append options " -iex \"set height 0\""
-+ append options " -iex \"set width 0\""
-+ append options " -iex \"set build-id-verbose 0\""
- append options " -iex \"new-ui console $extra_tty_name\""
- append options " -ex \"b $bpline\""
- append options " -ex \"run\""
-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
-@@ -238,7 +238,8 @@ if ![info exists INTERNAL_GDBFLAGS] {
- "-nx" \
- "-q" \
- {-iex "set height 0"} \
-- {-iex "set width 0"}]]
-+ {-iex "set width 0"} \
-+ {-iex "set build-id-verbose 0"}]]
-
- # If DEBUGINFOD_URLS is set, gdb will try to download sources and
- # debug info for f.i. system libraries. Prevent this.
-@@ -2493,6 +2494,17 @@ proc default_gdb_start { } {
- }
- }
-
-+ # Turn off the missing warnings as the testsuite does not expect it.
-+ send_gdb "set build-id-verbose 0\n"
-+ gdb_expect 10 {
-+ -re "$gdb_prompt $" {
-+ verbose "Disabled the missing debug infos warnings." 2
-+ }
-+ timeout {
-+ warning "Could not disable the missing debug infos warnings.."
-+ }
-+ }
-+
- gdb_debug_init
- return 0
- }
-diff --git a/gdb/testsuite/lib/mi-support.exp b/gdb/testsuite/lib/mi-support.exp
---- a/gdb/testsuite/lib/mi-support.exp
-+++ b/gdb/testsuite/lib/mi-support.exp
-@@ -321,6 +321,16 @@ proc default_mi_gdb_start { { flags {} } } {
- warning "Couldn't set the width to 0."
- }
- }
-+ # Turn off the missing warnings as the testsuite does not expect it.
-+ send_gdb "190-gdb-set build-id-verbose 0\n"
-+ gdb_expect 10 {
-+ -re ".*190-gdb-set build-id-verbose 0\r\n190\\\^done\r\n$mi_gdb_prompt$" {
-+ verbose "Disabled the missing debug infos warnings." 2
-+ }
-+ timeout {
-+ warning "Could not disable the missing debug infos warnings.."
-+ }
-+ }
-
- if { $separate_inferior_pty } {
- mi_create_inferior_pty
diff --git a/gdb-add-deprecated-settings-py-script.patch b/gdb-add-deprecated-settings-py-script.patch
new file mode 100644
index 0000000..dee2eaf
--- /dev/null
+++ b/gdb-add-deprecated-settings-py-script.patch
@@ -0,0 +1,68 @@
+From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
+From: Andrew Burgess <aburgess@redhat.com>
+Date: Tue, 19 Nov 2024 13:56:56 +0000
+Subject: gdb-add-deprecated-settings-py-script.patch
+
+;; Not a backport. Add a new script which defines a setting which is
+;; deprecated, but we don't want to remove it from Fedora GDB just yet.
+
+gdb: add script which defines a deprecated settings
+
+The build-id-core-load setting has not been useful since 2020, but its
+not clear when it will be OK to remove this from GDB.
+
+Rather than defining this setting in C++ code, lets just create it
+using this Python script, this helps keep the Fedora stuff out of core
+GDB.
+
+diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in
+--- a/gdb/data-directory/Makefile.in
++++ b/gdb/data-directory/Makefile.in
+@@ -86,6 +86,7 @@ PYTHON_FILE_LIST = \
+ gdb/unwinder.py \
+ gdb/xmethod.py \
+ gdb/command/__init__.py \
++ gdb/command/deprecated-settings.py \
+ gdb/command/explore.py \
+ gdb/command/frame_filters.py \
+ gdb/command/missing_files.py \
+diff --git a/gdb/python/lib/gdb/command/deprecated-settings.py b/gdb/python/lib/gdb/command/deprecated-settings.py
+new file mode 100644
+--- /dev/null
++++ b/gdb/python/lib/gdb/command/deprecated-settings.py
+@@ -0,0 +1,35 @@
++import gdb
++
++# This setting was added to Fedora GDB back in 2007. It controlled
++# whether GDB would use the build-id extracted from a core file to
++# auto-load the executable if the user had not already loaded an
++# executable.
++#
++# In 2020 this setting was effectively deprecated as the only use of
++# the setting's value was removed. After this GDB would always
++# auto-load an executable based on the build-id if no executable was
++# already loaded.
++#
++# For now we maintain this setting for backward compatibility reasons.
++
++class build_id_core_load(gdb.Parameter):
++ """This setting is deprecated. Changing it will have no effect.
++ This is maintained only for backwards compatibility.
++
++ When opening a core-file, and no executable is loaded, GDB will
++ always try to auto-load a suitable executable using the build-id
++ extracted from the core file (if a suitable build-id can be
++ found)."""
++
++ def __init__(self):
++ self.set_doc = "This setting is deprecated and has no effect."
++ self.show_doc = "This setting is deprecated and has no effect."
++ self.value = True
++
++ super().__init__("build-id-core-load", gdb.COMMAND_DATA, gdb.PARAM_BOOLEAN)
++ def validate(self):
++ return True
++ def get_set_string(self):
++ raise gdb.GdbError("The 'build-id-core-load' setting is deprecated.")
++
++build_id_core_load()
diff --git a/gdb-add-rpm-suggestion-script.patch b/gdb-add-rpm-suggestion-script.patch
index 731aeaa..2ef2eb4 100644
--- a/gdb-add-rpm-suggestion-script.patch
+++ b/gdb-add-rpm-suggestion-script.patch
@@ -14,19 +14,142 @@ suggests debuginfo RPMs to install.
diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in
--- a/gdb/data-directory/Makefile.in
+++ b/gdb/data-directory/Makefile.in
-@@ -89,6 +89,7 @@ PYTHON_FILE_LIST = \
- gdb/command/missing_debug.py \
+@@ -91,6 +91,7 @@ PYTHON_FILE_LIST = \
+ gdb/command/missing_files.py \
gdb/command/pretty_printers.py \
gdb/command/prompt.py \
+ gdb/command/rpm-suggestions.py \
gdb/command/type_printers.py \
gdb/command/unwinders.py \
gdb/command/xmethods.py \
+diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
+--- a/gdb/doc/gdb.texinfo
++++ b/gdb/doc/gdb.texinfo
+@@ -186,6 +186,7 @@
+ * Trace File Format:: @value{GDBN} trace file format
+ * Index Section Format:: .gdb_index section format
+ * Debuginfod:: Download debugging resources with @code{debuginfod}
++* RPM Suggestions:: RPM Suggestions from GDB
+ * Man Pages:: Manual pages
+ * Copying:: GNU General Public License says
+ how you can copy and share @value{GDBN}
+@@ -50341,6 +50342,111 @@
+
+ @end table
+
++@node RPM Suggestions
++@appendix Receiving RPM installation suggestions
++@cindex rpm suggestions
++
++When @value{GDBN} loads an executable, or shared library, and cannot
++find the corresponding debug information, @value{GDBN} will check to
++see if an RPM is available which could provide the missing debug
++information. If a suitable RPM is found then @value{GDBN} will print
++a hint before the next prompt is displayed:
++
++@smallexample
++(@value{GDBP}) file /bin/ls
++Reading symbols from /bin/ls...
++Reading symbols from .gnu_debugdata for /usr/bin/ls...
++(No debugging symbols found in .gnu_debugdata for /usr/bin/ls)
++Missing rpms, try: dnf --enablerepo='*debug*' install coreutils-debuginfo-9.3-7.fc39.x86_64
++(@value{GDBP})
++@end smallexample
++
++In this case, installing @file{coreutils-debuginfo-9.3-7.fc39.x86_64}
++will provide the missing debug information for @file{/bin/ls}. It is
++up to you to install the suggested package, if possible, and after
++that reload the executable in @value{GDBN} so that the newly installed
++debug information can be found.
++
++The RPM suggestion feature can be disabled:
++
++@table @code
++@kindex set rpm-suggestion enabled
++@kindex show rpm-suggestion enabled
++@cindex rpm suggestions, disabling
++@item set rpm-suggestion enabled @r{[}on@r{|}off@r{]}
++@itemx show rpm-suggestion enabled
++When @samp{on} @value{GDBN} will make RPM suggestions where possible.
++When @samp{off} all RPM suggestion will be disabled.
++@end table
++
++When opening a core file (@pxref{core-file command}), it may be the
++case that not only is the debug information missing, but the
++corresponding executable itself is missing. For example, if a core
++file is copied from one machine to another in order to debug.
++
++In this case @value{GDBN} is able to suggest a command which will
++install the missing executable based on the build-id of the
++executable. For example:
++
++@smallexample
++(@value{GDBP}) core-file /tmp/core.5489
++warning: Can't open file /usr/bin/sl during file-backed mapping note processing
++[New LWP 5489]
++Core was generated by `sl'.
++Program terminated with signal SIGQUIT, Quit.
++#0 0x00007f1b41ced1a7 in ?? ()
++Missing file(s), try: dnf --enablerepo='*debug*' install /usr/lib/.build-id/33/2f1a8e56693960e3beb2d70cd79ddfec451cc3 /usr/lib/debug/.build-id/33/2f1a8e56693960e3beb2d70cd79ddfec451cc3.debug
++(@value{GDBP})
++@end smallexample
++
++The core file was generated from the @file{/usr/bin/sl} binary, which
++is not present on the machine opening the core file. @value{GDBN} has
++suggested a command, based on the build-id of the binary, which was
++extracted from the core file, that would install both the missing
++binary, and the corresponding debug information.
++
++Unfortunately, @value{GDBN} doesn't know if the suggested command will
++actually find a matching RPM or not. Querying the RPM database to see
++which packages, if any, will provide a file with the given build-id,
++is rather slow. As @file{/usr/bin/sl} is available in an RPM, then
++the above command will succeed.
++
++It is possible to have @value{GDBN} check to see if there is a package
++available before making the suggestion, but this is significantly
++slower. To enable this mode use the following command:
++
++@table @code
++@kindex set rpm-suggestion build-id-mode
++@kindex show rpm-suggestion build-id-mode
++@cindex rpm suggestions, build-id-mode
++@item set rpm-suggestion build-id-mode @r{[}fast@r{|}slow@r{]}
++@itemx show rpm-suggestion build-id-mode
++When set to @samp{fast}, which is the default, @value{GDBN} will offer
++suggestions based on the build-id of any missing executables or shared
++libraries while opening a core file. This is fast, but @value{GDBN}
++has not checked if there is a package available that can supply the
++required file, so running the suggested command might not install any
++packages.
++
++When set to @samp{slow}, each time @value{GDBN} encounters an
++executable, or shared library, that is missing, @value{GDBN} will
++check to see if there is an RPM available that will supply the missing
++binary. If a suitable RPM is found then @value{GDBN} will offer a
++command which will install the missing RPM. If no suitable RPM is
++found then @value{GDBN} will make no suggestions.
++@end table
++
++It is possible to review all of the previous RPM suggestions that
++@value{GDBN} has made using the following command:
++
++@table @code
++@kindex info rpm-suggestions
++@cindex rpm suggestions, listing
++@item info rpm-suggestions
++List all of the RPM suggestions @value{GDBN} has made since the
++executable was last changed.
++@end table
++
+ @node Man Pages
+ @appendix Manual pages
+ @cindex Man pages
diff --git a/gdb/python/lib/gdb/command/rpm-suggestions.py b/gdb/python/lib/gdb/command/rpm-suggestions.py
new file mode 100644
--- /dev/null
+++ b/gdb/python/lib/gdb/command/rpm-suggestions.py
-@@ -0,0 +1,134 @@
+@@ -0,0 +1,552 @@
+# Copyright 2023 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
@@ -42,9 +165,19 @@ new file mode 100644
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
-+import sys
+import gdb
+import gdb.missing_debug
++import gdb.missing_objfile
++
++# These modules are all system modules, and should be available on any
++# correctly setup Python install.
++import sys
++import os
++import subprocess
++import re
++from threading import Thread
++import time
++
+try:
+ import rpm
+except ModuleNotFoundError:
@@ -68,6 +201,11 @@ new file mode 100644
+ gdb.events.before_prompt.disconnect(before_prompt)
+
+ gdb.events.before_prompt.connect(before_prompt)
++
++ # Implement 'info rpm-suggestions' when the 'rpm' module is not
++ # available. Just throws an error.
++ def info_rpm_suggestions():
++ raise gdb.GdbError("rpm-suggestions are disabled as the Python 'rpm' module is not installed")
+else:
+ # Track all the RPMs suggested during a single debug session so we
+ # don't suggest the same RPM twice. This is only cleared when the
@@ -80,6 +218,94 @@ new file mode 100644
+ # the prompt is shown.
+ __suggest_rpms = {}
+
++ # Track all the build-ids suggested during a single debug session so we
++ # don't suggest installing using the same build-id twice. This is only
++ # cleared when the main executable is changed.
++ __missing_build_ids = {}
++
++ # Track any build-ids that have been discovered missing since the last
++ # time the prompt was displayed. Build-ids in here are also present in
++ # the __MISSING_BUILD_IDS dictionary, but this dictionary is cleared
++ # each time the prompt is shown.
++ __suggest_build_ids = {}
++
++ # The build-id to RPM lookup is very slow. This cache maps
++ # build-ids to the set of RPM we can suggest installing. The key
++ # is the build-id string, and the value is a list of RPM names, or
++ # None if there was an error with the build-id to RPM lookup.
++ #
++ # This cache is never cleared, even when the executable is
++ # changed. The build-ids should be unique, so a build-id lookup
++ # should be good for the lifetime of the session.
++ __build_id_lookup_cache = {}
++
++ # When searching for an RPM given a build-id, if the search takes
++ # too long, then a message is printed to the user. We only print
++ # the message once between each GDB prompt. This flag is set True
++ # when the message is printed, and reset to False when a prompt is
++ # displayed.
++ __searching_message_printed = False
++
++ # Add a suggestion to install RPM_NAME (the full name of an RPM)
++ # to the list of suggestions to make the next time a prompt is
++ # displayed.
++ def add_rpm_suggestion(rpm_name):
++ global __missing_rpms
++ global __suggest_rpms
++
++ if not rpm_name in __missing_rpms:
++ __suggest_rpms[rpm_name] = True
++ __missing_rpms[rpm_name] = True
++
++ # Return True if RPM_NAME is installed, where RPM_NAME is the full
++ # name of an RPM.
++ def is_rpm_installed(ts, rpm_name):
++ res = ts.dbMatch(rpm.RPMDBI_LABEL, rpm_name)
++ return len(res) > 0
++
++ # Add a suggestion to install RPMs based on BUILD_ID, a string
++ # containing a build-id, to the list of suggestions to make the next
++ # time a prompt is displayed.
++ def add_build_id_suggestion(build_id):
++ global __missing_build_ids
++ global __suggest_build_ids
++
++ if not build_id in __missing_build_ids:
++ __suggest_build_ids[build_id] = True
++ __missing_build_ids[build_id] = True
++
++ # Return true if '/usr/lib' is in the debug-file-directory list.
++ # System packages install their debug information into /usr/lib,
++ # so if GDB isn't looking in that directory, then there's no
++ # reason to try and figure out a suitable RPM to install.
++ def using_suitable_debug_file_directory():
++ debug_file_directories = gdb.parameter("debug-file-directory")
++ for d in debug_file_directories.split(os.pathsep):
++ if d[:8] == "/usr/lib":
++ return True
++ return False
++
++ # Return True if rpm-suggestion is disabled for any reason.
++ def rpm_suggestion_is_disabled():
++ global param_build_id_verbose
++
++ # If /usr/lib/ is not being used to find debug information
++ # then there's no point offering any RPMs as GDB would not
++ # find the newly installed content.
++ if not using_suitable_debug_file_directory():
++ return True
++
++ # For backwards compatibility, if build-id-verbose is set to
++ # zero, then disable RPM suggestion.
++ if param_build_id_verbose.value == 0:
++ return True
++
++ # Is 'rpm-suggestion enabled' set to 'off'?
++ if not param_rpm_suggestion_enabled.value:
++ return True
++
++ return False
++
+
+ # Lookup RPMs that might provide the debug information for FILENAME,
+ # which is a string containing the path to an object file GDB could
@@ -88,59 +314,243 @@ new file mode 100644
+ # If a possible RPM is found then this is added to the globals
+ # __MISSING_RPMS and __SUGGEST_RPMS, which are used elsewhere in this
+ # script.
-+ def find_suggestions(filename):
-+ ts = rpm.TransactionSet()
++ def find_debug_suggestions(filename):
++ if rpm_suggestion_is_disabled():
++ return
+
++ ts = rpm.TransactionSet()
+ mi = ts.dbMatch(rpm.RPMDBI_BASENAMES, filename)
+ for h in mi:
+ # Build the debuginfo package name.
+ obj = h.format("%{name}-debuginfo-%{version}-%{release}.%{arch}")
++ rpm_name = str(obj)
+
+ # Check to see if the package is installed.
-+ mi2 = ts.dbMatch(rpm.RPMDBI_LABEL, str(obj))
-+ if len(mi2) > 0:
++ if is_rpm_installed(ts, rpm_name):
+ continue
+
-+ # Now build the name of the package FILENAME came from.
-+ obj = h.format("%{name}-%{version}-%{release}.%{arch}")
-+ rpm_name = str(obj)
-+ if not rpm_name in __missing_rpms:
-+ __suggest_rpms[rpm_name] = True
-+ __missing_rpms[rpm_name] = True
++ add_rpm_suggestion(rpm_name)
++
++
++ # Return a string which is the filename of the filename
++ # corresponding to BUILD_ID in one of the two locations under
++ # /usr/lib.
++ #
++ # When DEBUG_P is True, return a filename within:
++ # /usr/lib/debug/.build-id/ and when DEBUG_P is False, return a
++ # filename within: /usr/lib/.build-id/.
++ #
++ # The SUFFIX string is appended to the generated filename.
++ def build_id_to_usr_lib_filename(build_id, debug_p, suffix = ""):
++ key = build_id[:2]
++ rst = build_id[2:]
++
++ filename = '/usr/lib'
++ if debug_p:
++ filename += '/debug'
++ filename += '/.build-id/' + key + '/' + rst + suffix
++
++ return filename
++
++ # A regexp object used to match against the output of `dnf`. This
++ # is initialised the first time it is needed.
++ find_objfile_suggestions_re = None
++
++ # Given BUILD_ID, a string containing a build-id, run a `dnf`
++ # command to figure out if any RPMs can provide a file with that
++ # build-id.
++ #
++ # If any suitable RPMs are found then `add_rpm_suggestion` is called
++ # to register the suggestion.
++ #
++ # This function is pretty slow, which is a shame, as the results
++ # returned are much nicer than just telling the user to try the
++ # lookup command for themselves.
++ def find_objfile_suggestions_in_thread(build_id):
++ global find_objfile_suggestions_re
++
++ if find_objfile_suggestions_re is None:
++ find_objfile_suggestions_re = re.compile("^(.*)-debuginfo-(.*) : Debug information for package (.*)$")
++
++ result = subprocess.run(['dnf', "--enablerepo=*debug*", '--nogpgcheck', '-C', 'provides',
++ build_id_to_usr_lib_filename(build_id, True)],
++ capture_output=True, timeout=30)
++
++ lines = result.stdout.decode('utf-8').splitlines()
++ ts = rpm.TransactionSet()
++
++ for l in lines:
++ m = find_objfile_suggestions_re.match(l)
++ if not m:
++ continue
++ if m.group(1) != m.group(3):
++ return
++
++ main_rpm = m.group(1) + '-' + m.group(2)
++ dbg_rpm = m.group(1) + '-debuginfo-' + m.group(2)
++
++ if not is_rpm_installed(ts, main_rpm):
++ add_rpm_suggestion(main_rpm)
++
++ if not is_rpm_installed(ts, dbg_rpm):
++ add_rpm_suggestion(dbg_rpm)
++
++ return
+
++ # Call `find_objfile_suggestions_in_thread` is a separate thread,
++ # then wait for the thread to complete. Don't touch any shared
++ # state while waiting for the thread to complete. There's no
++ # locking on any of our caches, and the worker thread will add RPM
++ # suggestions as it wants.
++ #
++ # If thre thread takes too long to complete then print a message
++ # to the user telling them what's going on.
++ def find_objfile_suggestions_slow_core(build_id):
++ global __searching_message_printed
++
++ thread = Thread(target = find_objfile_suggestions_in_thread, args = (build_id, ))
++ thread.start()
++ start = time.time_ns()
++
++ while thread.is_alive ():
++ time.sleep(0.05)
++ now = time.time_ns ()
++ if not __searching_message_printed and (now - start > 1000000000):
++ print("Searching for packages to install that could improve debugging...")
++ __searching_message_printed = True
++
++ thread.join()
++
++
++ # Given BUILD_ID, a string containing a build-id, lookup suitable
++ # RPMs that could be installed to provide a file with the required
++ # build-id.
++ #
++ # Any suitable RPMs are recorded by calling `add_rpm_suggestion`, and
++ # will be printed before the next prompt.
++ def find_objfile_suggestions_slow(build_id):
++ global __build_id_lookup_cache
++ global __suggest_rpms
++
++ # The code to lookup an RPM given only a build-id is pretty
++ # slow. Cache the results to try and reduce the UI delays.
++ if build_id in __build_id_lookup_cache:
++ rpms = __build_id_lookup_cache[build_id]
++ if rpms is not None:
++ for r in rpms:
++ add_rpm_suggestion(r)
++ return
++
++ # Make sure the cache entry exists before we do the lookup.
++ # If, for any reason, the lookup raises an exception, then
++ # having a cache entry will prevent us retrying this lookup in
++ # the future.
++ __build_id_lookup_cache[build_id] = None
++
++ # Now do the lookup. This is the slow part.
++ find_objfile_suggestions_slow_core(build_id)
++
++ # Fill in the cache, for a given build-id which RPMs were
++ # suggested.
++ rpms = []
++ for r in __suggest_rpms:
++ rpms.append(r)
++ __build_id_lookup_cache[build_id] = rpms
++
++
++ # Given BUILD_ID, a string containing a build-id, just record that we
++ # should advise the user to try installing RPMs based on this build-id.
++ def find_objfile_suggestions_fast(build_id):
++ add_build_id_suggestion(build_id)
++
++ # Given BUILD_ID, a string containing a build-id, which GDB has failed
++ # to find, possibly record some information so that we can, at the next
++ # prompt, give some RPM installation advice to the user.
++ #
++ # We have two different strategies for RPM lookup based on a build-id,
++ # one approach is that we actually lookup the RPMs and only suggest
++ # something if there is a suitable RPM. However, this is pretty slow,
++ # and users will probably find this annoying.
++ #
++ # So we also have a fast way. With this approach we just record the
++ # build-id that was missing and tell the user to try installing based on
++ # the build-id. The downside with this is that if there is no RPM for
++ # that build-id, then the user will try the command, but nothing will
++ # install.
++ def find_objfile_suggestions(build_id):
++ if rpm_suggestion_is_disabled():
++ return
++
++ if param_rpm_suggestion_build_id_mode.fast_mode():
++ find_objfile_suggestions_fast(build_id)
++ else:
++ find_objfile_suggestions_slow(build_id)
+
+ # A missing debug handler class. Just forwards the name of the
+ # objfile for which we are missing debug information to
-+ # find_suggestions.
-+ class RPMSuggestionHandler(gdb.missing_debug.MissingDebugHandler):
++ # find_debug_suggestions.
++ class RPM_MissingDebugHandler(gdb.missing_debug.MissingDebugHandler):
+ def __init__(self):
+ super().__init__("rpm-suggestions")
+
+ def __call__(self, objfile):
-+ # Traditionally the 'build-id-verbose' parameter is what
-+ # controlled all RPM suggestion. Maybe once all the RPM
-+ # suggestion is performed via Python extensions then we might
-+ # consider renaming this parameter to something else, but for
-+ # now, for backward compatibility, I've retained this name.
-+ if gdb.parameter("build-id-verbose") > 0:
-+ find_suggestions(objfile.filename)
-+ return False
-+ return None
++ find_debug_suggestions(objfile.filename)
++ return False
++
++ # A missing objfile handler class. Just forwards the build-id of
++ # the objfile that is missing to find_objfile_suggestions.
++ class RPM_MissingObjfileHandler(gdb.missing_objfile.MissingObjfileHandler):
++ def __init__(self):
++ super().__init__("rpm-suggestions")
+
++ def __call__(self, pspace, build_id, filename):
++ find_objfile_suggestions(build_id)
++ return False
++
++ # Take a non-empty list of RPM names and print a command line a
++ # user could run to install these RPMs.
++ def print_rpm_suggestions(rpm_name_list):
++ print("Missing rpms, try: dnf --enablerepo='*debug*' install " + ' '.join(rpm_name_list))
++
++ # Take a non-empty list of build-id strings and print a series of
++ # lines that a user could run to instll the RPMs that provide
++ # files with this build-id.
++ #
++ # The printed commands will also install the corresponding debug
++ # packages for the executable with the given build-id.
++ def print_build_id_suggestions(build_id_list):
++ for build_id in build_id_list:
++ print("Missing file(s), try: dnf --enablerepo='*debug*' install "
++ + build_id_to_usr_lib_filename(build_id, False)
++ + ' '
++ + build_id_to_usr_lib_filename(build_id, True, ".debug"))
+
+ # Called before GDB displays its prompt. If the global __SUGGEST_RPMS
-+ # dictionary is not empty, then this hook prints treats the keys of
-+ # this dictionary as strings which are the names of RPMs. This hook
-+ # formats each RPM name into a suggested debuginfo-install command and
-+ # suggests this to the user.
++ # dictionary is not empty, then this hook prints the keys of this
++ # dictionary as strings which are the names of RPMs. This hook formats
++ # each RPM name into a suggested 'dnf install' command and suggests this
++ # to the user.
++ #
++ # Additionally, if the global __SUGGEST_BUILD_IDS dictionary is not
++ # empty, then this hook uses the keys of this dictionary as build-ids
++ # that were found to be missing, and formats these into some file based
++ # 'dnf install' suggestions to the user.
+ def before_prompt():
+ global __suggest_rpms
++ global __suggest_build_ids
++ global __searching_message_printed
++
++ # We allow the searching message to be printed just once
++ # between prompts.
++ __searching_message_printed = False
+
+ if len(__suggest_rpms) > 0:
-+ for p in __suggest_rpms.keys():
-+ print("Missing debuginfo, try: dnf debuginfo-install " + p)
++ print_rpm_suggestions(__suggest_rpms.keys())
+ __suggest_rpms = {}
+
++ if len(__suggest_build_ids) > 0:
++ print_build_id_suggestions(__suggest_build_ids.keys())
++ __suggest_build_ids = {}
+
+ # Called when the executable within a progrm space is changed. Clear
+ # the lists of RPM suggestions. We only clear the previous suggestion
@@ -149,18 +559,162 @@ new file mode 100644
+ def executable_changed_handler(event):
+ global __missing_rpms
+ global __suggest_rpms
++ global __suggest_build_ids
++ global __missing_build_ids
+
+ if not event.reload:
+ __missing_rpms = {}
+ __suggest_rpms = {}
++ __missing_build_ids = {}
++ __suggest_build_ids = {}
+
+
+ # Attach to the required GDB events.
+ gdb.events.executable_changed.connect(executable_changed_handler)
+ gdb.events.before_prompt.connect(before_prompt)
+
-+ # Register the missing debug handler with GDB.
-+ gdb.missing_debug.register_handler(None, RPMSuggestionHandler())
++ # Register the missing debug and missing objfile handlers with GDB.
++ gdb.missing_debug.register_handler(None, RPM_MissingDebugHandler())
++ gdb.missing_objfile.register_handler(None, RPM_MissingObjfileHandler())
++
++ # Implement the core of 'info rpm-suggestions'. Reprint all rpm
++ # suggestions.
++ def info_rpm_suggestions():
++ global __missing_rpms
++ global __missing_build_ids
++
++ if len(__missing_rpms) == 0 and len(__missing_build_ids) == 0:
++ print("No RPM suggestions have been made so far.")
++ return
++
++ if len(__missing_rpms) > 0:
++ print_rpm_suggestions(__missing_rpms.keys())
++ if len(__missing_build_ids) > 0:
++ print_build_id_suggestions(__missing_build_ids.keys())
++
++####################################################################
++# The following code is outside the 'else' block of the attempt to #
++# load the 'rpm' module. Nothing after this point depends on the #
++# 'rpm' module. #
++####################################################################
++
++# The 'set rpm-suggestion' prefix command.
++class rpm_suggestion_set_prefix(gdb.Command):
++ """Prefix command for 'set' rpm-suggestion related commands."""
++
++ def __init__(self):
++ super().__init__("set rpm-suggestion", gdb.COMMAND_NONE,
++ gdb.COMPLETE_NONE, True)
++
++# The 'show rpm-suggestion' prefix command.
++class rpm_suggestion_show_prefix(gdb.Command):
++ """Prefix command for 'show' rpm-suggestion related commands."""
++
++ def __init__(self):
++ super().__init__("show rpm-suggestion", gdb.COMMAND_NONE,
++ gdb.COMPLETE_NONE, True)
++
++# The 'set/show rpm-suggestion enabled' command.
++class rpm_suggestion_enabled(gdb.Parameter):
++ """
++ When 'on' GDB will search for RPMS that might provide additional
++ debug information, or provide missing executables or shared
++ libraries (when opening a core file), and will make suggestions
++ about what should be installed.
++
++ When 'off' GDB will not make these suggestions.
++ """
++
++ set_doc = "Set whether to perform rpm-suggestion."
++ show_doc = "Show whether rpm-suggestion is enabled."
++
++ def __init__(self):
++ super().__init__("rpm-suggestion enabled", gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)
++ self.value = True
++
++# The 'set/show rpm-suggestion enabled' command.
++class rpm_suggestion_build_id_mode(gdb.Parameter):
++ """
++ When set to 'fast' (the default), GDB doesn't try to map a build-id to
++ an actual RPM, instead, GDB just suggests a command based on the
++ build-id which might install some RPMs, if there are any RPMs that
++ supply that build-id. However, it is equally possible that there are no
++ suitable RPMs, and nothing will install. This approach has almost zero
++ overhead.
++
++ When set to 'slow', GDB first does the build-id to RPM check itself, and
++ only if something is found are RPMs installation commands suggested.
++ The suggested command will include the name of the RPM to install. This
++ approach is considerably slower as querying the RPM database for the RPM
++ that supplies a specific file is slow.
++ """
++
++ set_doc = "Set how build-id based rpm suggestions should be performed."
++ show_doc = "Show how build-id based rpm suggestions shoud be performed."
++
++ def __init__(self):
++ super().__init__("rpm-suggestion build-id-mode",
++ gdb.COMMAND_NONE, gdb.PARAM_ENUM, ["fast", "slow"])
++ self.value = "fast"
++
++ def fast_mode(self):
++ return self.value == "fast"
++
++# The 'info rpm-suggestions' command.
++class rpm_suggestion_info(gdb.Command):
++ """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)
++
++ def invoke(self, args, from_tty):
++ if args != "":
++ raise gdb.GdbError("unexpected arguments: %s" % args)
++
++ info_rpm_suggestions ()
++
++
++# Create the 'set/show rpm-suggestion' commands.
++rpm_suggestion_set_prefix()
++rpm_suggestion_show_prefix()
++param_rpm_suggestion_enabled = rpm_suggestion_enabled()
++param_rpm_suggestion_build_id_mode = rpm_suggestion_build_id_mode()
++
++# Create the 'info rpm-suggestions' commands.
++rpm_suggestion_info()
++
++# Create the 'build-id-verbose' parameter. This is implemented as an
++# integer for backward compatibility reasons. It would be nice to
++# convert this to a boolean, but it's not clear if that will break
++# existing users uses of this flag.
++
++class build_id_verbose(gdb.Parameter):
++ """
++ Level 0 disable printing the missing debug filesname,
++ Level 1 (default) enables printing the missing debug filenames,
++ """
++
++ set_doc = "Set debugging level of the build-id locator."
++ show_doc = "Show debugging level of the build-id locator."
++
++ def __init__(self):
++ super().__init__("build-id-verbose", gdb.COMMAND_NONE, gdb.PARAM_ZINTEGER)
++ self.value = 1
++
++param_build_id_verbose = build_id_verbose()
+diff --git a/gdb/testsuite/gdb.base/gdbinit-history.exp b/gdb/testsuite/gdb.base/gdbinit-history.exp
+--- a/gdb/testsuite/gdb.base/gdbinit-history.exp
++++ b/gdb/testsuite/gdb.base/gdbinit-history.exp
+@@ -179,7 +179,8 @@ proc test_empty_history_filename { } {
+ global env
+ global gdb_prompt
+
+- set common_history [list "set height 0" "set width 0"]
++ set common_history [list "set height 0" "set width 0" \
++ "set build-id-verbose 0"]
+
+ set test_dir [standard_output_file history_test]
+ remote_exec host "mkdir -p $test_dir"
diff --git a/gdb/testsuite/gdb.python/py-missing-debug.py b/gdb/testsuite/gdb.python/py-missing-debug.py
--- a/gdb/testsuite/gdb.python/py-missing-debug.py
+++ b/gdb/testsuite/gdb.python/py-missing-debug.py
@@ -178,3 +732,75 @@ diff --git a/gdb/testsuite/gdb.python/py-missing-debug.py b/gdb/testsuite/gdb.py
# A global log that is filled in by instances of the LOG_HANDLER class
# when they are called.
handler_call_log = []
+@@ -118,4 +125,7 @@ def register(name, locus=None):
+ rhandler = exception_handler()
+ handler_obj = handler()
+
++# Discard the rpm-suggestion handler.
++gdb.missing_file_handlers = []
++
+ print("Success")
+diff --git a/gdb/testsuite/gdb.python/py-missing-objfile.py b/gdb/testsuite/gdb.python/py-missing-objfile.py
+--- a/gdb/testsuite/gdb.python/py-missing-objfile.py
++++ b/gdb/testsuite/gdb.python/py-missing-objfile.py
+@@ -164,4 +164,7 @@ def register(name, locus=None):
+ rhandler = exception_handler()
+ handler_obj = handler()
+
++# Discard the rpm-suggestion handler.
++gdb.missing_file_handlers = []
++
+ print("Success")
+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
+@@ -238,7 +238,8 @@ if ![info exists INTERNAL_GDBFLAGS] {
+ "-nx" \
+ "-q" \
+ {-iex "set height 0"} \
+- {-iex "set width 0"}]]
++ {-iex "set width 0"} \
++ {-iex "set build-id-verbose 0"}]]
+
+ # If DEBUGINFOD_URLS is set, gdb will try to download sources and
+ # debug info for f.i. system libraries. Prevent this.
+@@ -2493,6 +2494,18 @@ proc default_gdb_start { } {
+ }
+ }
+
++ # Turn off the missing debug info messages as the testsuite does
++ # not expect them.
++ send_gdb "set build-id-verbose 0\n"
++ gdb_expect 10 {
++ -re "$gdb_prompt $" {
++ verbose "Disabled the missing debug info messages." 2
++ }
++ timeout {
++ warning "Could not disable the missing debug info messages."
++ }
++ }
++
+ gdb_debug_init
+ return 0
+ }
+diff --git a/gdb/testsuite/lib/mi-support.exp b/gdb/testsuite/lib/mi-support.exp
+--- a/gdb/testsuite/lib/mi-support.exp
++++ b/gdb/testsuite/lib/mi-support.exp
+@@ -321,6 +321,17 @@ proc default_mi_gdb_start { { flags {} } } {
+ warning "Couldn't set the width to 0."
+ }
+ }
++ # Turn off the missing debug info messages as the testsuite does
++ # not expect them.
++ send_gdb "190-gdb-set build-id-verbose 0\n"
++ gdb_expect 10 {
++ -re ".*190-gdb-set build-id-verbose 0\r\n190\\\^done\r\n$mi_gdb_prompt$" {
++ verbose "Disabled the missing debug info messages." 2
++ }
++ timeout {
++ warning "Could not disable the missing debug info messages."
++ }
++ }
+
+ if { $separate_inferior_pty } {
+ mi_create_inferior_pty
diff --git a/gdb-backport-buildid-related-changes.patch b/gdb-backport-buildid-related-changes.patch
new file mode 100644
index 0000000..4dd2d8b
--- /dev/null
+++ b/gdb-backport-buildid-related-changes.patch
@@ -0,0 +1,6753 @@
+From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
+From: Andrew Burgess <aburgess@redhat.com>
+Date: Wed, 31 Jul 2024 15:58:20 +0100
+Subject: gdb-backport-buildid-related-changes.patch
+
+;; Backport patches related to build-id usage when opening a core
+;; file, and also improving general build-id based lookup. These will
+;; all drop out when rebasing to GDB 16.
+
+backport the following patches related to build-id usage
+
+This one patch backports the following upstream commits:
+
+ * 195795b0fd4 gdb: fix a target: prefix issue in find_separate_debug_file
+ * ea4c968ce54 gdb: avoid '//' in filenames when searching for debuginfo
+ * 22836ca8859 gdb: check for multiple matching build-id files
+ * 6d45af96ea5 gdbserver: add gdbserver support for vFile::stat packet
+ * 3055e3d2f13 gdb: add GDB side target_ops::fileio_stat implementation
+ * 08a115cc1c4 gdb: add target_fileio_stat, but no implementations yet
+ * c7e38ee47c9 gdb: avoid duplicate search in build_id_to_bfd_suffix
+ * 10ac7e80c01 gdb: remove find_and_open_solib so_list method
+ * 3e35b4deae2 gdb/testsuite: tests for debug lookup within the sysroot
+ * 6dfd07222c0 gdb: convert separate-debug-file to new(ish) debug scheme
+ * 5cabc8098e6 gdb/python: implement Python find_exec_by_build_id hook
+ * ef1a41f20b7 gdb: add extension hook ext_lang_find_objfile_from_buildid
+ * 629bcc68d79 gdb: rename ext_lang_missing_debuginfo_result
+ * 73d7312ff61 gdb: use mapped file information to improve debuginfod text
+ * 5ddd0d7eefb gdb/python: avoid depending on the curses library
+ * 8358d39b4f0 gdb: unify build-id to objfile lookup code
+ * fa826a4bbe9 gdb: improve shared library build-id check for core-files
+ * a47a679c769 gdb/corefile: improve file backed mapping handling
+ * 522875f679b gdb/corefile: don't pretend unavailable sections are readable
+ * 97832471899 gdb/build-id: protect against weirdly short build-ids
+ * 9543c37620c gdb/testsuite: make gdb_gnu_strip_debug consistent
+ * fc240bb143c gdb/fileio: fix errno for packets where an attachment is expected
+ * df0445b370f gdb/testsuite: fix typo 'unsupport' to 'unsupported'
+
+I've merged these into a single (large) patch as these all merged
+without any changes (i.e. these represent exactly the upstream code),
+and all of these will be included in GDB 16, and so should all drop
+out together when we rebase to GDB 16 (the next rebase).
+
+All of these patches relate to either build-id usage when opening a
+core file, or improving GDB's build-id based debug lookup.
+
+diff --git a/gdb/NEWS b/gdb/NEWS
+--- a/gdb/NEWS
++++ b/gdb/NEWS
+@@ -1,6 +1,36 @@
+ What has changed in GDB?
+ (Organized release by release)
+
++*** Change since GDB 15
++
++* Python API
++
++ ** New module gdb.missing_objfile that facilitates dealing with
++ missing objfiles when opening a core-file.
++
++ ** New function gdb.missing_objfile.register_handler that can
++ register an instance of a sub-class of
++ gdb.missing_debug.MissingObjfileHandler as a handler for missing
++ objfiles.
++
++ ** New class gdb.missing_objfile.MissingObjfileHandler which can be
++ sub-classed to create handlers for missing objfiles.
++
++* New commands
++
++info missing-objfile-handlers
++ List all the registered missing-objfile handlers.
++
++enable missing-objfile-handler LOCUS HANDLER
++disable missing-objfile-handler LOCUS HANDLER
++ Enable or disable a missing-objfile handler with a name matching the
++ regular expression HANDLER, in LOCUS.
++
++ LOCUS can be 'global' to operate on global missing-objfile handler,
++ 'progspace' to operate on handlers within the current program space,
++ or can be a regular expression which is matched against the filename
++ of the primary executable in each program space.
++
+ *** Changes in GDB 15
+
+ * The MPX commands "show/set mpx bound" have been deprecated, as Intel
+@@ -212,6 +242,11 @@ qIsAddressTagged
+ file is about, this new packet provides a more generic way to perform such
+ a check.
+
++vFile:stat
++ Return information about files on the remote system. Like
++ vFile:fstat but takes a filename rather than an open file
++ descriptor.
++
+ *** Changes in GDB 14
+
+ * GDB now supports the AArch64 Scalable Matrix Extension 2 (SME2), which
+diff --git a/gdb/build-id.c b/gdb/build-id.c
+--- a/gdb/build-id.c
++++ b/gdb/build-id.c
+@@ -26,6 +26,9 @@
+ #include "filenames.h"
+ #include "gdbcore.h"
+ #include "cli/cli-style.h"
++#include "gdbsupport/scoped_fd.h"
++#include "debuginfod-support.h"
++#include "extension.h"
+
+ /* See build-id.h. */
+
+@@ -73,73 +76,177 @@ build_id_verify (bfd *abfd, size_t check_len, const bfd_byte *check)
+ return retval;
+ }
+
+-/* Helper for build_id_to_debug_bfd. LINK is a path to a potential
+- build-id-based separate debug file, potentially a symlink to the real file.
+- If the file exists and matches BUILD_ID, return a BFD reference to it. */
++/* Helper for build_id_to_debug_bfd. ORIGINAL_LINK with SUFFIX appended is
++ a path to a potential build-id-based separate debug file, potentially a
++ symlink to the real file. If the file exists and matches BUILD_ID,
++ return a BFD reference to it. */
+
+ static gdb_bfd_ref_ptr
+-build_id_to_debug_bfd_1 (const std::string &link, size_t build_id_len,
+- const bfd_byte *build_id)
++build_id_to_debug_bfd_1 (const std::string &original_link,
++ size_t build_id_len, const bfd_byte *build_id,
++ const char *suffix)
+ {
+- if (separate_debug_file_debug)
++ tribool supports_target_stat = TRIBOOL_UNKNOWN;
++
++ /* Drop the 'target:' prefix if the target filesystem is local. */
++ std::string_view original_link_view (original_link);
++ if (is_target_filename (original_link) && target_filesystem_is_local ())
++ original_link_view
++ = original_link_view.substr (strlen (TARGET_SYSROOT_PREFIX));
++
++ /* The upper bound of '10' here is completely arbitrary. The loop should
++ terminate via 'break' when either (a) a readable symlink is found, or
++ (b) a non-existing entry is found.
++
++ However, for remote targets, we rely on the remote returning sane
++ error codes. If a remote sends back the wrong error code then it
++ might trick GDB into thinking that the symlink exists, but points to a
++ missing file, in which case GDB will try the next seqno. We don't
++ want a broken remote to cause GDB to spin here forever, hence a fixed
++ upper bound. */
++
++ for (unsigned seqno = 0; seqno < 10; seqno++)
+ {
+- gdb_printf (gdb_stdlog, _(" Trying %s..."), link.c_str ());
+- gdb_flush (gdb_stdlog);
+- }
++ std::string link (original_link_view);
+
+- /* lrealpath() is expensive even for the usually non-existent files. */
+- gdb::unique_xmalloc_ptr<char> filename_holder;
+- const char *filename = nullptr;
+- if (is_target_filename (link))
+- filename = link.c_str ();
+- else if (access (link.c_str (), F_OK) == 0)
+- {
+- filename_holder.reset (lrealpath (link.c_str ()));
+- filename = filename_holder.get ();
+- }
++ if (seqno > 0)
++ string_appendf (link, ".%u", seqno);
+
+- if (filename == NULL)
+- {
+- if (separate_debug_file_debug)
+- gdb_printf (gdb_stdlog,
+- _(" no, unable to compute real path\n"));
++ link += suffix;
+
+- return {};
+- }
++ separate_debug_file_debug_printf ("Trying %s...", link.c_str ());
+
+- /* We expect to be silent on the non-existing files. */
+- gdb_bfd_ref_ptr debug_bfd = gdb_bfd_open (filename, gnutarget);
++ gdb::unique_xmalloc_ptr<char> filename_holder;
++ const char *filename = nullptr;
++ if (is_target_filename (link))
++ {
++ gdb_assert (link.length () >= strlen (TARGET_SYSROOT_PREFIX));
++ const char *link_on_target
++ = link.c_str () + strlen (TARGET_SYSROOT_PREFIX);
++
++ fileio_error target_errno;
++ if (supports_target_stat != TRIBOOL_FALSE)
++ {
++ struct stat sb;
++ int res = target_fileio_stat (nullptr, link_on_target, &sb,
++ &target_errno);
++
++ if (res != 0 && target_errno != FILEIO_ENOSYS)
++ {
++ separate_debug_file_debug_printf ("path doesn't exist");
++ break;
++ }
++ else if (res != 0 && target_errno == FILEIO_ENOSYS)
++ supports_target_stat = TRIBOOL_FALSE;
++ else
++ {
++ supports_target_stat = TRIBOOL_TRUE;
++ filename = link.c_str ();
++ }
++ }
++
++ if (supports_target_stat == TRIBOOL_FALSE)
++ {
++ gdb_assert (filename == nullptr);
++
++ /* Connecting to a target that doesn't support 'stat'. Try
++ 'readlink' as an alternative. This isn't ideal, but is
++ maybe better than nothing. Returns EINVAL if the path
++ isn't a symbolic link, which hints that the path is
++ available -- there are other errors e.g. ENOENT for when
++ the path doesn't exist, but we just assume that anything
++ other than EINVAL indicates the path doesn't exist. */
++ std::optional<std::string> link_target
++ = target_fileio_readlink (nullptr, link_on_target,
++ &target_errno);
++ if (link_target.has_value ()
++ || target_errno == FILEIO_EINVAL)
++ filename = link.c_str ();
++ else
++ {
++ separate_debug_file_debug_printf ("path doesn't exist");
++ break;
++ }
++ }
++ }
++ else
++ {
++ struct stat buf;
++
++ /* The `access' call below automatically dereferences LINK, but
++ we want to stop incrementing SEQNO once we find a symlink
++ that doesn't exist. */
++ if (lstat (link.c_str (), &buf) != 0)
++ {
++ separate_debug_file_debug_printf ("path doesn't exist");
++ break;
++ }
++
++ /* Can LINK be accessed, or if LINK is a symlink, can the file
++ pointed too be accessed? Do this as lrealpath() is
++ expensive, even for the usually non-existent files. */
++ if (access (link.c_str (), F_OK) == 0)
++ {
++ filename_holder.reset (lrealpath (link.c_str ()));
++ filename = filename_holder.get ();
++ }
++ }
+
+- if (debug_bfd == NULL)
+- {
+- if (separate_debug_file_debug)
+- gdb_printf (gdb_stdlog, _(" no, unable to open.\n"));
++ if (filename == nullptr)
++ {
++ separate_debug_file_debug_printf ("unable to compute real path");
++ continue;
++ }
+
+- return {};
+- }
++ /* We expect to be silent on the non-existing files. */
++ gdb_bfd_ref_ptr debug_bfd = gdb_bfd_open (filename, gnutarget);
+
+- if (!build_id_verify (debug_bfd.get(), build_id_len, build_id))
+- {
+- if (separate_debug_file_debug)
+- gdb_printf (gdb_stdlog, _(" no, build-id does not match.\n"));
++ if (debug_bfd == NULL)
++ {
++ separate_debug_file_debug_printf ("unable to open `%s`", filename);
++ continue;
++ }
+
+- return {};
+- }
++ if (!build_id_verify (debug_bfd.get(), build_id_len, build_id))
++ {
++ separate_debug_file_debug_printf ("build-id does not match");
++ continue;
++ }
+
+- if (separate_debug_file_debug)
+- gdb_printf (gdb_stdlog, _(" yes!\n"));
++ separate_debug_file_debug_printf ("found a match");
++ return debug_bfd;
++ }
+
+- return debug_bfd;
++ separate_debug_file_debug_printf ("no suitable file found");
++ return {};
+ }
+
+ /* Common code for finding BFDs of a given build-id. This function
+ works with both debuginfo files (SUFFIX == ".debug") and executable
+- files (SUFFIX == ""). */
++ files (SUFFIX == "").
++
++ The build-id will be split into a single byte sub-directory, followed by
++ the remaining build-id bytes as the filename, i.e. we use the lookup
++ format: `.build-id/xx/yy....zz`. As a consequence, if BUILD_ID_LEN is
++ less than 2 (bytes), no results will be found as there are not enough
++ bytes to form the `yy....zz` part of the lookup filename. */
+
+ static gdb_bfd_ref_ptr
+ build_id_to_bfd_suffix (size_t build_id_len, const bfd_byte *build_id,
+ const char *suffix)
+ {
++ SEPARATE_DEBUG_FILE_SCOPED_DEBUG_ENTER_EXIT;
++
++ if (build_id_len < 2)
++ {
++ /* Zero length build-ids are ignored by bfd. */
++ gdb_assert (build_id_len > 0);
++ separate_debug_file_debug_printf
++ ("Ignoring short build-id `%s' for build-id based lookup",
++ bin2hex (build_id, build_id_len).c_str ());
++ return {};
++ }
++
+ /* Keep backward compatibility so that DEBUG_FILE_DIRECTORY being "" will
+ cause "/.build-id/..." lookups. */
+
+@@ -158,30 +265,33 @@ build_id_to_bfd_suffix (size_t build_id_len, const bfd_byte *build_id,
+ std::string link = debugdir.get ();
+ link += "/.build-id/";
+
+- if (size > 0)
+- {
+- size--;
+- string_appendf (link, "%02x/", (unsigned) *data++);
+- }
++ gdb_assert (size > 1);
++ size--;
++ string_appendf (link, "%02x/", (unsigned) *data++);
+
+ while (size-- > 0)
+ string_appendf (link, "%02x", (unsigned) *data++);
+
+- link += suffix;
+-
+ gdb_bfd_ref_ptr debug_bfd
+- = build_id_to_debug_bfd_1 (link, build_id_len, build_id);
++ = build_id_to_debug_bfd_1 (link, build_id_len, build_id, suffix);
+ if (debug_bfd != NULL)
+ return debug_bfd;
+
+ /* Try to look under the sysroot as well. If the sysroot is
+ "/the/sysroot", it will give
+- "/the/sysroot/usr/lib/debug/.build-id/ab/cdef.debug". */
++ "/the/sysroot/usr/lib/debug/.build-id/ab/cdef.debug".
++
++ If the sysroot is 'target:' and the target filesystem is local to
++ GDB then 'target:/path/to/check' becomes '/path/to/check' which
++ we just checked above. */
+
+- if (!gdb_sysroot.empty ())
++ if (!gdb_sysroot.empty ()
++ && (gdb_sysroot != TARGET_SYSROOT_PREFIX
++ || !target_filesystem_is_local ()))
+ {
+ link = gdb_sysroot + link;
+- debug_bfd = build_id_to_debug_bfd_1 (link, build_id_len, build_id);
++ debug_bfd = build_id_to_debug_bfd_1 (link, build_id_len, build_id,
++ suffix);
+ if (debug_bfd != NULL)
+ return debug_bfd;
+ }
+@@ -198,9 +308,11 @@ build_id_to_debug_bfd (size_t build_id_len, const bfd_byte *build_id)
+ return build_id_to_bfd_suffix (build_id_len, build_id, ".debug");
+ }
+
+-/* See build-id.h. */
++/* Find and open a BFD for an executable file given a build-id. If no BFD
++ can be found, return NULL. The returned reference to the BFD must be
++ released by the caller. */
+
+-gdb_bfd_ref_ptr
++static gdb_bfd_ref_ptr
+ build_id_to_exec_bfd (size_t build_id_len, const bfd_byte *build_id)
+ {
+ return build_id_to_bfd_suffix (build_id_len, build_id, "");
+@@ -217,10 +329,9 @@ find_separate_debug_file_by_buildid (struct objfile *objfile,
+ build_id = build_id_bfd_get (objfile->obfd.get ());
+ if (build_id != NULL)
+ {
+- if (separate_debug_file_debug)
+- gdb_printf (gdb_stdlog,
+- _("\nLooking for separate debug info (build-id) for "
+- "%s\n"), objfile_name (objfile));
++ SEPARATE_DEBUG_FILE_SCOPED_DEBUG_START_END
++ ("looking for separate debug info (build-id) for %s",
++ objfile_name (objfile));
+
+ gdb_bfd_ref_ptr abfd (build_id_to_debug_bfd (build_id->size,
+ build_id->data));
+@@ -229,9 +340,9 @@ find_separate_debug_file_by_buildid (struct objfile *objfile,
+ && filename_cmp (bfd_get_filename (abfd.get ()),
+ objfile_name (objfile)) == 0)
+ {
+- if (separate_debug_file_debug)
+- gdb_printf (gdb_stdlog, "\"%s\": separate debug info file has no "
+- "debug info", bfd_get_filename (abfd.get ()));
++ separate_debug_file_debug_printf
++ ("\"%s\": separate debug info file has no debug info",
++ bfd_get_filename (abfd.get ()));
+ warnings->warn (_("\"%ps\": separate debug info file has no "
+ "debug info"),
+ styled_string (file_name_style.style (),
+@@ -243,3 +354,78 @@ find_separate_debug_file_by_buildid (struct objfile *objfile,
+
+ return std::string ();
+ }
++
++/* See build-id.h. */
++
++gdb_bfd_ref_ptr
++find_objfile_by_build_id (program_space *pspace,
++ const bfd_build_id *build_id,
++ const char *expected_filename)
++{
++ gdb_bfd_ref_ptr abfd;
++
++ for (unsigned attempt = 0, max_attempts = 1;
++ attempt < max_attempts && abfd == nullptr;
++ ++attempt)
++ {
++ /* Try to find the executable (or shared object) by looking for a
++ (sym)link on disk from the build-id to the object file. */
++ abfd = build_id_to_exec_bfd (build_id->size, build_id->data);
++
++ if (abfd != nullptr || attempt > 0)
++ break;
++
++ /* Attempt to query debuginfod for the executable. This will only
++ get run during the first attempt, if an extension language hook
++ (see below) asked for a second attempt then we will have already
++ broken out of the loop above. */
++ gdb::unique_xmalloc_ptr<char> path;
++ scoped_fd fd = debuginfod_exec_query (build_id->data, build_id->size,
++ expected_filename, &path);
++ if (fd.get () >= 0)
++ {
++ abfd = gdb_bfd_open (path.get (), gnutarget);
++
++ if (abfd == nullptr)
++ warning (_("\"%ps\" from debuginfod cannot be opened as bfd: %s"),
++ styled_string (file_name_style.style (), path.get ()),
++ gdb_bfd_errmsg (bfd_get_error (), nullptr).c_str ());
++ else if (!build_id_verify (abfd.get (), build_id->size,
++ build_id->data))
++ abfd = nullptr;
++ }
++
++ if (abfd != nullptr)
++ break;
++
++ ext_lang_missing_file_result ext_result
++ = ext_lang_find_objfile_from_buildid (pspace, build_id,
++ expected_filename);
++ if (!ext_result.filename ().empty ())
++ {
++ /* The extension identified the file for us. */
++ abfd = gdb_bfd_open (ext_result.filename ().c_str (), gnutarget);
++ if (abfd == nullptr)
++ {
++ warning (_("\"%ps\" from extension cannot be opened as bfd: %s"),
++ styled_string (file_name_style.style (),
++ ext_result.filename ().c_str ()),
++ gdb_bfd_errmsg (bfd_get_error (), nullptr).c_str ());
++ break;
++ }
++
++ /* If the extension gave us a path to a file then we always
++ assume that it is the correct file, we do no additional check
++ of its build-id. */
++ }
++ else if (ext_result.try_again ())
++ {
++ /* The extension might have installed the file in the expected
++ location, we should try again. */
++ max_attempts = 2;
++ continue;
++ }
++ }
++
++ return abfd;
++}
+diff --git a/gdb/build-id.h b/gdb/build-id.h
+--- a/gdb/build-id.h
++++ b/gdb/build-id.h
+@@ -40,13 +40,6 @@ extern int build_id_verify (bfd *abfd,
+ extern gdb_bfd_ref_ptr build_id_to_debug_bfd (size_t build_id_len,
+ const bfd_byte *build_id);
+
+-/* Find and open a BFD for an executable file given a build-id. If no BFD
+- can be found, return NULL. The returned reference to the BFD must be
+- released by the caller. */
+-
+-extern gdb_bfd_ref_ptr build_id_to_exec_bfd (size_t build_id_len,
+- const bfd_byte *build_id);
+-
+ /* Find the separate debug file for OBJFILE, by using the build-id
+ associated with OBJFILE's BFD. If successful, returns the file name for the
+ separate debug file, otherwise, return an empty string.
+@@ -60,6 +53,22 @@ extern gdb_bfd_ref_ptr build_id_to_exec_bfd (size_t build_id_len,
+ extern std::string find_separate_debug_file_by_buildid
+ (struct objfile *objfile, deferred_warnings *warnings);
+
++/* Find an objfile (executable or shared library) that matches BUILD_ID.
++ This is done by first checking in the debug-file-directory for a
++ suitable .build-id/ sub-directory, and looking for a file with the
++ required build-id (usually a symbolic link or hard link to the actual
++ file).
++
++ If that doesn't find us a file then we call to debuginfod to see if it
++ can provide the required file.
++
++ EXPECTED_FILENAME is used in output messages from debuginfod, this
++ should be the file we were looking for but couldn't find. */
++
++extern gdb_bfd_ref_ptr find_objfile_by_build_id
++ (struct program_space *pspace, const bfd_build_id *build_id,
++ const char *expected_filename);
++
+ /* Return an hex-string representation of BUILD_ID. */
+
+ static inline std::string
+diff --git a/gdb/corelow.c b/gdb/corelow.c
+--- a/gdb/corelow.c
++++ b/gdb/corelow.c
+@@ -47,17 +47,130 @@
+ #include "gdbsupport/pathstuff.h"
+ #include "gdbsupport/scoped_fd.h"
+ #include "gdbsupport/x86-xstate.h"
+-#include "debuginfod-support.h"
+ #include <unordered_map>
+ #include <unordered_set>
+ #include "cli/cli-cmds.h"
+ #include "xml-tdesc.h"
+ #include "memtag.h"
++#include "cli/cli-style.h"
+
+ #ifndef O_LARGEFILE
+ #define O_LARGEFILE 0
+ #endif
+
++/* A mem_range and the build-id associated with the file mapped into the
++ given range. */
++
++struct mem_range_and_build_id
++{
++ mem_range_and_build_id (mem_range &&r, const bfd_build_id *id)
++ : range (r),
++ build_id (id)
++ { /* Nothing. */ }
++
++ /* A range of memory addresses. */
++ mem_range range;
++
++ /* The build-id of the file mapped into RANGE. */
++ const bfd_build_id *build_id;
++};
++
++/* An instance of this class is created within the core_target and is used
++ to hold all the information that relating to mapped files, their address
++ ranges, and their corresponding build-ids. */
++
++struct mapped_file_info
++{
++ /* See comment on function definition. */
++
++ void add (const char *soname, const char *expected_filename,
++ const char *actual_filename, std::vector<mem_range> &&ranges,
++ const bfd_build_id *build_id);
++
++ /* See comment on function definition. */
++
++ std::optional <core_target_mapped_file_info>
++ lookup (const char *filename, const std::optional<CORE_ADDR> &addr);
++
++private:
++
++ /* Helper for ::lookup. BUILD_ID is a build-id that was found in
++ one of the data structures within this class. Lookup the
++ corresponding filename in m_build_id_to_filename_map and return a pair
++ containing the build-id and filename.
++
++ If no corresponding filename is found in m_build_id_to_filename_map
++ then the returned pair contains BUILD_ID and an empty string.
++
++ If BUILD_ID is nullptr then the returned pair contains nullptr and an
++ empty string. */
++
++ struct core_target_mapped_file_info
++ make_result (const bfd_build_id *build_id)
++ {
++ if (build_id != nullptr)
++ {
++ auto it = m_build_id_to_filename_map.find (build_id);
++ if (it != m_build_id_to_filename_map.end ())
++ return { build_id, it->second };
++ }
++
++ return { build_id, {} };
++ }
++
++ /* A type that maps a string to a build-id. */
++ using string_to_build_id_map
++ = std::unordered_map<std::string, const bfd_build_id *>;
++
++ /* A type that maps a build-id to a string. */
++ using build_id_to_string_map
++ = std::unordered_map<const bfd_build_id *, std::string>;
++
++ /* When loading a core file, the build-ids are extracted based on the
++ file backed mappings. This map associates the name of a file that was
++ mapped into the core file with the corresponding build-id. The
++ build-id pointers in this map will never be nullptr as we only record
++ files if they have a build-id. */
++
++ string_to_build_id_map m_filename_to_build_id_map;
++
++ /* Map a build-id pointer back to the name of the file that was mapped
++ into the inferior's address space. If we lookup a matching build-id
++ using either a soname or an address then this map allows us to also
++ provide a full path to a file with a matching build-id. */
++
++ build_id_to_string_map m_build_id_to_filename_map;
++
++ /* If the file that was mapped into the core file was a shared library
++ then it might have a DT_SONAME tag in its .dynamic section, this tag
++ contains the name of a shared object. When opening a shared library,
++ if it's basename appears in this map then we can use the corresponding
++ build-id.
++
++ In the rare case that two different files have the same DT_SONAME
++ value then the build-id pointer in this map will be nullptr, this
++ indicates that it's not possible to find a build-id based on the given
++ DT_SONAME value. */
++
++ string_to_build_id_map m_soname_to_build_id_map;
++
++ /* This vector maps memory ranges onto an associated build-id. The
++ ranges are those of the files mapped into the core file.
++
++ Entries in this vector must not overlap, and are sorted be increasing
++ memory address. Within each entry the build-id pointer will not be
++ nullptr.
++
++ While building this vector the entries are not sorted, they are
++ sorted once after the table has finished being built. */
++
++ std::vector<mem_range_and_build_id> m_address_to_build_id_list;
++
++ /* False if address_to_build_id_list is unsorted, otherwise true. */
++
++ bool m_address_to_build_id_list_sorted = false;
++};
++
+ /* The core file target. */
+
+ static const target_info core_target_info = {
+@@ -134,6 +247,34 @@ class core_target final : public process_stratum_target
+ /* See definition. */
+ void info_proc_mappings (struct gdbarch *gdbarch);
+
++ std::optional <core_target_mapped_file_info>
++ lookup_mapped_file_info (const char *filename,
++ const std::optional<CORE_ADDR> &addr)
++ {
++ return m_mapped_file_info.lookup (filename, addr);
++ }
++
++ /* Return a string containing the expected executable filename obtained
++ from the mapped file information within the core file. The filename
++ returned will be for the mapped file whose ELF headers are mapped at
++ the lowest address (i.e. which GDB encounters first).
++
++ If no suitable filename can be found then the returned string will be
++ empty.
++
++ If there are no build-ids embedded into the core file then the
++ returned string will be empty.
++
++ If a non-empty string is returned then there is no guarantee that the
++ named file exists on disk, or if it does exist on disk, then the
++ on-disk file might have a different build-id to the desired
++ build-id. */
++ const std::string &
++ expected_exec_filename () const
++ {
++ return m_expected_exec_filename;
++ }
++
+ private: /* per-core data */
+
+ /* Get rid of the core inferior. */
+@@ -156,19 +297,23 @@ class core_target final : public process_stratum_target
+ still be useful. */
+ std::vector<mem_range> m_core_unavailable_mappings;
+
+- /* Build m_core_file_mappings. Called from the constructor. */
+- void build_file_mappings ();
++ /* Data structure that holds information mapping filenames and address
++ ranges to the corresponding build-ids as well as the reverse build-id
++ to filename mapping. */
++ mapped_file_info m_mapped_file_info;
+
+- /* Helper method for xfer_partial. */
+- enum target_xfer_status xfer_memory_via_mappings (gdb_byte *readbuf,
+- const gdb_byte *writebuf,
+- ULONGEST offset,
+- ULONGEST len,
+- ULONGEST *xfered_len);
++ /* Build m_core_file_mappings and m_mapped_file_info. Called from the
++ constructor. */
++ void build_file_mappings ();
+
+ /* FIXME: kettenis/20031023: Eventually this field should
+ disappear. */
+ struct gdbarch *m_core_gdbarch = NULL;
++
++ /* If not empty then this contains the name of the executable discovered
++ when processing the memory-mapped file information. This will only
++ be set if we find a mapped with a suitable build-id. */
++ std::string m_expected_exec_filename;
+ };
+
+ core_target::core_target ()
+@@ -216,9 +361,51 @@ core_target::core_target ()
+ void
+ core_target::build_file_mappings ()
+ {
++ /* Type holding information about a single file mapped into the inferior
++ at the point when the core file was created. Associates a build-id
++ with the list of regions the file is mapped into. */
++ struct mapped_file
++ {
++ /* Type for a region of a file that was mapped into the inferior when
++ the core file was generated. */
++ struct region
++ {
++ /* Constructor. See member variables for argument descriptions. */
++ region (CORE_ADDR start_, CORE_ADDR end_, CORE_ADDR file_ofs_)
++ : start (start_),
++ end (end_),
++ file_ofs (file_ofs_)
++ { /* Nothing. */ }
++
++ /* The inferior address for the start of the mapped region. */
++ CORE_ADDR start;
++
++ /* The inferior address immediately after the mapped region. */
++ CORE_ADDR end;
++
++ /* The offset within the mapped file for this content. */
++ CORE_ADDR file_ofs;
++ };
++
++ /* If not nullptr, then this is the build-id associated with this
++ file. */
++ const bfd_build_id *build_id = nullptr;
++
++ /* If true then we have seen multiple different build-ids associated
++ with the same filename. The build_id field will have been set back
++ to nullptr, and we should not set build_id in future. */
++ bool ignore_build_id_p = false;
++
++ /* All the mapped regions of this file. */
++ std::vector<region> regions;
++ };
++
+ std::unordered_map<std::string, struct bfd *> bfd_map;
+ std::unordered_set<std::string> unavailable_paths;
+
++ /* All files mapped into the core file. The key is the filename. */
++ std::unordered_map<std::string, mapped_file> mapped_files;
++
+ /* See linux_read_core_file_mappings() in linux-tdep.c for an example
+ read_core_file_mappings method. */
+ gdbarch_read_core_file_mappings (m_core_gdbarch,
+@@ -239,87 +426,206 @@ core_target::build_file_mappings ()
+ weed out non-file-backed mappings. */
+ gdb_assert (filename != nullptr);
+
+- if (unavailable_paths.find (filename) != unavailable_paths.end ())
+- {
+- /* We have already seen some mapping for FILENAME but failed to
+- find/open the file. There is no point in trying the same
+- thing again so just record that the range [start, end) is
+- unavailable. */
+- m_core_unavailable_mappings.emplace_back (start, end - start);
+- return;
+- }
+-
+- struct bfd *bfd = bfd_map[filename];
+- if (bfd == nullptr)
++ /* 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)
+ {
+- /* Use exec_file_find() to do sysroot expansion. It'll
+- also strip the potential sysroot "target:" prefix. If
+- there is no sysroot, an equivalent (possibly more
+- canonical) pathname will be provided. */
+- gdb::unique_xmalloc_ptr<char> expanded_fname
+- = exec_file_find (filename, NULL);
+-
+- if (expanded_fname == nullptr && build_id != nullptr)
+- debuginfod_exec_query (build_id->data, build_id->size,
+- filename, &expanded_fname);
+-
+- if (expanded_fname == nullptr)
++ if (file_data.build_id == nullptr)
++ file_data.build_id = build_id;
++ else if (!build_id_equal (build_id, file_data.build_id))
+ {
+- m_core_unavailable_mappings.emplace_back (start, end - start);
+- unavailable_paths.insert (filename);
+- warning (_("Can't open file %s during file-backed mapping "
+- "note processing"),
+- filename);
+- return;
++ 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;
+ }
++ }
++ });
+
+- bfd = bfd_openr (expanded_fname.get (), "binary");
++ /* Get the build-id of the core file. */
++ const bfd_build_id *core_build_id
++ = build_id_bfd_get (current_program_space->core_bfd ());
+
+- if (bfd == nullptr || !bfd_check_format (bfd, bfd_object))
+- {
+- m_core_unavailable_mappings.emplace_back (start, end - start);
+- unavailable_paths.insert (filename);
+- warning (_("Can't open file %s which was expanded to %s "
++ for (const auto &iter : mapped_files)
++ {
++ const std::string &filename = iter.first;
++ const mapped_file &file_data = iter.second;
++
++ /* 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;
++
++ /* Use exec_file_find() to do sysroot expansion. It'll
++ also strip the potential sysroot "target:" prefix. If
++ there is no sysroot, an equivalent (possibly more
++ canonical) pathname will be provided. */
++ gdb::unique_xmalloc_ptr<char> expanded_fname
++ = exec_file_find (filename.c_str (), nullptr);
++
++ bool build_id_mismatch = false;
++ if (expanded_fname != nullptr && file_data.build_id != nullptr)
++ {
++ /* We temporarily open the bfd as a structured target, this
++ allows us to read the build-id from the bfd if there is one.
++ For this task it's OK if we reuse an already open bfd object,
++ so we make this call through GDB's bfd cache. Once we've
++ checked the build-id (if there is one) we'll drop this
++ reference and re-open the bfd using the "binary" target. */
++ gdb_bfd_ref_ptr tmp_bfd
++ = gdb_bfd_open (expanded_fname.get (), gnutarget);
++
++ if (tmp_bfd != nullptr
++ && bfd_check_format (tmp_bfd.get (), bfd_object)
++ && build_id_bfd_get (tmp_bfd.get ()) != nullptr)
++ {
++ /* The newly opened TMP_BFD has a build-id, and this mapped
++ file has a build-id extracted from the core-file. Check
++ the build-id's match, and if not, reject TMP_BFD. */
++ const struct bfd_build_id *found
++ = build_id_bfd_get (tmp_bfd.get ());
++ if (!build_id_equal (found, file_data.build_id))
++ build_id_mismatch = true;
++ }
++ }
++
++ gdb_bfd_ref_ptr abfd;
++ if (expanded_fname != nullptr && !build_id_mismatch)
++ {
++ struct bfd *b = bfd_openr (expanded_fname.get (), "binary");
++ abfd = gdb_bfd_ref_ptr::new_reference (b);
++ }
++
++ if ((expanded_fname == nullptr
++ || abfd == nullptr
++ || !bfd_check_format (abfd.get (), bfd_object))
++ && file_data.build_id != nullptr)
++ {
++ abfd = find_objfile_by_build_id (current_program_space,
++ file_data.build_id,
++ filename.c_str ());
++
++ if (abfd != nullptr)
++ {
++ /* The find_objfile_by_build_id will have opened ABFD using
++ the GNUTARGET global bfd type, however, we need the bfd
++ opened as the binary type (see the function's header
++ comment), so now we reopen ABFD with the desired binary
++ type. */
++ expanded_fname
++ = make_unique_xstrdup (bfd_get_filename (abfd.get ()));
++ struct bfd *b = bfd_openr (expanded_fname.get (), "binary");
++ gdb_assert (b != nullptr);
++ abfd = gdb_bfd_ref_ptr::new_reference (b);
++ }
++ }
++
++ std::vector<mem_range> ranges;
++ for (const mapped_file::region ®ion : file_data.regions)
++ ranges.emplace_back (region.start, region.end - region.start);
++
++ if (expanded_fname == nullptr
++ || abfd == nullptr
++ || !bfd_check_format (abfd.get (), bfd_object))
++ {
++ /* If ABFD was opened, but the wrong format, close it now. */
++ abfd = nullptr;
++
++ /* Record all regions for this file as unavailable. */
++ for (const mapped_file::region ®ion : file_data.regions)
++ m_core_unavailable_mappings.emplace_back (region.start,
++ region.end
++ - region.start);
++
++ /* And give the user an appropriate warning. */
++ if (build_id_mismatch)
++ {
++ if (expanded_fname == nullptr
++ || 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 ()));
++ 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 ()),
++ styled_string (file_name_style.style (),
++ expanded_fname.get ()));
++ }
++ else
++ {
++ if (expanded_fname == nullptr
++ || 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 ()));
++ else
++ warning (_("Can't open file %ps which was expanded to %ps "
+ "during file-backed mapping note processing"),
+- filename, expanded_fname.get ());
++ styled_string (file_name_style.style (),
++ filename.c_str ()),
++ styled_string (file_name_style.style (),
++ expanded_fname.get ()));
++ }
++ }
++ else
++ {
++ /* Ensure that the bfd will be closed when core_bfd is closed.
++ This can be checked before/after a core file detach via "maint
++ info bfds". */
++ gdb_bfd_record_inclusion (current_program_space->core_bfd (),
++ abfd.get ());
++
++ /* Create sections for each mapped region. */
++ for (const 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(). */
++ asection *sec = bfd_make_section_anyway (abfd.get (), "load");
++ if (sec == nullptr)
++ error (_("Can't make section"));
++ sec->filepos = region.file_ofs;
++ bfd_set_section_flags (sec, SEC_READONLY | SEC_HAS_CONTENTS);
++ bfd_set_section_size (sec, region.end - region.start);
++ bfd_set_section_vma (sec, region.start);
++ bfd_set_section_lma (sec, region.start);
++ bfd_set_section_alignment (sec, 2);
++
++ /* Set target_section fields. */
++ m_core_file_mappings.emplace_back (region.start, region.end, sec);
++ }
++ }
+
+- if (bfd != nullptr)
+- bfd_close (bfd);
+- return;
+- }
+- /* Ensure that the bfd will be closed when core_bfd is closed.
+- This can be checked before/after a core file detach via
+- "maint info bfds". */
+- gdb_bfd_record_inclusion (current_program_space->core_bfd (), bfd);
+- bfd_map[filename] = bfd;
+- }
++ /* If this is a bfd with a build-id then record the filename,
++ optional soname (DT_SONAME .dynamic attribute), and the range of
++ addresses at which this bfd is mapped. This information can be
++ used to perform build-id checking when loading the shared
++ libraries. */
++ if (file_data.build_id != nullptr)
++ {
++ normalize_mem_ranges (&ranges);
+
+- /* Make new BFD section. All sections have the same name,
+- which is permitted by bfd_make_section_anyway(). */
+- asection *sec = bfd_make_section_anyway (bfd, "load");
+- if (sec == nullptr)
+- error (_("Can't make section"));
+- sec->filepos = file_ofs;
+- bfd_set_section_flags (sec, SEC_READONLY | SEC_HAS_CONTENTS);
+- bfd_set_section_size (sec, end - start);
+- bfd_set_section_vma (sec, start);
+- bfd_set_section_lma (sec, start);
+- bfd_set_section_alignment (sec, 2);
+-
+- /* Set target_section fields. */
+- m_core_file_mappings.emplace_back (start, end, sec);
+-
+- /* If this is a bfd of a shared library, record its soname
+- and build id. */
+- if (build_id != nullptr)
+- {
+- gdb::unique_xmalloc_ptr<char> soname
+- = gdb_bfd_read_elf_soname (bfd->filename);
+- if (soname != nullptr)
+- set_cbfd_soname_build_id (current_program_space->cbfd,
+- soname.get (), build_id);
+- }
+- });
++ const char *actual_filename = nullptr;
++ gdb::unique_xmalloc_ptr<char> soname;
++ if (abfd != nullptr)
++ {
++ actual_filename = bfd_get_filename (abfd.get ());
++ soname = gdb_bfd_read_elf_soname (actual_filename);
++ }
++
++ m_mapped_file_info.add (soname.get (), filename.c_str (),
++ actual_filename, std::move (ranges),
++ file_data.build_id);
++ }
++ }
+
+ normalize_mem_ranges (&m_core_unavailable_mappings);
+ }
+@@ -564,36 +870,31 @@ rename_vmcore_idle_reg_sections (bfd *abfd, inferior *inf)
+ BFD ABFD. */
+
+ static void
+-locate_exec_from_corefile_build_id (bfd *abfd, int from_tty)
++locate_exec_from_corefile_build_id (bfd *abfd, core_target *target,
++ int from_tty)
+ {
+ const bfd_build_id *build_id = build_id_bfd_get (abfd);
+ if (build_id == nullptr)
+ return;
+
+- gdb_bfd_ref_ptr execbfd
+- = build_id_to_exec_bfd (build_id->size, build_id->data);
++ /* The filename used for the find_objfile_by_build_id call. */
++ std::string filename;
+
+- if (execbfd == nullptr)
++ if (!target->expected_exec_filename ().empty ())
++ filename = target->expected_exec_filename ();
++ else
+ {
+- /* Attempt to query debuginfod for the executable. */
+- gdb::unique_xmalloc_ptr<char> execpath;
+- scoped_fd fd = debuginfod_exec_query (build_id->data, build_id->size,
+- abfd->filename, &execpath);
+-
+- if (fd.get () >= 0)
+- {
+- execbfd = gdb_bfd_open (execpath.get (), gnutarget);
+-
+- if (execbfd == nullptr)
+- warning (_("\"%s\" from debuginfod cannot be opened as bfd: %s"),
+- execpath.get (),
+- gdb_bfd_errmsg (bfd_get_error (), nullptr).c_str ());
+- else if (!build_id_verify (execbfd.get (), build_id->size,
+- build_id->data))
+- execbfd.reset (nullptr);
+- }
++ /* We didn't find an executable name from the mapped file
++ information, so as a stand-in build a string based on the
++ build-id. */
++ std::string build_id_hex_str = bin2hex (build_id->data, build_id->size);
++ filename = string_printf ("with build-id %s", build_id_hex_str.c_str ());
+ }
+
++ gdb_bfd_ref_ptr execbfd
++ = find_objfile_by_build_id (current_program_space, build_id,
++ filename.c_str ());
++
+ if (execbfd != nullptr)
+ {
+ exec_file_attach (bfd_get_filename (execbfd.get ()), from_tty);
+@@ -724,7 +1025,7 @@ core_target_open (const char *arg, int from_tty)
+
+ if (current_program_space->exec_bfd () == nullptr)
+ locate_exec_from_corefile_build_id (current_program_space->core_bfd (),
+- from_tty);
++ target, from_tty);
+
+ post_create_inferior (from_tty);
+
+@@ -963,55 +1264,6 @@ core_target::files_info ()
+ print_section_info (&m_core_section_table, current_program_space->core_bfd ());
+ }
+ \f
+-/* Helper method for core_target::xfer_partial. */
+-
+-enum target_xfer_status
+-core_target::xfer_memory_via_mappings (gdb_byte *readbuf,
+- const gdb_byte *writebuf,
+- ULONGEST offset, ULONGEST len,
+- ULONGEST *xfered_len)
+-{
+- enum target_xfer_status xfer_status;
+-
+- xfer_status = (section_table_xfer_memory_partial
+- (readbuf, writebuf,
+- offset, len, xfered_len,
+- m_core_file_mappings));
+-
+- if (xfer_status == TARGET_XFER_OK || m_core_unavailable_mappings.empty ())
+- return xfer_status;
+-
+- /* There are instances - e.g. when debugging within a docker
+- container using the AUFS storage driver - where the pathnames
+- obtained from the note section are incorrect. Despite the path
+- being wrong, just knowing the start and end addresses of the
+- mappings is still useful; we can attempt an access of the file
+- stratum constrained to the address ranges corresponding to the
+- unavailable mappings. */
+-
+- ULONGEST memaddr = offset;
+- ULONGEST memend = offset + len;
+-
+- for (const auto &mr : m_core_unavailable_mappings)
+- {
+- if (mr.contains (memaddr))
+- {
+- if (!mr.contains (memend))
+- len = mr.start + mr.length - memaddr;
+-
+- xfer_status = this->beneath ()->xfer_partial (TARGET_OBJECT_MEMORY,
+- NULL,
+- readbuf,
+- writebuf,
+- offset,
+- len,
+- xfered_len);
+- break;
+- }
+- }
+-
+- return xfer_status;
+-}
+
+ enum target_xfer_status
+ core_target::xfer_partial (enum target_object object, const char *annex,
+@@ -1039,26 +1291,72 @@ core_target::xfer_partial (enum target_object object, const char *annex,
+ if (xfer_status == TARGET_XFER_OK)
+ return TARGET_XFER_OK;
+
+- /* Check file backed mappings. If they're available, use
+- core file provided mappings (e.g. from .note.linuxcore.file
+- or the like) as this should provide a more accurate
+- result. If not, check the stratum beneath us, which should
+- be the file stratum.
++ /* Check file backed mappings. If they're available, use core file
++ provided mappings (e.g. from .note.linuxcore.file or the like)
++ as this should provide a more accurate result. */
++ if (!m_core_file_mappings.empty ())
++ {
++ xfer_status = section_table_xfer_memory_partial
++ (readbuf, writebuf, offset, len, xfered_len,
++ m_core_file_mappings);
++ if (xfer_status == TARGET_XFER_OK)
++ return xfer_status;
++ }
+
+- We also check unavailable mappings due to Docker/AUFS driver
+- issues. */
+- if (!m_core_file_mappings.empty ()
+- || !m_core_unavailable_mappings.empty ())
++ /* If the access is within an unavailable file mapping then we try
++ to check in the stratum below (the executable stratum). The
++ thinking here is that if the mapping was read/write then the
++ contents would have been written into the core file and the
++ access would have been satisfied by m_core_section_table.
++
++ But if the access has not yet been resolved then we can assume
++ the access is read-only. If the executable was not found
++ during the mapped file check then we'll have an unavailable
++ mapping entry, however, if the user has provided the executable
++ (maybe in a different location) then we might be able to
++ resolve the access from there.
++
++ If that fails, but the access is within an unavailable region,
++ then the access itself should fail. */
++ for (const auto &mr : m_core_unavailable_mappings)
+ {
+- xfer_status = xfer_memory_via_mappings (readbuf, writebuf, offset,
++ if (mr.contains (offset))
++ {
++ if (!mr.contains (offset + len))
++ len = mr.start + mr.length - offset;
++
++ xfer_status
++ = this->beneath ()->xfer_partial (TARGET_OBJECT_MEMORY,
++ nullptr, readbuf,
++ writebuf, offset,
+ len, xfered_len);
++ if (xfer_status == TARGET_XFER_OK)
++ return TARGET_XFER_OK;
++
++ return TARGET_XFER_E_IO;
++ }
++ }
++
++ /* The following is acting as a fallback in case we encounter a
++ situation where the core file is lacking and mapped file
++ information. Here we query the exec file stratum to see if it
++ can resolve the access. Doing this when we are missing mapped
++ file information might be the best we can do, but there are
++ certainly cases this will get wrong, e.g. if an inferior created
++ a zero initialised mapping over the top of some data that exists
++ within the executable then this will return the executable data
++ rather than the zero data. Maybe we should just drop this
++ block? */
++ if (m_core_file_mappings.empty ()
++ && m_core_unavailable_mappings.empty ())
++ {
++ xfer_status
++ = this->beneath ()->xfer_partial (object, annex, readbuf,
++ writebuf, offset, len,
++ xfered_len);
++ if (xfer_status == TARGET_XFER_OK)
++ return TARGET_XFER_OK;
+ }
+- else
+- xfer_status = this->beneath ()->xfer_partial (object, annex, readbuf,
+- writebuf, offset, len,
+- xfered_len);
+- if (xfer_status == TARGET_XFER_OK)
+- return TARGET_XFER_OK;
+
+ /* Finally, attempt to access data in core file sections with
+ no contents. These will typically read as all zero. */
+@@ -1515,6 +1813,182 @@ maintenance_print_core_file_backed_mappings (const char *args, int from_tty)
+ targ->info_proc_mappings (targ->core_gdbarch ());
+ }
+
++/* Add more details discovered while processing the core-file's mapped file
++ information, we're building maps between filenames and the corresponding
++ build-ids, between address ranges and the corresponding build-ids, and
++ also a reverse map between build-id and the corresponding filename.
++
++ SONAME is the DT_SONAME attribute extracted from the .dynamic section of
++ a shared library that was mapped into the core file. This can be
++ nullptr if the mapped files was not a shared library, or didn't have a
++ DT_SONAME attribute.
++
++ EXPECTED_FILENAME is the name of the file that was mapped into the
++ inferior as extracted from the core file, this should never be nullptr.
++
++ ACTUAL_FILENAME is the name of the actual file GDB found to provide the
++ mapped file information, this can be nullptr if GDB failed to find a
++ suitable file. This might be different to EXPECTED_FILENAME, e.g. GDB
++ might have downloaded the file from debuginfod and so ACTUAL_FILENAME
++ will be a file in the debuginfod client cache.
++
++ RANGES is the list of memory ranges at which this file was mapped into
++ the inferior.
++
++ BUILD_ID is the build-id for this mapped file, this will never be
++ nullptr. Not every mapped file will have a build-id, but there's no
++ point calling this function if we failed to find a build-id; this
++ structure only exists so we can lookup files based on their build-id. */
++
++void
++mapped_file_info::add (const char *soname,
++ const char *expected_filename,
++ const char *actual_filename,
++ std::vector<mem_range> &&ranges,
++ const bfd_build_id *build_id)
++{
++ gdb_assert (build_id != nullptr);
++ gdb_assert (expected_filename != nullptr);
++
++ if (soname != nullptr)
++ {
++ /* If we already have an entry with this SONAME then this indicates
++ that the inferior has two files mapped into memory with different
++ file names (and most likely different build-ids), but with the
++ same DT_SONAME attribute. In this case we can't use the
++ DT_SONAME to figure out the expected build-id of a shared
++ library, so poison the entry for this SONAME by setting the entry
++ to nullptr. */
++ auto it = m_soname_to_build_id_map.find (soname);
++ if (it != m_soname_to_build_id_map.end ()
++ && it->second != nullptr
++ && !build_id_equal (it->second, build_id))
++ m_soname_to_build_id_map[soname] = nullptr;
++ else
++ m_soname_to_build_id_map[soname] = build_id;
++ }
++
++ /* When the core file is initially opened and the mapped files are
++ parsed, we group the build-id information based on the file name. As
++ a consequence, we should see each EXPECTED_FILENAME value exactly
++ once. This means that each insertion should always succeed. */
++ const auto [it, inserted]
++ = m_filename_to_build_id_map.emplace (expected_filename, build_id);
++ gdb_assert (inserted);
++
++ /* Setup the reverse build-id to file name map. */
++ if (actual_filename != nullptr)
++ m_build_id_to_filename_map.emplace (build_id, actual_filename);
++
++ /* Setup the list of memory range to build-id objects. */
++ for (mem_range &r : ranges)
++ m_address_to_build_id_list.emplace_back (std::move (r), build_id);
++
++ /* At this point the m_address_to_build_id_list is unsorted (we just
++ added some entries to the end of the list). All entries should be
++ added before any look-ups are performed, and the list is only sorted
++ when the first look-up is performed. */
++ gdb_assert (!m_address_to_build_id_list_sorted);
++}
++
++/* FILENAME is the name of a file GDB is trying to load, and ADDR is
++ (optionally) an address within the file in the inferior's address space.
++
++ Search through the information gathered from the core-file's mapped file
++ information looking for a file named FILENAME, or for a file that covers
++ ADDR. If a match is found then return the build-id for the file along
++ with the location where GDB found the mapped file.
++
++ The location of the mapped file might be the empty string if GDB was
++ unable to find the mapped file.
++
++ If no build-id can be found for FILENAME then GDB will return a pair
++ containing nullptr (for the build-id) and an empty string for the file
++ name. */
++
++std::optional <core_target_mapped_file_info>
++mapped_file_info::lookup (const char *filename,
++ const std::optional<CORE_ADDR> &addr)
++{
++ if (filename != nullptr)
++ {
++ /* If there's a matching entry in m_filename_to_build_id_map then the
++ associated build-id will not be nullptr, and can be used to
++ validate that FILENAME is correct. */
++ auto it = m_filename_to_build_id_map.find (filename);
++ if (it != m_filename_to_build_id_map.end ())
++ return make_result (it->second);
++ }
++
++ if (addr.has_value ())
++ {
++ /* On the first lookup, sort the address_to_build_id_list. */
++ if (!m_address_to_build_id_list_sorted)
++ {
++ std::sort (m_address_to_build_id_list.begin (),
++ m_address_to_build_id_list.end (),
++ [] (const mem_range_and_build_id &a,
++ const mem_range_and_build_id &b) {
++ return a.range < b.range;
++ });
++ m_address_to_build_id_list_sorted = true;
++ }
++
++ /* Look for the first entry whose range's start address is not less
++ than, or equal too, the address ADDR. If we find such an entry,
++ then the previous entry's range might contain ADDR. If it does
++ then that previous entry's build-id can be used. */
++ auto it = std::lower_bound
++ (m_address_to_build_id_list.begin (),
++ m_address_to_build_id_list.end (),
++ *addr,
++ [] (const mem_range_and_build_id &a,
++ const CORE_ADDR &b) {
++ return a.range.start <= b;
++ });
++
++ if (it != m_address_to_build_id_list.begin ())
++ {
++ --it;
++
++ if (it->range.contains (*addr))
++ return make_result (it->build_id);
++ }
++ }
++
++ if (filename != nullptr)
++ {
++ /* If the basename of FILENAME appears in m_soname_to_build_id_map
++ then when the mapped files were processed, we saw a file with a
++ DT_SONAME attribute corresponding to FILENAME, use that build-id
++ to validate FILENAME.
++
++ However, the build-id in this map might be nullptr if we saw
++ multiple mapped files with the same DT_SONAME attribute (though
++ this should be pretty rare). */
++ auto it
++ = m_soname_to_build_id_map.find (lbasename (filename));
++ if (it != m_soname_to_build_id_map.end ()
++ && it->second != nullptr)
++ return make_result (it->second);
++ }
++
++ return {};
++}
++
++/* See gdbcore.h. */
++
++std::optional <core_target_mapped_file_info>
++core_target_find_mapped_file (const char *filename,
++ std::optional<CORE_ADDR> addr)
++{
++ core_target *targ = get_current_core_target ();
++ if (targ == nullptr || current_program_space->cbfd.get () == nullptr)
++ return {};
++
++ return targ->lookup_mapped_file_info (filename, addr);
++}
++
+ void _initialize_corelow ();
+ void
+ _initialize_corelow ()
+diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in
+--- a/gdb/data-directory/Makefile.in
++++ b/gdb/data-directory/Makefile.in
+@@ -77,6 +77,8 @@ PYTHON_FILE_LIST = \
+ gdb/FrameIterator.py \
+ gdb/frames.py \
+ gdb/missing_debug.py \
++ gdb/missing_objfile.py \
++ gdb/missing_files.py \
+ gdb/printing.py \
+ gdb/prompt.py \
+ gdb/styling.py \
+@@ -86,7 +88,7 @@ PYTHON_FILE_LIST = \
+ gdb/command/__init__.py \
+ gdb/command/explore.py \
+ gdb/command/frame_filters.py \
+- gdb/command/missing_debug.py \
++ gdb/command/missing_files.py \
+ gdb/command/pretty_printers.py \
+ gdb/command/prompt.py \
+ gdb/command/type_printers.py \
+diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c
+--- a/gdb/debuginfod-support.c
++++ b/gdb/debuginfod-support.c
+@@ -409,7 +409,7 @@ debuginfod_exec_query (const unsigned char *build_id,
+ std::optional<target_terminal::scoped_restore_terminal_state> term_state;
+
+ {
+- user_data data ("executable for", filename);
++ user_data data ("file", filename);
+
+ debuginfod_set_user_data (c, &data);
+ if (target_supports_terminal_ours ())
+@@ -423,7 +423,7 @@ debuginfod_exec_query (const unsigned char *build_id,
+ debuginfod_set_user_data (c, nullptr);
+ }
+
+- print_outcome (fd.get (), "executable for", filename);
++ print_outcome (fd.get (), "file", filename);
+
+ if (fd.get () >= 0)
+ destname->reset (dname);
+diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
+--- a/gdb/doc/gdb.texinfo
++++ b/gdb/doc/gdb.texinfo
+@@ -21786,6 +21786,7 @@
+ @c (eg rooted in val of env var GDBSYMS) could exist for mappable symbol
+ @c files.
+
++@anchor{core-file command}
+ @kindex core-file
+ @item core-file @r{[}@var{filename}@r{]}
+ @itemx core
+@@ -24534,6 +24535,10 @@
+ @tab @code{vFile:fstat}
+ @tab Host I/O
+
++@item @code{hostio-stat-packet}
++@tab @code{vFile:stat}
++@tab Host I/O
++
+ @item @code{hostio-setfs-packet}
+ @tab @code{vFile:setfs}
+ @tab Host I/O
+@@ -46289,6 +46294,13 @@
+ If an error occurs the return value is -1. The format of the
+ returned binary attachment is as described in @ref{struct stat}.
+
++@item vFile:stat: @var{filename}
++Get information about the file @var{filename} on the target.
++On success the information is returned as a binary attachment
++and the return value is the size of this attachment in bytes.
++If an error occurs the return value is -1. The format of the
++returned binary attachment is as described in @ref{struct stat}.
++
+ @item vFile:unlink: @var{filename}
+ Delete the file at @var{filename} on the target. Return 0,
+ or -1 if an error occurs. The @var{filename} is a string.
+diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
+--- a/gdb/doc/python.texi
++++ b/gdb/doc/python.texi
+@@ -231,6 +231,7 @@
+ * TUI Windows In Python:: Implementing new TUI windows.
+ * 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.
+ @end menu
+
+ @node Basic Python
+@@ -5370,10 +5371,11 @@
+ objects. @xref{Frame Filter API}, for more information.
+ @end defvar
+
+-@defvar Progspace.missing_debug_handlers
+-The @code{missing_debug_handlers} attribute is a list of the missing
+-debug handler objects for this program space. @xref{Missing Debug
+-Info In Python}, for more information.
++@defvar Progspace.missing_file_handlers
++The @code{missing_file_handlers} attribute is a list of tuples. Each
++tuple holds a missing-file handler object for this program space. For
++more information, @pxref{Missing Debug Info In Python}, and
++@ref{Missing Objfiles In Python}.
+ @end defvar
+
+ A program space has the following methods:
+@@ -5549,6 +5551,7 @@
+ @code{gdb.Objfile.add_separate_debug_file} method, described below.
+ @end defvar
+
++@anchor{Objfile.build_id}
+ @defvar Objfile.build_id
+ The build ID of the objfile as a string.
+ If the objfile does not have a build ID then the value is @code{None}.
+@@ -8137,6 +8140,189 @@
+ for this objfile.
+ @end defun
+
++@node Missing Objfiles In Python
++@subsubsection Missing Objfiles In Python
++@cindex python, handle missing objfiles
++
++When @value{GDBN} opens a core file, for example with the
++@kbd{core-file} command (@pxref{core-file command}), @value{GDBN} will
++attempt to load the corresponding executable and shared libraries.
++Often these files can be found on the local machine, but sometimes
++these files cannot be found, in which case the debugging experience
++will be restricted.
++
++If @value{GDBN} fails to locate a particular file then there is an
++opportunity for a Python extension to step in. A Python extension can
++potentially locate the missing file using some platform- or
++project-specific steps, and inform @value{GDBN} of its location. Or a
++Python extension might provide some platform- or project-specific
++advice to the user about how to obtain the missing file.
++
++A missing-objfile Python extension consists of a handler object which
++has the @code{name} and @code{enabled} attributes, and implements the
++@code{__call__} method. When @value{GDBN} encounters a situation
++where a file cannot be found, but the build-id (@pxref{build ID}) for
++the missing file is known, then the @code{__call__} method is invoked
++to try and find the file. Full details of how handlers are written
++can be found below.
++
++@subheading The @code{gdb.missing_objfile} Module
++
++@value{GDBN} comes with a @code{gdb.missing_objfile} module which
++contains the following class and global function:
++
++@deftp{class} gdb.missing_objfile.MissingObjfileHandler
++
++@code{MissingObjfileHandler} is a base class from which user-created
++handlers can derive, though it is not required that handlers derive
++from this class, so long as any user created handler has the
++@code{name} and @code{enabled} attributes, and implements the
++@code{__call__} method.
++
++@defun MissingObjfileHandler.__init__ (name)
++The @var{name} is a string used to reference this missing-objfile
++handler within some @value{GDBN} commands. Valid names consist of the
++characters @samp{[-_a-zA-Z0-9]}, creating a handler with an invalid
++name raises a @code{ValueError} exception.
++@end defun
++
++@defun MissingObjfileHandler.__call__ (pspace, build_id, filename)
++
++Sub-classes must override the @code{__call__} method. The
++@var{pspace} argument will be a @code{gdb.Progspace}
++(@pxref{Progspaces In Python}), this is the program space in which
++@value{GDBN} is looking for the missing file.
++
++The @var{build_id} argument is a string containing the build-id of the
++file that is missing, this will be in the same format as returned by
++@code{Objfile.build_id} (@pxref{Objfile.build_id}).
++
++The @var{filename} argument contains the name of the file that
++@value{GDBN} is looking for. This information is provided to allow
++handlers to generate informative messages for the user. A handler is
++not required to place the missing file at this location. There might
++already be a file present at this location, but it might not match the
++required build-id, in which case @value{GDBN} will have ignored it.
++In some limited cases @value{GDBN} might not be able to establish the
++@var{filename} of the file it is searching for, in this case
++@value{GDBN} will use a string @samp{with build-id @var{build_id}} as a
++replacement.
++
++The return value from the @code{__call__} method indicates what
++@value{GDBN} should do next. The possible return values are:
++
++@itemize @bullet
++@item @code{None}
++
++This indicates that this handler could not locate the missing file and
++@value{GDBN} should call any other registered handlers.
++
++@item @code{True}
++
++This indicates that this handler has installed the missing file into a
++location where @value{GDBN} would normally expect to find it. The
++only location in which @value{GDBN} will look is within the
++@file{.build-id} sub-directory within the @var{debug-file-directory}
++(@pxref{debug-file-directory}).
++
++@value{GDBN} will repeat the normal lookup process, which should now
++find the previously missing file.
++
++If @value{GDBN} still doesn't find file after this second attempt,
++then the Python missing-objfile handlers are not invoked a second
++time, this prevents a badly behaved handler causing @value{GDBN} to
++get stuck in a loop. @value{GDBN} will continue without the missing
++file, though this will degrade the debugging experience.
++
++@item @code{False}
++
++This indicates that this handler has done everything that it intends
++to do but the missing file could not be found. @value{GDBN} will not
++call any other registered handlers to look for the missing file.
++@value{GDBN} will continue without the missing file, though this will
++degrade the debugging experience.
++
++@item A string
++
++The returned string should contain a filename. @value{GDBN} will not
++call any further registered handlers, and will instead use the
++returned filename as the missing file.
++@end itemize
++
++Invoking the @code{__call__} method from this base class will raise a
++@code{NotImplementedError} exception.
++@end defun
++
++@defvar MissingObjfileHandler.name
++A read-only attribute which is a string, the name of this handler
++passed to the @code{__init__} method.
++@end defvar
++
++@defvar MissingObjfileHandler.enabled
++A modifiable attribute containing a boolean; when @code{True}, the
++handler is enabled, and will be used by @value{GDBN}. When
++@code{False}, the handler has been disabled, and will not be used.
++@end defvar
++@end deftp
++
++@defun gdb.missing_objfile.register_handler (locus, handler, replace=@code{False})
++Register a new missing-objfile handler with @value{GDBN}.
++
++@var{handler} is an instance of a sub-class of
++@code{MissingObjfileHandler}, or at least an instance of an object that
++has the same attributes and methods as @code{MissingObjfileHandler}.
++
++@var{locus} specifies to which handler list to prepend @var{handler}.
++It can be either a @code{gdb.Progspace} (@pxref{Progspaces In Python})
++or @code{None}, in which case the handler is registered globally. The
++newly registered @var{handler} will be called before any other handler
++from the same locus. Two handlers in the same locus cannot have the
++same name, an attempt to add a handler with an already existing name
++raises an exception unless @var{replace} is @code{True}, in which case
++the old handler is deleted and the new handler is prepended to the
++selected handler list.
++
++@value{GDBN} first calls the handlers for the current program space,
++and then the globally registered handlers. As soon as a handler
++returns a value other than @code{None}, no further handlers are
++called.
++@end defun
++
++@subheading Managing Missing-Objfile Handlers
++
++@value{GDBN} defines the following commands to manage registered
++missing-objfile handlers:
++
++@table @code
++
++@kindex info missing-objfile-handlers
++@item info missing-objfile-handlers @r{[} @var{locus} @r{[} @var{name-regexp} @r{]} @r{]}
++Lists all registered missing-objfile handlers. Arguments @var{locus}
++and @var{name-regexp} are both optional and can be used to filter
++which handlers are listed.
++
++The @var{locus} argument should be either @kbd{global},
++@kbd{progspace}, or the name of an object file. Only handlers
++registered for the specified locus will be listed.
++
++The @var{name-regexp} is a regular expression used to match against
++handler names.
++
++@kindex disable missing-objfile-handler
++@item disable missing-objfile-handler @r{[} @var{locus} @r{[} @var{name-regexp} @r{]} @r{]}
++The @var{locus} and @var{name-regexp} are interpreted as in @kbd{info
++missing-objfile-handlers} above, but instead of listing the matching
++handlers, all of the matching handlers are disabled. The
++@code{enabled} field of each matching handler is set to @code{False}.
++
++@kindex enable missing-objfile-handler
++@item enable missing-objfile-handler @r{[} @var{locus} @r{[} @var{name-regexp} @r{]} @r{]}
++The @var{locus} and @var{name-regexp} are interpreted as in @kbd{info
++missing-objfile-handlers} above, but instead of listing the matching
++handlers, all of the matching handlers are enabled. The
++@code{enabled} field of each matching handler is set to @code{True}.
++@end table
++
+ @node Python Auto-loading
+ @subsection Python Auto-loading
+ @cindex Python auto-loading
+diff --git a/gdb/extension-priv.h b/gdb/extension-priv.h
+--- a/gdb/extension-priv.h
++++ b/gdb/extension-priv.h
+@@ -287,9 +287,22 @@ struct extension_language_ops
+ /* Give extension languages a chance to deal with missing debug
+ information. OBJFILE is the file for which GDB was unable to find
+ any debug information. */
+- ext_lang_missing_debuginfo_result
++ ext_lang_missing_file_result
+ (*handle_missing_debuginfo) (const struct extension_language_defn *,
+ struct objfile *objfile);
++
++ /* Give extension languages a chance to deal with missing objfiles.
++ PSPACE is the program space in which GDB is searching for a missing
++ objfile, and will not be NULL. BUILD_ID is the build-id of the
++ objfile we're looking for, and will not be NULL. FILENAME is the name
++ of the file we're looking for, and will not be NULL. See
++ ext_lang_find_objfile_from_buildid for some additional information
++ about the meaning of FILENAME. */
++ ext_lang_missing_file_result
++ (*find_objfile_from_buildid) (const struct extension_language_defn *,
++ program_space *pspace,
++ const struct bfd_build_id *build_id,
++ const char *filename);
+ };
+
+ /* State necessary to restore a signal handler to its previous value. */
+diff --git a/gdb/extension.c b/gdb/extension.c
+--- a/gdb/extension.c
++++ b/gdb/extension.c
+@@ -1040,7 +1040,7 @@ ext_lang_print_insn (struct gdbarch *gdbarch, CORE_ADDR address,
+
+ /* See extension.h. */
+
+-ext_lang_missing_debuginfo_result
++ext_lang_missing_file_result
+ ext_lang_handle_missing_debuginfo (struct objfile *objfile)
+ {
+ for (const struct extension_language_defn *extlang : extension_languages)
+@@ -1048,7 +1048,7 @@ ext_lang_handle_missing_debuginfo (struct objfile *objfile)
+ if (extlang->ops == nullptr
+ || extlang->ops->handle_missing_debuginfo == nullptr)
+ continue;
+- ext_lang_missing_debuginfo_result result
++ ext_lang_missing_file_result result
+ = extlang->ops->handle_missing_debuginfo (extlang, objfile);
+ if (!result.filename ().empty () || result.try_again ())
+ return result;
+@@ -1057,6 +1057,28 @@ ext_lang_handle_missing_debuginfo (struct objfile *objfile)
+ return {};
+ }
+
++/* See extension.h. */
++
++ext_lang_missing_file_result
++ext_lang_find_objfile_from_buildid (program_space *pspace,
++ const struct bfd_build_id *build_id,
++ const char *filename)
++{
++ for (const struct extension_language_defn *extlang : extension_languages)
++ {
++ if (extlang->ops == nullptr
++ || extlang->ops->find_objfile_from_buildid == nullptr)
++ continue;
++ ext_lang_missing_file_result result
++ = extlang->ops->find_objfile_from_buildid (extlang, pspace, build_id,
++ filename);
++ if (!result.filename ().empty () || result.try_again ())
++ return result;
++ }
++
++ return {};
++}
++
+ /* Called via an observer before gdb prints its prompt.
+ Iterate over the extension languages giving them a chance to
+ change the prompt. The first one to change the prompt wins,
+diff --git a/gdb/extension.h b/gdb/extension.h
+--- a/gdb/extension.h
++++ b/gdb/extension.h
+@@ -36,6 +36,7 @@ struct ui_file;
+ struct ui_out;
+ struct value;
+ struct value_print_options;
++struct program_space;
+
+ /* A function to load and process a script file.
+ The file has been opened and is ready to be read from the beginning.
+@@ -358,23 +359,23 @@ extern std::optional<int> ext_lang_print_insn
+ it. And the third option is for the extension to just return a null
+ result, indication there is nothing the extension can do to provide the
+ missing debug information. */
+-struct ext_lang_missing_debuginfo_result
++struct ext_lang_missing_file_result
+ {
+ /* Default result. The extension was unable to provide the missing debug
+ info. */
+- ext_lang_missing_debuginfo_result ()
++ ext_lang_missing_file_result ()
+ { /* Nothing. */ }
+
+ /* When TRY_AGAIN is true GDB should try searching again, the extension
+ may have installed the missing debug info into a suitable location.
+ When TRY_AGAIN is false this is equivalent to the default, no
+ argument, constructor. */
+- ext_lang_missing_debuginfo_result (bool try_again)
++ ext_lang_missing_file_result (bool try_again)
+ : m_try_again (try_again)
+ { /* Nothing. */ }
+
+ /* Look in FILENAME for the missing debug info. */
+- ext_lang_missing_debuginfo_result (std::string &&filename)
++ ext_lang_missing_file_result (std::string &&filename)
+ : m_filename (std::move (filename))
+ { /* Nothing. */ }
+
+@@ -404,9 +405,28 @@ struct ext_lang_missing_debuginfo_result
+
+ /* Called when GDB failed to find any debug information for OBJFILE. */
+
+-extern ext_lang_missing_debuginfo_result ext_lang_handle_missing_debuginfo
++extern ext_lang_missing_file_result ext_lang_handle_missing_debuginfo
+ (struct objfile *objfile);
+
++/* Called when GDB opens a core-file to find any object files for which a
++ build-id could be extracted from the core-file, but the matching file
++ could not otherwise be found by GDB.
++
++ PSPACE is the program space in which GDB is opening the core-file and
++ is looking for a missing object file. BUILD_ID is the build-id of the
++ file being looked for, and will not be NULL. FILENAME is the name of
++ the file GDB is looking for, this will not be NULL. The FILENAME is
++ provided only for creating helpful messages for the user. FILENAME
++ might already exist on disk but have the wrong build-id, of FILENAME
++ might not exist on disk. If the missing objfile can be found then it
++ does not have to be placed at the location FILENAME.
++
++ The returned object indicates if the file could be found or not. */
++
++extern ext_lang_missing_file_result ext_lang_find_objfile_from_buildid
++ (program_space *pspace, const struct bfd_build_id *build_id,
++ const char *filename);
++
+ #if GDB_SELF_TEST
+ namespace selftests {
+ extern void (*hook_set_active_ext_lang) ();
+diff --git a/gdb/gdbcore.h b/gdb/gdbcore.h
+--- a/gdb/gdbcore.h
++++ b/gdb/gdbcore.h
+@@ -196,4 +196,70 @@ class thread_section_name
+ std::string m_storage;
+ };
+
++/* Type returned from core_target_find_mapped_file. Holds information
++ about a mapped file that was processed when a core file was initially
++ loaded. */
++struct core_target_mapped_file_info
++{
++ /* Constructor. BUILD_ID is not nullptr, and is the build-id for the
++ mapped file. FILENAME is the location of the file that GDB loaded to
++ provide the mapped file. This might be different from the name of the
++ mapped file mentioned in the core file, e.g. if GDB downloads a file
++ from debuginfod then FILENAME would point into the debuginfod client
++ cache. The FILENAME can be the empty string if GDB was unable to find
++ a file to provide the mapped file. */
++
++ core_target_mapped_file_info (const bfd_build_id *build_id,
++ const std::string filename)
++ : m_build_id (build_id),
++ m_filename (filename)
++ {
++ gdb_assert (m_build_id != nullptr);
++ }
++
++ /* The build-id for this mapped file. */
++
++ const bfd_build_id *
++ build_id () const
++ {
++ return m_build_id;
++ }
++
++ /* The file GDB used to provide this mapped file. */
++
++ const std::string &
++ filename () const
++ {
++ return m_filename;
++ }
++
++private:
++ const bfd_build_id *m_build_id = nullptr;
++ const std::string m_filename;
++};
++
++/* If the current inferior has a core_target for its process target, then
++ lookup information about a mapped file that was discovered when the
++ core file was loaded.
++
++ The FILENAME is the file we're looking for. The ADDR, if provided, is a
++ mapped address within the inferior which is known to be part of the file
++ we are looking for.
++
++ As an example, when loading shared libraries this function can be
++ called, in that case FILENAME will be the name of the shared library
++ that GDB is trying to load and ADDR will be an inferior address which is
++ part of the shared library we are looking for.
++
++ This function looks for a mapped file which matches FILENAME and/or
++ which covers ADDR and returns information about that file.
++
++ The returned information includes the name of the mapped file if known
++ and the build-id for the mapped file if known.
++
++ */
++std::optional<core_target_mapped_file_info>
++core_target_find_mapped_file (const char *filename,
++ std::optional<CORE_ADDR> addr);
++
+ #endif /* !defined (GDBCORE_H) */
+diff --git a/gdb/inf-child.c b/gdb/inf-child.c
+--- a/gdb/inf-child.c
++++ b/gdb/inf-child.c
+@@ -320,6 +320,21 @@ inf_child_target::fileio_fstat (int fd, struct stat *sb, fileio_error *target_er
+ return ret;
+ }
+
++/* Implementation of to_fileio_stat. */
++
++int
++inf_child_target::fileio_stat (struct inferior *inf, const char *filename,
++ struct stat *sb, fileio_error *target_errno)
++{
++ int ret;
++
++ ret = lstat (filename, sb);
++ if (ret == -1)
++ *target_errno = host_to_fileio_error (errno);
++
++ return ret;
++}
++
+ /* Implementation of to_fileio_close. */
+
+ int
+diff --git a/gdb/inf-child.h b/gdb/inf-child.h
+--- a/gdb/inf-child.h
++++ b/gdb/inf-child.h
+@@ -81,6 +81,8 @@ class inf_child_target
+ int fileio_pread (int fd, gdb_byte *read_buf, int len,
+ ULONGEST offset, fileio_error *target_errno) override;
+ int fileio_fstat (int fd, struct stat *sb, fileio_error *target_errno) override;
++ int fileio_stat (struct inferior *inf, const char *filename,
++ struct stat *sb, fileio_error *target_errno) override;
+ int fileio_close (int fd, fileio_error *target_errno) override;
+ int fileio_unlink (struct inferior *inf,
+ const char *filename,
+diff --git a/gdb/python/lib/gdb/__init__.py b/gdb/python/lib/gdb/__init__.py
+--- a/gdb/python/lib/gdb/__init__.py
++++ b/gdb/python/lib/gdb/__init__.py
+@@ -87,8 +87,9 @@ xmethods = []
+ frame_filters = {}
+ # Initial frame unwinders.
+ frame_unwinders = []
+-# Initial missing debug handlers.
+-missing_debug_handlers = []
++# The missing file handlers. Each item is a tuple with the form
++# (TYPE, HANDLER) where TYPE is a string either 'debug' or 'objfile'.
++missing_file_handlers = []
+
+
+ def _execute_unwinders(pending_frame):
+@@ -271,6 +272,61 @@ class Thread(threading.Thread):
+ super().start()
+
+
++def _filter_missing_file_handlers(handlers, handler_type):
++ """Each list of missing file handlers is a list of tuples, the first
++ item in the tuple is a string either 'debug' or 'objfile' to
++ indicate what type of handler it is. The second item in the tuple
++ is the actual handler object.
++
++ This function takes HANDLER_TYPE which is a string, either 'debug'
++ or 'objfile' and HANDLERS, a list of tuples. The function returns
++ an iterable over all of the handler objects (extracted from the
++ tuples) which match HANDLER_TYPE.
++ """
++
++ return map(lambda t: t[1], filter(lambda t: t[0] == handler_type, handlers))
++
++
++def _handle_missing_files(pspace, handler_type, cb):
++ """Helper for _handle_missing_debuginfo and _handle_missing_objfile.
++
++ Arguments:
++ pspace: The gdb.Progspace in which we're operating. Used to
++ lookup program space specific handlers.
++ handler_type: A string, either 'debug' or 'objfile', this is the
++ type of handler we're looking for.
++ cb: A callback which takes a handler and returns the result of
++ calling the handler.
++
++ Returns:
++ None: No suitable file could be found.
++ False: A handler has decided that the requested file cannot be
++ found, and no further searching should be done.
++ True: The file has been found and installed in a location
++ where GDB would normally look for it. GDB should
++ repeat its lookup process, the file should now be in
++ place.
++ A string: This is the filename of where the missing file can
++ be found.
++ """
++
++ for handler in _filter_missing_file_handlers(
++ pspace.missing_file_handlers, handler_type
++ ):
++ if handler.enabled:
++ result = cb(handler)
++ if result is not None:
++ return result
++
++ for handler in _filter_missing_file_handlers(missing_file_handlers, handler_type):
++ if handler.enabled:
++ result = cb(handler)
++ if result is not None:
++ return result
++
++ return None
++
++
+ def _handle_missing_debuginfo(objfile):
+ """Internal function called from GDB to execute missing debug
+ handlers.
+@@ -293,18 +349,46 @@ def _handle_missing_debuginfo(objfile):
+ A string: This is the filename of a file containing the
+ required debug information.
+ """
++
+ pspace = objfile.progspace
+
+- for handler in pspace.missing_debug_handlers:
+- if handler.enabled:
+- result = handler(objfile)
+- if result is not None:
+- return result
++ return _handle_missing_files(pspace, "debug", lambda h: h(objfile))
+
+- for handler in missing_debug_handlers:
+- if handler.enabled:
+- result = handler(objfile)
+- if result is not None:
+- return result
+
+- return None
++def _handle_missing_objfile(pspace, buildid, filename):
++ """Internal function called from GDB to execute missing objfile
++ handlers.
++
++ Run each of the currently registered, and enabled missing objfile
++ handler objects for the gdb.Progspace passed in as an argument,
++ and then from the global list. Stop after the first handler that
++ returns a result other than None.
++
++ Arguments:
++ pspace: A gdb.Progspace for which the missing objfile handlers
++ should be run. This is the program space in which an
++ objfile was found to be missing.
++ buildid: A string containing the build-id we're looking for.
++ filename: The filename of the file GDB tried to find but
++ couldn't. This is not where the file should be
++ placed if found, in fact, this file might already
++ exist on disk but have the wrong build-id. This is
++ mostly provided in order to be used in messages to
++ the user.
++
++ Returns:
++ None: No objfile could be found for this build-id.
++ False: A handler has done all it can with for this build-id,
++ but no objfile could be found.
++ True: An objfile might have been installed by a handler, GDB
++ should check again. The only place GDB checks is within
++ the .build-id sub-directory within the
++ debug-file-directory. If the required file was not
++ installed there then GDB will not find it.
++ A string: This is the filename of a file containing the
++ missing objfile.
++ """
++
++ return _handle_missing_files(
++ pspace, "objfile", lambda h: h(pspace, buildid, filename)
++ )
+diff --git a/gdb/python/lib/gdb/command/missing_debug.py b/gdb/python/lib/gdb/command/missing_files.py
+similarity index 54%
+rename from gdb/python/lib/gdb/command/missing_debug.py
+rename to gdb/python/lib/gdb/command/missing_files.py
+--- a/gdb/python/lib/gdb/command/missing_debug.py
++++ b/gdb/python/lib/gdb/command/missing_files.py
+@@ -1,4 +1,4 @@
+-# Missing debug related commands.
++# Missing debug and objfile related commands.
+ #
+ # Copyright 2023-2024 Free Software Foundation, Inc.
+ #
+@@ -21,7 +21,7 @@ import gdb
+
+
+ def validate_regexp(exp, idstring):
+- """Compile exp into a compiler regular expression object.
++ """Compile exp into a compiled regular expression object.
+
+ Arguments:
+ exp: The string to compile into a re.Pattern object.
+@@ -33,14 +33,15 @@ def validate_regexp(exp, idstring):
+ Raises:
+ SyntaxError: If exp is an invalid regexp.
+ """
++
+ try:
+ return re.compile(exp)
+ except SyntaxError:
+ raise SyntaxError("Invalid %s regexp: %s." % (idstring, exp))
+
+
+-def parse_missing_debug_command_args(arg):
+- """Internal utility to parse missing debug handler command argv.
++def parse_missing_file_command_args(arg):
++ """Internal utility to parse missing file handler command argv.
+
+ Arguments:
+ arg: The arguments to the command. The format is:
+@@ -52,6 +53,7 @@ def parse_missing_debug_command_args(arg):
+ Raises:
+ SyntaxError: an error processing ARG
+ """
++
+ argv = gdb.string_to_argv(arg)
+ argc = len(argv)
+ if argc > 2:
+@@ -68,10 +70,10 @@ def parse_missing_debug_command_args(arg):
+ )
+
+
+-class InfoMissingDebugHanders(gdb.Command):
+- """GDB command to list missing debug handlers.
++class InfoMissingFileHandlers(gdb.Command):
++ """GDB command to list missing HTYPE handlers.
+
+- Usage: info missing-debug-handlers [LOCUS-REGEXP [NAME-REGEXP]]
++ Usage: info missing-HTYPE-handlers [LOCUS-REGEXP [NAME-REGEXP]]
+
+ LOCUS-REGEXP is a regular expression matching the location of the
+ handler. If it is omitted, all registered handlers from all
+@@ -79,38 +81,47 @@ class InfoMissingDebugHanders(gdb.Command):
+ the handlers from the current progspace, or a regular expression
+ matching filenames of progspaces.
+
+- NAME-REGEXP is a regular expression to filter missing debug
++ NAME-REGEXP is a regular expression to filter missing HTYPE
+ handler names. If this omitted for a specified locus, then all
+ registered handlers in the locus are listed.
+ """
+
+- def __init__(self):
+- super().__init__("info missing-debug-handlers", gdb.COMMAND_FILES)
++ def __init__(self, handler_type):
++ # Update the doc string before calling the parent constructor,
++ # replacing the string 'HTYPE' with the value of HANDLER_TYPE.
++ # The parent constructor will grab a copy of this string to
++ # use as the commands help text.
++ self.__doc__ = self.__doc__.replace("HTYPE", handler_type)
++ super().__init__(
++ "info missing-" + handler_type + "-handlers", gdb.COMMAND_FILES
++ )
++ self.handler_type = handler_type
+
+ def list_handlers(self, title, handlers, name_re):
+- """Lists the missing debug handlers whose name matches regexp.
++ """Lists the missing file handlers whose name matches regexp.
+
+ Arguments:
+ title: The line to print before the list.
+- handlers: The list of the missing debug handlers.
++ handlers: The list of the missing file handlers.
+ name_re: handler name filter.
+ """
++
+ if not handlers:
+ return
+ print(title)
+- for handler in handlers:
++ for handler in gdb._filter_missing_file_handlers(handlers, self.handler_type):
+ if name_re.match(handler.name):
+ print(
+ " %s%s" % (handler.name, "" if handler.enabled else " [disabled]")
+ )
+
+ def invoke(self, arg, from_tty):
+- locus_re, name_re = parse_missing_debug_command_args(arg)
++ locus_re, name_re = parse_missing_file_command_args(arg)
+
+ if locus_re.match("progspace") and locus_re.pattern != "":
+ cp = gdb.current_progspace()
+ self.list_handlers(
+- "Progspace %s:" % cp.filename, cp.missing_debug_handlers, name_re
++ "Progspace %s:" % cp.filename, cp.missing_file_handlers, name_re
+ )
+
+ for progspace in gdb.progspaces():
+@@ -125,58 +136,71 @@ class InfoMissingDebugHanders(gdb.Command):
+ msg = "Progspace %s:" % filename
+ self.list_handlers(
+ msg,
+- progspace.missing_debug_handlers,
++ progspace.missing_file_handlers,
+ name_re,
+ )
+
+ # Print global handlers last, as these are invoked last.
+ if locus_re.match("global"):
+- self.list_handlers("Global:", gdb.missing_debug_handlers, name_re)
++ self.list_handlers("Global:", gdb.missing_file_handlers, name_re)
+
+
+-def do_enable_handler1(handlers, name_re, flag):
+- """Enable/disable missing debug handlers whose names match given regex.
++def do_enable_handler1(handlers, name_re, flag, handler_type):
++ """Enable/disable missing file handlers whose names match given regex.
+
+ Arguments:
+- handlers: The list of missing debug handlers.
++ handlers: The list of missing file handlers.
+ name_re: Handler name filter.
+ flag: A boolean indicating if we should enable or disable.
++ handler_type: A string, either 'debug' or 'objfile', use to control
++ which handlers are modified.
+
+ Returns:
+ The number of handlers affected.
+ """
++
+ total = 0
+- for handler in handlers:
++ for handler in gdb._filter_missing_file_handlers(handlers, handler_type):
+ if name_re.match(handler.name) and handler.enabled != flag:
+ handler.enabled = flag
+ total += 1
+ return total
+
+
+-def do_enable_handler(arg, flag):
+- """Enable or disable missing debug handlers."""
+- (locus_re, name_re) = parse_missing_debug_command_args(arg)
++def do_enable_handler(arg, flag, handler_type):
++ """Enable or disable missing file handlers."""
++
++ (locus_re, name_re) = parse_missing_file_command_args(arg)
+ total = 0
+ if locus_re.match("global"):
+- total += do_enable_handler1(gdb.missing_debug_handlers, name_re, flag)
++ total += do_enable_handler1(
++ gdb.missing_file_handlers, name_re, flag, handler_type
++ )
+ if locus_re.match("progspace") and locus_re.pattern != "":
+ total += do_enable_handler1(
+- gdb.current_progspace().missing_debug_handlers, name_re, flag
++ gdb.current_progspace().missing_file_handlers, name_re, flag, handler_type
+ )
+ for progspace in gdb.progspaces():
+ filename = progspace.filename or ""
+ if locus_re.match(filename):
+- total += do_enable_handler1(progspace.missing_debug_handlers, name_re, flag)
++ total += do_enable_handler1(
++ progspace.missing_file_handlers, name_re, flag, handler_type
++ )
+ print(
+- "%d missing debug handler%s %s"
+- % (total, "" if total == 1 else "s", "enabled" if flag else "disabled")
++ "%d missing %s handler%s %s"
++ % (
++ total,
++ handler_type,
++ "" if total == 1 else "s",
++ "enabled" if flag else "disabled",
++ )
+ )
+
+
+-class EnableMissingDebugHandler(gdb.Command):
+- """GDB command to enable missing debug handlers.
++class EnableMissingFileHandler(gdb.Command):
++ """GDB command to enable missing HTYPE handlers.
+
+- Usage: enable missing-debug-handler [LOCUS-REGEXP [NAME-REGEXP]]
++ Usage: enable missing-HTYPE-handler [LOCUS-REGEXP [NAME-REGEXP]]
+
+ LOCUS-REGEXP is a regular expression specifying the handlers to
+ enable. It can be 'global', 'progspace' for the current
+@@ -187,18 +211,26 @@ class EnableMissingDebugHandler(gdb.Command):
+ in the locus are affected.
+ """
+
+- def __init__(self):
+- super().__init__("enable missing-debug-handler", gdb.COMMAND_FILES)
++ def __init__(self, handler_type):
++ # Update the doc string before calling the parent constructor,
++ # replacing the string 'HTYPE' with the value of HANDLER_TYPE.
++ # The parent constructor will grab a copy of this string to
++ # use as the commands help text.
++ self.__doc__ = self.__doc__.replace("HTYPE", handler_type)
++ super().__init__(
++ "enable missing-" + handler_type + "-handler", gdb.COMMAND_FILES
++ )
++ self.handler_type = handler_type
+
+ def invoke(self, arg, from_tty):
+ """GDB calls this to perform the command."""
+- do_enable_handler(arg, True)
++ do_enable_handler(arg, True, self.handler_type)
+
+
+-class DisableMissingDebugHandler(gdb.Command):
+- """GDB command to disable missing debug handlers.
++class DisableMissingFileHandler(gdb.Command):
++ """GDB command to disable missing HTYPE handlers.
+
+- Usage: disable missing-debug-handler [LOCUS-REGEXP [NAME-REGEXP]]
++ Usage: disable missing-HTYPE-handler [LOCUS-REGEXP [NAME-REGEXP]]
+
+ LOCUS-REGEXP is a regular expression specifying the handlers to
+ enable. It can be 'global', 'progspace' for the current
+@@ -209,19 +241,28 @@ class DisableMissingDebugHandler(gdb.Command):
+ in the locus are affected.
+ """
+
+- def __init__(self):
+- super().__init__("disable missing-debug-handler", gdb.COMMAND_FILES)
++ def __init__(self, handler_type):
++ # Update the doc string before calling the parent constructor,
++ # replacing the string 'HTYPE' with the value of HANDLER_TYPE.
++ # The parent constructor will grab a copy of this string to
++ # use as the commands help text.
++ self.__doc__ = self.__doc__.replace("HTYPE", handler_type)
++ super().__init__(
++ "disable missing-" + handler_type + "-handler", gdb.COMMAND_FILES
++ )
++ self.handler_type = handler_type
+
+ def invoke(self, arg, from_tty):
+ """GDB calls this to perform the command."""
+- do_enable_handler(arg, False)
++ do_enable_handler(arg, False, self.handler_type)
+
+
+-def register_missing_debug_handler_commands():
+- """Installs the missing debug handler commands."""
+- InfoMissingDebugHanders()
+- EnableMissingDebugHandler()
+- DisableMissingDebugHandler()
++def register_missing_file_handler_commands():
++ """Installs the missing file handler commands."""
++ for handler_type in ["debug", "objfile"]:
++ InfoMissingFileHandlers(handler_type)
++ EnableMissingFileHandler(handler_type)
++ DisableMissingFileHandler(handler_type)
+
+
+-register_missing_debug_handler_commands()
++register_missing_file_handler_commands()
+diff --git a/gdb/python/lib/gdb/missing_debug.py b/gdb/python/lib/gdb/missing_debug.py
+--- a/gdb/python/lib/gdb/missing_debug.py
++++ b/gdb/python/lib/gdb/missing_debug.py
+@@ -17,48 +17,11 @@
+ MissingDebugHandler base class, and register_handler function.
+ """
+
+-import sys
+-
+ import gdb
++from gdb.missing_files import MissingFileHandler
+
+-if sys.version_info >= (3, 7):
+- # Functions str.isascii() and str.isalnum are available starting Python
+- # 3.7.
+- def isascii(ch):
+- return ch.isascii()
+-
+- def isalnum(ch):
+- return ch.isalnum()
+-
+-else:
+- # Fall back to curses.ascii.isascii() and curses.ascii.isalnum() for
+- # earlier versions.
+- from curses.ascii import isalnum, isascii
+-
+-
+-def _validate_name(name):
+- """Validate a missing debug handler name string.
+-
+- If name is valid as a missing debug handler name, then this
+- function does nothing. If name is not valid then an exception is
+- raised.
+-
+- Arguments:
+- name: A string, the name of a missing debug handler.
+-
+- Returns:
+- Nothing.
+
+- Raises:
+- ValueError: If name is invalid as a missing debug handler
+- name.
+- """
+- for ch in name:
+- if not isascii(ch) or not (isalnum(ch) or ch in "_-"):
+- raise ValueError("invalid character '%s' in handler name: %s" % (ch, name))
+-
+-
+-class MissingDebugHandler(object):
++class MissingDebugHandler(MissingFileHandler):
+ """Base class for missing debug handlers written in Python.
+
+ A missing debug handler has a single method __call__ along with
+@@ -69,41 +32,8 @@ class MissingDebugHandler(object):
+ enabled: When true this handler is enabled.
+ """
+
+- def __init__(self, name):
+- """Constructor.
+-
+- Args:
+- name: An identifying name for this handler.
+-
+- Raises:
+- TypeError: name is not a string.
+- ValueError: name contains invalid characters.
+- """
+-
+- if not isinstance(name, str):
+- raise TypeError("incorrect type for name: %s" % type(name))
+-
+- _validate_name(name)
+-
+- self._name = name
+- self._enabled = True
+-
+- @property
+- def name(self):
+- return self._name
+-
+- @property
+- def enabled(self):
+- return self._enabled
+-
+- @enabled.setter
+- def enabled(self, value):
+- if not isinstance(value, bool):
+- raise TypeError("incorrect type for enabled attribute: %s" % type(value))
+- self._enabled = value
+-
+ def __call__(self, objfile):
+- """GDB handle missing debug information for an objfile.
++ """Handle missing debug information for an objfile.
+
+ Arguments:
+ objfile: A gdb.Objfile for which GDB could not find any
+@@ -124,62 +54,5 @@ class MissingDebugHandler(object):
+
+
+ def register_handler(locus, handler, replace=False):
+- """Register handler in given locus.
+-
+- The handler is prepended to the locus's missing debug handlers
+- list. The name of handler should be unique (or replace must be
+- True).
+-
+- Arguments:
+- locus: Either a progspace, or None (in which case the unwinder
+- is registered globally).
+- handler: An object of a gdb.MissingDebugHandler subclass.
+-
+- replace: If True, replaces existing handler with the same name
+- within locus. Otherwise, raises RuntimeException if
+- unwinder with the same name already exists.
+-
+- Returns:
+- Nothing.
+-
+- Raises:
+- RuntimeError: The name of handler is not unique.
+- TypeError: Bad locus type.
+- AttributeError: Required attributes of handler are missing.
+- """
+-
+- if locus is None:
+- if gdb.parameter("verbose"):
+- gdb.write("Registering global %s handler ...\n" % handler.name)
+- locus = gdb
+- elif isinstance(locus, gdb.Progspace):
+- if gdb.parameter("verbose"):
+- gdb.write(
+- "Registering %s handler for %s ...\n" % (handler.name, locus.filename)
+- )
+- else:
+- raise TypeError("locus should be gdb.Progspace or None")
+-
+- # Some sanity checks on HANDLER. Calling getattr will raise an
+- # exception if the attribute doesn't exist, which is what we want.
+- # These checks are not exhaustive; we don't check the attributes
+- # have the correct types, or the method has the correct signature,
+- # but this should catch some basic mistakes.
+- getattr(handler, "name")
+- getattr(handler, "enabled")
+- call_method = getattr(handler, "__call__")
+- if not callable(call_method):
+- raise AttributeError(
+- "'%s' object's '__call__' attribute is not callable"
+- % type(handler).__name__
+- )
+-
+- i = 0
+- for needle in locus.missing_debug_handlers:
+- if needle.name == handler.name:
+- if replace:
+- del locus.missing_debug_handlers[i]
+- else:
+- raise RuntimeError("Handler %s already exists." % handler.name)
+- i += 1
+- locus.missing_debug_handlers.insert(0, handler)
++ """See gdb.missing_files.register_handler."""
++ gdb.missing_files.register_handler("debug", locus, handler, replace)
+diff --git a/gdb/python/lib/gdb/missing_files.py b/gdb/python/lib/gdb/missing_files.py
+new file mode 100644
+--- /dev/null
++++ b/gdb/python/lib/gdb/missing_files.py
+@@ -0,0 +1,204 @@
++# Copyright (C) 2023-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 <http://www.gnu.org/licenses/>.
++
++"""
++MissingFileHandler base class, and support functions used by the
++missing_debug.py and missing_objfile.py modules.
++"""
++
++import sys
++
++import gdb
++
++if sys.version_info >= (3, 7):
++ # Functions str.isascii() and str.isalnum are available starting Python
++ # 3.7.
++ def isascii(ch):
++ return ch.isascii()
++
++ def isalnum(ch):
++ return ch.isalnum()
++
++else:
++ # Older version of Python doesn't have str.isascii() and
++ # str.isalnum() so provide our own.
++ #
++ # We could import isalnum() and isascii() from the curses library,
++ # but that adds an extra dependency. Given these functions are
++ # both small and trivial lets implement them here.
++ #
++ # These definitions are based on those in the curses library, but
++ # simplified as we know C will always be a single character 'str'.
++
++ def isdigit(c):
++ return 48 <= ord(c) <= 57
++
++ def islower(c):
++ return 97 <= ord(c) <= 122
++
++ def isupper(c):
++ return 65 <= ord(c) <= 90
++
++ def isalpha(c):
++ return isupper(c) or islower(c)
++
++ def isalnum(c):
++ return isalpha(c) or isdigit(c)
++
++ def isascii(c):
++ return 0 <= ord(c) <= 127
++
++
++def _validate_name(name):
++ """Validate a missing file handler name string.
++
++ If name is valid as a missing file handler name, then this
++ function does nothing. If name is not valid then an exception is
++ raised.
++
++ Arguments:
++ name: A string, the name of a missing file handler.
++
++ Returns:
++ Nothing.
++
++ Raises:
++ ValueError: If name is invalid as a missing file handler
++ name.
++ """
++
++ for ch in name:
++ if not isascii(ch) or not (isalnum(ch) or ch in "_-"):
++ raise ValueError("invalid character '%s' in handler name: %s" % (ch, name))
++
++
++class MissingFileHandler(object):
++ """Base class for missing file handlers written in Python.
++
++ A missing file handler has a single method __call__ along with the
++ read/write attribute enabled, and a read-only attribute name. The
++ attributes are provided by this class while the __call__ method is
++ provided by a sub-class. Each sub-classes __call__ method will
++ have a different signature.
++
++ Attributes:
++ name: Read-only attribute, the name of this handler.
++ enabled: When true this handler is enabled.
++ """
++
++ def __init__(self, name):
++ """Constructor.
++
++ Args:
++ name: An identifying name for this handler.
++
++ Raises:
++ TypeError: name is not a string.
++ ValueError: name contains invalid characters.
++ """
++
++ if not isinstance(name, str):
++ raise TypeError("incorrect type for name: %s" % type(name))
++
++ _validate_name(name)
++
++ self._name = name
++ self._enabled = True
++
++ @property
++ def name(self):
++ return self._name
++
++ @property
++ def enabled(self):
++ return self._enabled
++
++ @enabled.setter
++ def enabled(self, value):
++ if not isinstance(value, bool):
++ raise TypeError("incorrect type for enabled attribute: %s" % type(value))
++ self._enabled = value
++
++
++def register_handler(handler_type, locus, handler, replace=False):
++ """Register handler in given locus.
++
++ The handler is prepended to the locus's missing file handlers
++ list. The name of handler should be unique (or replace must be
++ True), and the name must pass the _validate_name check.
++
++ Arguments:
++ handler_type: A string, either 'debug' or 'objfile' indicating the
++ type of handler to be registered.
++ locus: Either a progspace, or None (in which case the unwinder
++ is registered globally).
++ handler: An object used as a missing file handler. Usually a
++ sub-class of MissingFileHandler.
++ replace: If True, replaces existing handler with the same name
++ within locus. Otherwise, raises RuntimeException if
++ unwinder with the same name already exists.
++
++ Returns:
++ Nothing.
++
++ Raises:
++ RuntimeError: The name of handler is not unique.
++ TypeError: Bad locus type.
++ AttributeError: Required attributes of handler are missing.
++ ValueError: If the name of the handler is invalid, or if
++ handler_type is neither 'debug' or 'objfile'.
++ """
++
++ if handler_type != "debug" and handler_type != "objfile":
++ raise ValueError("handler_type must be 'debug' or 'objfile'")
++
++ if locus is None:
++ if gdb.parameter("verbose"):
++ gdb.write("Registering global %s handler ...\n" % handler.name)
++ locus = gdb
++ elif isinstance(locus, gdb.Progspace):
++ if gdb.parameter("verbose"):
++ gdb.write(
++ "Registering %s handler for %s ...\n" % (handler.name, locus.filename)
++ )
++ else:
++ raise TypeError("locus should be gdb.Progspace or None")
++
++ # Some sanity checks on HANDLER. Calling getattr will raise an
++ # exception if the attribute doesn't exist, which is what we want.
++ # These checks are not exhaustive; we don't check the attributes
++ # have the correct types, or the method has the correct signature,
++ # but this should catch some basic mistakes.
++ name = getattr(handler, "name")
++ _validate_name(name)
++
++ getattr(handler, "enabled")
++
++ call_method = getattr(handler, "__call__")
++ if not callable(call_method):
++ raise AttributeError(
++ "'%s' object's '__call__' attribute is not callable"
++ % type(handler).__name__
++ )
++
++ i = 0
++ for needle in locus.missing_file_handlers:
++ if needle[0] == handler_type and needle[1].name == handler.name:
++ if replace:
++ del locus.missing_file_handlers[i]
++ else:
++ raise RuntimeError("Handler %s already exists." % handler.name)
++ i += 1
++ locus.missing_file_handlers.insert(0, (handler_type, handler))
+diff --git a/gdb/python/lib/gdb/missing_objfile.py b/gdb/python/lib/gdb/missing_objfile.py
+new file mode 100644
+--- /dev/null
++++ b/gdb/python/lib/gdb/missing_objfile.py
+@@ -0,0 +1,67 @@
++# Copyright (C) 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 <http://www.gnu.org/licenses/>.
++
++"""
++MissingObjfileHandler base class, and register_handler function.
++"""
++
++import gdb
++from gdb.missing_files import MissingFileHandler
++
++
++class MissingObjfileHandler(MissingFileHandler):
++ """Base class for missing objfile handlers written in Python.
++
++ A missing objfile handler has a single method __call__ along with
++ the read/write attribute enabled, and a read-only attribute name.
++
++ Attributes:
++ name: Read-only attribute, the name of this handler.
++ enabled: When true this handler is enabled.
++ """
++
++ def __call__(self, buildid, filename):
++ """Handle a missing objfile when GDB can knows the build-id.
++
++ Arguments:
++
++ buildid: A string containing the build-id for the objfile
++ GDB is searching for.
++ filename: A string containing the name of the file GDB is
++ searching for. This is provided only for the purpose
++ of creating diagnostic messages. If the file is found
++ it does not have to be placed here, and this file
++ might already exist but GDB has determined it is not
++ suitable for use, e.g. if the build-id doesn't match.
++
++ Returns:
++
++ True: GDB should try again to locate the missing objfile,
++ the handler may have installed the missing file.
++ False: GDB should move on without the objfile. The
++ handler has determined that this objfile is not
++ available.
++ A string: GDB should load the file at the given path; it
++ contains the requested objfile.
++ None: This handler can't help with this objfile. GDB
++ should try any other registered handlers.
++
++ """
++ raise NotImplementedError("MissingObjfileHandler.__call__()")
++
++
++def register_handler(locus, handler, replace=False):
++ """See gdb.missing_files.register_handler."""
++ gdb.missing_files.register_handler("objfile", locus, handler, replace)
+diff --git a/gdb/python/py-progspace.c b/gdb/python/py-progspace.c
+--- a/gdb/python/py-progspace.c
++++ b/gdb/python/py-progspace.c
+@@ -55,8 +55,8 @@ struct pspace_object
+ /* The debug method list. */
+ PyObject *xmethods;
+
+- /* The missing debug handler list. */
+- PyObject *missing_debug_handlers;
++ /* The missing file handler list. */
++ PyObject *missing_file_handlers;
+ };
+
+ extern PyTypeObject pspace_object_type
+@@ -166,7 +166,7 @@ pspy_dealloc (PyObject *self)
+ Py_XDECREF (ps_self->frame_unwinders);
+ Py_XDECREF (ps_self->type_printers);
+ Py_XDECREF (ps_self->xmethods);
+- Py_XDECREF (ps_self->missing_debug_handlers);
++ Py_XDECREF (ps_self->missing_file_handlers);
+ Py_TYPE (self)->tp_free (self);
+ }
+
+@@ -202,8 +202,8 @@ pspy_initialize (pspace_object *self)
+ if (self->xmethods == NULL)
+ return 0;
+
+- self->missing_debug_handlers = PyList_New (0);
+- if (self->missing_debug_handlers == nullptr)
++ self->missing_file_handlers = PyList_New (0);
++ if (self->missing_file_handlers == nullptr)
+ return 0;
+
+ return 1;
+@@ -349,18 +349,18 @@ pspy_get_xmethods (PyObject *o, void *ignore)
+ /* Return the list of missing debug handlers for this program space. */
+
+ static PyObject *
+-pspy_get_missing_debug_handlers (PyObject *o, void *ignore)
++pspy_get_missing_file_handlers (PyObject *o, void *ignore)
+ {
+ pspace_object *self = (pspace_object *) o;
+
+- Py_INCREF (self->missing_debug_handlers);
+- return self->missing_debug_handlers;
++ Py_INCREF (self->missing_file_handlers);
++ return self->missing_file_handlers;
+ }
+
+ /* Set this program space's list of missing debug handlers to HANDLERS. */
+
+ static int
+-pspy_set_missing_debug_handlers (PyObject *o, PyObject *handlers,
++pspy_set_missing_file_handlers (PyObject *o, PyObject *handlers,
+ void *ignore)
+ {
+ pspace_object *self = (pspace_object *) o;
+@@ -380,9 +380,9 @@ pspy_set_missing_debug_handlers (PyObject *o, PyObject *handlers,
+ }
+
+ /* Take care in case the LHS and RHS are related somehow. */
+- gdbpy_ref<> tmp (self->missing_debug_handlers);
++ gdbpy_ref<> tmp (self->missing_file_handlers);
+ Py_INCREF (handlers);
+- self->missing_debug_handlers = handlers;
++ self->missing_file_handlers = handlers;
+
+ return 0;
+ }
+@@ -779,8 +779,8 @@ static gdb_PyGetSetDef pspace_getset[] =
+ "Type printers.", NULL },
+ { "xmethods", pspy_get_xmethods, NULL,
+ "Debug methods.", NULL },
+- { "missing_debug_handlers", pspy_get_missing_debug_handlers,
+- pspy_set_missing_debug_handlers, "Missing debug handlers.", NULL },
++ { "missing_file_handlers", pspy_get_missing_file_handlers,
++ pspy_set_missing_file_handlers, "Missing file handlers.", NULL },
+ { NULL }
+ };
+
+diff --git a/gdb/python/python.c b/gdb/python/python.c
+--- a/gdb/python/python.c
++++ b/gdb/python/python.c
+@@ -35,6 +35,7 @@
+ #include "location.h"
+ #include "run-on-main-thread.h"
+ #include "observable.h"
++#include "build-id.h"
+
+ #if GDB_SELF_TEST
+ #include "gdbsupport/selftest.h"
+@@ -128,8 +129,11 @@ static std::optional<std::string> gdbpy_colorize
+ (const std::string &filename, const std::string &contents);
+ static std::optional<std::string> gdbpy_colorize_disasm
+ (const std::string &content, gdbarch *gdbarch);
+-static ext_lang_missing_debuginfo_result gdbpy_handle_missing_debuginfo
++static ext_lang_missing_file_result gdbpy_handle_missing_debuginfo
+ (const struct extension_language_defn *extlang, struct objfile *objfile);
++static ext_lang_missing_file_result gdbpy_find_objfile_from_buildid
++ (const struct extension_language_defn *extlang, program_space *pspace,
++ const struct bfd_build_id *build_id, const char *missing_filename);
+
+ /* The interface between gdb proper and loading of python scripts. */
+
+@@ -177,7 +181,8 @@ static const struct extension_language_ops python_extension_ops =
+
+ gdbpy_print_insn,
+
+- gdbpy_handle_missing_debuginfo
++ gdbpy_handle_missing_debuginfo,
++ gdbpy_find_objfile_from_buildid
+ };
+
+ #endif /* HAVE_PYTHON */
+@@ -1761,10 +1766,10 @@ gdbpy_get_current_objfile (PyObject *unused1, PyObject *unused2)
+ /* Implement the 'handle_missing_debuginfo' hook for Python. GDB has
+ failed to find any debug information for OBJFILE. The extension has a
+ chance to record this, or even install the required debug information.
+- See the description of ext_lang_missing_debuginfo_result in
+- extension-priv.h for details of the return value. */
++ See the description of ext_lang_missing_file_result in extension-priv.h
++ for details of the return value. */
+
+-static ext_lang_missing_debuginfo_result
++static ext_lang_missing_file_result
+ gdbpy_handle_missing_debuginfo (const struct extension_language_defn *extlang,
+ struct objfile *objfile)
+ {
+@@ -1812,7 +1817,7 @@ gdbpy_handle_missing_debuginfo (const struct extension_language_defn *extlang,
+ if (PyBool_Check (pyo_execute_ret.get ()))
+ {
+ bool try_again = PyObject_IsTrue (pyo_execute_ret.get ());
+- return ext_lang_missing_debuginfo_result (try_again);
++ return ext_lang_missing_file_result (try_again);
+ }
+
+ if (!gdbpy_is_string (pyo_execute_ret.get ()))
+@@ -1832,7 +1837,108 @@ gdbpy_handle_missing_debuginfo (const struct extension_language_defn *extlang,
+ return {};
+ }
+
+- return ext_lang_missing_debuginfo_result (std::string (filename.get ()));
++ return ext_lang_missing_file_result (std::string (filename.get ()));
++}
++
++/* Implement the find_objfile_from_buildid hook for Python. PSPACE is the
++ program space in which GDB is trying to find an objfile, BUILD_ID is the
++ build-id for the missing objfile, and EXPECTED_FILENAME is a non-NULL
++ string which can be used (if needed) in messages to the user, and
++ represents the file GDB is looking for. */
++
++static ext_lang_missing_file_result
++gdbpy_find_objfile_from_buildid (const struct extension_language_defn *extlang,
++ program_space *pspace,
++ const struct bfd_build_id *build_id,
++ const char *missing_filename)
++{
++ gdb_assert (pspace != nullptr);
++ gdb_assert (build_id != nullptr);
++ gdb_assert (missing_filename != nullptr);
++
++ /* Early exit if Python is not initialised. */
++ if (!gdb_python_initialized || gdb_python_module == nullptr)
++ return {};
++
++ gdbpy_enter enter_py;
++
++ /* Convert BUILD_ID into a Python object. */
++ std::string hex_form = bin2hex (build_id->data, build_id->size);
++ gdbpy_ref<> pyo_buildid = host_string_to_python_string (hex_form.c_str ());
++ if (pyo_buildid == nullptr)
++ {
++ gdbpy_print_stack ();
++ return {};
++ }
++
++ /* Convert MISSING_FILENAME to a Python object. */
++ gdbpy_ref<> pyo_filename = host_string_to_python_string (missing_filename);
++ if (pyo_filename == nullptr)
++ {
++ gdbpy_print_stack ();
++ return {};
++ }
++
++ /* Convert PSPACE to a Python object. */
++ gdbpy_ref<> pyo_pspace = pspace_to_pspace_object (pspace);
++ if (pyo_pspace == nullptr)
++ {
++ gdbpy_print_stack ();
++ return {};
++ }
++
++ /* Lookup the helper function within the GDB module. */
++ gdbpy_ref<> pyo_handler
++ (PyObject_GetAttrString (gdb_python_module, "_handle_missing_objfile"));
++ if (pyo_handler == nullptr)
++ {
++ gdbpy_print_stack ();
++ return {};
++ }
++
++ /* Call the function, passing in the Python objfile object. */
++ gdbpy_ref<> pyo_execute_ret
++ (PyObject_CallFunctionObjArgs (pyo_handler.get (), pyo_pspace.get (),
++ pyo_buildid.get (), pyo_filename.get (),
++ nullptr));
++ if (pyo_execute_ret == nullptr)
++ {
++ /* If the handler is cancelled due to a Ctrl-C, then propagate
++ the Ctrl-C as a GDB exception instead of swallowing it. */
++ gdbpy_print_stack_or_quit ();
++ return {};
++ }
++
++ /* Parse the result, and convert it back to the C++ object. */
++ if (pyo_execute_ret == Py_None)
++ return {};
++
++ if (PyBool_Check (pyo_execute_ret.get ()))
++ {
++ /* We know the value is a bool, so it must be either Py_True or
++ Py_False. Anything else would not get past the above check. */
++ bool try_again = pyo_execute_ret.get () == Py_True;
++ return ext_lang_missing_file_result (try_again);
++ }
++
++ if (!gdbpy_is_string (pyo_execute_ret.get ()))
++ {
++ PyErr_SetString (PyExc_ValueError,
++ "return value from _find_objfile_by_buildid should "
++ "be None, a bool, or a str");
++ gdbpy_print_stack ();
++ return {};
++ }
++
++ gdb::unique_xmalloc_ptr<char> filename
++ = python_string_to_host_string (pyo_execute_ret.get ());
++ if (filename == nullptr)
++ {
++ gdbpy_print_stack ();
++ return {};
++ }
++
++ return ext_lang_missing_file_result (std::string (filename.get ()));
+ }
+
+ /* Compute the list of active python type printers and store them in
+diff --git a/gdb/remote.c b/gdb/remote.c
+--- a/gdb/remote.c
++++ b/gdb/remote.c
+@@ -246,6 +246,7 @@ enum {
+ PACKET_vFile_unlink,
+ PACKET_vFile_readlink,
+ PACKET_vFile_fstat,
++ PACKET_vFile_stat,
+ PACKET_qXfer_auxv,
+ PACKET_qXfer_features,
+ PACKET_qXfer_exec_file,
+@@ -996,6 +997,9 @@ class remote_target : public process_stratum_target
+
+ int fileio_fstat (int fd, struct stat *sb, fileio_error *target_errno) override;
+
++ int fileio_stat (struct inferior *inf, const char *filename,
++ struct stat *sb, fileio_error *target_errno) override;
++
+ int fileio_close (int fd, fileio_error *target_errno) override;
+
+ int fileio_unlink (struct inferior *inf,
+@@ -12661,6 +12665,9 @@ remote_target::remote_hostio_send_command (int command_bytes, int which_packet,
+ return -1;
+ }
+
++ if (*remote_errno != FILEIO_SUCCESS)
++ return -1;
++
+ /* Make sure we saw an attachment if and only if we expected one. */
+ if ((attachment_tmp == NULL && attachment != NULL)
+ || (attachment_tmp != NULL && attachment == NULL))
+@@ -13027,6 +13034,41 @@ remote_target::fileio_readlink (struct inferior *inf, const char *filename,
+ return ret;
+ }
+
++/* Helper function to handle ::fileio_fstat and ::fileio_stat result
++ processing. When this function is called the remote syscall has been
++ performed and we know we didn't get an error back.
++
++ ATTACHMENT and ATTACHMENT_LEN are the attachment data extracted from the
++ remote syscall reply. EXPECTED_LEN is the length returned from the
++ fstat or stat call, this the length of the returned data (in ATTACHMENT)
++ once it has been decoded. The fstat/stat result (from the ATTACHMENT
++ data) is to be placed in ST. */
++
++static int
++fileio_process_fstat_and_stat_reply (const char *attachment,
++ int attachment_len,
++ int expected_len,
++ struct stat *st)
++{
++ struct fio_stat fst;
++
++ int read_len
++ = remote_unescape_input ((gdb_byte *) attachment, attachment_len,
++ (gdb_byte *) &fst, sizeof (fst));
++
++ if (read_len != expected_len)
++ error (_("vFile:fstat returned %d, but %d bytes."),
++ expected_len, read_len);
++
++ if (read_len != sizeof (fst))
++ error (_("vFile:fstat returned %d bytes, but expecting %d."),
++ read_len, (int) sizeof (fst));
++
++ remote_fileio_to_host_stat (&fst, st);
++
++ return 0;
++}
++
+ /* Implementation of to_fileio_fstat. */
+
+ int
+@@ -13037,8 +13079,6 @@ remote_target::fileio_fstat (int fd, struct stat *st, fileio_error *remote_errno
+ int left = get_remote_packet_size ();
+ int attachment_len, ret;
+ const char *attachment;
+- struct fio_stat fst;
+- int read_len;
+
+ remote_buffer_add_string (&p, &left, "vFile:fstat:");
+
+@@ -13070,19 +13110,41 @@ remote_target::fileio_fstat (int fd, struct stat *st, fileio_error *remote_errno
+ return 0;
+ }
+
+- read_len = remote_unescape_input ((gdb_byte *) attachment, attachment_len,
+- (gdb_byte *) &fst, sizeof (fst));
++ return fileio_process_fstat_and_stat_reply (attachment, attachment_len,
++ ret, st);
++}
+
+- if (read_len != ret)
+- error (_("vFile:fstat returned %d, but %d bytes."), ret, read_len);
++/* Implementation of to_fileio_stat. */
+
+- if (read_len != sizeof (fst))
+- error (_("vFile:fstat returned %d bytes, but expecting %d."),
+- read_len, (int) sizeof (fst));
++int
++remote_target::fileio_stat (struct inferior *inf, const char *filename,
++ struct stat *st, fileio_error *remote_errno)
++{
++ struct remote_state *rs = get_remote_state ();
++ char *p = rs->buf.data ();
++ int left = get_remote_packet_size () - 1;
+
+- remote_fileio_to_host_stat (&fst, st);
++ if (remote_hostio_set_filesystem (inf, remote_errno) != 0)
++ return {};
+
+- return 0;
++ remote_buffer_add_string (&p, &left, "vFile:stat:");
++
++ remote_buffer_add_bytes (&p, &left, (const gdb_byte *) filename,
++ strlen (filename));
++
++ int attachment_len;
++ const char *attachment;
++ int ret = remote_hostio_send_command (p - rs->buf.data (), PACKET_vFile_stat,
++ remote_errno, &attachment,
++ &attachment_len);
++
++ /* Unlike ::fileio_fstat, the stat fileio call was added later on, and
++ has none of the legacy bfd issues, so we can just return the error. */
++ if (ret < 0)
++ return ret;
++
++ return fileio_process_fstat_and_stat_reply (attachment, attachment_len,
++ ret, st);
+ }
+
+ /* Implementation of to_filesystem_is_local. */
+@@ -16157,6 +16219,8 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
+
+ add_packet_config_cmd (PACKET_vFile_fstat, "vFile:fstat", "hostio-fstat", 0);
+
++ add_packet_config_cmd (PACKET_vFile_stat, "vFile:stat", "hostio-stat", 0);
++
+ add_packet_config_cmd (PACKET_vAttach, "vAttach", "attach", 0);
+
+ add_packet_config_cmd (PACKET_vRun, "vRun", "run", 0);
+diff --git a/gdb/solib-aix.c b/gdb/solib-aix.c
+--- a/gdb/solib-aix.c
++++ b/gdb/solib-aix.c
+@@ -689,6 +689,11 @@ const solib_ops solib_aix_so_ops =
+ solib_aix_open_symbol_file_object,
+ solib_aix_in_dynsym_resolve_code,
+ solib_aix_bfd_open,
++ nullptr,
++ nullptr,
++ nullptr,
++ nullptr,
++ default_find_solib_addr,
+ };
+
+ void _initialize_solib_aix ();
+diff --git a/gdb/solib-darwin.c b/gdb/solib-darwin.c
+--- a/gdb/solib-darwin.c
++++ b/gdb/solib-darwin.c
+@@ -665,4 +665,9 @@ const solib_ops darwin_so_ops =
+ open_symbol_file_object,
+ darwin_in_dynsym_resolve_code,
+ darwin_bfd_open,
++ nullptr,
++ nullptr,
++ nullptr,
++ nullptr,
++ default_find_solib_addr,
+ };
+diff --git a/gdb/solib-dsbt.c b/gdb/solib-dsbt.c
+--- a/gdb/solib-dsbt.c
++++ b/gdb/solib-dsbt.c
+@@ -914,6 +914,11 @@ const solib_ops dsbt_so_ops =
+ open_symbol_file_object,
+ dsbt_in_dynsym_resolve_code,
+ solib_bfd_open,
++ nullptr,
++ nullptr,
++ nullptr,
++ nullptr,
++ default_find_solib_addr,
+ };
+
+ void _initialize_dsbt_solib ();
+diff --git a/gdb/solib-frv.c b/gdb/solib-frv.c
+--- a/gdb/solib-frv.c
++++ b/gdb/solib-frv.c
+@@ -1086,4 +1086,9 @@ const solib_ops frv_so_ops =
+ open_symbol_file_object,
+ frv_in_dynsym_resolve_code,
+ solib_bfd_open,
++ nullptr,
++ nullptr,
++ nullptr,
++ nullptr,
++ default_find_solib_addr,
+ };
+diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c
+--- a/gdb/solib-svr4.c
++++ b/gdb/solib-svr4.c
+@@ -3353,6 +3353,15 @@ svr4_iterate_over_objfiles_in_search_order
+ }
+ }
+
++/* See solib_ops::find_solib_addr in solist.h. */
++
++static std::optional<CORE_ADDR>
++svr4_find_solib_addr (solib &so)
++{
++ auto *li = gdb::checked_static_cast<lm_info_svr4 *> (so.lm_info.get ());
++ return li->l_addr_inferior;
++}
++
+ const struct solib_ops svr4_so_ops =
+ {
+ svr4_relocate_section_addresses,
+@@ -3363,11 +3372,11 @@ const struct solib_ops svr4_so_ops =
+ open_symbol_file_object,
+ svr4_in_dynsym_resolve_code,
+ solib_bfd_open,
+- nullptr,
+ svr4_same,
+ svr4_keep_data_in_core,
+ svr4_update_solib_event_breakpoints,
+ svr4_handle_solib_event,
++ svr4_find_solib_addr,
+ };
+
+ void _initialize_svr4_solib ();
+diff --git a/gdb/solib-target.c b/gdb/solib-target.c
+--- a/gdb/solib-target.c
++++ b/gdb/solib-target.c
+@@ -412,4 +412,9 @@ const solib_ops solib_target_so_ops =
+ solib_target_open_symbol_file_object,
+ solib_target_in_dynsym_resolve_code,
+ solib_bfd_open,
++ nullptr,
++ nullptr,
++ nullptr,
++ nullptr,
++ default_find_solib_addr,
+ };
+diff --git a/gdb/solib.c b/gdb/solib.c
+--- a/gdb/solib.c
++++ b/gdb/solib.c
+@@ -45,7 +45,6 @@
+ #include "gdb_bfd.h"
+ #include "gdbsupport/filestuff.h"
+ #include "gdbsupport/scoped_fd.h"
+-#include "debuginfod-support.h"
+ #include "source.h"
+ #include "cli/cli-style.h"
+
+@@ -113,7 +112,6 @@ show_solib_search_path (struct ui_file *file, int from_tty,
+ static gdb::unique_xmalloc_ptr<char>
+ solib_find_1 (const char *in_pathname, int *fd, bool is_solib)
+ {
+- const solib_ops *ops = gdbarch_so_ops (current_inferior ()->arch ());
+ int found_file = -1;
+ gdb::unique_xmalloc_ptr<char> temp_pathname;
+ const char *fskind = effective_target_file_system_kind ();
+@@ -296,12 +294,6 @@ solib_find_1 (const char *in_pathname, int *fd, bool is_solib)
+ target_lbasename (fskind, in_pathname),
+ O_RDONLY | O_BINARY, &temp_pathname);
+
+- /* If not found, and we're looking for a solib, try to use target
+- supplied solib search method. */
+- if (is_solib && found_file < 0 && ops->find_and_open_solib)
+- found_file = ops->find_and_open_solib (in_pathname, O_RDONLY | O_BINARY,
+- &temp_pathname);
+-
+ /* If not found, next search the inferior's $PATH environment variable. */
+ if (found_file < 0 && sysroot == NULL)
+ found_file = openp (current_inferior ()->environment.get ("PATH"),
+@@ -475,58 +467,6 @@ solib_bfd_open (const char *pathname)
+ return abfd;
+ }
+
+-/* Mapping of a core file's shared library sonames to their respective
+- build-ids. Added to the registries of core file bfds. */
+-
+-typedef std::unordered_map<std::string, std::string> soname_build_id_map;
+-
+-/* Key used to associate a soname_build_id_map to a core file bfd. */
+-
+-static const struct registry<bfd>::key<soname_build_id_map>
+- cbfd_soname_build_id_data_key;
+-
+-/* See solib.h. */
+-
+-void
+-set_cbfd_soname_build_id (gdb_bfd_ref_ptr abfd, const char *soname,
+- const bfd_build_id *build_id)
+-{
+- gdb_assert (abfd.get () != nullptr);
+- gdb_assert (soname != nullptr);
+- gdb_assert (build_id != nullptr);
+-
+- soname_build_id_map *mapptr
+- = cbfd_soname_build_id_data_key.get (abfd.get ());
+-
+- if (mapptr == nullptr)
+- mapptr = cbfd_soname_build_id_data_key.emplace (abfd.get ());
+-
+- (*mapptr)[soname] = build_id_to_string (build_id);
+-}
+-
+-/* If SONAME had a build-id associated with it in ABFD's registry by a
+- previous call to set_cbfd_soname_build_id then return the build-id
+- as a NULL-terminated hex string. */
+-
+-static gdb::unique_xmalloc_ptr<char>
+-get_cbfd_soname_build_id (gdb_bfd_ref_ptr abfd, const char *soname)
+-{
+- if (abfd.get () == nullptr || soname == nullptr)
+- return {};
+-
+- soname_build_id_map *mapptr
+- = cbfd_soname_build_id_data_key.get (abfd.get ());
+-
+- if (mapptr == nullptr)
+- return {};
+-
+- auto it = mapptr->find (lbasename (soname));
+- if (it == mapptr->end ())
+- return {};
+-
+- return make_unique_xstrdup (it->second.c_str ());
+-}
+-
+ /* Given a pointer to one of the shared objects in our list of mapped
+ objects, use the recorded name to open a bfd descriptor for the
+ object, build a section table, relocate all the section addresses
+@@ -546,36 +486,55 @@ solib_map_sections (solib &so)
+
+ gdb::unique_xmalloc_ptr<char> filename (tilde_expand (so.so_name.c_str ()));
+ gdb_bfd_ref_ptr abfd (ops->bfd_open (filename.get ()));
+- gdb::unique_xmalloc_ptr<char> build_id_hexstr
+- = get_cbfd_soname_build_id (current_program_space->cbfd,
+- so.so_name.c_str ());
++
++ /* If we have a core target then the core target might have some helpful
++ information (i.e. build-ids) about the shared libraries we are trying
++ to load. Grab those hints now and use the below to validate or find
++ the shared libraries.
++
++ If we don't have a core target then this will return an empty struct
++ with no hint information, we then lookup the shared library based on
++ its filename. */
++ std::optional<CORE_ADDR> solib_addr = ops->find_solib_addr (so);
++ std::optional <const core_target_mapped_file_info> mapped_file_info
++ = core_target_find_mapped_file (so.so_name.c_str (), solib_addr);
+
+ /* If we already know the build-id of this solib from a core file, verify
+ it matches ABFD's build-id. If there is a mismatch or the solib wasn't
+ found, attempt to query debuginfod for the correct solib. */
+- if (build_id_hexstr.get () != nullptr)
++ if (mapped_file_info.has_value ())
+ {
+- bool mismatch = false;
++ bool mismatch = (abfd != nullptr
++ && build_id_bfd_get (abfd.get ()) != nullptr
++ && !build_id_equal (mapped_file_info->build_id (),
++ build_id_bfd_get (abfd.get ())));
+
+- if (abfd != nullptr && abfd->build_id != nullptr)
+- {
+- std::string build_id = build_id_to_string (abfd->build_id);
+-
+- if (build_id != build_id_hexstr.get ())
+- mismatch = true;
+- }
+ if (abfd == nullptr || mismatch)
+ {
+- scoped_fd fd = debuginfod_exec_query (
+- (const unsigned char *) build_id_hexstr.get (), 0,
+- so.so_name.c_str (), &filename);
+-
+- if (fd.get () >= 0)
+- abfd = ops->bfd_open (filename.get ());
+- else if (mismatch)
+- warning (_ ("Build-id of %ps does not match core file."),
+- styled_string (file_name_style.style (),
+- filename.get ()));
++ /* If GDB found a suitable file during the file mapping
++ processing stage then lets use that. We don't check the
++ build-id after opening this file, either this file was found
++ by build-id, in which case it's going to match, or this file
++ doesn't have a build-id, so checking tells us nothing.
++ However, if it was good enough during the mapped file
++ processing, we assume it's good enough now. */
++ if (!mapped_file_info->filename ().empty ())
++ abfd = ops->bfd_open (mapped_file_info->filename ().c_str ());
++ else
++ abfd = nullptr;
++
++ if (abfd == nullptr)
++ abfd = find_objfile_by_build_id (current_program_space,
++ mapped_file_info->build_id (),
++ so.so_name.c_str ());
++
++ if (abfd == nullptr && mismatch)
++ {
++ warning (_ ("Build-id of %ps does not match core file."),
++ styled_string (file_name_style.style (),
++ filename.get ()));
++ abfd = nullptr;
++ }
+ }
+ }
+
+@@ -1705,6 +1664,14 @@ remove_user_added_objfile (struct objfile *objfile)
+ }
+ }
+
++/* See solist.h. */
++
++std::optional<CORE_ADDR>
++default_find_solib_addr (solib &so)
++{
++ return {};
++}
++
+ void _initialize_solib ();
+
+ void
+diff --git a/gdb/solib.h b/gdb/solib.h
+--- a/gdb/solib.h
++++ b/gdb/solib.h
+@@ -136,11 +136,4 @@ extern void update_solib_breakpoints (void);
+
+ extern void handle_solib_event (void);
+
+-/* Associate SONAME with BUILD_ID in ABFD's registry so that it can be
+- retrieved with get_cbfd_soname_build_id. */
+-
+-extern void set_cbfd_soname_build_id (gdb_bfd_ref_ptr abfd,
+- const char *soname,
+- const bfd_build_id *build_id);
+-
+ #endif /* SOLIB_H */
+diff --git a/gdb/solist.h b/gdb/solist.h
+--- a/gdb/solist.h
++++ b/gdb/solist.h
+@@ -132,14 +132,6 @@ struct solib_ops
+ /* Find and open shared library binary file. */
+ gdb_bfd_ref_ptr (*bfd_open) (const char *pathname);
+
+- /* Optional extra hook for finding and opening a solib.
+- If TEMP_PATHNAME is non-NULL: If the file is successfully opened a
+- pointer to a malloc'd and realpath'd copy of SONAME is stored there,
+- otherwise NULL is stored there. */
+- int (*find_and_open_solib) (const char *soname,
+- unsigned o_flags,
+- gdb::unique_xmalloc_ptr<char> *temp_pathname);
+-
+ /* Given two so_list objects, one from the GDB thread list
+ and another from the list returned by current_sos, return 1
+ if they represent the same library.
+@@ -167,6 +159,23 @@ struct solib_ops
+ NULL, in which case no specific preprocessing is necessary
+ for this target. */
+ void (*handle_event) (void);
++
++ /* Return an address within the inferior's address space which is known
++ to be part of SO. If there is no such address, or GDB doesn't know
++ how to figure out such an address then an empty optional is
++ returned.
++
++ The returned address can be used when loading the shared libraries
++ for a core file. GDB knows the build-ids for (some) files mapped
++ into the inferior's address space, and knows the address ranges which
++ those mapped files cover. If GDB can figure out a representative
++ address for the library then this can be used to match a library to a
++ mapped file, and thus to a build-id. GDB can then use this
++ information to help locate the shared library objfile, if the objfile
++ is not in the expected place (as defined by the shared libraries file
++ name). */
++
++ std::optional<CORE_ADDR> (*find_solib_addr) (solib &so);
+ };
+
+ /* A unique pointer to a so_list. */
+@@ -186,4 +195,9 @@ extern gdb_bfd_ref_ptr solib_bfd_fopen (const char *pathname, int fd);
+ /* Find solib binary file and open it. */
+ extern gdb_bfd_ref_ptr solib_bfd_open (const char *in_pathname);
+
++/* A default implementation of the solib_ops::find_solib_addr callback.
++ This just returns an empty std::optional<CORE_ADDR> indicating GDB is
++ unable to find an address within the library SO. */
++extern std::optional<CORE_ADDR> default_find_solib_addr (solib &so);
++
+ #endif
+diff --git a/gdb/symfile-debug.c b/gdb/symfile-debug.c
+--- a/gdb/symfile-debug.c
++++ b/gdb/symfile-debug.c
+@@ -637,7 +637,7 @@ objfile::find_and_add_separate_symbol_file (symfile_add_flags symfile_flags)
+ the user a system specific message that guides them to finding
+ the missing debug info. */
+
+- ext_lang_missing_debuginfo_result ext_result
++ ext_lang_missing_file_result ext_result
+ = ext_lang_handle_missing_debuginfo (this);
+ if (!ext_result.filename ().empty ())
+ {
+diff --git a/gdb/symfile.c b/gdb/symfile.c
+--- a/gdb/symfile.c
++++ b/gdb/symfile.c
+@@ -1237,6 +1237,8 @@ separate_debug_file_exists (const std::string &name, unsigned long crc,
+ struct objfile *parent_objfile,
+ deferred_warnings *warnings)
+ {
++ SEPARATE_DEBUG_FILE_SCOPED_DEBUG_ENTER_EXIT;
++
+ unsigned long file_crc;
+ int file_crc_p;
+ struct stat parent_stat, abfd_stat;
+@@ -1251,19 +1253,13 @@ separate_debug_file_exists (const std::string &name, unsigned long crc,
+ if (filename_cmp (name.c_str (), objfile_name (parent_objfile)) == 0)
+ return 0;
+
+- if (separate_debug_file_debug)
+- {
+- gdb_printf (gdb_stdlog, _(" Trying %s..."), name.c_str ());
+- gdb_flush (gdb_stdlog);
+- }
++ separate_debug_file_debug_printf ("Trying %s...", name.c_str ());
+
+ gdb_bfd_ref_ptr abfd (gdb_bfd_open (name.c_str (), gnutarget));
+
+ if (abfd == NULL)
+ {
+- if (separate_debug_file_debug)
+- gdb_printf (gdb_stdlog, _(" no, unable to open.\n"));
+-
++ separate_debug_file_debug_printf ("unable to open file");
+ return 0;
+ }
+
+@@ -1285,10 +1281,7 @@ separate_debug_file_exists (const std::string &name, unsigned long crc,
+ if (abfd_stat.st_dev == parent_stat.st_dev
+ && abfd_stat.st_ino == parent_stat.st_ino)
+ {
+- if (separate_debug_file_debug)
+- gdb_printf (gdb_stdlog,
+- _(" no, same file as the objfile.\n"));
+-
++ separate_debug_file_debug_printf ("same file as the objfile");
+ return 0;
+ }
+ verified_as_different = 1;
+@@ -1300,9 +1293,7 @@ separate_debug_file_exists (const std::string &name, unsigned long crc,
+
+ if (!file_crc_p)
+ {
+- if (separate_debug_file_debug)
+- gdb_printf (gdb_stdlog, _(" no, error computing CRC.\n"));
+-
++ separate_debug_file_debug_printf ("error computing CRC");
+ return 0;
+ }
+
+@@ -1318,20 +1309,18 @@ separate_debug_file_exists (const std::string &name, unsigned long crc,
+ {
+ if (!gdb_bfd_crc (parent_objfile->obfd.get (), &parent_crc))
+ {
+- if (separate_debug_file_debug)
+- gdb_printf (gdb_stdlog,
+- _(" no, error computing CRC.\n"));
+-
++ separate_debug_file_debug_printf ("error computing CRC");
+ return 0;
+ }
+ }
+
+ if (verified_as_different || parent_crc != file_crc)
+ {
+- if (separate_debug_file_debug)
+- gdb_printf (gdb_stdlog, "the debug information found in \"%s\""
+- " does not match \"%s\" (CRC mismatch).\n",
+- name.c_str (), objfile_name (parent_objfile));
++ separate_debug_file_debug_printf
++ ("the debug information found in \"%s\" does not match "
++ "\"%s\" (CRC mismatch).", name.c_str (),
++ objfile_name (parent_objfile));
++
+ warnings->warn (_("the debug information found in \"%ps\""
+ " does not match \"%ps\" (CRC mismatch)."),
+ styled_string (file_name_style.style (),
+@@ -1343,8 +1332,7 @@ separate_debug_file_exists (const std::string &name, unsigned long crc,
+ return 0;
+ }
+
+- if (separate_debug_file_debug)
+- gdb_printf (gdb_stdlog, _(" yes!\n"));
++ separate_debug_file_debug_printf ("found a match");
+
+ return 1;
+ }
+@@ -1385,23 +1373,18 @@ find_separate_debug_file (const char *dir,
+ unsigned long crc32, struct objfile *objfile,
+ deferred_warnings *warnings)
+ {
+- if (separate_debug_file_debug)
+- gdb_printf (gdb_stdlog,
+- _("\nLooking for separate debug info (debug link) for "
+- "%s\n"), objfile_name (objfile));
++ SEPARATE_DEBUG_FILE_SCOPED_DEBUG_START_END
++ ("looking for separate debug info (debug link) for %s",
++ objfile_name (objfile));
+
+ /* First try in the same directory as the original file. */
+- std::string debugfile = dir;
+- debugfile += debuglink;
++ std::string debugfile = path_join (dir, debuglink);
+
+ if (separate_debug_file_exists (debugfile, crc32, objfile, warnings))
+ return debugfile;
+
+ /* Then try in the subdirectory named DEBUG_SUBDIRECTORY. */
+- debugfile = dir;
+- debugfile += DEBUG_SUBDIRECTORY;
+- debugfile += "/";
+- debugfile += debuglink;
++ debugfile = path_join (dir, DEBUG_SUBDIRECTORY, debuglink);
+
+ if (separate_debug_file_exists (debugfile, crc32, objfile, warnings))
+ return debugfile;
+@@ -1414,10 +1397,13 @@ find_separate_debug_file (const char *dir,
+ bool target_prefix = is_target_filename (dir);
+ const char *dir_notarget
+ = target_prefix ? dir + strlen (TARGET_SYSROOT_PREFIX) : dir;
++ const char *target_prefix_str = target_prefix ? TARGET_SYSROOT_PREFIX : "";
+ std::vector<gdb::unique_xmalloc_ptr<char>> debugdir_vec
+ = dirnames_to_char_ptr_vec (debug_file_directory.c_str ());
+- gdb::unique_xmalloc_ptr<char> canon_sysroot
+- = gdb_realpath (gdb_sysroot.c_str ());
++ const char *sysroot_str = gdb_sysroot.c_str ();
++ if (is_target_filename (sysroot_str) && target_filesystem_is_local ())
++ sysroot_str += strlen (TARGET_SYSROOT_PREFIX);
++ gdb::unique_xmalloc_ptr<char> canon_sysroot = gdb_realpath (sysroot_str);
+
+ /* MS-Windows/MS-DOS don't allow colons in file names; we must
+ convert the drive letter into a one-letter directory, so that the
+@@ -1442,12 +1428,8 @@ find_separate_debug_file (const char *dir,
+
+ for (const gdb::unique_xmalloc_ptr<char> &debugdir : debugdir_vec)
+ {
+- debugfile = target_prefix ? TARGET_SYSROOT_PREFIX : "";
+- debugfile += debugdir;
+- debugfile += "/";
+- debugfile += drive;
+- debugfile += dir_notarget;
+- debugfile += debuglink;
++ debugfile = path_join (target_prefix_str, debugdir.get (),
++ drive.c_str (), dir_notarget, debuglink);
+
+ if (separate_debug_file_exists (debugfile, crc32, objfile, warnings))
+ return debugfile;
+@@ -1464,39 +1446,18 @@ find_separate_debug_file (const char *dir,
+ {
+ /* If the file is in the sysroot, try using its base path in
+ the global debugfile directory. */
+- debugfile = target_prefix ? TARGET_SYSROOT_PREFIX : "";
+- debugfile += debugdir;
+- debugfile += "/";
+- debugfile += base_path;
+- debugfile += "/";
+- debugfile += debuglink;
++ debugfile = path_join (target_prefix_str, debugdir.get (),
++ base_path, debuglink);
+
+ if (separate_debug_file_exists (debugfile, crc32, objfile, warnings))
+ return debugfile;
+
+ /* If the file is in the sysroot, try using its base path in
+- the sysroot's global debugfile directory. GDB_SYSROOT
+- might refer to a target: path; we strip the "target:"
+- prefix -- but if that would yield the empty string, we
+- don't bother at all, because that would just give the
+- same result as above. */
++ the sysroot's global debugfile directory. */
+ if (gdb_sysroot != TARGET_SYSROOT_PREFIX)
+ {
+- debugfile = target_prefix ? TARGET_SYSROOT_PREFIX : "";
+- if (is_target_filename (gdb_sysroot))
+- {
+- std::string root
+- = gdb_sysroot.substr (strlen (TARGET_SYSROOT_PREFIX));
+- gdb_assert (!root.empty ());
+- debugfile += root;
+- }
+- else
+- debugfile += gdb_sysroot;
+- debugfile += debugdir;
+- debugfile += "/";
+- debugfile += base_path;
+- debugfile += "/";
+- debugfile += debuglink;
++ debugfile = path_join (gdb_sysroot.c_str (), debugdir.get (),
++ base_path, debuglink);
+
+ if (separate_debug_file_exists (debugfile, crc32, objfile,
+ warnings))
+diff --git a/gdb/symfile.h b/gdb/symfile.h
+--- a/gdb/symfile.h
++++ b/gdb/symfile.h
+@@ -371,6 +371,25 @@ extern gdb_bfd_ref_ptr find_separate_debug_file_in_section (struct objfile *);
+
+ extern bool separate_debug_file_debug;
+
++/* Print a "separate-debug-file" debug statement. */
++
++#define separate_debug_file_debug_printf(fmt, ...) \
++ debug_prefixed_printf_cond (separate_debug_file_debug, \
++ "separate-debug-file", \
++ fmt, ##__VA_ARGS__)
++
++/* Print "separate-debug-file" enter/exit debug statements. */
++
++#define SEPARATE_DEBUG_FILE_SCOPED_DEBUG_ENTER_EXIT \
++ scoped_debug_enter_exit (separate_debug_file_debug, \
++ "separate-debug-file")
++
++/* Print "separate-debug-file" start/end debug statements. */
++
++#define SEPARATE_DEBUG_FILE_SCOPED_DEBUG_START_END(fmt, ...) \
++ scoped_debug_start_end (separate_debug_file_debug, \
++ "separate-debug-file", fmt, ##__VA_ARGS__)
++
+ /* Read full symbols immediately. */
+
+ extern int readnow_symbol_files;
+diff --git a/gdb/target.c b/gdb/target.c
+--- a/gdb/target.c
++++ b/gdb/target.c
+@@ -3196,6 +3196,14 @@ target_ops::fileio_fstat (int fd, struct stat *sb, fileio_error *target_errno)
+ return -1;
+ }
+
++int
++target_ops::fileio_stat (struct inferior *inf, const char *filename,
++ struct stat *sb, fileio_error *target_errno)
++{
++ *target_errno = FILEIO_ENOSYS;
++ return -1;
++}
++
+ int
+ target_ops::fileio_close (int fd, fileio_error *target_errno)
+ {
+@@ -3315,6 +3323,29 @@ target_fileio_fstat (int fd, struct stat *sb, fileio_error *target_errno)
+
+ /* See target.h. */
+
++int
++target_fileio_stat (struct inferior *inf, const char *filename,
++ struct stat *sb, fileio_error *target_errno)
++{
++ for (target_ops *t = default_fileio_target (); t != NULL; t = t->beneath ())
++ {
++ int ret = t->fileio_stat (inf, filename, sb, target_errno);
++
++ if (ret == -1 && *target_errno == FILEIO_ENOSYS)
++ continue;
++
++ target_debug_printf_nofunc ("target_fileio_stat (%s) = %d (%d)",
++ filename, ret,
++ ret != -1 ? 0 : *target_errno);
++ return ret;
++ }
++
++ *target_errno = FILEIO_ENOSYS;
++ return -1;
++}
++
++/* See target.h. */
++
+ int
+ target_fileio_close (int fd, fileio_error *target_errno)
+ {
+diff --git a/gdb/target.h b/gdb/target.h
+--- a/gdb/target.h
++++ b/gdb/target.h
+@@ -1011,6 +1011,14 @@ struct target_ops
+ *TARGET_ERRNO). */
+ virtual int fileio_fstat (int fd, struct stat *sb, fileio_error *target_errno);
+
++ /* Get information about the file FILENAME and put it in SB. Look for
++ FILENAME in the filesystem as seen by INF. If INF is NULL, use the
++ filesystem seen by the debugger (GDB or, for remote targets, the
++ remote stub). Return 0 on success, or -1 if an error occurs (and
++ set *TARGET_ERRNO). */
++ virtual int fileio_stat (struct inferior *inf, const char *filename,
++ struct stat *sb, fileio_error *target_errno);
++
+ /* Close FD on the target. Return 0, or -1 if an error occurs
+ (and set *TARGET_ERRNO). */
+ virtual int fileio_close (int fd, fileio_error *target_errno);
+@@ -2220,6 +2228,14 @@ extern int target_fileio_pread (int fd, gdb_byte *read_buf, int len,
+ extern int target_fileio_fstat (int fd, struct stat *sb,
+ fileio_error *target_errno);
+
++/* Get information about the file at FILENAME on the target and put it in
++ SB. Look in the filesystem as seen by INF. If INF is NULL, use the
++ filesystem seen by the debugger (GDB or, for remote targets, the remote
++ stub). Return 0 on success, or -1 if an error occurs (and set
++ *TARGET_ERRNO). */
++extern int target_fileio_stat (struct inferior *inf, const char *filename,
++ struct stat *sb, fileio_error *target_errno);
++
+ /* Close FD on the target. Return 0, or -1 if an error occurs
+ (and set *TARGET_ERRNO). */
+ extern int target_fileio_close (int fd, fileio_error *target_errno);
+diff --git a/gdb/testsuite/gdb.base/build-id-seqno.c b/gdb/testsuite/gdb.base/build-id-seqno.c
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.base/build-id-seqno.c
+@@ -0,0 +1,22 @@
++/* This testcase is part of GDB, the GNU debugger.
++
++ Copyright 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 <http://www.gnu.org/licenses/>. */
++
++int
++main (void)
++{
++ return 0;
++}
+diff --git a/gdb/testsuite/gdb.base/build-id-seqno.exp b/gdb/testsuite/gdb.base/build-id-seqno.exp
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.base/build-id-seqno.exp
+@@ -0,0 +1,133 @@
++# This testcase is part of GDB, the GNU debugger.
++#
++# Copyright 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 <http://www.gnu.org/licenses/>.
++
++# Setup a .build-id/ based debug directory containing multiple entries
++# for the same build-id, with each entry given a different sequence
++# number.
++#
++# Ensure that GDB will scan over broken symlinks for the same build-id
++# (but different sequence number) to find later working symlinks.
++#
++# This test only places debug information on the host, so it is always
++# local to GDB.
++
++require {!is_remote host}
++
++standard_testfile
++
++if {[build_executable "failed to prepare" $testfile $srcfile] == -1} {
++ return -1
++}
++
++# Split out BINFILE.debug. Remove debug from BINFILE.
++if {[gdb_gnu_strip_debug $binfile] != 0} {
++ return -1
++}
++
++# Get the '.build-id/xx/xxx...xxx' part of the filename.
++set build_id_filename [build_id_debug_filename_get $binfile]
++
++# Hide (rename) BINFILE.debug, this should ensure GDB can't find it
++# directly but needs to look for the build-id based file in the debug
++# directory.
++set hidden_debuginfo [standard_output_file "hidden_$testfile.debug"]
++remote_exec build "mv ${binfile}.debug $hidden_debuginfo"
++
++# A filename that doesn't exist. Some symlinks will point at this
++# file.
++set missing_debuginfo [host_standard_output_file "missing_debuginfo"]
++
++# Create the debug directory, and the .build-id directory structure
++# within it.
++set debugdir [host_standard_output_file "debug"]
++remote_exec host "mkdir -p $debugdir/[file dirname $build_id_filename]"
++
++set host_hidden_debuginfo [gdb_remote_download host $hidden_debuginfo]
++remote_exec host "ln -fs $host_hidden_debuginfo $debugdir/$build_id_filename"
++
++# Start GDB and load global BINFILE. If FIND_DEBUGINFO is true then
++# we expect GDB to find the debug information matching BINFILE,
++# otherwise, we expect GDB not to find the debug information.
++proc load_binfile_check_debug_is_found { find_debuginfo testname } {
++ with_test_prefix "$testname" {
++ clean_restart
++
++ gdb_test_no_output "set debug-file-directory $::debugdir" \
++ "set debug-file-directory"
++
++ gdb_file_cmd $::binfile
++
++ if { $find_debuginfo } {
++ gdb_assert { [regexp [string_to_regexp \
++ "Reading symbols from $::hidden_debuginfo..."] \
++ $::gdb_file_cmd_msg] } \
++ "debuginfo was read via build-id"
++ } else {
++ gdb_assert { [regexp "\\(No debugging symbols found in \[^\r\n\]+/$::testfile\\)" \
++ $::gdb_file_cmd_msg] } \
++ }
++ }
++}
++
++# Return a copy of FILENAME, which should end '.debug', with NUMBER
++# added, e.g. add_seqno 1 "foo.debug" --> "foo.1.debug".
++proc add_seqno { number filename } {
++ return [regsub "\.debug\$" $filename ".${number}.debug"]
++}
++
++load_binfile_check_debug_is_found true \
++ "find debuginfo with a single build-id file"
++
++remote_exec host "ln -fs $host_hidden_debuginfo \
++ $debugdir/[add_seqno 1 $build_id_filename]"
++remote_exec host "ln -fs $host_hidden_debuginfo \
++ $debugdir/[add_seqno 2 $build_id_filename]"
++remote_exec host "ln -fs $host_hidden_debuginfo \
++ $debugdir/[add_seqno 3 $build_id_filename]"
++
++load_binfile_check_debug_is_found true \
++ "find debuginfo with 4 build-id files"
++
++remote_exec host "ln -fs $missing_debuginfo $debugdir/$build_id_filename"
++
++load_binfile_check_debug_is_found true \
++ "find debuginfo, first build-id file is bad"
++
++remote_exec host "ln -fs $missing_debuginfo \
++ $debugdir/[add_seqno 1 $build_id_filename]"
++remote_exec host "ln -fs $missing_debuginfo \
++ $debugdir/[add_seqno 3 $build_id_filename]"
++
++load_binfile_check_debug_is_found true \
++ "find debuginfo, first 2 build-id files are bad"
++
++remote_exec host "ln -fs $missing_debuginfo \
++ $debugdir/[add_seqno 2 $build_id_filename]"
++
++load_binfile_check_debug_is_found false \
++ "cannot find debuginfo, all build-id files are bad"
++
++remote_exec host "ln -fs $host_hidden_debuginfo \
++ $debugdir/[add_seqno 3 $build_id_filename]"
++
++load_binfile_check_debug_is_found true \
++ "find debuginfo, last build-id file is good"
++
++remote_exec host "rm -f $debugdir/[add_seqno 1 $build_id_filename]"
++
++load_binfile_check_debug_is_found false \
++ "cannot find debuginfo, file with seqno 1 is missing"
+diff --git a/gdb/testsuite/gdb.base/corefile-buildid.exp b/gdb/testsuite/gdb.base/corefile-buildid.exp
+--- a/gdb/testsuite/gdb.base/corefile-buildid.exp
++++ b/gdb/testsuite/gdb.base/corefile-buildid.exp
+@@ -172,11 +172,9 @@ proc locate_exec_from_core_build_id {corefile buildid suffix \
+ "mkdir -p [file join $debugdir [file dirname $buildid]]"
+
+ set files_list {}
++ lappend files_list $binfile $buildid
+ if {$sepdebug} {
+- lappend files_list "$binfile.stripped" $buildid
+ lappend files_list "$binfile.debug" "$buildid.debug"
+- } else {
+- lappend files_list $binfile $buildid
+ }
+ if {$shared} {
+ global sharedir
+@@ -200,12 +198,7 @@ proc locate_exec_from_core_build_id {corefile buildid suffix \
+ gdb_test "core-file $corefile" "Program terminated with .*" \
+ "load core file"
+ if {$symlink} {
+- if {$sepdebug} {
+- set expected_file [file join $builddir \
+- [file tail "$binfile.stripped"]]
+- } else {
+- set expected_file [file join $builddir [file tail $binfile]]
+- }
++ set expected_file [file join $builddir [file tail $binfile]]
+ } else {
+ set expected_file $buildid
+ }
+@@ -245,15 +238,12 @@ proc do_corefile_buildid_tests {args} {
+
+ if {$sepdebug} {
+ # Strip debuginfo into its own file.
+- if {[gdb_gnu_strip_debug [standard_output_file $program_to_run]] \
+- != 0} {
++ if {[gdb_gnu_strip_debug [standard_output_file $program_to_run] \
++ no-debuglink] != 0} {
+ untested "could not strip executable for [join $suffix \ ]"
+ return
+ }
+
+- # Run the stripped program instead of the original.
+- set program_to_run [file join $builddir \
+- [file tail "$binfile.stripped"]]
+ lappend suffix "sepdebug"
+ }
+
+diff --git a/gdb/testsuite/gdb.base/corefile.exp b/gdb/testsuite/gdb.base/corefile.exp
+--- a/gdb/testsuite/gdb.base/corefile.exp
++++ b/gdb/testsuite/gdb.base/corefile.exp
+@@ -199,6 +199,45 @@ gdb_test "up" "#\[0-9\]* *(\[0-9xa-fH'\]* in)? .* \\(.*\\).*" "up (reinit)"
+
+ gdb_test "core" "No core file now."
+
++# Temporarily move coremmap.data out of the way and reload the core
++# file. We should still be able to read buf2 as the contents of this
++# are written into the core file. In contrast buf2ro should no longer
++# be readable as the contents of this region are not within the core
++# file, GDB relies on reading this from the coremmap.data file, which
++# can no longer be found.
++set coremmap_data_filename \
++ [standard_output_file coredir.[getpid]/coremmap.data]
++set coremmap_data_backup_filename \
++ [standard_output_file coredir.[getpid]/coremmap.data.backup]
++remote_exec host "mv ${coremmap_data_filename} \
++ ${coremmap_data_backup_filename}"
++
++clean_restart $binfile
++
++# Load the core file and check we get a warning about the
++# coremmap.data file being missing.
++gdb_test_multiple "core-file $corefile" "warn about coremmap.data missing" {
++ -re -wrap "warning: Can't open file \[^\r\n\]+/coremmap.data during file-backed mapping note processing\r\n.*" {
++ pass $gdb_test_name
++ }
++}
++
++# This xfail was just copied from earlier in the script where we also
++# read from buf2.
++setup_xfail "*-*-sunos*" "*-*-aix*"
++gdb_test "x/8bd buf2" \
++ ".*:.*0.*1.*2.*3.*4.*5.*6.*7.*" \
++ "accessing mmapped data in core file with coremmap.data removed"
++
++gdb_test "x/8bd buf2ro" \
++ "$hex\[^:\]*:\\s+Cannot access memory at address $hex" \
++ "accessing read-only mmapped data in core file with coremmap.data removed"
++
++# Restore the coremmap.data file so later tests don't give warnings
++# when the core file is reloaded.
++remote_exec host "mv ${coremmap_data_backup_filename} \
++ ${coremmap_data_filename}"
++
+ # Test that we can unload the core with the "detach" command.
+
+ proc_with_prefix corefile_detach {} {
+diff --git a/gdb/testsuite/gdb.base/solib-search.exp b/gdb/testsuite/gdb.base/solib-search.exp
+--- a/gdb/testsuite/gdb.base/solib-search.exp
++++ b/gdb/testsuite/gdb.base/solib-search.exp
+@@ -43,7 +43,11 @@ set right_binfile2_lib \
+ set binfile1_lib [standard_output_file ${libname1}.so]
+ set binfile2_lib [standard_output_file ${libname2}.so]
+
+-set lib_flags [list debug ldflags=-Wl,-Bsymbolic]
++# When this test was written, GDB's ability to track down shared
++# libraries for a core file based on the build-id much poorer. As GDB
++# has improved we now need to disable build-ids in order for this test
++# to function as expected.
++set lib_flags [list debug no-build-id ldflags=-Wl,-Bsymbolic]
+ set wrong_lib_flags "$lib_flags additional_flags=-DARRAY_SIZE=1"
+ set right_lib_flags "$lib_flags additional_flags=-DARRAY_SIZE=8192 additional_flags=-DRIGHT"
+
+diff --git a/gdb/testsuite/gdb.base/sysroot-debug-lookup.exp b/gdb/testsuite/gdb.base/sysroot-debug-lookup.exp
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.base/sysroot-debug-lookup.exp
+@@ -0,0 +1,184 @@
++# Copyright 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 <http://www.gnu.org/licenses/>.
++
++# Test GDB's ability to find debug information by looking within the
++# sysroot.
++#
++# We compile a static binary (to reduce what we need to copy into the
++# sysroot), split the debug information from the binary, and setup a
++# sysroot.
++#
++# The debug-file-directory is set to just '/debug', but we're
++# expecting GDB to actually look in '$SYSROOT/debug'.
++#
++# There's a test for using .build-id based lookup, and a test for
++# gnu_debuglink based lookup.
++
++require {!is_remote host}
++
++standard_testfile main.c
++
++# Create a copy of BINFILE, split out the debug information, and then
++# setup a sysroot. Hide (by moving) the actual debug information file
++# and create a symlink to the hidden debug information from within the
++# sysroot.
++#
++# Start GDB, set the sysroot, and then load the executable, ensure GDB
++# finds the debug information, which must have happened by lookin in
++# the sysroot.
++proc_with_prefix lookup_via_build_id {} {
++ set filename ${::binfile}_1
++ if { [build_executable "build exec" ${filename} $::srcfile \
++ {additional_flags=-static debug build-id}] } {
++ return
++ }
++
++ # Split debug information into a .debug file, remove debug
++ # information from FILENAME. Don't add a .gnu_debuglink to
++ # FILENAME, we rely on the build-id.
++ if {[gdb_gnu_strip_debug $filename { no-debuglink }] != 0} {
++ unsupported "cannot split debug information from executable"
++ return
++ }
++
++ set sysroot [standard_output_file "sysroot1"]
++ set debug_dir "/debug"
++
++ set debug_symlink \
++ ${sysroot}${debug_dir}/[build_id_debug_filename_get $filename]
++
++ set build_id_dir [file dirname $debug_symlink]
++
++ set debug_filename ${filename}_hidden_debug
++
++ remote_exec build "mkdir -p $build_id_dir"
++ remote_exec build "mv $filename.debug $debug_filename"
++ remote_exec build "ln -sf $debug_filename $debug_symlink"
++
++ foreach_with_prefix sysroot_prefix { "" "target:" } {
++ clean_restart
++
++ gdb_test_no_output "set sysroot ${sysroot_prefix}$sysroot" "set sysroot"
++ gdb_test_no_output "set debug-file-directory $debug_dir"
++
++ gdb_file_cmd $filename
++
++ gdb_assert { $::gdb_file_cmd_debug_info eq "debug" } \
++ "ensure debug information was found"
++
++ if { $sysroot_prefix eq "target:"
++ && [target_info gdb_protocol] == "extended-remote"} {
++ # Only when using the extended-remote board will we have
++ # started a remote target by this point. In this case GDB
++ # will see the 'target:' prefix as remote, and so the
++ # reported filename will include the 'target:' prefix.
++ #
++ # In all other cases we will still be using the default,
++ # initial target, in which case GDB considers the
++ # 'target:' prefix to indicate the local filesystem.
++ set lookup_filename $sysroot_prefix$debug_symlink
++ } else {
++ set lookup_filename $debug_filename
++ }
++ set re [string_to_regexp "Reading symbols from $lookup_filename..."]
++ gdb_assert {[regexp $re $::gdb_file_cmd_msg]} \
++ "debug symbols read from correct file"
++ }
++}
++
++# Create a copy of BINFILE, split out the debug information, and then
++# setup a sysroot. Hide (by moving) the actual debug information file
++# and create a symlink to the hidden debug information from within the
++# sysroot.
++#
++# Copy the executable into the sysroot and then start GDB, set the
++# sysroot, and load the executable. Check that GDB finds the debug
++# information, which must have happened by lookin in the sysroot.
++proc_with_prefix lookup_via_debuglink {} {
++ set filename ${::binfile}_2
++ if { [build_executable "build exec" ${filename} $::srcfile \
++ {additional_flags=-static debug no-build-id}] } {
++ return
++ }
++
++ # Split debug information into a .debug file, remove debug
++ # information from FILENAME.
++ if {[gdb_gnu_strip_debug $filename] != 0} {
++ unsupported "cannot split debug information from executable"
++ return
++ }
++
++ # We're going to setup the sysroot like this:
++ #
++ # sysroot2/
++ # bin/
++ # $FILENAME
++ # debug/
++ # bin/
++ # $FILENAME.debug
++ #
++ # When looking up debug information via the debuglink, GDB will
++ # only search in the sysroot if the original objfile was in the
++ # sysroot. And GDB will resolve symlinks, so if the objfile is
++ # symlinked to outside the sysroot, GDB will not search in the
++ # sysroot for the debug information.
++ #
++ # So we have to copy the executable into the sysroot.
++ #
++ # We are OK to symlink the debug information to a file outside the
++ # sysroot though.
++
++ set sysroot [standard_output_file "sysroot2"]
++
++ foreach path { bin debug/bin } {
++ remote_exec build "mkdir -p $sysroot/$path"
++ }
++
++ # Copy the executable into the sysroot.
++ set file_basename [file tail $filename]
++ set exec_in_sysroot ${sysroot}/bin/${file_basename}
++ remote_exec build "cp $filename $exec_in_sysroot"
++
++ # Rename the debug file outside of the sysroot, this should stop
++ # GDB finding this file "by accident".
++ set debug_filename ${filename}_hidden_debug
++ remote_exec build "mv $filename.debug $debug_filename"
++
++ # Symlink the debug information into the sysroot.
++ set debug_symlink \
++ ${sysroot}/debug/bin/${file_basename}.debug
++ remote_exec build "ln -sf $debug_filename $debug_symlink"
++
++ foreach_with_prefix sysroot_prefix { "" "target:" } {
++ # Restart GDB and setup the sysroot and debug directory.
++ clean_restart
++ gdb_test_no_output "set sysroot ${sysroot_prefix}$sysroot" "set sysroot"
++ gdb_test_no_output "set debug-file-directory /debug"
++
++ # Load the executable, we expect GDB to find the debug information
++ # in the sysroot.
++ gdb_file_cmd ${sysroot_prefix}$exec_in_sysroot
++
++ gdb_assert { $::gdb_file_cmd_debug_info eq "debug" } \
++ "ensure debug information was found"
++
++ set re [string_to_regexp "Reading symbols from ${sysroot_prefix}$debug_symlink..."]
++ gdb_assert {[regexp $re $::gdb_file_cmd_msg]} \
++ "debug symbols read from correct file"
++ }
++}
++
++lookup_via_build_id
++lookup_via_debuglink
+diff --git a/gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.exp b/gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.exp
+--- a/gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.exp
++++ b/gdb/testsuite/gdb.debuginfod/build-id-no-debug-warning.exp
+@@ -30,14 +30,11 @@ if {[build_executable "build executable" ${testfile} ${srcfile} \
+ return -1
+ }
+
+-# Split BINFILE into BINFILE.stripped and BINFILE.debug, the first is
+-# the executable with the debug information removed, and the second is
+-# the debug information.
++# Split debug information from BINFILE into BINFILE.debug.
+ #
+-# However, by passing the "no-debuglink" flag we prevent this proc
+-# from adding a .gnu_debuglink section to the executable. Any lookup
+-# of the debug information by GDB will need to be done based on the
+-# build-id.
++# By passing the "no-debuglink" flag we prevent this proc from adding
++# a .gnu_debuglink section to BINFILE. Any lookup of the debug
++# information by GDB will need to be done based on the build-id.
+ if {[gdb_gnu_strip_debug $binfile no-debuglink]} {
+ unsupported "cannot produce separate debug info files"
+ return -1
+@@ -59,12 +56,6 @@ set debuginfod_debugdir [standard_output_file "debug"]
+ remote_exec build "mkdir $debuginfod_debugdir"
+ remote_exec build "mv $debugfile $debuginfod_debugdir"
+
+-# This is BINFILE with the debug information removed. We are going to
+-# place this in the BUILD_ID_DEBUG_FILE location, this would usually
+-# represent a mistake by the user, and will trigger a warning from
+-# GDB, this is the warning we are checking for.
+-set stripped_binfile [standard_output_file "${binfile}.stripped"]
+-
+ # Create the .build-id/PREFIX directory name from
+ # .build-id/PREFIX/SUFFIX.debug filename.
+ set debugdir [file dirname ${build_id_debug_file}]
+@@ -76,7 +67,7 @@ remote_exec build "mkdir -p $debugdir"
+ # information, which will point back at this file, which also doesn't
+ # have debug information, which could cause a loop. But GDB will spot
+ # this and give a warning.
+-remote_exec build "mv ${stripped_binfile} ${build_id_debug_file}"
++remote_exec build "mv ${binfile} ${build_id_debug_file}"
+
+ # Now start GDB.
+ clean_restart
+diff --git a/gdb/testsuite/gdb.debuginfod/corefile-mapped-file-1.c b/gdb/testsuite/gdb.debuginfod/corefile-mapped-file-1.c
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.debuginfod/corefile-mapped-file-1.c
+@@ -0,0 +1,24 @@
++/* Copyright 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 <http://www.gnu.org/licenses/>. */
++
++/* This is in the shared library. */
++extern int foo (void);
++
++int
++main (void)
++{
++ int res = foo ();
++ return res;
++}
+diff --git a/gdb/testsuite/gdb.debuginfod/corefile-mapped-file-2.c b/gdb/testsuite/gdb.debuginfod/corefile-mapped-file-2.c
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.debuginfod/corefile-mapped-file-2.c
+@@ -0,0 +1,22 @@
++/* Copyright 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 <http://www.gnu.org/licenses/>. */
++
++volatile int *library_ptr = (int *) POINTER_VALUE;
++
++int
++foo (void)
++{
++ return *library_ptr;
++}
+diff --git a/gdb/testsuite/gdb.debuginfod/corefile-mapped-file-3.c b/gdb/testsuite/gdb.debuginfod/corefile-mapped-file-3.c
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.debuginfod/corefile-mapped-file-3.c
+@@ -0,0 +1,45 @@
++/* Copyright 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 <http://www.gnu.org/licenses/>. */
++
++#include <assert.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <unistd.h>
++#include <sys/mman.h>
++
++volatile void* library_base_address = 0;
++volatile int *ptr = 0;
++
++int
++main ()
++{
++ struct stat buf;
++ int res;
++
++ int fd = open (SHLIB_FILENAME, O_RDONLY);
++ assert (fd != -1);
++
++ res = fstat (fd, &buf);
++ assert (res == 0);
++
++ library_base_address
++ = mmap (NULL, buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
++
++ res = *ptr; /* Undefined behaviour here. */
++
++ return 0;
++}
+diff --git a/gdb/testsuite/gdb.debuginfod/corefile-mapped-file.exp b/gdb/testsuite/gdb.debuginfod/corefile-mapped-file.exp
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.debuginfod/corefile-mapped-file.exp
+@@ -0,0 +1,380 @@
++# Copyright 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 <http://www.gnu.org/licenses/>. */
++
++# This test checks GDB's ability to use build-ids when setting up file backed
++# mappings as part of reading a core-file.
++#
++# A core-file contains a list of the files that were mapped into the process
++# at the time of the core-file creation. If the file was mapped read-only
++# then the file contents will not be present in the core-file, but instead GDB
++# is expected to open the mapped file and read the contents from there if
++# needed. And this is what GDB does.
++#
++# GDB (via the BFD library) will also spot if a mapped looks like a valid ELF
++# and contains a build-id, this build-id is passed back to GDB so that GDB can
++# validate the on-disk file it finds matches the file that was mapped when the
++# core-file was created.
++#
++# In addition, if the on-disk file is found to have a non-matching build-id
++# then GDB can use debuginfod to (try) and download a suitable file.
++#
++# This test is about checking that this file backed mapping mechanism works
++# correctly; that GDB will spot when the build-ids fail to match and will
++# refuse to load an incorrect file. Additionally we check that the correct
++# file can be downloaded from debuginfod.
++#
++# The test is rather contrived though. Instead of relying on having a shared
++# library mapped at the time of crash we mmap a shared library into the
++# process and then check this mapping within the test.
++#
++# The problem with using a normal shared library load for this test is that
++# the shared library list is processed as part of a separate step when opening
++# the core file. Right now this separate step doesn't check the build-ids
++# correctly, so GDB will potentially open the wrong shared library file. The
++# sections of this incorrect shared library are then added to GDB's list of
++# target sections, and are used to satisfy memory reads, which can give the
++# wrong results.
++#
++# This obviously needs fixing, but is a separate problem from the one being
++# tested here, so this test deliberately checks the mapping using a file that
++# is mmaped rather than loaded as a shared library, as such the file is in the
++# core-files list of mapped files, but is not in the shared library list.
++#
++# Despite this test living in the gdb.debuginfod/ directory, only the last
++# part of this test actually uses debuginfod, everything up to that point is
++# pretty generic.
++
++require {!is_remote host}
++require {!is_remote target}
++
++load_lib debuginfod-support.exp
++
++require allow_shlib_tests
++
++standard_testfile -1.c -2.c -3.c
++
++# Compile an executable that loads the shared library as an actual
++# shared library, then use GDB to figure out the offset of the
++# variable 'library_ptr' within the library.
++set library_filename [standard_output_file "libfoo.so"]
++set binfile2 [standard_output_file "library_loader"]
++
++if {[prepare_for_testing_full "build exec which loads the shared library" \
++ [list $library_filename \
++ { debug shlib build-id \
++ additional_flags=-DPOINTER_VALUE=0x12345678 } \
++ $srcfile2 {}] \
++ [list $binfile2 [list debug shlib=$library_filename ] \
++ $srcfile { debug }]] != 0} {
++ return
++}
++
++if {![runto_main]} {
++ return
++}
++
++if { [is_address_zero_readable] } {
++ return
++}
++
++set ptr_address [get_hexadecimal_valueof "&library_ptr" "unknown"]
++
++set ptr_offset "unknown"
++gdb_test_multiple "info proc mappings" "" {
++ -re "^\\s+($hex)\\s+($hex)\\s+$hex\\s+($hex)\[^\r\n\]+$library_filename\r\n" {
++ set low_addr $expect_out(1,string)
++ set high_addr $expect_out(2,string)
++ set file_offset $expect_out(3,string)
++
++ if {[expr $ptr_address >= $low_addr] && [expr $ptr_address < $high_addr]} {
++ set mapping_offset [expr $ptr_address - $low_addr]
++ set ptr_offset [format 0x%x [expr $file_offset + $mapping_offset]]
++ }
++
++ exp_continue
++ }
++
++ -re "^$gdb_prompt $" {
++ }
++
++ -re "(^\[^\r\n\]*)\r\n" {
++ set tmp $expect_out(1,string)
++ exp_continue
++ }
++}
++
++gdb_assert { $ptr_offset ne "unknown" } \
++ "found pointer offset"
++
++set ptr_size [get_integer_valueof "sizeof (library_ptr)" "unknown"]
++set ptr_format_char ""
++if { $ptr_size == 2 } {
++ set ptr_format_char "b"
++} elseif { $ptr_size == 4 } {
++ set ptr_format_char "w"
++} elseif { $ptr_size == 8 } {
++ set ptr_format_char "g"
++}
++if { $ptr_format_char eq "" } {
++ untested "could not figure out size of library_ptr variable"
++ return
++}
++
++# Helper proc to read a value from inferior memory. Reads at address held in
++# global PTR_ADDRESS, and use PTR_FORMAT_CHAR for the size of the read.
++proc read_ptr_value { } {
++ set value ""
++ gdb_test_multiple "x/1${::ptr_format_char}x ${::ptr_address}" "" {
++ -re -wrap "^${::hex}(?:\\s+<\[^>\]+>)?:\\s+($::hex)" {
++ set value $expect_out(1,string)
++ }
++ -re -wrap "^${::hex}(?:\\s+<\[^>\]+>)?:\\s+Cannot access memory at address ${::hex}" {
++ set value "unavailable"
++ }
++ }
++ return $value
++}
++
++set ptr_expected_value [read_ptr_value]
++if { $ptr_expected_value eq "" } {
++ untested "could not find expected value for library_ptr"
++ return
++}
++
++# Now compile a second executable. This one doesn't load the shared
++# library as an actual shared library, but instead mmaps the library
++# into the executable.
++#
++# Load this executable within GDB and confirm that we can use the
++# offset we calculated previously to view the value of 'library_ptr'.
++set opts [list debug additional_flags=-DSHLIB_FILENAME=\"$library_filename\"]
++if {[prepare_for_testing "prepare second executable" $binfile \
++ $srcfile3 $opts] != 0} {
++ return
++}
++
++if {![runto_main]} {
++ return
++}
++
++gdb_breakpoint [gdb_get_line_number "Undefined behaviour here" $srcfile3]
++gdb_continue_to_breakpoint "run to breakpoint"
++
++set library_base_address \
++ [get_hexadecimal_valueof "library_base_address" "unknown"]
++set ptr_address [format 0x%x [expr $library_base_address + $ptr_offset]]
++
++set ptr_value [read_ptr_value]
++gdb_assert { $ptr_value == $ptr_expected_value } \
++ "check value of pointer variable"
++
++# Now rerun the second executable outside of GDB. The executable should crash
++# and generate a corefile.
++set corefile [core_find $binfile]
++if {$corefile eq ""} {
++ untested "could not generate core file"
++ return
++}
++
++# Load a core file from the global COREFILE. Use TESTNAME as the name
++# of the test.
++#
++# If LINE_RE is not the empty string then this is a regexp for a line
++# that we expect to see in the output when loading the core file, if
++# the line is not present then this test will fail.
++#
++# Any lines beginning with 'warning: ' will cause this test to fail.
++#
++# A couple of other standard lines that are produced when loading a
++# core file are also checked for, just to make sure the core file
++# loading has progressed as expected.
++proc load_core_file { testname { line_re "" } } {
++ set code {}
++
++ if { $line_re ne "" } {
++ append code {
++ -re "^$line_re\r\n" {
++ set saw_expected_line true
++ exp_continue
++ }
++ }
++ set saw_expected_line false
++ } else {
++ set saw_expected_line true
++ }
++
++ set saw_unknown_warning false
++ set saw_generated_by_line false
++ set saw_prog_terminated_line false
++
++ append code {
++ -re "^warning: \[^\r\n\]+\r\n" {
++ set saw_unknown_warning true
++ exp_continue
++ }
++
++ -re "^Core was generated by \[^\r\n\]+\r\n" {
++ set saw_generated_by_line true
++ exp_continue
++ }
++
++ -re "^Program terminated with signal SIGSEGV, Segmentation fault\\.\r\n" {
++ set saw_prog_terminated_line true
++ exp_continue
++ }
++
++ -re "^$::gdb_prompt $" {
++ gdb_assert {$saw_generated_by_line \
++ && $saw_prog_terminated_line \
++ && $saw_expected_line \
++ && !$saw_unknown_warning} \
++ $gdb_test_name
++ }
++
++ -re "^\[^\r\n\]*\r\n" {
++ exp_continue
++ }
++ }
++
++ set res [catch { return [gdb_test_multiple "core-file $::corefile" \
++ "$testname" $code] } string]
++
++ if {$res == 1} {
++ global errorInfo errorCode
++ return -code error -errorinfo $errorInfo -errorcode $errorCode $string
++ } elseif {$res == 2} {
++ return $string
++ } else {
++ # We expect RES to be 2 (TCL_RETURN) or 1 (TCL_ERROR). If we get
++ # here then somehow the 'catch' above finished without hitting
++ # either of those cases, which is .... weird.
++ perror "unexepcted return value, code = $res, value = $string"
++ return -1
++ }
++}
++
++# And now restart GDB, load the core-file and check that the library shows as
++# being mapped in, and that we can still read the library_ptr value from
++# memory.
++clean_restart $binfile
++
++load_core_file "load core file"
++
++set library_base_address [get_hexadecimal_valueof "library_base_address" \
++ "unknown" "get library_base_address in core-file"]
++set ptr_address [format 0x%x [expr $library_base_address + $ptr_offset]]
++
++set ptr_value [read_ptr_value]
++gdb_assert { $ptr_value == $ptr_expected_value } \
++ "check value of pointer variable from core-file"
++
++# Now move the shared library file away and restart GDB. This time when we
++# load the core-file we should see a warning that GDB has failed to map in the
++# library file. An attempt to read the variable from the library file should
++# fail / give a warning.
++set library_backup_filename [standard_output_file "libfoo.so.backup"]
++remote_exec build "mv \"$library_filename\" \"$library_backup_filename\""
++
++clean_restart $binfile
++
++load_core_file "load corefile with library file missing" \
++ "warning: Can't open file [string_to_regexp $library_filename] during file-backed mapping note processing"
++
++set ptr_value [read_ptr_value]
++gdb_assert { $ptr_value eq "unavailable" } \
++ "check value of pointer is unavailable with library file missing"
++
++# Now symlink the .build-id/xx/xxx...xxx filename within the debug
++# directory to library we just moved aside. Restart GDB and setup the
++# debug-file-directory before loading the core file.
++#
++# GDB should lookup the file to map via the build-id link in the
++# .build-id/ directory.
++set debugdir [standard_output_file "debugdir"]
++set build_id_filename \
++ $debugdir/[build_id_debug_filename_get $library_backup_filename ""]
++
++remote_exec build "mkdir -p [file dirname $build_id_filename]"
++remote_exec build "ln -sf $library_backup_filename $build_id_filename"
++
++clean_restart $binfile
++
++gdb_test_no_output "set debug-file-directory $debugdir" \
++ "set debug-file-directory"
++
++load_core_file "load corefile, lookup in debug-file-directory"
++
++set ptr_value [read_ptr_value]
++gdb_assert { $ptr_value == $ptr_expected_value } \
++ "check value of pointer variable from core-file, lookup in debug-file-directory"
++
++# Build a new version of the shared library, keep the library the same size,
++# but change the contents so the build-id changes. Then restart GDB and load
++# the core-file again. GDB should spot that the build-id for the shared
++# library is not as expected, and should refuse to map in the shared library.
++if {[build_executable "build second version of shared library" \
++ $library_filename $srcfile2 \
++ { debug shlib build-id \
++ additional_flags=-DPOINTER_VALUE=0x11223344 }] != 0} {
++ return
++}
++
++clean_restart $binfile
++
++load_core_file "load corefile with wrong library in place" \
++ "warning: File [string_to_regexp $library_filename] doesn't match build-id from core-file during file-backed mapping processing"
++
++set ptr_value [read_ptr_value]
++gdb_assert { $ptr_value eq "unavailable" } \
++ "check value of pointer is unavailable with wrong library in place"
++
++# Setup a debuginfod server which can serve the original shared library file.
++# Then restart GDB and load the core-file. GDB should download the original
++# shared library from debuginfod and use that to provide the file backed
++# mapping.
++if {![allow_debuginfod_tests]} {
++ untested "skippig debuginfod parts of this test"
++ return
++}
++
++set server_dir [standard_output_file "debuginfod.server"]
++file mkdir $server_dir
++file rename -force $library_backup_filename $server_dir
++
++prepare_for_debuginfod cache db
++
++set url [start_debuginfod $db $server_dir]
++if { $url eq "" } {
++ unresolved "failed to start debuginfod server"
++ return
++}
++
++with_debuginfod_env $cache {
++ setenv DEBUGINFOD_URLS $url
++
++ clean_restart
++ gdb_test_no_output "set debuginfod enabled on" \
++ "enabled debuginfod for initial test"
++ gdb_load $binfile
++
++ load_core_file "load corefile, download library from debuginfod" \
++ "Downloading\[^\r\n\]* file [string_to_regexp $library_filename]\\.\\.\\."
++
++ set ptr_value [read_ptr_value]
++ gdb_assert { $ptr_value == $ptr_expected_value } \
++ "check value of pointer variable after downloading library file"
++}
++
++stop_debuginfod
+diff --git a/gdb/testsuite/gdb.debuginfod/solib-with-soname-1.c b/gdb/testsuite/gdb.debuginfod/solib-with-soname-1.c
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.debuginfod/solib-with-soname-1.c
+@@ -0,0 +1,39 @@
++/* Copyright 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 <http://www.gnu.org/licenses/>. */
++
++#include <stdlib.h>
++
++/* It is important that these two variables have names of the same length
++ so that the debug information in the two library versions is laid out
++ the same. If they differ then the .dynamic section might move, which
++ will trigger a different check within GDB than the one we actually want
++ to check. */
++
++#if LIB_VERSION == 1
++volatile int *library_1_var = (volatile int *) 0x12345678;
++#elif LIB_VERSION == 2
++volatile int *library_2_var = (volatile int *) 0x11223344;
++#else
++# error Unknown library version
++#endif
++
++int
++foo (void)
++{
++ /* This should trigger a core dump. */
++ abort ();
++
++ return 0;
++}
+diff --git a/gdb/testsuite/gdb.debuginfod/solib-with-soname-2.c b/gdb/testsuite/gdb.debuginfod/solib-with-soname-2.c
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.debuginfod/solib-with-soname-2.c
+@@ -0,0 +1,41 @@
++/* Copyright 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 <http://www.gnu.org/licenses/>. */
++
++#include <assert.h>
++#include <dlfcn.h>
++#include <stddef.h>
++
++/* This is in the shared library. */
++extern int foo (void);
++
++/* This is updated by the .exp file. */
++char *libname = "libfoo_2.so";
++
++int
++main (void)
++{
++ void *handle;
++ int res, tmp;
++
++ handle = dlopen (libname, RTLD_LAZY);
++ assert (handle != NULL);
++
++ res = foo ();
++
++ tmp = dlclose (handle);
++ assert (tmp == 0);
++
++ return res;
++}
+diff --git a/gdb/testsuite/gdb.debuginfod/solib-with-soname.exp b/gdb/testsuite/gdb.debuginfod/solib-with-soname.exp
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.debuginfod/solib-with-soname.exp
+@@ -0,0 +1,290 @@
++# Copyright 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 <http://www.gnu.org/licenses/>. */
++
++# This test exercises GDB's ability to validate build-ids when loading
++# shared libraries for a core file.
++#
++# The test creates two "versions" of a shared library, sets up a
++# symlink to point to one version of the library, and creates a core file.
++#
++# We then try re-loading the core file and executable and check that
++# GDB is able to correctly load the shared library. To confuse things
++# we retarget the library symlink at the other version of the library.
++#
++# After that we repeat the test, but this time deleting the symlink
++# completely.
++#
++# Then we remove the version of the library completely, at this point
++# we do expect GDB to give a warning about being unable to load the library.
++#
++# And finally, we setup debuginfod and have it serve the missing
++# library file, GDB should correctly download the library file.
++#
++# Despite this test living in the gdb.debuginfod/ directory, only the last
++# part of this test actually uses debuginfod, everything up to that point is
++# pretty generic.
++
++load_lib debuginfod-support.exp
++
++require allow_shlib_tests
++require {istarget "*-linux*"}
++require {!is_remote host}
++require {!using_fission}
++
++standard_testfile -1.c -2.c
++
++# Build two similar, but slightly different versions of the shared
++# library. Both libraries have DT_SONAME set to the generic
++# libfoo.so, we'll create a symlink with that name later.
++set library_1_filename [standard_output_file "libfoo_1.so"]
++set library_2_filename [standard_output_file "libfoo_2.so"]
++
++# The generic name for the library.
++set library_filename [standard_output_file "libfoo.so"]
++
++# When compiling a shared library the -Wl,-soname,NAME option is
++# automatically added based on the final name of the library. We want
++# to compile libfoo_1.so, but set the soname to libfoo.so. To achieve
++# this we first compile into libfoo.so, and then rename the library to
++# libfoo_1.so.
++if {[build_executable "build libfoo_1.so" $library_filename \
++ $srcfile \
++ { debug shlib build-id \
++ additional_flags=-DLIB_VERSION=1 }] == -1} {
++ return
++}
++remote_exec build "mv ${library_filename} ${library_1_filename}"
++
++# See the comment above, but this time we rename to libfoo_2.so.
++if {[build_executable "build libfoo_2.so" $library_filename \
++ $srcfile \
++ { debug shlib build-id \
++ additional_flags=-DLIB_VERSION=2 }] == -1} {
++ return
++}
++remote_exec build "mv ${library_filename} ${library_2_filename}"
++
++# Create libfoo.so symlink to the libfoo_1.so library. If this
++# symlink creation fails then we assume we can't create symlinks on
++# this host. If this succeeds then later symlink creation is required
++# to succeed, and will trigger an FAIL if it doesn't.
++set status \
++ [remote_exec build \
++ "ln -sf ${library_1_filename} ${library_filename}"]
++if {[lindex $status 0] != 0} {
++ unsupported "host does not support symbolic links"
++ return
++}
++
++# Build the executable. This links against libfoo.so, which is
++# poining at libfoo_1.so. Just to confuse things even more, this
++# executable uses dlopen to load libfoo_2.so. Weird!
++if { [build_executable "build executable" ${binfile} ${srcfile2} \
++ [list debug shlib=${library_filename} shlib_load]] == -1 } {
++ return
++}
++
++# If the board file is automatically splitting the debug information
++# into a separate file (e.g. the cc-with-gnu-debuglink.exp board) then
++# this test isn't going to work.
++clean_restart
++gdb_file_cmd $binfile
++if {$gdb_file_cmd_debug_info ne "debug"} {
++ unsupported "failed to find debug information"
++ return
++}
++if {[regexp "${testfile}.debug" $gdb_file_cmd_msg]} {
++ unsupported "debug information has been split to a separate file"
++ return
++}
++
++# Run BINFILE which will generate a corefile.
++set corefile [core_find $binfile]
++if {$corefile eq ""} {
++ untested "could not generate core file"
++ return
++}
++
++# Helper proc to load global BINFILE and then load global COREFILE.
++#
++# If EXPECT_WARNING is true then we require a warning about being
++# unable to load the shared library symbols, otherwise, EXPECT_WARNING
++# is false and we require no warning.
++#
++# If EXPECT_DOWNLOAD is true then we require a line indicating that
++# the shared library is being downloaded from debuginfod, otherwise
++# the shared library should not be downloaded.
++#
++# If DEBUGDIR is not the empty string then 'debug-file-directory' is
++# set to the value of DEBUGDIR.
++proc load_exec_and_core_file { expect_warning expect_download testname \
++ {debugdir ""} } {
++ with_test_prefix $testname {
++ clean_restart $::binfile
++
++ if { $debugdir ne "" } {
++ gdb_test_no_output "set debug-file-directory $debugdir" \
++ "set debug directory"
++ }
++
++ set saw_warning false
++ set saw_download false
++ set saw_generated false
++ set saw_terminated false
++
++ gdb_test_multiple "core-file $::corefile" "load core file" {
++ -re "^Core was generated by \[^\r\n\]+\r\n" {
++ set saw_generated true
++ exp_continue
++ }
++ -re "^Program terminated with signal \[^\r\n\]+\r\n" {
++ set saw_terminated true
++ exp_continue
++ }
++ -re "^warning: Can't open file \[^\r\n\]+ during file-backed mapping note processing\r\n" {
++ # Ignore warnings from the file backed mapping phase.
++ exp_continue
++ }
++ -re "^warning: Could not load shared library symbols for \[^\r\n\]+/libfoo\\.so\\.\r\n" {
++ set saw_warning true
++ exp_continue
++ }
++ -re "^Downloading file \[^\r\n\]+/libfoo_1\\.so\\.\\.\\.\r\n" {
++ set saw_download true
++ exp_continue
++ }
++ -re "^$::gdb_prompt $" {
++ gdb_assert { $saw_generated && $saw_terminated \
++ && $saw_warning == $expect_warning \
++ && $saw_download == $expect_download } \
++ $gdb_test_name
++ }
++ -re "^\[^\r\n\]*\r\n" {
++ exp_continue
++ }
++ }
++
++ # If we don't expect a warning then debug symbols from the
++ # shared library should be available. Confirm we can read a
++ # variable from the shared library. If we do expect a warning
++ # then the shared library debug symbols have not loaded, and
++ # the library variable should not be available.
++ if { !$expect_warning } {
++ gdb_test "print/x library_1_var" " = 0x12345678" \
++ "check library_1_var can be read"
++ } else {
++ gdb_test "print/x library_1_var" \
++ "^No symbol \"library_1_var\" in current context\\." \
++ "check library_1_var cannot be read"
++ }
++ }
++}
++
++# Initial test, just load the executable and core file. At this point
++# everything should load fine as everything is where we expect to find
++# it.
++load_exec_and_core_file false false \
++ "load core file, all libraries as expected"
++
++# Update libfoo.so symlink to point at the second library then reload
++# the core file. GDB should spot that the symlink points to the wrong
++# file, but should be able to figure out the correct file to load as
++# the right file will be in the mapped file list.
++set status [remote_exec build \
++ "ln -sf ${library_2_filename} ${library_filename}"]
++gdb_assert { [lindex $status 0] == 0 } \
++ "update library symlink to point to the wrong file"
++
++load_exec_and_core_file false false \
++ "load core file, symlink points to wrong file"
++
++# Remove libfoo.so symlink and reload the core file. As in the
++# previous test GDB should be able to figure out the correct file to
++# load as the correct file will still appear in the mapped file list.
++set status [remote_exec build "rm -f ${library_filename}"]
++gdb_assert { [lindex $status 0] == 0 } "remove library symlink"
++
++load_exec_and_core_file false false \
++ "load core file, symlink removed"
++
++# Remove LIBRARY_1_FILENAME. We'll now see a warning that the mapped
++# file can't be loaded (we ignore that warning), and we'll see a
++# warning that the shared library can't be loaded.
++set library_1_backup_filename ${library_1_filename}.backup
++set status \
++ [remote_exec build \
++ "mv ${library_1_filename} ${library_1_backup_filename}"]
++gdb_assert { [lindex $status 0] == 0 } \
++ "remove libfoo_1.so"
++
++load_exec_and_core_file true false \
++ "load core file, libfoo_1.so removed"
++
++# Symlink the .build-id/xx/xxx...xxx filename within the debug
++# directory to LIBRARY_1_BACKUP_FILENAME, now when we restart GDB it
++# should find the missing library within the debug directory.
++set debugdir [standard_output_file "debugdir"]
++set build_id_filename \
++ $debugdir/[build_id_debug_filename_get $library_1_backup_filename ""]
++set status \
++ [remote_exec build \
++ "mkdir -p [file dirname $build_id_filename]"]
++gdb_assert { [lindex $status 0] == 0 } \
++ "create sub-directory within the debug directory"
++set status \
++ [remote_exec build \
++ "ln -sf $library_1_backup_filename $build_id_filename"]
++gdb_assert { [lindex $status 0] == 0 } \
++ "create symlink within the debug directory "
++
++load_exec_and_core_file false false \
++ "load core file, find libfoo_1.so through debug-file-directory" \
++ $debugdir
++
++# Setup a debuginfod server which can serve the original shared
++# library file.
++if {![allow_debuginfod_tests]} {
++ untested "skippig debuginfod parts of this test"
++ return
++}
++
++set server_dir [standard_output_file "debuginfod.server"]
++file mkdir $server_dir
++file rename -force $library_1_backup_filename $server_dir
++
++prepare_for_debuginfod cache db
++
++set url [start_debuginfod $db $server_dir]
++if { $url eq "" } {
++ unresolved "failed to start debuginfod server"
++ return
++}
++
++with_debuginfod_env $cache {
++ setenv DEBUGINFOD_URLS $url
++
++ save_vars { GDBFLAGS } {
++ append GDBFLAGS " -ex \"set debuginfod enabled on\""
++
++ # Reload the executable and core file. GDB should download
++ # the file libfoo_1.so using debuginfod during the mapped file
++ # phase, but should then reuse that download during the shared
++ # library phase.
++ load_exec_and_core_file false true \
++ "load core file, use debuginfod"
++ }
++}
++
++stop_debuginfod
+diff --git a/gdb/testsuite/gdb.dwarf2/short-build-id.exp b/gdb/testsuite/gdb.dwarf2/short-build-id.exp
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.dwarf2/short-build-id.exp
+@@ -0,0 +1,119 @@
++# Copyright 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 <http://www.gnu.org/licenses/>.
++
++# Create a file with an artificially short (1-byte) build-id, and
++# check that GDB doesn't try to load debug information. If we do try
++# then we end up loading from: `debug-directory/.build-id/xx/.debug`
++# which isn't right.
++
++load_lib dwarf.exp
++
++# This test can only be run on targets which support DWARF-2 and use gas.
++require dwarf2_support
++
++# No remote host testing either.
++require {!is_remote host}
++
++standard_testfile main.c
++
++# Create an assembler file which encodes BUILDID as the build-id. Compile
++# this along with the global SRCFILE to create a test executable.
++#
++# Split the debug information out from the newly created executable and place
++# it into the debug file directory.
++#
++# Load the executable into GDB and check to see if the debug information was
++# loaded or not. For this test we are expecting that the debug information
++# was not loaded. The reason is that, with short values for BUILDID, GDB ends
++# up looking for the debug information in weird locations.
++proc run_test { buildid } {
++ set len [string length $buildid]
++
++ set asm_file [standard_output_file "$::testfile.$len.S"]
++ Dwarf::assemble $asm_file {
++ declare_labels int_label int_label2
++
++ upvar buildid buildid
++
++ build_id $buildid
++
++ cu { label cu_start } {
++ compile_unit {{language @DW_LANG_C}} {
++ int_label2: base_type {
++ {name int}
++ {byte_size 4 sdata}
++ {encoding @DW_ATE_signed}
++ }
++
++ constant {
++ {name the_int}
++ {type :$int_label2}
++ {const_value 99 data1}
++ }
++ }
++ }
++
++ aranges {} cu_start {
++ arange {} 0 0
++ }
++ }
++
++ set execfile [standard_output_file $::testfile.$len]
++
++ if { [build_executable_from_specs "failed to build" \
++ $execfile {debug no-build-id} \
++ $::srcfile debug \
++ $asm_file {}] } {
++ return
++ }
++
++ # Create the debug directory.
++ set debugdir [standard_output_file "debugdir.$len"]
++ set build_id_dir $debugdir/.build-id/$buildid
++ remote_exec host "mkdir -p $build_id_dir"
++
++ # Split out the debug information.
++ if {[gdb_gnu_strip_debug $execfile no-debuglink]} {
++ unresolved "failed to split out debug information"
++ return
++ }
++
++ # Move the debug information into the debug directory. We place the debug
++ # information into a file called just '.debug'. GDB should not check this
++ # file, but at one point GDB would check this file, even though this
++ # doesn't make much sense.
++ set execfile_debug ${execfile}.debug
++ remote_exec host "mv $execfile_debug $build_id_dir/.debug"
++
++ # Start GDB, set the debug-file-directory, and try loading the file.
++ clean_restart
++
++ gdb_test_no_output "set debug-file-directory $debugdir" \
++ "set debug-file-directory"
++
++ gdb_file_cmd $execfile
++
++ gdb_assert { $::gdb_file_cmd_debug_info eq "nodebug" } \
++ "no debug should be loaded"
++
++ # For sanity, read something that was encoded in the debug
++ # information, this should fail.
++ gdb_test "print the_int" \
++ "(?:No symbol table is loaded|No symbol \"the_int\" in current context).*"
++}
++
++foreach_with_prefix buildid { a4 "" } {
++ run_test $buildid
++}
+diff --git a/gdb/testsuite/gdb.python/py-missing-objfile-lib.c b/gdb/testsuite/gdb.python/py-missing-objfile-lib.c
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.python/py-missing-objfile-lib.c
+@@ -0,0 +1,35 @@
++/* This test program is part of GDB, the GNU debugger.
++
++ Copyright 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 <http://www.gnu.org/licenses/>. */
++
++struct lib_type
++{
++ int a;
++ int b;
++};
++
++volatile struct lib_type global_lib_var = { 0, 0 };
++
++int
++foo (void)
++{
++ int res = 0;
++
++ res += global_lib_var.a;
++ res += global_lib_var.b;
++
++ return 0;
++}
+diff --git a/gdb/testsuite/gdb.python/py-missing-objfile.c b/gdb/testsuite/gdb.python/py-missing-objfile.c
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.python/py-missing-objfile.c
+@@ -0,0 +1,49 @@
++/* This test program is part of GDB, the GNU debugger.
++
++ Copyright 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 <http://www.gnu.org/licenses/>. */
++
++#include <stdlib.h>
++
++struct exec_type
++{
++ int a;
++ int b;
++ int c;
++};
++
++volatile struct exec_type global_exec_var = { 0, 0, 0 };
++
++extern int foo (void);
++
++void
++dump_core (void)
++{
++ abort ();
++}
++
++int
++main (void)
++{
++ int res = foo ();
++
++ res += global_exec_var.a;
++ res += global_exec_var.b;
++ res += global_exec_var.c;
++
++ dump_core ();
++
++ return res;
++}
+diff --git a/gdb/testsuite/gdb.python/py-missing-objfile.exp b/gdb/testsuite/gdb.python/py-missing-objfile.exp
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.python/py-missing-objfile.exp
+@@ -0,0 +1,565 @@
++# Copyright (C) 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 <http://www.gnu.org/licenses/>.
++
++load_lib gdb-python.exp
++
++require allow_python_tests
++require {!is_remote host}
++
++standard_testfile .c -lib.c
++
++# Build the library.
++set libname ${testfile}-lib
++set libfile [standard_output_file $libname]
++if { [build_executable "build shlib" $libfile $srcfile2 \
++ {debug shlib build-id}] == -1} {
++ return
++}
++
++# Build the executable.
++set opts [list debug build-id shlib=${libfile}]
++if { [build_executable "build exec" $binfile $srcfile $opts] == -1} {
++ return
++}
++
++# The cc-with-gnu-debuglink board will split the debug out into the
++# .debug directory. This test script relies on having GDB lookup the
++# objfile and debug via the build-id, which this test sets up. Trying
++# to do that, while also supporting the cc-with-gnu-debuglink board is
++# just too complicated.
++if {[file isdirectory [standard_output_file ".debug"]]} {
++ unsupported "split debug testing not supported"
++ return
++}
++
++set remote_python_file \
++ [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
++
++# Generate a core file.
++set corefile [core_find $binfile {}]
++if {$corefile == ""} {
++ unsupported "core file not generated"
++ return 0
++}
++
++# Create a directory named DIRNAME for use as the
++# debug-file-directory. Populate the directory with links (based on
++# the build-ids) to each file in the list FILES.
++#
++# Return the full filename of DIRNAME on the host.
++proc setup_debugdir { dirname files } {
++ set debugdir [host_standard_output_file $dirname]
++
++ # Create basic empty directory structure (in case FILES is empty).
++ remote_exec host "mkdir -p $debugdir/.build-id/"
++
++ foreach file $files {
++ set build_id_filename [build_id_debug_filename_get $file ""]
++
++ remote_exec host "mkdir -p $debugdir/[file dirname $build_id_filename]"
++ remote_exec host "ln -s $file $debugdir/$build_id_filename"
++ }
++
++ return $debugdir
++}
++
++# Query some symbols in the inferior to see if GDB managed to find the
++# executable (when EXEC_LOADED is true) and/or the library (when LIB_LOADED
++# is true).
++proc check_loaded_debug { exec_loaded lib_loaded } {
++ if { $exec_loaded } {
++ gdb_test "whatis global_exec_var" "^type = volatile struct exec_type"
++
++ if { $lib_loaded } {
++ gdb_test "whatis global_lib_var" "^type = volatile struct lib_type"
++ } else {
++ gdb_test "whatis global_lib_var" \
++ "^No symbol \"global_lib_var\" in current context\\."
++ }
++ } else {
++ gdb_test "whatis global_exec_var" \
++ "^No symbol table is loaded\\. Use the \"file\" command\\."
++ gdb_test "whatis global_lib_var" \
++ "^No symbol table is loaded\\. Use the \"file\" command\\."
++ }
++}
++
++# Load the global corefile. The EXTRA_RE is checked for prior to GDB
++# announcing that the core-file has been loaded.
++proc load_core_file { {extra_re ".*"} } {
++ gdb_test "core-file $::corefile" \
++ [multi_line \
++ "$extra_re" \
++ "Core was generated by \[^\r\n\]+" \
++ "Program terminated with signal SIGABRT, Aborted\\." \
++ "\[^\r\n\]+(?:\r\n\[^\r\n\]+)?"] \
++ "loaded the core file"
++}
++
++# Set the debug-file-directory to DIRNAME.
++proc set_debug_file_dir { dirname } {
++ gdb_test_no_output "set debug-file-directory $dirname" \
++ "set debug-file-directory"
++}
++
++# Restart GDB and load the support Python script.
++proc clean_restart_load_python {} {
++ clean_restart
++ gdb_test "source $::remote_python_file" "^Success" \
++ "load python script"
++}
++
++# For sanity, lets check that we can load the specify the executable
++# and then load the core-file the easy way.
++with_test_prefix "initial sanity check" {
++ clean_restart $binfile
++ load_core_file
++ check_loaded_debug true true
++}
++
++# Move the executable and library into a location that the core-file
++# can't possibly know about. After this the only way GDB can track
++# down these files will be by looking in the debug-file-directory.
++set hidden_dir [host_standard_output_file "hidden"]
++set hidden_binfile "$hidden_dir/$testfile"
++set hidden_libfile "$hidden_dir/$libname"
++remote_exec host "mkdir -p $hidden_dir"
++remote_exec host "mv $libfile $hidden_libfile"
++remote_exec host "mv $binfile $hidden_binfile"
++
++# If using the fission-dwp board then we'll have .dwp files that also
++# need to be moved.
++if {[remote_file host exists ${libfile}.dwp]} {
++ remote_exec host "mv ${libfile}.dwp ${hidden_libfile}.dwp"
++}
++
++if {[remote_file host exists ${binfile}.dwp]} {
++ remote_exec host "mv ${binfile}.dwp ${hidden_binfile}.dwp"
++}
++
++with_test_prefix "no objfiles, no debug-file-directory" {
++ clean_restart
++ load_core_file
++ check_loaded_debug false false
++}
++
++# Setup some debug-file-directories.
++set debugdir_no_lib \
++ [setup_debugdir "debugdir.no-lib" [list "$hidden_binfile"]]
++set debugdir_empty \
++ [setup_debugdir "debugdir.empty" {}]
++set debugdir_all \
++ [setup_debugdir "debugdir.all" [list "$hidden_libfile" \
++ "$hidden_binfile"]]
++
++with_test_prefix "no objfiles available" {
++ # Another sanity check that GDB can find the files via the
++ # debug-file-directory.
++ clean_restart
++ set_debug_file_dir $debugdir_empty
++ load_core_file
++ check_loaded_debug false false
++}
++
++with_test_prefix "all objfiles available" {
++ # Another sanity check that GDB can find the files via the
++ # debug-file-directory.
++ set_debug_file_dir $debugdir_all
++ load_core_file
++ check_loaded_debug true true
++}
++
++with_test_prefix "lib objfile missing" {
++ # Another sanity check that GDB can find the files via the
++ # debug-file-directory.
++ set_debug_file_dir $debugdir_no_lib
++ load_core_file
++ check_loaded_debug true false
++}
++
++with_test_prefix "all objfiles missing, handler returns None" {
++ clean_restart_load_python
++ gdb_test_no_output \
++ "python gdb.missing_objfile.register_handler(None, handler_obj)" \
++ "register initial handler"
++ load_core_file
++
++ check_loaded_debug false false
++
++ # The handler should be called three times, once for the
++ # mapped-file, once for the core-file's exec, and once for the
++ # shared library.
++ gdb_test "python print(handler_obj.call_count)" "^3" \
++ "check handler was called three times"
++}
++
++with_test_prefix "lib objfile missing, handler returns None" {
++ # Reset handler_obj.
++ gdb_test_no_output "python handler_obj.set_mode(Mode.RETURN_NONE)"
++
++ set_debug_file_dir $debugdir_no_lib
++ load_core_file
++ check_loaded_debug true false
++
++ # The handler will be called twice, once when GDB tries to
++ # load the shared library during the memory-mapped file phase,
++ # then again for the shared library loading.
++ gdb_test "python print(handler_obj.call_count)" "^2" \
++ "check handler was called three times"
++}
++
++with_test_prefix "handler installs lib objfile" {
++ set build_id_filename [build_id_debug_filename_get \
++ $hidden_libfile ""]
++ remote_exec host \
++ "mkdir -p $debugdir_no_lib/[file dirname $build_id_filename]"
++ gdb_test_no_output "python handler_obj.set_mode(Mode.RETURN_TRUE, \
++ \"$hidden_libfile\", \"$debugdir_no_lib/$build_id_filename\")" \
++ "configure handler"
++
++ load_core_file
++ check_loaded_debug true true
++
++ # Cleanup so the test can be reproduced again later if needed.
++ remote_exec host "rm $debugdir_no_lib/$build_id_filename"
++}
++
++with_test_prefix "handler points to lib objfile" {
++ set build_id_filename [build_id_debug_filename_get \
++ $hidden_libfile ""]
++ remote_exec host \
++ "mkdir -p $debugdir_no_lib/[file dirname $build_id_filename]"
++ gdb_test_no_output "python handler_obj.set_mode(Mode.RETURN_STRING, \
++ \"$hidden_libfile\")" \
++ "configure handler"
++
++ load_core_file
++ check_loaded_debug true true
++
++ # Cleanup so the test can be reproduced again later if needed.
++ remote_exec host "rm $debugdir_no_lib/$build_id_filename"
++
++ # The handler will only have been called once when loading the
++ # memory-mapped file. GDB is smart enough to reuse the previously
++ # discovered BFD object as the shared library.
++ gdb_test "python print(handler_obj.call_count)" "^1" \
++ "check good handler hasn't been called again"
++
++ # Validate the filename and build-id arguments passed to the handler.
++ set expected_buildid [get_build_id $hidden_libfile]
++ gdb_test "python print(handler_last_buildid)" "^$expected_buildid"
++ gdb_test "python print(handler_last_filename)" \
++ "^[string_to_regexp $libfile]"
++}
++
++# Register another global handler, this one raises an exception. Reload the
++# core-file, the bad handler should be invoked first, which raises an
++# excetption, at which point GDB should skip further Python handlers.
++with_test_prefix "handler raises an exception" {
++ gdb_test_no_output \
++ "python gdb.missing_objfile.register_handler(None, rhandler)"
++
++ foreach_with_prefix exception_type {gdb.GdbError TypeError} {
++ gdb_test_no_output \
++ "python rhandler.exception_type = $exception_type"
++
++ # Load the core file. We expect the exception message to appear at
++ # least once in the output.
++ set re [string_to_regexp \
++ "Python Exception <class '$exception_type'>: message"]
++ load_core_file "${re}.*"
++
++ # Our original handler is still registered, but should not have been
++ # called again (as the exception occurs first).
++ gdb_test "python print(handler_obj.call_count)" "^1" \
++ "check good handler hasn't been called again"
++ }
++}
++
++# Re-start GDB.
++clean_restart_load_python
++
++# Attempt to register a missing-debug-handler with NAME. The expectation is
++# that this should fail as NAME contains some invalid characters.
++proc check_bad_name {name} {
++ set name_re [string_to_regexp $name]
++ set re \
++ [multi_line \
++ "ValueError.*: invalid character '.' in handler name: $name_re" \
++ "Error occurred in Python.*"]
++
++ gdb_test "python register(\"$name\")" $re \
++ "check that '$name' is not accepted"
++}
++
++# We don't attempt to be exhaustive here, just check a few random examples
++# of invalid names.
++check_bad_name "!! Bad Name"
++check_bad_name "Bad Name"
++check_bad_name "(Bad Name)"
++check_bad_name "Bad \[Name\]"
++check_bad_name "Bad,Name"
++check_bad_name "Bad;Name"
++
++# Check that there are no handlers registered.
++gdb_test_no_output "info missing-objfile-handlers" \
++ "check no handlers are registered"
++
++# Grab the current program space object, used for registering handler later.
++gdb_test_no_output "python pspace = gdb.selected_inferior().progspace"
++
++# Now register some handlers.
++foreach hspec {{\"Foo\" None}
++ {\"-bar\" None}
++ {\"baz-\" pspace}
++ {\"abc-def\" pspace}} {
++ lassign $hspec name locus
++ gdb_test "python register($name, $locus)"
++}
++
++with_test_prefix "all handlers enabled" {
++ gdb_test "info missing-objfile-handlers" \
++ [multi_line \
++ "Current Progspace:" \
++ " abc-def" \
++ " baz-" \
++ "Global:" \
++ " -bar" \
++ " Foo"]
++
++ set_debug_file_dir $debugdir_no_lib
++ load_core_file
++
++ # As we perform two look ups, first for the mapped-file then for the
++ # shared library, each handler will be called twice.
++ gdb_test "python print(handler_call_log)" \
++ [string_to_regexp {['abc-def', 'baz-', '-bar', 'Foo', 'abc-def', 'baz-', '-bar', 'Foo']}]
++ gdb_test_no_output "python handler_call_log = \[\]" \
++ "reset call log"
++}
++
++with_test_prefix "disable 'baz-'" {
++ gdb_test "disable missing-objfile-handler progspace baz-" \
++ "^1 missing objfile handler disabled"
++
++ gdb_test "info missing-objfile-handlers" \
++ [multi_line \
++ "Progspace \[^\r\n\]+:" \
++ " abc-def" \
++ " baz- \\\[disabled\\\]" \
++ "Global:" \
++ " -bar" \
++ " Foo"]
++
++ load_core_file
++ gdb_test "python print(handler_call_log)" \
++ [string_to_regexp {['abc-def', '-bar', 'Foo', 'abc-def', '-bar', 'Foo']}]
++ gdb_test_no_output "python handler_call_log = \[\]" \
++ "reset call log"
++}
++
++with_test_prefix "disable 'Foo'" {
++ gdb_test "disable missing-objfile-handler .* Foo" \
++ "^1 missing objfile handler disabled"
++
++ gdb_test "info missing-objfile-handlers" \
++ [multi_line \
++ "Progspace \[^\r\n\]+:" \
++ " abc-def" \
++ " baz- \\\[disabled\\\]" \
++ "Global:" \
++ " -bar" \
++ " Foo \\\[disabled\\\]"]
++
++ load_core_file
++ gdb_test "python print(handler_call_log)" \
++ [string_to_regexp {['abc-def', '-bar', 'abc-def', '-bar']}]
++ gdb_test_no_output "python handler_call_log = \[\]" \
++ "reset call log"
++}
++
++with_test_prefix "disable everything" {
++ gdb_test "disable missing-objfile-handler .* .*" \
++ "^2 missing objfile handlers disabled"
++
++ gdb_test "info missing-objfile-handlers" \
++ [multi_line \
++ "Progspace \[^\r\n\]+:" \
++ " abc-def \\\[disabled\\\]" \
++ " baz- \\\[disabled\\\]" \
++ "Global:" \
++ " -bar \\\[disabled\\\]" \
++ " Foo \\\[disabled\\\]"]
++
++ load_core_file
++ gdb_test "python print(handler_call_log)" \
++ [string_to_regexp {[]}]
++ gdb_test_no_output "python handler_call_log = \[\]" \
++ "reset call log"
++}
++
++with_test_prefix "enable 'abc-def'" {
++ set re [string_to_regexp $hidden_binfile]
++
++ gdb_test "enable missing-objfile-handler \"$re\" abc-def" \
++ "^1 missing objfile handler enabled" \
++ "enable missing-objfile-handler"
++
++ gdb_test "info missing-objfile-handlers" \
++ [multi_line \
++ "Progspace \[^\r\n\]+:" \
++ " abc-def" \
++ " baz- \\\[disabled\\\]" \
++ "Global:" \
++ " -bar \\\[disabled\\\]" \
++ " Foo \\\[disabled\\\]"]
++
++ load_core_file
++ gdb_test "python print(handler_call_log)" \
++ [string_to_regexp {['abc-def', 'abc-def']}]
++ gdb_test_no_output "python handler_call_log = \[\]" \
++ "reset call log"
++}
++
++with_test_prefix "enable global handlers" {
++ gdb_test "enable missing-objfile-handler global" \
++ "^2 missing objfile handlers enabled"
++
++ gdb_test "info missing-objfile-handlers" \
++ [multi_line \
++ "Progspace \[^\r\n\]+:" \
++ " abc-def" \
++ " baz- \\\[disabled\\\]" \
++ "Global:" \
++ " -bar" \
++ " Foo"]
++
++ load_core_file
++ gdb_test "python print(handler_call_log)" \
++ [string_to_regexp {['abc-def', '-bar', 'Foo', 'abc-def', '-bar', 'Foo']}]
++ gdb_test_no_output "python handler_call_log = \[\]" \
++ "reset call log"
++}
++
++# Add handler_obj to the global handler list, and configure it to
++# return False. We should call all of the program space specific
++# handlers (which return None), and then call handler_obj from the
++# global list, which returns False, at which point we shouldn't call
++# anyone else.
++with_test_prefix "return False handler in global list" {
++ gdb_test "enable missing-objfile-handler progspace" \
++ "^1 missing objfile handler enabled"
++
++ gdb_test_no_output \
++ "python gdb.missing_objfile.register_handler(None, handler_obj)" \
++ "register handler_obj in global list"
++
++ gdb_test "info missing-objfile-handlers" \
++ [multi_line \
++ "Progspace \[^\r\n\]+:" \
++ " abc-def" \
++ " baz-" \
++ "Global:" \
++ " handler" \
++ " -bar" \
++ " Foo"]
++
++ gdb_test_no_output "python handler_obj.set_mode(Mode.RETURN_FALSE)" \
++ "confirgure handler"
++
++ load_core_file
++ gdb_test "python print(handler_call_log)" \
++ [string_to_regexp {['abc-def', 'baz-', 'handler', 'abc-def', 'baz-', 'handler']}]
++ gdb_test_no_output "python handler_call_log = \[\]" \
++ "reset call log"
++}
++
++# Now add handler_obj to the current program space's handler list. We
++# use the same handler object here, that's fine. We should only see a
++# call to the first handler object in the call log.
++with_test_prefix "return False handler in progspace list" {
++ gdb_test_no_output \
++ "python gdb.missing_objfile.register_handler(pspace, handler_obj)" \
++ "register handler_obj in progspace list"
++
++ gdb_test "info missing-objfile-handlers" \
++ [multi_line \
++ "Progspace \[^\r\n\]+:" \
++ " handler" \
++ " abc-def" \
++ " baz-" \
++ "Global:" \
++ " handler" \
++ " -bar" \
++ " Foo"]
++
++ load_core_file
++ gdb_test "python print(handler_call_log)" \
++ [string_to_regexp {['handler', 'handler']}]
++ gdb_test_no_output "python handler_call_log = \[\]" \
++ "reset call log"
++}
++
++with_test_prefix "check handler replacement" {
++ # First, check we can have the same name appear in both program
++ # space and global lists without giving an error.
++ gdb_test_no_output "python register(\"Foo\", pspace)"
++
++ gdb_test "info missing-objfile-handlers" \
++ [multi_line \
++ "Progspace \[^\r\n\]+:" \
++ " Foo" \
++ " handler" \
++ " abc-def" \
++ " baz-" \
++ "Global:" \
++ " handler" \
++ " -bar" \
++ " Foo"]
++
++ # Now check that we get an error if we try to add a handler with
++ # the same name.
++ gdb_test "python gdb.missing_objfile.register_handler(pspace, log_handler(\"Foo\"))" \
++ [multi_line \
++ "RuntimeError.*: Handler Foo already exists\\." \
++ "Error occurred in Python.*"]
++
++ gdb_test "python gdb.missing_objfile.register_handler(handler=log_handler(\"Foo\"), locus=pspace)" \
++ [multi_line \
++ "RuntimeError.*: Handler Foo already exists\\." \
++ "Error occurred in Python.*"]
++
++ # And now try again, but this time with 'replace=True', we
++ # shouldn't get an error in this case.
++ gdb_test_no_output \
++ "python gdb.missing_objfile.register_handler(pspace, log_handler(\"Foo\"), replace=True)"
++
++ gdb_test_no_output \
++ "python gdb.missing_objfile.register_handler(handler=log_handler(\"Foo\"), locus=None, replace=True)"
++
++ # Now disable a handler and check we still need to use 'replace=True'.
++ gdb_test "disable missing-objfile-handler progspace Foo" \
++ "^1 missing objfile handler disabled"
++
++ gdb_test "python gdb.missing_objfile.register_handler(pspace, log_handler(\"Foo\"))" \
++ [multi_line \
++ "RuntimeError.*: Handler Foo already exists\\." \
++ "Error occurred in Python.*"] \
++ "still get an error when handler is disabled"
++
++ gdb_test_no_output \
++ "python gdb.missing_objfile.register_handler(pspace, log_handler(\"Foo\"), replace=True)" \
++ "can replace a disabled handler"
++}
+diff --git a/gdb/testsuite/gdb.python/py-missing-objfile.py b/gdb/testsuite/gdb.python/py-missing-objfile.py
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.python/py-missing-objfile.py
+@@ -0,0 +1,167 @@
++# Copyright (C) 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 <http://www.gnu.org/licenses/>.
++
++import shutil
++import os
++from enum import Enum
++
++import gdb
++from gdb.missing_objfile import MissingObjfileHandler
++
++# A global log that is filled in by instances of the LOG_HANDLER class
++# when they are called.
++handler_call_log = []
++
++# A global holding a string, the build-id of the last missing objfile
++# which triggered the 'handler' class below. This is set in the
++# __call__ method of the 'handler' class and then checked from the
++# expect script.
++handler_last_buildid = None
++
++
++# A global holding a string, the filename of the last missing objfile
++# which triggered the 'handler' class below. This is set in the
++# __call__ method of the 'handler' class and then checked from the
++# expect script.
++handler_last_filename = None
++
++
++# A helper function that makes some assertions about the arguments
++# passed to a MissingObjfileHandler.__call__() method.
++def check_args(pspace, buildid, filename):
++ assert type(filename) == str
++ assert filename != ""
++ assert type(pspace) == gdb.Progspace
++ assert type(buildid) == str
++ assert buildid != ""
++
++
++# Enum used to configure the 'handler' class from the test script.
++class Mode(Enum):
++ RETURN_NONE = 0
++ RETURN_TRUE = 1
++ RETURN_FALSE = 2
++ RETURN_STRING = 3
++
++
++# A missing objfile handler which can be configured to return each of
++# the different possible return types.
++class handler(MissingObjfileHandler):
++ def __init__(self):
++ super().__init__("handler")
++ self._call_count = 0
++ self._mode = Mode.RETURN_NONE
++
++ def __call__(self, pspace, buildid, filename):
++ global handler_call_log, handler_last_buildid, handler_last_filename
++ check_args(pspace, buildid, filename)
++ handler_call_log.append(self.name)
++ handler_last_buildid = buildid
++ handler_last_filename = filename
++ self._call_count += 1
++ if self._mode == Mode.RETURN_NONE:
++ return None
++
++ if self._mode == Mode.RETURN_TRUE:
++ shutil.copy(self._src, self._dest)
++
++ # If we're using the fission-dwp board then there will
++ # also be a .dwp file that needs to be copied.
++ dwp_src = self._src + ".dwp"
++ if os.path.exists(dwp_src):
++ dwp_dest = self._dest + ".dwp"
++ shutil.copy(dwp_src, dwp_dest)
++
++ return True
++
++ if self._mode == Mode.RETURN_FALSE:
++ return False
++
++ if self._mode == Mode.RETURN_STRING:
++ return self._dest
++
++ assert False
++
++ @property
++ def call_count(self):
++ """Return a count, the number of calls to __call__ since the last
++ call to set_mode.
++ """
++ return self._call_count
++
++ def set_mode(self, mode, *args):
++ self._call_count = 0
++ self._mode = mode
++
++ if mode == Mode.RETURN_NONE:
++ assert len(args) == 0
++ return
++
++ if mode == Mode.RETURN_TRUE:
++ assert len(args) == 2
++ self._src = args[0]
++ self._dest = args[1]
++ return
++
++ if mode == Mode.RETURN_FALSE:
++ assert len(args) == 0
++ return
++
++ if mode == Mode.RETURN_STRING:
++ assert len(args) == 1
++ self._dest = args[0]
++ return
++
++ assert False
++
++
++# A missing objfile handler which raises an exception. The type of
++# exception to be raised is configured from the test script.
++class exception_handler(MissingObjfileHandler):
++ def __init__(self):
++ super().__init__("exception_handler")
++ self.exception_type = None
++
++ def __call__(self, pspace, buildid, filename):
++ global handler_call_log
++ check_args(pspace, buildid, filename)
++ handler_call_log.append(self.name)
++ assert self.exception_type is not None
++ raise self.exception_type("message")
++
++
++# A very simple logging missing objfile handler. Always returns None
++# so that GDB will try any other registered handlers, but first logs
++# the name of this handler into the global HANDLER_CALL_LOG, which can
++# then be checked from the test script.
++class log_handler(MissingObjfileHandler):
++ def __call__(self, pspace, buildid, filename):
++ global handler_call_log
++ check_args(pspace, buildid, filename)
++ handler_call_log.append(self.name)
++ return None
++
++
++# A basic helper function, this keeps lines shorter in the TCL script.
++def register(name, locus=None):
++ gdb.missing_objfile.register_handler(locus, log_handler(name))
++
++
++# Create instances of the handlers, but don't install any. We install
++# these as needed from the TCL script.
++rhandler = exception_handler()
++handler_obj = handler()
++
++print("Success")
+diff --git a/gdb/testsuite/gdb.server/build-id-seqno.c b/gdb/testsuite/gdb.server/build-id-seqno.c
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.server/build-id-seqno.c
+@@ -0,0 +1,22 @@
++/* This testcase is part of GDB, the GNU debugger.
++
++ Copyright 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 <http://www.gnu.org/licenses/>. */
++
++int
++main (void)
++{
++ return 0;
++}
+diff --git a/gdb/testsuite/gdb.server/build-id-seqno.exp b/gdb/testsuite/gdb.server/build-id-seqno.exp
+new file mode 100644
+--- /dev/null
++++ b/gdb/testsuite/gdb.server/build-id-seqno.exp
+@@ -0,0 +1,198 @@
++# This testcase is part of GDB, the GNU debugger.
++#
++# Copyright 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 <http://www.gnu.org/licenses/>.
++
++# Setup a .build-id/ based debug directory containing multiple entries
++# for the same build-id, with each entry given a different sequence
++# number.
++#
++# Ensure that GDB will scan over broken symlinks for the same build-id
++# (but different sequence number) to find later working symlinks.
++#
++# This test places the build-id files within a directory next to where
++# gdbserver is started, and places a relative address in the
++# debug-file-directory, in this way we require GDB to find the debug
++# information via gdbserver.
++
++require {!is_remote host}
++
++load_lib gdbserver-support.exp
++
++standard_testfile
++
++if {[build_executable "failed to prepare" $testfile $srcfile] == -1} {
++ return -1
++}
++
++# Split out BINFILE.debug. Remove debug from BINFILE.
++if {[gdb_gnu_strip_debug $binfile] != 0} {
++ return -1
++}
++
++# Get the '.build-id/xx/xxx...xxx' part of the filename.
++set build_id_filename [build_id_debug_filename_get $binfile]
++
++# Hide (rename) BINFILE.debug, this should ensure GDB can't find it
++# directly but needs to look for the build-id based file in the debug
++# directory.
++set hidden_debuginfo [standard_output_file "hidden_$testfile.debug"]
++remote_exec build "mv ${binfile}.debug $hidden_debuginfo"
++
++# A filename that doesn't exist. Some symlinks will point at this
++# file.
++set missing_debuginfo "missing_debuginfo"
++
++# Helper called from gdb_finish when the 'target' is remote. Ensure the
++# debug directory we create is deleted.
++proc cleanup_remote_target {} {
++ remote_exec target "rm -fr debug/"
++}
++
++if { ![is_remote target] } {
++ set gdbserver_dir [standard_output_file "gdbserver-dir"]/
++} else {
++ lappend gdb_finish_hooks cleanup_remote_target
++ set gdbserver_dir ""
++}
++
++# Copy files to the target (if needed).
++set target_binfile [gdb_remote_download target $binfile]
++set target_debuginfo [gdb_remote_download target $hidden_debuginfo]
++
++# Setup the debug information on the target.
++set debugdir "${gdbserver_dir}debug"
++remote_exec target \
++ "mkdir -p $debugdir/[file dirname $build_id_filename]"
++remote_exec target \
++ "ln -sf $target_debuginfo $debugdir/$build_id_filename"
++
++# Start GDB and load global BINFILE. If DEBUGINFO_FILE is not the
++# empty string then this contains the '.build-id/xx/xxx....xxxx' part
++# of the filename which we expect GDB to read from the remote target.
++# If DEBUGINFO_FILE is the empty string then we don't expect GDB to
++# find any debug information.
++proc load_binfile_check_debug_is_found { debuginfo_file testname } {
++ with_test_prefix "$testname" {
++ with_timeout_factor 5 {
++ # Probing for .build-id based debug files on remote
++ # targets uses the vFile:stat packet by default, though
++ # there is a work around that avoids this which can be
++ # used if GDB is connected to an older gdbserver without
++ # 'stat' support.
++ #
++ # Check the work around works by disabling use of the
++ # vFile:stat packet.
++ foreach_with_prefix stat_pkt {auto off} {
++ clean_restart
++
++ gdb_test_no_output "set debug-file-directory debug" \
++ "set debug-file-directory"
++
++ gdb_test_no_output "set sysroot target:"
++
++ gdb_test "set remote hostio-stat-packet $stat_pkt"
++
++ # Make sure we're disconnected, in case we're testing with an
++ # extended-remote board, therefore already connected.
++ gdb_test "disconnect" ".*"
++
++ # Start gdbserver. This needs to be done after starting GDB. When
++ # gdbserver is running local to GDB, start gdbserver in a sub-directory,
++ # this prevents GDB from finding the debug information itself.
++ if { ![is_remote target] } {
++ with_cwd $::gdbserver_dir {
++ set res [gdbserver_start "" $::target_binfile]
++ }
++ } else {
++ set res [gdbserver_start "" $::target_binfile]
++ }
++ set gdbserver_protocol [lindex $res 0]
++ set gdbserver_gdbport [lindex $res 1]
++
++ # Connect to gdbserver. The output will be placed into the global
++ # GDB_TARGET_REMOTE_CMD_MSG, and we'll match against this below.
++ gdb_assert {[gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport] == 0} \
++ "connect to gdbserver"
++
++ if { $debuginfo_file ne "" } {
++ gdb_assert { [regexp "Reading symbols from target:debug/[string_to_regexp $debuginfo_file]\\.\\.\\." \
++ $::gdb_target_remote_cmd_msg] } \
++ "debuginfo was read via build-id"
++ gdb_assert { [regexp "Reading debug/[string_to_regexp $debuginfo_file] from remote target\\.\\.\\." \
++ $::gdb_target_remote_cmd_msg] } \
++ "debuginfo was read from remote target"
++ } else {
++ gdb_assert { [regexp "\\(No debugging symbols found in \[^\r\n\]+/$::testfile\\)" \
++ $::gdb_target_remote_cmd_msg] }
++ }
++ }
++ }
++ }
++}
++
++# Return a copy of FILENAME, which should end '.debug', with NUMBER
++# added, e.g. add_seqno 1 "foo.debug" --> "foo.1.debug".
++proc add_seqno { number filename } {
++ return [regsub "\.debug\$" $filename ".${number}.debug"]
++}
++
++# Precompute sequence numbered build-id filenames.
++set build_id_1_filename [add_seqno 1 $build_id_filename]
++set build_id_2_filename [add_seqno 2 $build_id_filename]
++set build_id_3_filename [add_seqno 3 $build_id_filename]
++
++load_binfile_check_debug_is_found $build_id_filename \
++ "find debuginfo with a single build-id file"
++
++remote_exec target "ln -fs $target_debuginfo \
++ $debugdir/$build_id_1_filename"
++remote_exec target "ln -fs $target_debuginfo \
++ $debugdir/$build_id_2_filename"
++remote_exec target "ln -fs $target_debuginfo \
++ $debugdir/$build_id_3_filename"
++
++load_binfile_check_debug_is_found $build_id_filename \
++ "find debuginfo with 4 build-id files"
++
++remote_exec target "ln -fs $missing_debuginfo $debugdir/$build_id_filename"
++
++load_binfile_check_debug_is_found $build_id_1_filename \
++ "find debuginfo, first build-id file is bad"
++
++remote_exec target "ln -fs $missing_debuginfo \
++ $debugdir/$build_id_1_filename"
++remote_exec target "ln -fs $missing_debuginfo \
++ $debugdir/$build_id_3_filename"
++
++load_binfile_check_debug_is_found $build_id_2_filename \
++ "find debuginfo, first 2 build-id files are bad"
++
++remote_exec target "ln -fs $missing_debuginfo \
++ $debugdir/$build_id_2_filename"
++
++load_binfile_check_debug_is_found "" \
++ "cannot find debuginfo, all build-id files are bad"
++
++remote_exec target "ln -fs $target_debuginfo \
++ $debugdir/$build_id_3_filename"
++
++load_binfile_check_debug_is_found $build_id_3_filename \
++ "find debuginfo, last build-id file is good"
++
++remote_exec target "rm -f $debugdir/$build_id_1_filename"
++
++load_binfile_check_debug_is_found "" \
++ "cannot find debuginfo, file with seqno 1 is missing"
+diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp
+--- a/gdb/testsuite/lib/dwarf.exp
++++ b/gdb/testsuite/lib/dwarf.exp
+@@ -2993,25 +2993,32 @@ namespace eval Dwarf {
+
+ proc _note {type name hexdata} {
+ set namelen [expr [string length $name] + 1]
++ set datalen [expr [string length $hexdata] / 2]
+
+ # Name size.
+ _op .4byte $namelen
+ # Data size.
+- _op .4byte [expr [string length $hexdata] / 2]
++ _op .4byte $datalen
+ # Type.
+ _op .4byte $type
+ # The name.
+ _op .ascii [_quote $name]
+- # Alignment.
++ # Alignment (to 4-byte boundary).
+ set align 2
+ set total [expr {($namelen + (1 << $align) - 1) & -(1 << $align)}]
+ for {set i $namelen} {$i < $total} {incr i} {
+- _op .byte 0
++ _op .byte 0 padding
+ }
+ # The data.
+ foreach {a b} [split $hexdata {}] {
+ _op .byte 0x$a$b
+ }
++ # Alignment (to 4-byte boundary).
++ set align 2
++ set total [expr {($datalen + (1 << $align) - 1) & -(1 << $align)}]
++ for {set i $datalen} {$i < $total} {incr i} {
++ _op .byte 0 padding
++ }
+ }
+
+ # Emit a note section holding the given build-id.
+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
+@@ -5337,6 +5337,7 @@ proc quote_for_host { args } {
+ # debug information
+ # - text_segment=addr: Tell the linker to place the text segment at ADDR.
+ # - build-id: Ensure the final binary includes a build-id.
++# - no-build-id: Ensure the final binary does not include a build-id.
+ # - column-info/no-column-info: Enable/Disable generation of column table
+ # information.
+ #
+@@ -5448,6 +5449,18 @@ proc gdb_compile {source dest type options} {
+ lappend new_options "additional_flags=-Wl,--build-id"
+ }
+
++ # If the 'no-build-id' option is used then disable the build-id.
++ if {[lsearch -exact $options no-build-id] > 0} {
++ lappend new_options "additional_flags=-Wl,--build-id=none"
++ }
++
++ # Sanity check. If both 'build-id' and 'no-build-id' are used
++ # then what is expected from us!
++ if {[lsearch -exact $options build-id] > 0
++ && [lsearch -exact $options no-build-id] > 0} {
++ error "cannot use build-id and no-build-id options"
++ }
++
+ # Treating .c input files as C++ is deprecated in Clang, so
+ # explicitly force C++ language.
+ if { !$getting_compiler_info
+@@ -8144,21 +8157,25 @@ proc get_build_id { filename } {
+
+ # Return the build-id hex string (usually 160 bits as 40 hex characters)
+ # converted to the form: .build-id/ab/cdef1234...89.debug
++#
++# The '.debug' suffix can be changed by passing the SUFFIX argument.
++#
+ # Return "" if no build-id found.
+-proc build_id_debug_filename_get { filename } {
++proc build_id_debug_filename_get { filename {suffix ".debug"} } {
+ set data [get_build_id $filename]
+ if { $data == "" } {
+ return ""
+ }
+ regsub {^..} $data {\0/} data
+- return ".build-id/${data}.debug"
++ return ".build-id/${data}${suffix}"
+ }
+
+ # DEST should be a file compiled with debug information. This proc
+-# creates two new files DEST.debug which contains the debug
+-# information extracted from DEST, and DEST.stripped, which is a copy
+-# of DEST with the debug information removed. A '.gnu_debuglink'
+-# section will be added to DEST.stripped that points to DEST.debug.
++# creates DEST.debug which contains the debug information extracted
++# from DEST, and DEST is updated with the debug information removed.
++#
++# By default a '.gnu_debuglink' section will be added to DEST that
++# points to DEST.debug.
+ #
+ # If ARGS is passed, it is a list of optional flags. The currently
+ # supported flags are:
+@@ -8166,7 +8183,7 @@ proc build_id_debug_filename_get { filename } {
+ # - no-main : remove the symbol entry for main from the separate
+ # debug file DEST.debug,
+ # - no-debuglink : don't add the '.gnu_debuglink' section to
+-# DEST.stripped.
++# DEST.
+ #
+ # Function returns zero on success. Function will return non-zero failure code
+ # on some targets not supporting separate debug info (such as i386-msdos).
+@@ -8225,20 +8242,26 @@ proc gdb_gnu_strip_debug { dest args } {
+ # Unless the "no-debuglink" flag is passed, then link the two
+ # previous output files together, adding the .gnu_debuglink
+ # section to the stripped_file, containing a pointer to the
+- # debug_file, save the new file in dest.
++ # debug_file.
+ if {[lsearch -exact $args "no-debuglink"] == -1} {
+- set result [catch "exec $objcopy_program --add-gnu-debuglink=${debug_file} ${stripped_file} ${dest}" output]
++ set result [catch "exec $objcopy_program --add-gnu-debuglink=${debug_file} ${stripped_file} ${stripped_file}-tmp" output]
+ verbose "result is $result"
+ verbose "output is $output"
+ if {$result == 1} {
+ return 1
+ }
++ file delete "${stripped_file}"
++ file rename "${stripped_file}-tmp" "${stripped_file}"
+ }
+
+ # Workaround PR binutils/10802:
+ # Preserve the 'x' bit also for PIEs (Position Independent Executables).
+- set perm [file attributes ${stripped_file} -permissions]
+- file attributes ${dest} -permissions $perm
++ set perm [file attributes ${dest} -permissions]
++ file attributes ${stripped_file} -permissions $perm
++
++ # Move the stripped_file back into dest.
++ file delete ${dest}
++ file rename ${stripped_file} ${dest}
+
+ return 0
+ }
+diff --git a/gdbserver/hostio.cc b/gdbserver/hostio.cc
+--- a/gdbserver/hostio.cc
++++ b/gdbserver/hostio.cc
+@@ -486,6 +486,42 @@ handle_fstat (char *own_buf, int *new_packet_len)
+ write_enn (own_buf);
+ }
+
++static void
++handle_stat (char *own_buf, int *new_packet_len)
++{
++ int bytes_sent;
++ char *p;
++ struct stat st;
++ struct fio_stat fst;
++ char filename[HOSTIO_PATH_MAX];
++
++ p = own_buf + strlen ("vFile:stat:");
++
++ if (require_filename (&p, filename)
++ || require_end (p))
++ {
++ hostio_packet_error (own_buf);
++ return;
++ }
++
++ if (lstat (filename, &st) == -1)
++ {
++ hostio_error (own_buf);
++ return;
++ }
++
++ host_to_fileio_stat (&st, &fst);
++
++ bytes_sent = hostio_reply_with_data (own_buf,
++ (char *) &fst, sizeof (fst),
++ new_packet_len);
++
++ /* If the response does not fit into a single packet, do not attempt
++ to return a partial response, but simply fail. */
++ if (bytes_sent < sizeof (fst))
++ write_enn (own_buf);
++}
++
+ static void
+ handle_close (char *own_buf)
+ {
+@@ -603,6 +639,8 @@ handle_vFile (char *own_buf, int packet_len, int *new_packet_len)
+ handle_pwrite (own_buf, packet_len);
+ else if (startswith (own_buf, "vFile:fstat:"))
+ handle_fstat (own_buf, new_packet_len);
++ else if (startswith (own_buf, "vFile:stat:"))
++ handle_stat (own_buf, new_packet_len);
+ else if (startswith (own_buf, "vFile:close:"))
+ handle_close (own_buf);
+ else if (startswith (own_buf, "vFile:unlink:"))
+diff --git a/gdbsupport/pathstuff.cc b/gdbsupport/pathstuff.cc
+--- a/gdbsupport/pathstuff.cc
++++ b/gdbsupport/pathstuff.cc
+@@ -198,11 +198,17 @@ path_join (gdb::array_view<const char *> paths)
+ {
+ const char *path = paths[i];
+
+- if (i > 0)
+- gdb_assert (strlen (path) == 0 || !IS_ABSOLUTE_PATH (path));
+-
+- if (!ret.empty () && !IS_DIR_SEPARATOR (ret.back ()))
+- ret += '/';
++ if (!ret.empty ())
++ {
++ /* If RET doesn't already end with a separator then add one. */
++ if (!IS_DIR_SEPARATOR (ret.back ()))
++ ret += '/';
++
++ /* Now that RET ends with a separator, ignore any at the start of
++ PATH. */
++ while (IS_DIR_SEPARATOR (path[0]))
++ ++path;
++ }
+
+ ret.append (path);
+ }
+diff --git a/gdbsupport/pathstuff.h b/gdbsupport/pathstuff.h
+--- a/gdbsupport/pathstuff.h
++++ b/gdbsupport/pathstuff.h
+@@ -64,8 +64,10 @@ extern const char *child_path (const char *parent, const char *child);
+
+ /* Join elements in PATHS into a single path.
+
+- The first element can be absolute or relative. All the others must be
+- relative. */
++ The first element can be absolute or relative. Only a single directory
++ separator will be placed between elements of PATHS, if one element ends
++ with a directory separator, or an element starts with a directory
++ separator, then these will be collapsed into a single separator. */
+
+ extern std::string path_join (gdb::array_view<const char *> paths);
+
diff --git a/gdb-remove-qnx-neutrino-support.patch b/gdb-remove-qnx-neutrino-support.patch
new file mode 100644
index 0000000..3a35b17
--- /dev/null
+++ b/gdb-remove-qnx-neutrino-support.patch
@@ -0,0 +1,2923 @@
+From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
+From: Simon Marchi <simon.marchi@polymtl.ca>
+Date: Thu, 25 Jul 2024 13:41:35 -0400
+Subject: gdb-remove-qnx-neutrino-support.patch
+
+;; Backport of upstream commit 36fb20fa93484b104d. This is not really
+;; relevant for our branch, but later commits that are important, and
+;; which we want to backport, conflict unless the cleanup in this
+;; patch is in place.
+;;
+;; This commit will be part of GDB 16, so this back-port should drop
+;; out when we rebase onto GDB 16.
+;;
+;; gdb: remove QNX Neutrino support
+;;
+;; Remove the support for the QNX Neutrino OS (tdep and native bits). This
+;; has been unmaintained for years, and we don't have a way to see if it
+;; works (or even builds, for the native parts). Without somebody actively
+;; maintaining it, this is just a burden for developers, especially that
+;; this port does a few weird unique things that require reasoning about
+;; when doing big change.
+;;
+;; Support for GDBserver was removed in 2020, commit 613f149a90d6
+;; ("gdbserver: remove support for Neutrino").
+;;
+;; Change-Id: I4e25ec26ab06636629adebd02ceb161ee31c232d
+;; Approved-by: Kevin Buettner <kevinb@redhat.com>
+
+diff --git a/gdb/Makefile.in b/gdb/Makefile.in
+--- a/gdb/Makefile.in
++++ b/gdb/Makefile.in
+@@ -828,7 +828,6 @@ ALL_TARGET_OBS = \
+ i386-go32-tdep.o \
+ i386-linux-tdep.o \
+ i386-netbsd-tdep.o \
+- i386-nto-tdep.o \
+ i386-obsd-tdep.o \
+ i386-sol2-tdep.o \
+ i386-tdep.o \
+@@ -856,7 +855,6 @@ ALL_TARGET_OBS = \
+ nds32-tdep.o \
+ nios2-linux-tdep.o \
+ nios2-tdep.o \
+- nto-tdep.o \
+ obsd-tdep.o \
+ or1k-linux-tdep.o \
+ or1k-tdep.o \
+@@ -1441,7 +1439,6 @@ HFILES_NO_SRCDIR = \
+ nds32-tdep.h \
+ nios2-tdep.h \
+ elf-none-tdep.h \
+- nto-tdep.h \
+ objc-lang.h \
+ objfiles.h \
+ obsd-nat.h \
+@@ -1573,7 +1570,6 @@ HFILES_NO_SRCDIR = \
+ compile/gcc-c-plugin.h \
+ compile/gcc-cp-plugin.h \
+ config/nm-linux.h \
+- config/nm-nto.h \
+ config/djgpp/langinfo.h \
+ config/djgpp/nl_types.h \
+ config/i386/nm-i386gnu.h \
+diff --git a/gdb/config/nm-nto.h b/gdb/config/nm-nto.h
+deleted file mode 100644
+--- a/gdb/config/nm-nto.h
++++ /dev/null
+@@ -1,29 +0,0 @@
+-/* Native support for QNX Neutrino version 6.
+-
+- Copyright (C) 2003-2024 Free Software Foundation, Inc.
+-
+- This code was donated by QNX Software Systems Ltd.
+-
+- This file is part of GDB.
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License as published by
+- the Free Software Foundation; either version 3 of the License, or
+- (at your option) any later version.
+-
+- This program is distributed in the hope that it will be useful,
+- but WITHOUT ANY WARRANTY; without even the implied warranty of
+- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- GNU General Public License for more details.
+-
+- You should have received a copy of the GNU General Public License
+- along with this program. If not, see <http://www.gnu.org/licenses/>. */
+-
+-#ifndef CONFIG_NM_NTO_H
+-#define CONFIG_NM_NTO_H
+-
+-/* Setup the valid realtime signal range. */
+-#define REALTIME_LO 41
+-#define REALTIME_HI 56
+-
+-#endif /* CONFIG_NM_NTO_H */
+diff --git a/gdb/configure.host b/gdb/configure.host
+--- a/gdb/configure.host
++++ b/gdb/configure.host
+@@ -110,7 +110,6 @@ i[34567]86-*-mingw32*) gdb_host=mingw
+ i[34567]86-*-msdosdjgpp*) gdb_host=go32 ;;
+ i[34567]86-*-linux*) gdb_host=linux ;;
+ i[34567]86-*-gnu*) gdb_host=i386gnu ;;
+-i[3456]86-*-nto*) gdb_host=nto ;;
+ i[34567]86-*-openbsd*) gdb_host=obsd ;;
+ i[34567]86-*-solaris2* | x86_64-*-solaris2*)
+ gdb_host=sol2 ;;
+diff --git a/gdb/configure.nat b/gdb/configure.nat
+--- a/gdb/configure.nat
++++ b/gdb/configure.nat
+@@ -421,15 +421,6 @@ case ${gdb_host} in
+
+ esac
+ ;;
+- nto)
+- case ${gdb_host_cpu} in
+- i386)
+- # Host: Intel 386 running QNX.
+- NATDEPFILES='nto-procfs.o'
+- NAT_FILE='config/nm-nto.h'
+- ;;
+- esac
+- ;;
+ obsd)
+ case ${gdb_host_cpu} in
+ i386)
+diff --git a/gdb/configure.tgt b/gdb/configure.tgt
+--- a/gdb/configure.tgt
++++ b/gdb/configure.tgt
+@@ -304,11 +304,6 @@ i[34567]86-*-openbsd*)
+ # Target: OpenBSD/i386
+ gdb_target_obs="i386-bsd-tdep.o i386-obsd-tdep.o bsd-uthread.o"
+ ;;
+-i[34567]86-*-nto*)
+- # Target: Intel 386 running qnx6.
+- gdb_target_obs="solib-svr4.o \
+- i386-nto-tdep.o nto-tdep.o"
+- ;;
+ i[34567]86-*-solaris2* | x86_64-*-solaris2*)
+ # Target: Solaris x86_64
+ gdb_target_obs="${i386_tobjs} ${amd64_tobjs} \
+@@ -803,7 +798,6 @@ case "${targ}" in
+ gdb_osabi=GDB_OSABI_FREEBSD ;;
+ *-*-linux* | *-*-uclinux*)
+ gdb_osabi=GDB_OSABI_LINUX ;;
+-*-*-nto*) gdb_osabi=GDB_OSABI_QNXNTO ;;
+ m68*-*-openbsd* | m88*-*-openbsd* | vax-*-openbsd*) ;;
+ *-*-openbsd*) gdb_osabi=GDB_OSABI_OPENBSD ;;
+ *-*-solaris*) gdb_osabi=GDB_OSABI_SOLARIS ;;
+diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
+--- a/gdb/doc/gdb.texinfo
++++ b/gdb/doc/gdb.texinfo
+@@ -2742,7 +2742,7 @@
+ @end smallexample
+
+ This command is available when debugging locally on most targets, excluding
+-@sc{djgpp}, Cygwin, MS Windows, and QNX Neutrino.
++@sc{djgpp}, Cygwin, and MS Windows.
+
+ @kindex set startup-with-shell
+ @anchor{set startup-with-shell}
+@@ -22011,7 +22011,7 @@
+ @cindex shared libraries
+ @anchor{Shared Libraries}
+ @value{GDBN} supports @sc{gnu}/Linux, MS-Windows, SunOS,
+-Darwin/Mach-O, SVr4, IBM RS/6000 AIX, QNX Neutrino, FDPIC (FR-V), and
++Darwin/Mach-O, SVr4, IBM RS/6000 AIX, FDPIC (FR-V), and
+ DSBT (TIC6X) shared libraries.
+
+ On MS-Windows @value{GDBN} must be linked with the Expat library to support
+@@ -25203,16 +25203,6 @@
+ These commands enable and disable tracing of entries into and exits
+ from the @code{syscall} interface.
+
+-@item info pidlist
+-@kindex info pidlist
+-@cindex process list, QNX Neutrino
+-For QNX Neutrino only, this command displays the list of all the
+-processes and all the threads within each process.
+-
+-@item info meminfo
+-@kindex info meminfo
+-@cindex mapinfo list, QNX Neutrino
+-For QNX Neutrino only, this command displays the list of all mapinfos.
+ @end table
+
+ @node DJGPP Native
+diff --git a/gdb/i386-nto-tdep.c b/gdb/i386-nto-tdep.c
+deleted file mode 100644
+--- a/gdb/i386-nto-tdep.c
++++ /dev/null
+@@ -1,379 +0,0 @@
+-/* Target-dependent code for QNX Neutrino x86.
+-
+- Copyright (C) 2003-2024 Free Software Foundation, Inc.
+-
+- Contributed by QNX Software Systems Ltd.
+-
+- This file is part of GDB.
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License as published by
+- the Free Software Foundation; either version 3 of the License, or
+- (at your option) any later version.
+-
+- This program is distributed in the hope that it will be useful,
+- but WITHOUT ANY WARRANTY; without even the implied warranty of
+- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- GNU General Public License for more details.
+-
+- You should have received a copy of the GNU General Public License
+- along with this program. If not, see <http://www.gnu.org/licenses/>. */
+-
+-#include "extract-store-integer.h"
+-#include "frame.h"
+-#include "osabi.h"
+-#include "regcache.h"
+-#include "target.h"
+-
+-#include "i386-tdep.h"
+-#include "i387-tdep.h"
+-#include "nto-tdep.h"
+-#include "solib.h"
+-#include "solib-svr4.h"
+-
+-#ifndef X86_CPU_FXSR
+-#define X86_CPU_FXSR (1L << 12)
+-#endif
+-
+-/* Why 13? Look in our /usr/include/x86/context.h header at the
+- x86_cpu_registers structure and you'll see an 'exx' junk register
+- that is just filler. Don't ask me, ask the kernel guys. */
+-#define NUM_GPREGS 13
+-
+-/* Mapping between the general-purpose registers in `struct xxx'
+- format and GDB's register cache layout. */
+-
+-/* From <x86/context.h>. */
+-static int i386nto_gregset_reg_offset[] =
+-{
+- 7 * 4, /* %eax */
+- 6 * 4, /* %ecx */
+- 5 * 4, /* %edx */
+- 4 * 4, /* %ebx */
+- 11 * 4, /* %esp */
+- 2 * 4, /* %epb */
+- 1 * 4, /* %esi */
+- 0 * 4, /* %edi */
+- 8 * 4, /* %eip */
+- 10 * 4, /* %eflags */
+- 9 * 4, /* %cs */
+- 12 * 4, /* %ss */
+- -1 /* filler */
+-};
+-
+-/* Given a GDB register number REGNUM, return the offset into
+- Neutrino's register structure or -1 if the register is unknown. */
+-
+-static int
+-nto_reg_offset (int regnum)
+-{
+- if (regnum >= 0 && regnum < ARRAY_SIZE (i386nto_gregset_reg_offset))
+- return i386nto_gregset_reg_offset[regnum];
+-
+- return -1;
+-}
+-
+-static void
+-i386nto_supply_gregset (struct regcache *regcache, char *gpregs)
+-{
+- struct gdbarch *gdbarch = regcache->arch ();
+- i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch);
+-
+- gdb_assert (tdep->gregset_reg_offset == i386nto_gregset_reg_offset);
+- i386_gregset.supply_regset (&i386_gregset, regcache, -1,
+- gpregs, NUM_GPREGS * 4);
+-}
+-
+-static void
+-i386nto_supply_fpregset (struct regcache *regcache, char *fpregs)
+-{
+- if (nto_cpuinfo_valid && nto_cpuinfo_flags | X86_CPU_FXSR)
+- i387_supply_fxsave (regcache, -1, fpregs);
+- else
+- i387_supply_fsave (regcache, -1, fpregs);
+-}
+-
+-static void
+-i386nto_supply_regset (struct regcache *regcache, int regset, char *data)
+-{
+- switch (regset)
+- {
+- case NTO_REG_GENERAL:
+- i386nto_supply_gregset (regcache, data);
+- break;
+- case NTO_REG_FLOAT:
+- i386nto_supply_fpregset (regcache, data);
+- break;
+- }
+-}
+-
+-static int
+-i386nto_regset_id (int regno)
+-{
+- if (regno == -1)
+- return NTO_REG_END;
+- else if (regno < I386_NUM_GREGS)
+- return NTO_REG_GENERAL;
+- else if (regno < I386_NUM_GREGS + I387_NUM_REGS)
+- return NTO_REG_FLOAT;
+- else if (regno < I386_SSE_NUM_REGS)
+- return NTO_REG_FLOAT; /* We store xmm registers in fxsave_area. */
+-
+- return -1; /* Error. */
+-}
+-
+-static int
+-i386nto_register_area (struct gdbarch *gdbarch,
+- int regno, int regset, unsigned *off)
+-{
+- i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch);
+-
+- *off = 0;
+- if (regset == NTO_REG_GENERAL)
+- {
+- if (regno == -1)
+- return NUM_GPREGS * 4;
+-
+- *off = nto_reg_offset (regno);
+- if (*off == -1)
+- return 0;
+- return 4;
+- }
+- else if (regset == NTO_REG_FLOAT)
+- {
+- unsigned off_adjust, regsize, regset_size, regno_base;
+- /* The following are flags indicating number in our fxsave_area. */
+- int first_four = (regno >= I387_FCTRL_REGNUM (tdep)
+- && regno <= I387_FISEG_REGNUM (tdep));
+- int second_four = (regno > I387_FISEG_REGNUM (tdep)
+- && regno <= I387_FOP_REGNUM (tdep));
+- int st_reg = (regno >= I387_ST0_REGNUM (tdep)
+- && regno < I387_ST0_REGNUM (tdep) + 8);
+- int xmm_reg = (regno >= I387_XMM0_REGNUM (tdep)
+- && regno < I387_MXCSR_REGNUM (tdep));
+-
+- if (nto_cpuinfo_valid && nto_cpuinfo_flags | X86_CPU_FXSR)
+- {
+- off_adjust = 32;
+- regsize = 16;
+- regset_size = 512;
+- /* fxsave_area structure. */
+- if (first_four)
+- {
+- /* fpu_control_word, fpu_status_word, fpu_tag_word, fpu_operand
+- registers. */
+- regsize = 2; /* Two bytes each. */
+- off_adjust = 0;
+- regno_base = I387_FCTRL_REGNUM (tdep);
+- }
+- else if (second_four)
+- {
+- /* fpu_ip, fpu_cs, fpu_op, fpu_ds registers. */
+- regsize = 4;
+- off_adjust = 8;
+- regno_base = I387_FISEG_REGNUM (tdep) + 1;
+- }
+- else if (st_reg)
+- {
+- /* ST registers. */
+- regsize = 16;
+- off_adjust = 32;
+- regno_base = I387_ST0_REGNUM (tdep);
+- }
+- else if (xmm_reg)
+- {
+- /* XMM registers. */
+- regsize = 16;
+- off_adjust = 160;
+- regno_base = I387_XMM0_REGNUM (tdep);
+- }
+- else if (regno == I387_MXCSR_REGNUM (tdep))
+- {
+- regsize = 4;
+- off_adjust = 24;
+- regno_base = I387_MXCSR_REGNUM (tdep);
+- }
+- else
+- {
+- /* Whole regset. */
+- gdb_assert (regno == -1);
+- off_adjust = 0;
+- regno_base = 0;
+- regsize = regset_size;
+- }
+- }
+- else
+- {
+- regset_size = 108;
+- /* fsave_area structure. */
+- if (first_four || second_four)
+- {
+- /* fpu_control_word, ... , fpu_ds registers. */
+- regsize = 4;
+- off_adjust = 0;
+- regno_base = I387_FCTRL_REGNUM (tdep);
+- }
+- else if (st_reg)
+- {
+- /* One of ST registers. */
+- regsize = 10;
+- off_adjust = 7 * 4;
+- regno_base = I387_ST0_REGNUM (tdep);
+- }
+- else
+- {
+- /* Whole regset. */
+- gdb_assert (regno == -1);
+- off_adjust = 0;
+- regno_base = 0;
+- regsize = regset_size;
+- }
+- }
+-
+- if (regno != -1)
+- *off = off_adjust + (regno - regno_base) * regsize;
+- else
+- *off = 0;
+- return regsize;
+- }
+- return -1;
+-}
+-
+-static int
+-i386nto_regset_fill (const struct regcache *regcache, int regset, char *data)
+-{
+- if (regset == NTO_REG_GENERAL)
+- {
+- int regno;
+-
+- for (regno = 0; regno < NUM_GPREGS; regno++)
+- {
+- int offset = nto_reg_offset (regno);
+- if (offset != -1)
+- regcache->raw_collect (regno, data + offset);
+- }
+- }
+- else if (regset == NTO_REG_FLOAT)
+- {
+- if (nto_cpuinfo_valid && nto_cpuinfo_flags | X86_CPU_FXSR)
+- i387_collect_fxsave (regcache, -1, data);
+- else
+- i387_collect_fsave (regcache, -1, data);
+- }
+- else
+- return -1;
+-
+- return 0;
+-}
+-
+-/* Return whether THIS_FRAME corresponds to a QNX Neutrino sigtramp
+- routine. */
+-
+-static int
+-i386nto_sigtramp_p (const frame_info_ptr &this_frame)
+-{
+- CORE_ADDR pc = get_frame_pc (this_frame);
+- const char *name;
+-
+- find_pc_partial_function (pc, &name, NULL, NULL);
+- return name && strcmp ("__signalstub", name) == 0;
+-}
+-
+-/* Assuming THIS_FRAME is a QNX Neutrino sigtramp routine, return the
+- address of the associated sigcontext structure. */
+-
+-static CORE_ADDR
+-i386nto_sigcontext_addr (const frame_info_ptr &this_frame)
+-{
+- struct gdbarch *gdbarch = get_frame_arch (this_frame);
+- enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+- gdb_byte buf[4];
+- CORE_ADDR ptrctx;
+-
+- /* We store __ucontext_t addr in EDI register. */
+- get_frame_register (this_frame, I386_EDI_REGNUM, buf);
+- ptrctx = extract_unsigned_integer (buf, 4, byte_order);
+- ptrctx += 24 /* Context pointer is at this offset. */;
+-
+- return ptrctx;
+-}
+-
+-static void
+-init_i386nto_ops (void)
+-{
+- nto_regset_id = i386nto_regset_id;
+- nto_supply_gregset = i386nto_supply_gregset;
+- nto_supply_fpregset = i386nto_supply_fpregset;
+- nto_supply_altregset = nto_dummy_supply_regset;
+- nto_supply_regset = i386nto_supply_regset;
+- nto_register_area = i386nto_register_area;
+- nto_regset_fill = i386nto_regset_fill;
+- nto_fetch_link_map_offsets =
+- svr4_ilp32_fetch_link_map_offsets;
+-}
+-
+-static void
+-i386nto_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+-{
+- i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch);
+- static solib_ops nto_svr4_so_ops;
+-
+- /* Deal with our strange signals. */
+- nto_initialize_signals ();
+-
+- /* NTO uses ELF. */
+- i386_elf_init_abi (info, gdbarch);
+-
+- /* Neutrino rewinds to look more normal. Need to override the i386
+- default which is [unfortunately] to decrement the PC. */
+- set_gdbarch_decr_pc_after_break (gdbarch, 0);
+-
+- tdep->gregset_reg_offset = i386nto_gregset_reg_offset;
+- tdep->gregset_num_regs = ARRAY_SIZE (i386nto_gregset_reg_offset);
+- tdep->sizeof_gregset = NUM_GPREGS * 4;
+-
+- tdep->sigtramp_p = i386nto_sigtramp_p;
+- tdep->sigcontext_addr = i386nto_sigcontext_addr;
+- tdep->sc_reg_offset = i386nto_gregset_reg_offset;
+- tdep->sc_num_regs = ARRAY_SIZE (i386nto_gregset_reg_offset);
+-
+- /* Setjmp()'s return PC saved in EDX (5). */
+- tdep->jb_pc_offset = 20; /* 5x32 bit ints in. */
+-
+- set_solib_svr4_fetch_link_map_offsets
+- (gdbarch, svr4_ilp32_fetch_link_map_offsets);
+-
+- /* Initialize this lazily, to avoid an initialization order
+- dependency on solib-svr4.c's _initialize routine. */
+- if (nto_svr4_so_ops.in_dynsym_resolve_code == NULL)
+- {
+- nto_svr4_so_ops = svr4_so_ops;
+-
+- /* Our loader handles solib relocations differently than svr4. */
+- nto_svr4_so_ops.relocate_section_addresses
+- = nto_relocate_section_addresses;
+-
+- /* Supply a nice function to find our solibs. */
+- nto_svr4_so_ops.find_and_open_solib
+- = nto_find_and_open_solib;
+-
+- /* Our linker code is in libc. */
+- nto_svr4_so_ops.in_dynsym_resolve_code
+- = nto_in_dynsym_resolve_code;
+- }
+- set_gdbarch_so_ops (gdbarch, &nto_svr4_so_ops);
+-
+- set_gdbarch_wchar_bit (gdbarch, 32);
+- set_gdbarch_wchar_signed (gdbarch, 0);
+-}
+-
+-void _initialize_i386nto_tdep ();
+-void
+-_initialize_i386nto_tdep ()
+-{
+- init_i386nto_ops ();
+- gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_QNXNTO,
+- i386nto_init_abi);
+- gdbarch_register_osabi_sniffer (bfd_arch_i386, bfd_target_elf_flavour,
+- nto_elf_osabi_sniffer);
+-}
+diff --git a/gdb/nto-procfs.c b/gdb/nto-procfs.c
+deleted file mode 100644
+--- a/gdb/nto-procfs.c
++++ /dev/null
+@@ -1,1588 +0,0 @@
+-/* Machine independent support for QNX Neutrino /proc (process file system)
+- for GDB. Written by Colin Burgess at QNX Software Systems Limited.
+-
+- Copyright (C) 2003-2024 Free Software Foundation, Inc.
+-
+- Contributed by QNX Software Systems Ltd.
+-
+- This file is part of GDB.
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License as published by
+- the Free Software Foundation; either version 3 of the License, or
+- (at your option) any later version.
+-
+- This program is distributed in the hope that it will be useful,
+- but WITHOUT ANY WARRANTY; without even the implied warranty of
+- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- GNU General Public License for more details.
+-
+- You should have received a copy of the GNU General Public License
+- along with this program. If not, see <http://www.gnu.org/licenses/>. */
+-
+-
+-#include <fcntl.h>
+-#include <spawn.h>
+-#include <sys/debug.h>
+-#include <sys/procfs.h>
+-#include <sys/neutrino.h>
+-#include <sys/syspage.h>
+-#include <dirent.h>
+-#include <sys/netmgr.h>
+-#include <sys/auxv.h>
+-
+-#include "gdbcore.h"
+-#include "inferior.h"
+-#include "target.h"
+-#include "objfiles.h"
+-#include "gdbthread.h"
+-#include "nto-tdep.h"
+-#include "command.h"
+-#include "regcache.h"
+-#include "solib.h"
+-#include "inf-child.h"
+-#include "gdbsupport/filestuff.h"
+-#include "gdbsupport/scoped_fd.h"
+-
+-#define NULL_PID 0
+-#define _DEBUG_FLAG_TRACE (_DEBUG_FLAG_TRACE_EXEC|_DEBUG_FLAG_TRACE_RD|\
+- _DEBUG_FLAG_TRACE_WR|_DEBUG_FLAG_TRACE_MODIFY)
+-
+-int ctl_fd;
+-
+-static sighandler_t ofunc;
+-
+-static procfs_run run;
+-
+-/* Create the "native" and "procfs" targets. */
+-
+-struct nto_procfs_target : public inf_child_target
+-{
+- void open (const char *arg, int from_tty) override;
+-
+- void attach (const char *, int) override = 0;
+-
+- void post_attach (int);
+-
+- void detach (inferior *, int) override;
+-
+- void resume (ptid_t, int, enum gdb_signal) override;
+-
+- ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override;
+-
+- void fetch_registers (struct regcache *, int) override;
+- void store_registers (struct regcache *, int) override;
+-
+- enum target_xfer_status xfer_partial (enum target_object object,
+- const char *annex,
+- gdb_byte *readbuf,
+- const gdb_byte *writebuf,
+- ULONGEST offset, ULONGEST len,
+- ULONGEST *xfered_len) override;
+-
+- void files_info () override;
+-
+- int insert_breakpoint (struct gdbarch *, struct bp_target_info *) override;
+-
+- int remove_breakpoint (struct gdbarch *, struct bp_target_info *,
+- enum remove_bp_reason) override;
+-
+- int can_use_hw_breakpoint (enum bptype, int, int) override;
+-
+- int insert_hw_breakpoint (struct gdbarch *, struct bp_target_info *) override;
+-
+- int remove_hw_breakpoint (struct gdbarch *, struct bp_target_info *) override;
+-
+- int insert_watchpoint (CORE_ADDR, int, enum target_hw_bp_type,
+- struct expression *) override;
+-
+- int remove_watchpoint (CORE_ADDR, int, enum target_hw_bp_type,
+- struct expression *) override;
+-
+- bool stopped_by_watchpoint () override;
+-
+- void kill () override;
+-
+- void create_inferior (const char *, const std::string &,
+- char **, int) override;
+-
+- void mourn_inferior () override;
+-
+- void pass_signals (gdb::array_view<const unsigned char>) override;
+-
+- bool thread_alive (ptid_t ptid) override;
+-
+- void update_thread_list () override;
+-
+- std::string pid_to_str (ptid_t) override;
+-
+- void interrupt () override;
+-
+- const char *extra_thread_info (struct thread_info *) override;
+-
+- const char *pid_to_exec_file (int pid) override;
+-};
+-
+-/* For "target native". */
+-
+-static const target_info nto_native_target_info = {
+- "native",
+- N_("QNX Neutrino local process"),
+- N_("QNX Neutrino local process (started by the \"run\" command).")
+-};
+-
+-class nto_procfs_target_native final : public nto_procfs_target
+-{
+- const target_info &info () const override
+- { return nto_native_target_info; }
+-};
+-
+-/* For "target procfs <node>". */
+-
+-static const target_info nto_procfs_target_info = {
+- "procfs",
+- N_("QNX Neutrino local or remote process"),
+- N_("QNX Neutrino process. target procfs NODE")
+-};
+-
+-struct nto_procfs_target_procfs final : public nto_procfs_target
+-{
+- const target_info &info () const override
+- { return nto_procfs_target_info; }
+-};
+-
+-static ptid_t do_attach (ptid_t ptid);
+-
+-/* These two globals are only ever set in procfs_open_1, but are
+- referenced elsewhere. 'nto_procfs_node' is a flag used to say
+- whether we are local, or we should get the current node descriptor
+- for the remote QNX node. */
+-static char *nodestr;
+-static unsigned nto_procfs_node = ND_LOCAL_NODE;
+-
+-/* Return the current QNX Node, or error out. This is a simple
+- wrapper for the netmgr_strtond() function. The reason this
+- is required is because QNX node descriptors are transient so
+- we have to re-acquire them every time. */
+-static unsigned
+-nto_node (void)
+-{
+- unsigned node;
+-
+- if (ND_NODE_CMP (nto_procfs_node, ND_LOCAL_NODE) == 0
+- || nodestr == NULL)
+- return ND_LOCAL_NODE;
+-
+- node = netmgr_strtond (nodestr, 0);
+- if (node == -1)
+- error (_("Lost the QNX node. Debug session probably over."));
+-
+- return (node);
+-}
+-
+-static enum gdb_osabi
+-procfs_is_nto_target (bfd *abfd)
+-{
+- return GDB_OSABI_QNXNTO;
+-}
+-
+-/* This is called when we call 'target native' or 'target procfs
+- <arg>' from the (gdb) prompt. For QNX6 (nto), the only valid arg
+- will be a QNX node string, eg: "/net/some_node". If arg is not a
+- valid QNX node, we will default to local. */
+-void
+-nto_procfs_target::open (const char *arg, int from_tty)
+-{
+- char *endstr;
+- char buffer[50];
+- int total_size;
+- procfs_sysinfo *sysinfo;
+- char nto_procfs_path[PATH_MAX];
+-
+- /* Offer to kill previous inferiors before opening this target. */
+- target_preopen (from_tty);
+-
+- nto_is_nto_target = procfs_is_nto_target;
+-
+- /* Set the default node used for spawning to this one,
+- and only override it if there is a valid arg. */
+-
+- xfree (nodestr);
+- nodestr = NULL;
+-
+- nto_procfs_node = ND_LOCAL_NODE;
+- nodestr = (arg != NULL) ? xstrdup (arg) : NULL;
+-
+- if (nodestr)
+- {
+- nto_procfs_node = netmgr_strtond (nodestr, &endstr);
+- if (nto_procfs_node == -1)
+- {
+- if (errno == ENOTSUP)
+- gdb_printf ("QNX Net Manager not found.\n");
+- gdb_printf ("Invalid QNX node %s: error %d (%s).\n", nodestr,
+- errno, safe_strerror (errno));
+- xfree (nodestr);
+- nodestr = NULL;
+- nto_procfs_node = ND_LOCAL_NODE;
+- }
+- else if (*endstr)
+- {
+- if (*(endstr - 1) == '/')
+- *(endstr - 1) = 0;
+- else
+- *endstr = 0;
+- }
+- }
+- snprintf (nto_procfs_path, PATH_MAX - 1, "%s%s",
+- (nodestr != NULL) ? nodestr : "", "/proc");
+-
+- scoped_fd fd (open (nto_procfs_path, O_RDONLY));
+- if (fd.get () == -1)
+- {
+- gdb_printf ("Error opening %s : %d (%s)\n", nto_procfs_path, errno,
+- safe_strerror (errno));
+- error (_("Invalid procfs arg"));
+- }
+-
+- sysinfo = (void *) buffer;
+- if (devctl (fd.get (), DCMD_PROC_SYSINFO, sysinfo, sizeof buffer, 0) != EOK)
+- {
+- gdb_printf ("Error getting size: %d (%s)\n", errno,
+- safe_strerror (errno));
+- error (_("Devctl failed."));
+- }
+- else
+- {
+- total_size = sysinfo->total_size;
+- sysinfo = alloca (total_size);
+- if (sysinfo == NULL)
+- {
+- gdb_printf ("Memory error: %d (%s)\n", errno,
+- safe_strerror (errno));
+- error (_("alloca failed."));
+- }
+- else
+- {
+- if (devctl (fd.get (), DCMD_PROC_SYSINFO, sysinfo, total_size, 0)
+- != EOK)
+- {
+- gdb_printf ("Error getting sysinfo: %d (%s)\n", errno,
+- safe_strerror (errno));
+- error (_("Devctl failed."));
+- }
+- else
+- {
+- if (sysinfo->type !=
+- nto_map_arch_to_cputype
+- (gdbarch_bfd_arch_info
+- (current_inferior ()->arch ())->arch_name))
+- error (_("Invalid target CPU."));
+- }
+- }
+- }
+-
+- inf_child_target::open (arg, from_tty);
+- gdb_printf ("Debugging using %s\n", nto_procfs_path);
+-}
+-
+-static void
+-procfs_set_thread (ptid_t ptid)
+-{
+- pid_t tid;
+-
+- tid = ptid.tid ();
+- devctl (ctl_fd, DCMD_PROC_CURTHREAD, &tid, sizeof (tid), 0);
+-}
+-
+-/* Return true if the thread TH is still alive. */
+-
+-bool
+-nto_procfs_target::thread_alive (ptid_t ptid)
+-{
+- pid_t tid;
+- pid_t pid;
+- procfs_status status;
+- int err;
+-
+- tid = ptid.tid ();
+- pid = ptid.pid ();
+-
+- if (kill (pid, 0) == -1)
+- return false;
+-
+- status.tid = tid;
+- if ((err = devctl (ctl_fd, DCMD_PROC_TIDSTATUS,
+- &status, sizeof (status), 0)) != EOK)
+- return false;
+-
+- /* Thread is alive or dead but not yet joined,
+- or dead and there is an alive (or dead unjoined) thread with
+- higher tid.
+-
+- If the tid is not the same as requested, requested tid is dead. */
+- return (status.tid == tid) && (status.state != STATE_DEAD);
+-}
+-
+-static void
+-update_thread_private_data_name (struct thread_info *new_thread,
+- const char *newname)
+-{
+- nto_thread_info *pti = get_nto_thread_info (new_thread);
+-
+- gdb_assert (newname != NULL);
+- gdb_assert (new_thread != NULL);
+-
+- if (pti)
+- {
+- pti = new nto_thread_info;
+- new_thread->priv.reset (pti);
+- }
+-
+- pti->name = newname;
+-}
+-
+-static void
+-update_thread_private_data (struct thread_info *new_thread,
+- pthread_t tid, int state, int flags)
+-{
+- procfs_info pidinfo;
+- struct _thread_name *tn;
+- procfs_threadctl tctl;
+-
+-#if _NTO_VERSION > 630
+- gdb_assert (new_thread != NULL);
+-
+- if (devctl (ctl_fd, DCMD_PROC_INFO, &pidinfo,
+- sizeof(pidinfo), 0) != EOK)
+- return;
+-
+- memset (&tctl, 0, sizeof (tctl));
+- tctl.cmd = _NTO_TCTL_NAME;
+- tn = (struct _thread_name *) (&tctl.data);
+-
+- /* Fetch name for the given thread. */
+- tctl.tid = tid;
+- tn->name_buf_len = sizeof (tctl.data) - sizeof (*tn);
+- tn->new_name_len = -1; /* Getting, not setting. */
+- if (devctl (ctl_fd, DCMD_PROC_THREADCTL, &tctl, sizeof (tctl), NULL) != EOK)
+- tn->name_buf[0] = '\0';
+-
+- tn->name_buf[_NTO_THREAD_NAME_MAX] = '\0';
+-
+- update_thread_private_data_name (new_thread, tn->name_buf);
+-
+- nto_thread_info *pti = get_nto_thread_info (new_thread);
+- pti->tid = tid;
+- pti->state = state;
+- pti->flags = flags;
+-#endif /* _NTO_VERSION */
+-}
+-
+-void
+-nto_procfs_target::update_thread_list ()
+-{
+- procfs_status status;
+- pid_t pid;
+- ptid_t ptid;
+- pthread_t tid;
+- struct thread_info *new_thread;
+-
+- if (ctl_fd == -1)
+- return;
+-
+- prune_threads ();
+-
+- pid = current_inferior ()->pid;
+-
+- status.tid = 1;
+-
+- for (tid = 1;; ++tid)
+- {
+- if (status.tid == tid
+- && (devctl (ctl_fd, DCMD_PROC_TIDSTATUS, &status, sizeof (status), 0)
+- != EOK))
+- break;
+- if (status.tid != tid)
+- /* The reason why this would not be equal is that devctl might have
+- returned different tid, meaning the requested tid no longer exists
+- (e.g. thread exited). */
+- continue;
+- ptid = ptid_t (pid, 0, tid);
+- new_thread = this->find_thread (ptid);
+- if (!new_thread)
+- new_thread = add_thread (ptid);
+- update_thread_private_data (new_thread, tid, status.state, 0);
+- status.tid++;
+- }
+- return;
+-}
+-
+-static void
+-procfs_pidlist (const char *args, int from_tty)
+-{
+- struct dirent *dirp = NULL;
+- char buf[PATH_MAX];
+- procfs_info *pidinfo = NULL;
+- procfs_debuginfo *info = NULL;
+- procfs_status *status = NULL;
+- pid_t num_threads = 0;
+- pid_t pid;
+- char name[512];
+- char procfs_dir[PATH_MAX];
+-
+- snprintf (procfs_dir, sizeof (procfs_dir), "%s%s",
+- (nodestr != NULL) ? nodestr : "", "/proc");
+-
+- gdb_dir_up dp (opendir (procfs_dir));
+- if (dp == NULL)
+- {
+- gdb_printf (gdb_stderr, "failed to opendir \"%s\" - %d (%s)",
+- procfs_dir, errno, safe_strerror (errno));
+- return;
+- }
+-
+- /* Start scan at first pid. */
+- rewinddir (dp.get ());
+-
+- do
+- {
+- /* Get the right pid and procfs path for the pid. */
+- do
+- {
+- dirp = readdir (dp.get ());
+- if (dirp == NULL)
+- return;
+- snprintf (buf, sizeof (buf), "%s%s/%s/as",
+- (nodestr != NULL) ? nodestr : "",
+- "/proc", dirp->d_name);
+- pid = atoi (dirp->d_name);
+- }
+- while (pid == 0);
+-
+- /* Open the procfs path. */
+- scoped_fd fd (open (buf, O_RDONLY));
+- if (fd.get () == -1)
+- {
+- gdb_printf (gdb_stderr, "failed to open %s - %d (%s)\n",
+- buf, errno, safe_strerror (errno));
+- continue;
+- }
+-
+- pidinfo = (procfs_info *) buf;
+- if (devctl (fd.get (), DCMD_PROC_INFO, pidinfo, sizeof (buf), 0) != EOK)
+- {
+- gdb_printf (gdb_stderr,
+- "devctl DCMD_PROC_INFO failed - %d (%s)\n",
+- errno, safe_strerror (errno));
+- break;
+- }
+- num_threads = pidinfo->num_threads;
+-
+- info = (procfs_debuginfo *) buf;
+- if (devctl (fd.get (), DCMD_PROC_MAPDEBUG_BASE, info, sizeof (buf), 0)
+- != EOK)
+- strcpy (name, "unavailable");
+- else
+- strcpy (name, info->path);
+-
+- /* Collect state info on all the threads. */
+- status = (procfs_status *) buf;
+- for (status->tid = 1; status->tid <= num_threads; status->tid++)
+- {
+- const int err
+- = devctl (fd.get (), DCMD_PROC_TIDSTATUS, status, sizeof (buf), 0);
+- gdb_printf ("%s - %d", name, pid);
+- if (err == EOK && status->tid != 0)
+- gdb_printf ("/%d\n", status->tid);
+- else
+- {
+- gdb_printf ("\n");
+- break;
+- }
+- }
+- }
+- while (dirp != NULL);
+-}
+-
+-static void
+-procfs_meminfo (const char *args, int from_tty)
+-{
+- procfs_mapinfo *mapinfos = NULL;
+- static int num_mapinfos = 0;
+- procfs_mapinfo *mapinfo_p, *mapinfo_p2;
+- int flags = ~0, err, num, i, j;
+-
+- struct
+- {
+- procfs_debuginfo info;
+- char buff[_POSIX_PATH_MAX];
+- } map;
+-
+- struct info
+- {
+- unsigned addr;
+- unsigned size;
+- unsigned flags;
+- unsigned debug_vaddr;
+- unsigned long long offset;
+- };
+-
+- struct printinfo
+- {
+- unsigned long long ino;
+- unsigned dev;
+- struct info text;
+- struct info data;
+- char name[256];
+- } printme;
+-
+- /* Get the number of map entrys. */
+- err = devctl (ctl_fd, DCMD_PROC_MAPINFO, NULL, 0, &num);
+- if (err != EOK)
+- {
+- printf ("failed devctl num mapinfos - %d (%s)\n", err,
+- safe_strerror (err));
+- return;
+- }
+-
+- mapinfos = XNEWVEC (procfs_mapinfo, num);
+-
+- num_mapinfos = num;
+- mapinfo_p = mapinfos;
+-
+- /* Fill the map entrys. */
+- err = devctl (ctl_fd, DCMD_PROC_MAPINFO, mapinfo_p, num
+- * sizeof (procfs_mapinfo), &num);
+- if (err != EOK)
+- {
+- printf ("failed devctl mapinfos - %d (%s)\n", err, safe_strerror (err));
+- xfree (mapinfos);
+- return;
+- }
+-
+- num = std::min (num, num_mapinfos);
+-
+- /* Run through the list of mapinfos, and store the data and text info
+- so we can print it at the bottom of the loop. */
+- for (mapinfo_p = mapinfos, i = 0; i < num; i++, mapinfo_p++)
+- {
+- if (!(mapinfo_p->flags & flags))
+- mapinfo_p->ino = 0;
+-
+- if (mapinfo_p->ino == 0) /* Already visited. */
+- continue;
+-
+- map.info.vaddr = mapinfo_p->vaddr;
+-
+- err = devctl (ctl_fd, DCMD_PROC_MAPDEBUG, &map, sizeof (map), 0);
+- if (err != EOK)
+- continue;
+-
+- memset (&printme, 0, sizeof printme);
+- printme.dev = mapinfo_p->dev;
+- printme.ino = mapinfo_p->ino;
+- printme.text.addr = mapinfo_p->vaddr;
+- printme.text.size = mapinfo_p->size;
+- printme.text.flags = mapinfo_p->flags;
+- printme.text.offset = mapinfo_p->offset;
+- printme.text.debug_vaddr = map.info.vaddr;
+- strcpy (printme.name, map.info.path);
+-
+- /* Check for matching data. */
+- for (mapinfo_p2 = mapinfos, j = 0; j < num; j++, mapinfo_p2++)
+- {
+- if (mapinfo_p2->vaddr != mapinfo_p->vaddr
+- && mapinfo_p2->ino == mapinfo_p->ino
+- && mapinfo_p2->dev == mapinfo_p->dev)
+- {
+- map.info.vaddr = mapinfo_p2->vaddr;
+- err =
+- devctl (ctl_fd, DCMD_PROC_MAPDEBUG, &map, sizeof (map), 0);
+- if (err != EOK)
+- continue;
+-
+- if (strcmp (map.info.path, printme.name))
+- continue;
+-
+- /* Lower debug_vaddr is always text, if necessary, swap. */
+- if ((int) map.info.vaddr < (int) printme.text.debug_vaddr)
+- {
+- memcpy (&(printme.data), &(printme.text),
+- sizeof (printme.data));
+- printme.text.addr = mapinfo_p2->vaddr;
+- printme.text.size = mapinfo_p2->size;
+- printme.text.flags = mapinfo_p2->flags;
+- printme.text.offset = mapinfo_p2->offset;
+- printme.text.debug_vaddr = map.info.vaddr;
+- }
+- else
+- {
+- printme.data.addr = mapinfo_p2->vaddr;
+- printme.data.size = mapinfo_p2->size;
+- printme.data.flags = mapinfo_p2->flags;
+- printme.data.offset = mapinfo_p2->offset;
+- printme.data.debug_vaddr = map.info.vaddr;
+- }
+- mapinfo_p2->ino = 0;
+- }
+- }
+- mapinfo_p->ino = 0;
+-
+- gdb_printf ("%s\n", printme.name);
+- gdb_printf ("\ttext=%08x bytes @ 0x%08x\n", printme.text.size,
+- printme.text.addr);
+- gdb_printf ("\t\tflags=%08x\n", printme.text.flags);
+- gdb_printf ("\t\tdebug=%08x\n", printme.text.debug_vaddr);
+- gdb_printf ("\t\toffset=%s\n", phex (printme.text.offset, 8));
+- if (printme.data.size)
+- {
+- gdb_printf ("\tdata=%08x bytes @ 0x%08x\n", printme.data.size,
+- printme.data.addr);
+- gdb_printf ("\t\tflags=%08x\n", printme.data.flags);
+- gdb_printf ("\t\tdebug=%08x\n", printme.data.debug_vaddr);
+- gdb_printf ("\t\toffset=%s\n", phex (printme.data.offset, 8));
+- }
+- gdb_printf ("\tdev=0x%x\n", printme.dev);
+- gdb_printf ("\tino=0x%x\n", (unsigned int) printme.ino);
+- }
+- xfree (mapinfos);
+- return;
+-}
+-
+-/* Print status information about what we're accessing. */
+-void
+-nto_procfs_target::files_info ()
+-{
+- struct inferior *inf = current_inferior ();
+-
+- gdb_printf ("\tUsing the running image of %s %s via %s.\n",
+- inf->attach_flag ? "attached" : "child",
+- target_pid_to_str (ptid_t (inf->pid)).c_str (),
+- (nodestr != NULL) ? nodestr : "local node");
+-}
+-
+-/* Target to_pid_to_exec_file implementation. */
+-
+-const char *
+-nto_procfs_target::pid_to_exec_file (const int pid)
+-{
+- int proc_fd;
+- static char proc_path[PATH_MAX];
+- ssize_t rd;
+-
+- /* Read exe file name. */
+- snprintf (proc_path, sizeof (proc_path), "%s/proc/%d/exefile",
+- (nodestr != NULL) ? nodestr : "", pid);
+- proc_fd = open (proc_path, O_RDONLY);
+- if (proc_fd == -1)
+- return NULL;
+-
+- rd = read (proc_fd, proc_path, sizeof (proc_path) - 1);
+- close (proc_fd);
+- if (rd <= 0)
+- {
+- proc_path[0] = '\0';
+- return NULL;
+- }
+- proc_path[rd] = '\0';
+- return proc_path;
+-}
+-
+-/* Attach to process PID, then initialize for debugging it. */
+-void
+-nto_procfs_target::attach (const char *args, int from_tty)
+-{
+- int pid;
+- struct inferior *inf;
+-
+- pid = parse_pid_to_attach (args);
+-
+- if (pid == getpid ())
+- error (_("Attaching GDB to itself is not a good idea..."));
+-
+- target_announce_attach (from_tty, pid);
+-
+- ptid_t ptid = do_attach (ptid_t (pid));
+- inf = current_inferior ();
+- inferior_appeared (inf, pid);
+- inf->attach_flag = true;
+-
+- if (!inf->target_is_pushed (ops))
+- inf->push_target (ops);
+-
+- update_thread_list ();
+-
+- switch_to_thread (this->find_thread (ptid));
+-}
+-
+-void
+-nto_procfs_target::post_attach (pid_t pid)
+-{
+- if (current_program_space->exec_bfd ())
+- solib_create_inferior_hook (0);
+-}
+-
+-static ptid_t
+-do_attach (ptid_t ptid)
+-{
+- procfs_status status;
+- struct sigevent event;
+- char path[PATH_MAX];
+-
+- snprintf (path, PATH_MAX - 1, "%s%s/%d/as",
+- (nodestr != NULL) ? nodestr : "", "/proc", ptid.pid ());
+- ctl_fd = open (path, O_RDWR);
+- if (ctl_fd == -1)
+- error (_("Couldn't open proc file %s, error %d (%s)"), path, errno,
+- safe_strerror (errno));
+- if (devctl (ctl_fd, DCMD_PROC_STOP, &status, sizeof (status), 0) != EOK)
+- error (_("Couldn't stop process"));
+-
+- /* Define a sigevent for process stopped notification. */
+- event.sigev_notify = SIGEV_SIGNAL_THREAD;
+- event.sigev_signo = SIGUSR1;
+- event.sigev_code = 0;
+- event.sigev_value.sival_ptr = NULL;
+- event.sigev_priority = -1;
+- devctl (ctl_fd, DCMD_PROC_EVENT, &event, sizeof (event), 0);
+-
+- if (devctl (ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status), 0) == EOK
+- && status.flags & _DEBUG_FLAG_STOPPED)
+- SignalKill (nto_node (), ptid.pid (), 0, SIGCONT, 0, 0);
+- nto_init_solib_absolute_prefix ();
+- return ptid_t (ptid.pid (), 0, status.tid);
+-}
+-
+-/* Ask the user what to do when an interrupt is received. */
+-static void
+-interrupt_query (void)
+-{
+- if (query (_("Interrupted while waiting for the program.\n\
+-Give up (and stop debugging it)? ")))
+- {
+- target_mourn_inferior (inferior_ptid);
+- quit ();
+- }
+-}
+-
+-/* The user typed ^C twice. */
+-static void
+-nto_handle_sigint_twice (int signo)
+-{
+- signal (signo, ofunc);
+- interrupt_query ();
+- signal (signo, nto_handle_sigint_twice);
+-}
+-
+-static void
+-nto_handle_sigint (int signo)
+-{
+- /* If this doesn't work, try more severe steps. */
+- signal (signo, nto_handle_sigint_twice);
+-
+- target_interrupt ();
+-}
+-
+-sptid_t
+-nto_procfs_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
+- target_wait_flags options)
+-{
+- sigset_t set;
+- siginfo_t info;
+- procfs_status status;
+- static int exit_signo = 0; /* To track signals that cause termination. */
+-
+- ourstatus->set_spurious ();
+-
+- if (inferior_ptid == null_ptid)
+- {
+- ourstatus->set_stopped (GDB_SIGNAL_0);
+- exit_signo = 0;
+- return null_ptid;
+- }
+-
+- sigemptyset (&set);
+- sigaddset (&set, SIGUSR1);
+-
+- devctl (ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status), 0);
+- while (!(status.flags & _DEBUG_FLAG_ISTOP))
+- {
+- ofunc = signal (SIGINT, nto_handle_sigint);
+- sigwaitinfo (&set, &info);
+- signal (SIGINT, ofunc);
+- devctl (ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status), 0);
+- }
+-
+- nto_inferior_data (NULL)->stopped_flags = status.flags;
+- nto_inferior_data (NULL)->stopped_pc = status.ip;
+-
+- if (status.flags & _DEBUG_FLAG_SSTEP)
+- ourstatus->set_stopped (GDB_SIGNAL_TRAP);
+- /* Was it a breakpoint? */
+- else if (status.flags & _DEBUG_FLAG_TRACE)
+- ourstatus->set_stopped (GDB_SIGNAL_TRAP);
+- else if (status.flags & _DEBUG_FLAG_ISTOP)
+- {
+- switch (status.why)
+- {
+- case _DEBUG_WHY_SIGNALLED:
+- ourstatus->set_stopped (gdb_signal_from_host (status.info.si_signo));
+- exit_signo = 0;
+- break;
+- case _DEBUG_WHY_FAULTED:
+- if (status.info.si_signo == SIGTRAP)
+- {
+- ourstatus->set_stopped (0);
+- exit_signo = 0;
+- }
+- else
+- {
+- ourstatus->set_stopped
+- (gdb_signal_from_host (status.info.si_signo));
+- exit_signo = ourstatus->sig ();
+- }
+- break;
+-
+- case _DEBUG_WHY_TERMINATED:
+- {
+- int waitval = 0;
+-
+- waitpid (inferior_ptid.pid (), &waitval, WNOHANG);
+- if (exit_signo)
+- {
+- /* Abnormal death. */
+- ourstatus->set_signalled (exit_signo);
+- }
+- else
+- {
+- /* Normal death. */
+- ourstatus->set_exited (WEXITSTATUS (waitval));
+- }
+- exit_signo = 0;
+- break;
+- }
+-
+- case _DEBUG_WHY_REQUESTED:
+- /* We are assuming a requested stop is due to a SIGINT. */
+- ourstatus->set_stopped (GDB_SIGNAL_INT);
+- exit_signo = 0;
+- break;
+- }
+- }
+-
+- return ptid_t (status.pid, 0, status.tid);
+-}
+-
+-/* Read the current values of the inferior's registers, both the
+- general register set and floating point registers (if supported)
+- and update gdb's idea of their current values. */
+-void
+-nto_procfs_target::fetch_registers (struct regcache *regcache, int regno)
+-{
+- union
+- {
+- procfs_greg greg;
+- procfs_fpreg fpreg;
+- procfs_altreg altreg;
+- }
+- reg;
+- int regsize;
+-
+- procfs_set_thread (regcache->ptid ());
+- if (devctl (ctl_fd, DCMD_PROC_GETGREG, ®, sizeof (reg), ®size) == EOK)
+- nto_supply_gregset (regcache, (char *) ®.greg);
+- if (devctl (ctl_fd, DCMD_PROC_GETFPREG, ®, sizeof (reg), ®size)
+- == EOK)
+- nto_supply_fpregset (regcache, (char *) ®.fpreg);
+- if (devctl (ctl_fd, DCMD_PROC_GETALTREG, ®, sizeof (reg), ®size)
+- == EOK)
+- nto_supply_altregset (regcache, (char *) ®.altreg);
+-}
+-
+-/* Helper for procfs_xfer_partial that handles memory transfers.
+- Arguments are like target_xfer_partial. */
+-
+-static enum target_xfer_status
+-procfs_xfer_memory (gdb_byte *readbuf, const gdb_byte *writebuf,
+- ULONGEST memaddr, ULONGEST len, ULONGEST *xfered_len)
+-{
+- int nbytes;
+-
+- if (lseek (ctl_fd, (off_t) memaddr, SEEK_SET) != (off_t) memaddr)
+- return TARGET_XFER_E_IO;
+-
+- if (writebuf != NULL)
+- nbytes = write (ctl_fd, writebuf, len);
+- else
+- nbytes = read (ctl_fd, readbuf, len);
+- if (nbytes <= 0)
+- return TARGET_XFER_E_IO;
+- *xfered_len = nbytes;
+- return TARGET_XFER_OK;
+-}
+-
+-/* Target to_xfer_partial implementation. */
+-
+-enum target_xfer_status
+-nto_procfs_target::xfer_partial (enum target_object object,
+- const char *annex, gdb_byte *readbuf,
+- const gdb_byte *writebuf, ULONGEST offset,
+- ULONGEST len, ULONGEST *xfered_len)
+-{
+- switch (object)
+- {
+- case TARGET_OBJECT_MEMORY:
+- return procfs_xfer_memory (readbuf, writebuf, offset, len, xfered_len);
+- case TARGET_OBJECT_AUXV:
+- if (readbuf != NULL)
+- {
+- int err;
+- CORE_ADDR initial_stack;
+- debug_process_t procinfo;
+- /* For 32-bit architecture, size of auxv_t is 8 bytes. */
+- const unsigned int sizeof_auxv_t = sizeof (auxv_t);
+- const unsigned int sizeof_tempbuf = 20 * sizeof_auxv_t;
+- int tempread;
+- gdb_byte *const tempbuf = alloca (sizeof_tempbuf);
+-
+- if (tempbuf == NULL)
+- return TARGET_XFER_E_IO;
+-
+- err = devctl (ctl_fd, DCMD_PROC_INFO, &procinfo,
+- sizeof procinfo, 0);
+- if (err != EOK)
+- return TARGET_XFER_E_IO;
+-
+- initial_stack = procinfo.initial_stack;
+-
+- /* procfs is always 'self-hosted', no byte-order manipulation. */
+- tempread = nto_read_auxv_from_initial_stack (initial_stack, tempbuf,
+- sizeof_tempbuf,
+- sizeof (auxv_t));
+- tempread = std::min (tempread, len) - offset;
+- memcpy (readbuf, tempbuf + offset, tempread);
+- *xfered_len = tempread;
+- return tempread ? TARGET_XFER_OK : TARGET_XFER_EOF;
+- }
+- /* Fallthru */
+- default:
+- return this->beneath ()->xfer_partial (object, annex,
+- readbuf, writebuf, offset, len,
+- xfered_len);
+- }
+-}
+-
+-/* Take a program previously attached to and detaches it.
+- The program resumes execution and will no longer stop
+- on signals, etc. We'd better not have left any breakpoints
+- in the program or it'll die when it hits one. */
+-void
+-nto_procfs_target::detach (inferior *inf, int from_tty)
+-{
+- target_announce_detach ();
+-
+- if (siggnal)
+- SignalKill (nto_node (), inf->pid, 0, 0, 0, 0);
+-
+- close (ctl_fd);
+- ctl_fd = -1;
+-
+- switch_to_no_thread ();
+- detach_inferior (inf->pid);
+- init_thread_list ();
+- inf_child_maybe_unpush_target (ops);
+-}
+-
+-static int
+-procfs_breakpoint (CORE_ADDR addr, int type, int size)
+-{
+- procfs_break brk;
+-
+- brk.type = type;
+- brk.addr = addr;
+- brk.size = size;
+- errno = devctl (ctl_fd, DCMD_PROC_BREAK, &brk, sizeof (brk), 0);
+- if (errno != EOK)
+- return 1;
+- return 0;
+-}
+-
+-int
+-nto_procfs_target::insert_breakpoint (struct gdbarch *gdbarch,
+- struct bp_target_info *bp_tgt)
+-{
+- bp_tgt->placed_address = bp_tgt->reqstd_address;
+- return procfs_breakpoint (bp_tgt->placed_address, _DEBUG_BREAK_EXEC, 0);
+-}
+-
+-int
+-nto_procfs_target::remove_breakpoint (struct gdbarch *gdbarch,
+- struct bp_target_info *bp_tgt,
+- enum remove_bp_reason reason)
+-{
+- return procfs_breakpoint (bp_tgt->placed_address, _DEBUG_BREAK_EXEC, -1);
+-}
+-
+-int
+-nto_procfs_target::insert_hw_breakpoint (struct gdbarch *gdbarch,
+- struct bp_target_info *bp_tgt)
+-{
+- bp_tgt->placed_address = bp_tgt->reqstd_address;
+- return procfs_breakpoint (bp_tgt->placed_address,
+- _DEBUG_BREAK_EXEC | _DEBUG_BREAK_HW, 0);
+-}
+-
+-int
+-nto_procfs_target::remove_hw_breakpoint (struct gdbarch *gdbarch,
+- struct bp_target_info *bp_tgt)
+-{
+- return procfs_breakpoint (bp_tgt->placed_address,
+- _DEBUG_BREAK_EXEC | _DEBUG_BREAK_HW, -1);
+-}
+-
+-void
+-nto_procfs_target::resume (ptid_t ptid, int step, enum gdb_signal signo)
+-{
+- int signal_to_pass;
+- procfs_status status;
+- sigset_t *run_fault = (sigset_t *) (void *) &run.fault;
+-
+- if (inferior_ptid == null_ptid)
+- return;
+-
+- procfs_set_thread (ptid == minus_one_ptid ? inferior_ptid :
+- ptid);
+-
+- run.flags = _DEBUG_RUN_FAULT | _DEBUG_RUN_TRACE;
+- if (step)
+- run.flags |= _DEBUG_RUN_STEP;
+-
+- sigemptyset (run_fault);
+- sigaddset (run_fault, FLTBPT);
+- sigaddset (run_fault, FLTTRACE);
+- sigaddset (run_fault, FLTILL);
+- sigaddset (run_fault, FLTPRIV);
+- sigaddset (run_fault, FLTBOUNDS);
+- sigaddset (run_fault, FLTIOVF);
+- sigaddset (run_fault, FLTIZDIV);
+- sigaddset (run_fault, FLTFPE);
+- /* Peter V will be changing this at some point. */
+- sigaddset (run_fault, FLTPAGE);
+-
+- run.flags |= _DEBUG_RUN_ARM;
+-
+- signal_to_pass = gdb_signal_to_host (signo);
+-
+- if (signal_to_pass)
+- {
+- devctl (ctl_fd, DCMD_PROC_STATUS, &status, sizeof (status), 0);
+- signal_to_pass = gdb_signal_to_host (signo);
+- if (status.why & (_DEBUG_WHY_SIGNALLED | _DEBUG_WHY_FAULTED))
+- {
+- if (signal_to_pass != status.info.si_signo)
+- {
+- SignalKill (nto_node (), inferior_ptid.pid (), 0,
+- signal_to_pass, 0, 0);
+- run.flags |= _DEBUG_RUN_CLRFLT | _DEBUG_RUN_CLRSIG;
+- }
+- else /* Let it kill the program without telling us. */
+- sigdelset (&run.trace, signal_to_pass);
+- }
+- }
+- else
+- run.flags |= _DEBUG_RUN_CLRSIG | _DEBUG_RUN_CLRFLT;
+-
+- errno = devctl (ctl_fd, DCMD_PROC_RUN, &run, sizeof (run), 0);
+- if (errno != EOK)
+- {
+- perror (_("run error!\n"));
+- return;
+- }
+-}
+-
+-void
+-nto_procfs_target::mourn_inferior ()
+-{
+- if (inferior_ptid != null_ptid)
+- {
+- SignalKill (nto_node (), inferior_ptid.pid (), 0, SIGKILL, 0, 0);
+- close (ctl_fd);
+- }
+- switch_to_no_thread ();
+- init_thread_list ();
+- inf_child_mourn_inferior (ops);
+-}
+-
+-/* This function breaks up an argument string into an argument
+- vector suitable for passing to execvp().
+- E.g., on "run a b c d" this routine would get as input
+- the string "a b c d", and as output it would fill in argv with
+- the four arguments "a", "b", "c", "d". The only additional
+- functionality is simple quoting. The gdb command:
+- run a "b c d" f
+- will fill in argv with the three args "a", "b c d", "e". */
+-static void
+-breakup_args (char *scratch, char **argv)
+-{
+- char *pp, *cp = scratch;
+- char quoting = 0;
+-
+- for (;;)
+- {
+- /* Scan past leading separators. */
+- quoting = 0;
+- while (*cp == ' ' || *cp == '\t' || *cp == '\n')
+- cp++;
+-
+- /* Break if at end of string. */
+- if (*cp == '\0')
+- break;
+-
+- /* Take an arg. */
+- if (*cp == '"')
+- {
+- cp++;
+- quoting = strchr (cp, '"') ? 1 : 0;
+- }
+-
+- *argv++ = cp;
+-
+- /* Scan for next arg separator. */
+- pp = cp;
+- if (quoting)
+- cp = strchr (pp, '"');
+- if ((cp == NULL) || (!quoting))
+- cp = strchr (pp, ' ');
+- if (cp == NULL)
+- cp = strchr (pp, '\t');
+- if (cp == NULL)
+- cp = strchr (pp, '\n');
+-
+- /* No separators => end of string => break. */
+- if (cp == NULL)
+- {
+- pp = cp;
+- break;
+- }
+-
+- /* Replace the separator with a terminator. */
+- *cp++ = '\0';
+- }
+-
+- /* Execv requires a null-terminated arg vector. */
+- *argv = NULL;
+-}
+-
+-void
+-nto_procfs_target::create_inferior (const char *exec_file,
+- const std::string &allargs,
+- char **env, int from_tty)
+-{
+- struct inheritance inherit;
+- pid_t pid;
+- int flags, errn;
+- char **argv, *args;
+- const char *in = "", *out = "", *err = "";
+- int fd, fds[3];
+- sigset_t set;
+- struct inferior *inf;
+-
+- argv = xmalloc ((allargs.size () / (unsigned) 2 + 2) *
+- sizeof (*argv));
+- argv[0] = const_cast<char *> (get_exec_file (1));
+- if (!argv[0])
+- {
+- if (exec_file)
+- argv[0] = exec_file;
+- else
+- return;
+- }
+-
+- args = xstrdup (allargs.c_str ());
+- breakup_args (args, (exec_file != NULL) ? &argv[1] : &argv[0]);
+-
+- argv = nto_parse_redirection (argv, &in, &out, &err);
+-
+- fds[0] = STDIN_FILENO;
+- fds[1] = STDOUT_FILENO;
+- fds[2] = STDERR_FILENO;
+-
+- /* If the user specified I/O via gdb's --tty= arg, use it, but only
+- if the i/o is not also being specified via redirection. */
+- const char *inferior_tty = current_inferior ()->tty ();
+- if (inferior_tty != nullptr)
+- {
+- if (!in[0])
+- in = inferior_tty;
+- if (!out[0])
+- out = inferior_tty;
+- if (!err[0])
+- err = inferior_tty;
+- }
+-
+- if (in[0])
+- {
+- fd = open (in, O_RDONLY);
+- if (fd == -1)
+- perror (in);
+- else
+- fds[0] = fd;
+- }
+- if (out[0])
+- {
+- fd = open (out, O_WRONLY);
+- if (fd == -1)
+- perror (out);
+- else
+- fds[1] = fd;
+- }
+- if (err[0])
+- {
+- fd = open (err, O_WRONLY);
+- if (fd == -1)
+- perror (err);
+- else
+- fds[2] = fd;
+- }
+-
+- /* Clear any pending SIGUSR1's but keep the behavior the same. */
+- signal (SIGUSR1, signal (SIGUSR1, SIG_IGN));
+-
+- sigemptyset (&set);
+- sigaddset (&set, SIGUSR1);
+- sigprocmask (SIG_UNBLOCK, &set, NULL);
+-
+- memset (&inherit, 0, sizeof (inherit));
+-
+- if (ND_NODE_CMP (nto_procfs_node, ND_LOCAL_NODE) != 0)
+- {
+- inherit.nd = nto_node ();
+- inherit.flags |= SPAWN_SETND;
+- inherit.flags &= ~SPAWN_EXEC;
+- }
+- inherit.flags |= SPAWN_SETGROUP | SPAWN_HOLD;
+- inherit.pgroup = SPAWN_NEWPGROUP;
+- pid = spawnp (argv[0], 3, fds, &inherit, argv,
+- ND_NODE_CMP (nto_procfs_node, ND_LOCAL_NODE) == 0 ? env : 0);
+- xfree (args);
+-
+- sigprocmask (SIG_BLOCK, &set, NULL);
+-
+- if (pid == -1)
+- error (_("Error spawning %s: %d (%s)"), argv[0], errno,
+- safe_strerror (errno));
+-
+- if (fds[0] != STDIN_FILENO)
+- close (fds[0]);
+- if (fds[1] != STDOUT_FILENO)
+- close (fds[1]);
+- if (fds[2] != STDERR_FILENO)
+- close (fds[2]);
+-
+- ptid_t ptid = do_attach (ptid_t (pid));
+- update_thread_list ();
+- switch_to_thread (this->find_thread (ptid));
+-
+- inf = current_inferior ();
+- inferior_appeared (inf, pid);
+- inf->attach_flag = false;
+-
+- flags = _DEBUG_FLAG_KLC; /* Kill-on-Last-Close flag. */
+- errn = devctl (ctl_fd, DCMD_PROC_SET_FLAG, &flags, sizeof (flags), 0);
+- if (errn != EOK)
+- {
+- /* FIXME: expected warning? */
+- /* warning( "Failed to set Kill-on-Last-Close flag: errno = %d(%s)\n",
+- errn, safe_strerror(errn) ); */
+- }
+- if (!inf->target_is_pushed (ops))
+- inf->push_target (ops);
+- target_terminal::init ();
+-
+- if (current_program_space->exec_bfd () != NULL
+- || (current_program_space->symfile_object_file != NULL
+- && current_program_space->symfile_object_file->obfd != NULL))
+- solib_create_inferior_hook (0);
+-}
+-
+-void
+-nto_procfs_target::interrupt ()
+-{
+- devctl (ctl_fd, DCMD_PROC_STOP, NULL, 0, 0);
+-}
+-
+-void
+-nto_procfs_target::kill ()
+-{
+- target_mourn_inferior (inferior_ptid);
+-}
+-
+-/* Fill buf with regset and return devctl cmd to do the setting. Return
+- -1 if we fail to get the regset. Store size of regset in regsize. */
+-static int
+-get_regset (int regset, char *buf, int bufsize, int *regsize)
+-{
+- int dev_get, dev_set;
+- switch (regset)
+- {
+- case NTO_REG_GENERAL:
+- dev_get = DCMD_PROC_GETGREG;
+- dev_set = DCMD_PROC_SETGREG;
+- break;
+-
+- case NTO_REG_FLOAT:
+- dev_get = DCMD_PROC_GETFPREG;
+- dev_set = DCMD_PROC_SETFPREG;
+- break;
+-
+- case NTO_REG_ALT:
+- dev_get = DCMD_PROC_GETALTREG;
+- dev_set = DCMD_PROC_SETALTREG;
+- break;
+-
+- case NTO_REG_SYSTEM:
+- default:
+- return -1;
+- }
+- if (devctl (ctl_fd, dev_get, buf, bufsize, regsize) != EOK)
+- return -1;
+-
+- return dev_set;
+-}
+-
+-void
+-nto_procfs_target::store_registers (struct regcache *regcache, int regno)
+-{
+- union
+- {
+- procfs_greg greg;
+- procfs_fpreg fpreg;
+- procfs_altreg altreg;
+- }
+- reg;
+- unsigned off;
+- int len, regset, regsize, dev_set, err;
+- char *data;
+- ptid_t ptid = regcache->ptid ();
+-
+- if (ptid == null_ptid)
+- return;
+- procfs_set_thread (ptid);
+-
+- if (regno == -1)
+- {
+- for (regset = NTO_REG_GENERAL; regset < NTO_REG_END; regset++)
+- {
+- dev_set = get_regset (regset, (char *) ®,
+- sizeof (reg), ®size);
+- if (dev_set == -1)
+- continue;
+-
+- if (nto_regset_fill (regcache, regset, (char *) ®) == -1)
+- continue;
+-
+- err = devctl (ctl_fd, dev_set, ®, regsize, 0);
+- if (err != EOK)
+- gdb_printf (gdb_stderr,
+- "Warning unable to write regset %d: %s\n",
+- regno, safe_strerror (err));
+- }
+- }
+- else
+- {
+- regset = nto_regset_id (regno);
+- if (regset == -1)
+- return;
+-
+- dev_set = get_regset (regset, (char *) ®, sizeof (reg), ®size);
+- if (dev_set == -1)
+- return;
+-
+- len = nto_register_area (regcache->arch (),
+- regno, regset, &off);
+-
+- if (len < 1)
+- return;
+-
+- regcache->raw_collect (regno, (char *) ® + off);
+-
+- err = devctl (ctl_fd, dev_set, ®, regsize, 0);
+- if (err != EOK)
+- gdb_printf (gdb_stderr,
+- "Warning unable to write regset %d: %s\n", regno,
+- safe_strerror (err));
+- }
+-}
+-
+-/* Set list of signals to be handled in the target. */
+-
+-void
+-nto_procfs_target::pass_signals
+- (gdb::array_view<const unsigned char> pass_signals)
+-{
+- int signo;
+-
+- sigfillset (&run.trace);
+-
+- for (signo = 1; signo < NSIG; signo++)
+- {
+- int target_signo = gdb_signal_from_host (signo);
+- if (target_signo < pass_signals.size () && pass_signals[target_signo])
+- sigdelset (&run.trace, signo);
+- }
+-}
+-
+-std::string
+-nto_procfs_target::pid_to_str (ptid_t ptid)
+-{
+- int pid, tid;
+- struct tidinfo *tip;
+-
+- pid = ptid.pid ();
+- tid = ptid.tid ();
+-
+-#if 0 /* NYI */
+- tip = procfs_thread_info (pid, tid);
+- if (tip != NULL)
+- snprintf (&buf[n], 1023, " (state = 0x%02x)", tip->state);
+-#endif
+-
+- return string_printf ("process %d", pid);
+-}
+-
+-/* to_can_run implementation for "target procfs". Note this really
+- means "can this target be the default run target", which there can
+- be only one, and we make it be "target native" like other ports.
+- "target procfs <node>" wouldn't make sense as default run target, as
+- it needs <node>. */
+-
+-int
+-nto_procfs_target::can_run ()
+-{
+- return 0;
+-}
+-
+-/* "target procfs". */
+-static nto_procfs_target_procfs nto_procfs_ops;
+-
+-/* "target native". */
+-static nto_procfs_target_native nto_native_ops;
+-
+-/* Create the "native" and "procfs" targets. */
+-
+-static void
+-init_procfs_targets (void)
+-{
+- /* Register "target native". This is the default run target. */
+- add_target (nto_native_target_info, inf_child_open_target);
+- set_native_target (&nto_native_ops);
+-
+- /* Register "target procfs <node>". */
+- add_target (nto_procfs_target_info, inf_child_open_target);
+-}
+-
+-#define OSTYPE_NTO 1
+-
+-void _initialize_procfs ();
+-void
+-_initialize_procfs ()
+-{
+- sigset_t set;
+-
+- init_procfs_targets ();
+-
+- /* We use SIGUSR1 to gain control after we block waiting for a process.
+- We use sigwaitevent to wait. */
+- sigemptyset (&set);
+- sigaddset (&set, SIGUSR1);
+- sigprocmask (SIG_BLOCK, &set, NULL);
+-
+- /* Initially, make sure all signals are reported. */
+- sigfillset (&run.trace);
+-
+- /* Stuff some information. */
+- nto_cpuinfo_flags = SYSPAGE_ENTRY (cpuinfo)->flags;
+- nto_cpuinfo_valid = 1;
+-
+- add_info ("pidlist", procfs_pidlist, _("pidlist"));
+- add_info ("meminfo", procfs_meminfo, _("memory information"));
+-
+- nto_is_nto_target = procfs_is_nto_target;
+-}
+-
+-
+-static int
+-procfs_hw_watchpoint (int addr, int len, enum target_hw_bp_type type)
+-{
+- procfs_break brk;
+-
+- switch (type)
+- {
+- case hw_read:
+- brk.type = _DEBUG_BREAK_RD;
+- break;
+- case hw_access:
+- brk.type = _DEBUG_BREAK_RW;
+- break;
+- default: /* Modify. */
+-/* FIXME: brk.type = _DEBUG_BREAK_RWM gives EINVAL for some reason. */
+- brk.type = _DEBUG_BREAK_RW;
+- }
+- brk.type |= _DEBUG_BREAK_HW; /* Always ask for HW. */
+- brk.addr = addr;
+- brk.size = len;
+-
+- errno = devctl (ctl_fd, DCMD_PROC_BREAK, &brk, sizeof (brk), 0);
+- if (errno != EOK)
+- {
+- perror (_("Failed to set hardware watchpoint"));
+- return -1;
+- }
+- return 0;
+-}
+-
+-bool
+-nto_procfs_target::can_use_hw_breakpoint (enum bptype type,
+- int cnt, int othertype)
+-{
+- return 1;
+-}
+-
+-int
+-nto_procfs_target::remove_hw_watchpoint (CORE_ADDR addr, int len,
+- enum target_hw_bp_type type,
+- struct expression *cond)
+-{
+- return procfs_hw_watchpoint (addr, -1, type);
+-}
+-
+-int
+-nto_procfs_target::insert_hw_watchpoint (CORE_ADDR addr, int len,
+- enum target_hw_bp_type type,
+- struct expression *cond)
+-{
+- return procfs_hw_watchpoint (addr, len, type);
+-}
+-
+-bool
+-nto_procfs_target::stopped_by_watchpoint ()
+-{
+- /* NOTE: nto_stopped_by_watchpoint will be called ONLY while we are
+- stopped due to a SIGTRAP. This assumes gdb works in 'all-stop' mode;
+- future gdb versions will likely run in 'non-stop' mode in which case
+- we will have to store/examine statuses per thread in question.
+- Until then, this will work fine. */
+-
+- struct inferior *inf = current_inferior ();
+- struct nto_inferior_data *inf_data;
+-
+- gdb_assert (inf != NULL);
+-
+- inf_data = nto_inferior_data (inf);
+-
+- return inf_data->stopped_flags
+- & (_DEBUG_FLAG_TRACE_RD
+- | _DEBUG_FLAG_TRACE_WR
+- | _DEBUG_FLAG_TRACE_MODIFY);
+-}
+diff --git a/gdb/nto-tdep.c b/gdb/nto-tdep.c
+deleted file mode 100644
+--- a/gdb/nto-tdep.c
++++ /dev/null
+@@ -1,521 +0,0 @@
+-/* nto-tdep.c - general QNX Neutrino target functionality.
+-
+- Copyright (C) 2003-2024 Free Software Foundation, Inc.
+-
+- Contributed by QNX Software Systems Ltd.
+-
+- This file is part of GDB.
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License as published by
+- the Free Software Foundation; either version 3 of the License, or
+- (at your option) any later version.
+-
+- This program is distributed in the hope that it will be useful,
+- but WITHOUT ANY WARRANTY; without even the implied warranty of
+- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- GNU General Public License for more details.
+-
+- You should have received a copy of the GNU General Public License
+- along with this program. If not, see <http://www.gnu.org/licenses/>. */
+-
+-#include <sys/stat.h>
+-#include "nto-tdep.h"
+-#include "extract-store-integer.h"
+-#include "top.h"
+-#include "inferior.h"
+-#include "infrun.h"
+-#include "gdbarch.h"
+-#include "bfd.h"
+-#include "elf-bfd.h"
+-#include "solib-svr4.h"
+-#include "gdbcore.h"
+-#include "objfiles.h"
+-#include "source.h"
+-#include "gdbsupport/pathstuff.h"
+-
+-#define QNX_NOTE_NAME "QNX"
+-#define QNX_INFO_SECT_NAME "QNX_info"
+-
+-#ifdef __CYGWIN__
+-#include <sys/cygwin.h>
+-#endif
+-
+-#ifdef __CYGWIN__
+-static char default_nto_target[] = "C:\\QNXsdk\\target\\qnx6";
+-#elif defined(__sun__) || defined(linux)
+-static char default_nto_target[] = "/opt/QNXsdk/target/qnx6";
+-#else
+-static char default_nto_target[] = "";
+-#endif
+-
+-struct nto_target_ops current_nto_target;
+-
+-static const registry<inferior>::key<struct nto_inferior_data>
+- nto_inferior_data_reg;
+-
+-static char *
+-nto_target (void)
+-{
+- char *p = getenv ("QNX_TARGET");
+-
+-#ifdef __CYGWIN__
+- static char buf[PATH_MAX];
+- if (p)
+- cygwin_conv_path (CCP_WIN_A_TO_POSIX, p, buf, PATH_MAX);
+- else
+- cygwin_conv_path (CCP_WIN_A_TO_POSIX, default_nto_target, buf, PATH_MAX);
+- return buf;
+-#else
+- return p ? p : default_nto_target;
+-#endif
+-}
+-
+-/* Take a string such as i386, rs6000, etc. and map it onto CPUTYPE_X86,
+- CPUTYPE_PPC, etc. as defined in nto-share/dsmsgs.h. */
+-int
+-nto_map_arch_to_cputype (const char *arch)
+-{
+- if (!strcmp (arch, "i386") || !strcmp (arch, "x86"))
+- return CPUTYPE_X86;
+- if (!strcmp (arch, "rs6000") || !strcmp (arch, "powerpc"))
+- return CPUTYPE_PPC;
+- if (!strcmp (arch, "mips"))
+- return CPUTYPE_MIPS;
+- if (!strcmp (arch, "arm"))
+- return CPUTYPE_ARM;
+- if (!strcmp (arch, "sh"))
+- return CPUTYPE_SH;
+- return CPUTYPE_UNKNOWN;
+-}
+-
+-int
+-nto_find_and_open_solib (const char *solib, unsigned o_flags,
+- gdb::unique_xmalloc_ptr<char> *temp_pathname)
+-{
+- char *buf, *arch_path, *nto_root;
+- const char *endian;
+- const char *base;
+- const char *arch;
+- int arch_len, len, ret;
+-#define PATH_FMT \
+- "%s/lib:%s/usr/lib:%s/usr/photon/lib:%s/usr/photon/dll:%s/lib/dll"
+-
+- nto_root = nto_target ();
+- gdbarch *gdbarch = current_inferior ()->arch ();
+- if (strcmp (gdbarch_bfd_arch_info (gdbarch)->arch_name, "i386") == 0)
+- {
+- arch = "x86";
+- endian = "";
+- }
+- else if (strcmp (gdbarch_bfd_arch_info (gdbarch)->arch_name,
+- "rs6000") == 0
+- || strcmp (gdbarch_bfd_arch_info (gdbarch)->arch_name,
+- "powerpc") == 0)
+- {
+- arch = "ppc";
+- endian = "be";
+- }
+- else
+- {
+- arch = gdbarch_bfd_arch_info (gdbarch)->arch_name;
+- endian = gdbarch_byte_order (gdbarch)
+- == BFD_ENDIAN_BIG ? "be" : "le";
+- }
+-
+- /* In case nto_root is short, add strlen(solib)
+- so we can reuse arch_path below. */
+-
+- arch_len = (strlen (nto_root) + strlen (arch) + strlen (endian) + 2
+- + strlen (solib));
+- arch_path = (char *) alloca (arch_len);
+- xsnprintf (arch_path, arch_len, "%s/%s%s", nto_root, arch, endian);
+-
+- len = strlen (PATH_FMT) + strlen (arch_path) * 5 + 1;
+- buf = (char *) alloca (len);
+- xsnprintf (buf, len, PATH_FMT, arch_path, arch_path, arch_path, arch_path,
+- arch_path);
+-
+- base = lbasename (solib);
+- ret = openp (buf, OPF_TRY_CWD_FIRST | OPF_RETURN_REALPATH, base, o_flags,
+- temp_pathname);
+- if (ret < 0 && base != solib)
+- {
+- xsnprintf (arch_path, arch_len, "/%s", solib);
+- ret = open (arch_path, o_flags, 0);
+- if (temp_pathname)
+- {
+- if (ret >= 0)
+- *temp_pathname = gdb_realpath (arch_path);
+- else
+- temp_pathname->reset (NULL);
+- }
+- }
+- return ret;
+-}
+-
+-void
+-nto_init_solib_absolute_prefix (void)
+-{
+- char buf[PATH_MAX * 2], arch_path[PATH_MAX];
+- char *nto_root;
+- const char *endian;
+- const char *arch;
+-
+- nto_root = nto_target ();
+- gdbarch *gdbarch = current_inferior ()->arch ();
+- if (strcmp (gdbarch_bfd_arch_info (gdbarch)->arch_name, "i386") == 0)
+- {
+- arch = "x86";
+- endian = "";
+- }
+- else if (strcmp (gdbarch_bfd_arch_info (gdbarch)->arch_name,
+- "rs6000") == 0
+- || strcmp (gdbarch_bfd_arch_info (gdbarch)->arch_name,
+- "powerpc") == 0)
+- {
+- arch = "ppc";
+- endian = "be";
+- }
+- else
+- {
+- arch = gdbarch_bfd_arch_info (gdbarch)->arch_name;
+- endian = gdbarch_byte_order (gdbarch)
+- == BFD_ENDIAN_BIG ? "be" : "le";
+- }
+-
+- xsnprintf (arch_path, sizeof (arch_path), "%s/%s%s", nto_root, arch, endian);
+-
+- xsnprintf (buf, sizeof (buf), "set solib-absolute-prefix %s", arch_path);
+- execute_command (buf, 0);
+-}
+-
+-char **
+-nto_parse_redirection (char *pargv[], const char **pin, const char **pout,
+- const char **perr)
+-{
+- char **argv;
+- const char *in, *out, *err, *p;
+- int argc, i, n;
+-
+- for (n = 0; pargv[n]; n++);
+- if (n == 0)
+- return NULL;
+- in = "";
+- out = "";
+- err = "";
+-
+- argv = XCNEWVEC (char *, n + 1);
+- argc = n;
+- for (i = 0, n = 0; n < argc; n++)
+- {
+- p = pargv[n];
+- if (*p == '>')
+- {
+- p++;
+- if (*p)
+- out = p;
+- else
+- out = pargv[++n];
+- }
+- else if (*p == '<')
+- {
+- p++;
+- if (*p)
+- in = p;
+- else
+- in = pargv[++n];
+- }
+- else if (*p++ == '2' && *p++ == '>')
+- {
+- if (*p == '&' && *(p + 1) == '1')
+- err = out;
+- else if (*p)
+- err = p;
+- else
+- err = pargv[++n];
+- }
+- else
+- argv[i++] = pargv[n];
+- }
+- *pin = in;
+- *pout = out;
+- *perr = err;
+- return argv;
+-}
+-
+-static CORE_ADDR
+-lm_addr (const solib &so)
+-{
+- auto *li = gdb::checked_static_cast<const lm_info_svr4 *> (so.lm_info.get ());
+-
+- return li->l_addr;
+-}
+-
+-static CORE_ADDR
+-nto_truncate_ptr (CORE_ADDR addr)
+-{
+- gdbarch *gdbarch = current_inferior ()->arch ();
+- if (gdbarch_ptr_bit (gdbarch) == sizeof (CORE_ADDR) * 8)
+- /* We don't need to truncate anything, and the bit twiddling below
+- will fail due to overflow problems. */
+- return addr;
+- else
+- return addr & (((CORE_ADDR) 1 << gdbarch_ptr_bit (gdbarch)) - 1);
+-}
+-
+-static Elf_Internal_Phdr *
+-find_load_phdr (bfd *abfd)
+-{
+- Elf_Internal_Phdr *phdr;
+- unsigned int i;
+-
+- if (!elf_tdata (abfd))
+- return NULL;
+-
+- phdr = elf_tdata (abfd)->phdr;
+- for (i = 0; i < elf_elfheader (abfd)->e_phnum; i++, phdr++)
+- {
+- if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_X))
+- return phdr;
+- }
+- return NULL;
+-}
+-
+-void
+-nto_relocate_section_addresses (solib &so, target_section *sec)
+-{
+- /* Neutrino treats the l_addr base address field in link.h as different than
+- the base address in the System V ABI and so the offset needs to be
+- calculated and applied to relocations. */
+- Elf_Internal_Phdr *phdr = find_load_phdr (sec->the_bfd_section->owner);
+- unsigned vaddr = phdr ? phdr->p_vaddr : 0;
+-
+- sec->addr = nto_truncate_ptr (sec->addr + lm_addr (so) - vaddr);
+- sec->endaddr = nto_truncate_ptr (sec->endaddr + lm_addr (so) - vaddr);
+-}
+-
+-/* This is cheating a bit because our linker code is in libc.so. If we
+- ever implement lazy linking, this may need to be re-examined. */
+-int
+-nto_in_dynsym_resolve_code (CORE_ADDR pc)
+-{
+- if (in_plt_section (pc))
+- return 1;
+- return 0;
+-}
+-
+-void
+-nto_dummy_supply_regset (struct regcache *regcache, char *regs)
+-{
+- /* Do nothing. */
+-}
+-
+-static void
+-nto_sniff_abi_note_section (bfd *abfd, asection *sect, void *obj)
+-{
+- const char *sectname;
+- unsigned int sectsize;
+- /* Buffer holding the section contents. */
+- char *note;
+- unsigned int namelen;
+- const char *name;
+- const unsigned sizeof_Elf_Nhdr = 12;
+-
+- sectname = bfd_section_name (sect);
+- sectsize = bfd_section_size (sect);
+-
+- if (sectsize > 128)
+- sectsize = 128;
+-
+- if (sectname != NULL && strstr (sectname, QNX_INFO_SECT_NAME) != NULL)
+- *(enum gdb_osabi *) obj = GDB_OSABI_QNXNTO;
+- else if (sectname != NULL && strstr (sectname, "note") != NULL
+- && sectsize > sizeof_Elf_Nhdr)
+- {
+- note = XNEWVEC (char, sectsize);
+- bfd_get_section_contents (abfd, sect, note, 0, sectsize);
+- namelen = (unsigned int) bfd_h_get_32 (abfd, note);
+- name = note + sizeof_Elf_Nhdr;
+- if (sectsize >= namelen + sizeof_Elf_Nhdr
+- && namelen == sizeof (QNX_NOTE_NAME)
+- && 0 == strcmp (name, QNX_NOTE_NAME))
+- *(enum gdb_osabi *) obj = GDB_OSABI_QNXNTO;
+-
+- XDELETEVEC (note);
+- }
+-}
+-
+-enum gdb_osabi
+-nto_elf_osabi_sniffer (bfd *abfd)
+-{
+- enum gdb_osabi osabi = GDB_OSABI_UNKNOWN;
+-
+- bfd_map_over_sections (abfd,
+- nto_sniff_abi_note_section,
+- &osabi);
+-
+- return osabi;
+-}
+-
+-static const char * const nto_thread_state_str[] =
+-{
+- "DEAD", /* 0 0x00 */
+- "RUNNING", /* 1 0x01 */
+- "READY", /* 2 0x02 */
+- "STOPPED", /* 3 0x03 */
+- "SEND", /* 4 0x04 */
+- "RECEIVE", /* 5 0x05 */
+- "REPLY", /* 6 0x06 */
+- "STACK", /* 7 0x07 */
+- "WAITTHREAD", /* 8 0x08 */
+- "WAITPAGE", /* 9 0x09 */
+- "SIGSUSPEND", /* 10 0x0a */
+- "SIGWAITINFO", /* 11 0x0b */
+- "NANOSLEEP", /* 12 0x0c */
+- "MUTEX", /* 13 0x0d */
+- "CONDVAR", /* 14 0x0e */
+- "JOIN", /* 15 0x0f */
+- "INTR", /* 16 0x10 */
+- "SEM", /* 17 0x11 */
+- "WAITCTX", /* 18 0x12 */
+- "NET_SEND", /* 19 0x13 */
+- "NET_REPLY" /* 20 0x14 */
+-};
+-
+-const char *
+-nto_extra_thread_info (struct target_ops *self, struct thread_info *ti)
+-{
+- if (ti != NULL && ti->priv != NULL)
+- {
+- nto_thread_info *priv = get_nto_thread_info (ti);
+-
+- if (priv->state < ARRAY_SIZE (nto_thread_state_str))
+- return nto_thread_state_str [priv->state];
+- }
+- return "";
+-}
+-
+-void
+-nto_initialize_signals (void)
+-{
+- /* We use SIG45 for pulses, or something, so nostop, noprint
+- and pass them. */
+- signal_stop_update (gdb_signal_from_name ("SIG45"), 0);
+- signal_print_update (gdb_signal_from_name ("SIG45"), 0);
+- signal_pass_update (gdb_signal_from_name ("SIG45"), 1);
+-
+- /* By default we don't want to stop on these two, but we do want to pass. */
+-#if defined(SIGSELECT)
+- signal_stop_update (SIGSELECT, 0);
+- signal_print_update (SIGSELECT, 0);
+- signal_pass_update (SIGSELECT, 1);
+-#endif
+-
+-#if defined(SIGPHOTON)
+- signal_stop_update (SIGPHOTON, 0);
+- signal_print_update (SIGPHOTON, 0);
+- signal_pass_update (SIGPHOTON, 1);
+-#endif
+-}
+-
+-/* Read AUXV from initial_stack. */
+-LONGEST
+-nto_read_auxv_from_initial_stack (CORE_ADDR initial_stack, gdb_byte *readbuf,
+- LONGEST len, size_t sizeof_auxv_t)
+-{
+- gdb_byte targ32[4]; /* For 32 bit target values. */
+- gdb_byte targ64[8]; /* For 64 bit target values. */
+- CORE_ADDR data_ofs = 0;
+- ULONGEST anint;
+- LONGEST len_read = 0;
+- gdb_byte *buff;
+- enum bfd_endian byte_order;
+- int ptr_size;
+-
+- if (sizeof_auxv_t == 16)
+- ptr_size = 8;
+- else
+- ptr_size = 4;
+-
+- /* Skip over argc, argv and envp... Comment from ldd.c:
+-
+- The startup frame is set-up so that we have:
+- auxv
+- NULL
+- ...
+- envp2
+- envp1 <----- void *frame + (argc + 2) * sizeof(char *)
+- NULL
+- ...
+- argv2
+- argv1
+- argc <------ void * frame
+-
+- On entry to ldd, frame gives the address of argc on the stack. */
+- /* Read argc. 4 bytes on both 64 and 32 bit arches and luckily little
+- * endian. So we just read first 4 bytes. */
+- if (target_read_memory (initial_stack + data_ofs, targ32, 4) != 0)
+- return 0;
+-
+- byte_order = gdbarch_byte_order (current_inferior ()->arch ());
+-
+- anint = extract_unsigned_integer (targ32, sizeof (targ32), byte_order);
+-
+- /* Size of pointer is assumed to be 4 bytes (32 bit arch.) */
+- data_ofs += (anint + 2) * ptr_size; /* + 2 comes from argc itself and
+- NULL terminating pointer in
+- argv. */
+-
+- /* Now loop over env table: */
+- anint = 0;
+- while (target_read_memory (initial_stack + data_ofs, targ64, ptr_size)
+- == 0)
+- {
+- if (extract_unsigned_integer (targ64, ptr_size, byte_order) == 0)
+- anint = 1; /* Keep looping until non-null entry is found. */
+- else if (anint)
+- break;
+- data_ofs += ptr_size;
+- }
+- initial_stack += data_ofs;
+-
+- memset (readbuf, 0, len);
+- buff = readbuf;
+- while (len_read <= len-sizeof_auxv_t)
+- {
+- if (target_read_memory (initial_stack + len_read, buff, sizeof_auxv_t)
+- == 0)
+- {
+- /* Both 32 and 64 bit structures have int as the first field. */
+- const ULONGEST a_type
+- = extract_unsigned_integer (buff, sizeof (targ32), byte_order);
+-
+- if (a_type == AT_NULL)
+- break;
+- buff += sizeof_auxv_t;
+- len_read += sizeof_auxv_t;
+- }
+- else
+- break;
+- }
+- return len_read;
+-}
+-
+-/* Return nto_inferior_data for the given INFERIOR. If not yet created,
+- construct it. */
+-
+-struct nto_inferior_data *
+-nto_inferior_data (struct inferior *const inferior)
+-{
+- struct inferior *const inf = inferior ? inferior : current_inferior ();
+- struct nto_inferior_data *inf_data;
+-
+- gdb_assert (inf != NULL);
+-
+- inf_data = nto_inferior_data_reg.get (inf);
+- if (inf_data == NULL)
+- inf_data = nto_inferior_data_reg.emplace (inf);
+-
+- return inf_data;
+-}
+diff --git a/gdb/nto-tdep.h b/gdb/nto-tdep.h
+deleted file mode 100644
+--- a/gdb/nto-tdep.h
++++ /dev/null
+@@ -1,194 +0,0 @@
+-/* nto-tdep.h - QNX Neutrino target header.
+-
+- Copyright (C) 2003-2024 Free Software Foundation, Inc.
+-
+- Contributed by QNX Software Systems Ltd.
+-
+- This file is part of GDB.
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License as published by
+- the Free Software Foundation; either version 3 of the License, or
+- (at your option) any later version.
+-
+- This program is distributed in the hope that it will be useful,
+- but WITHOUT ANY WARRANTY; without even the implied warranty of
+- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- GNU General Public License for more details.
+-
+- You should have received a copy of the GNU General Public License
+- along with this program. If not, see <http://www.gnu.org/licenses/>. */
+-
+-#ifndef NTO_TDEP_H
+-#define NTO_TDEP_H
+-
+-#include "solist.h"
+-#include "osabi.h"
+-#include "regset.h"
+-#include "gdbthread.h"
+-#include "gdbsupport/gdb-checked-static-cast.h"
+-
+-/* Target operations defined for Neutrino targets (<target>-nto-tdep.c). */
+-
+-struct nto_target_ops
+-{
+-/* The CPUINFO flags from the remote. Currently used by
+- i386 for fxsave but future proofing other hosts.
+- This is initialized in procfs_attach or nto_start_remote
+- depending on our host/target. It would only be invalid
+- if we were talking to an older pdebug which didn't support
+- the cpuinfo message. */
+- unsigned cpuinfo_flags;
+-
+-/* True if successfully retrieved cpuinfo from remote. */
+- int cpuinfo_valid;
+-
+-/* Given a register, return an id that represents the Neutrino
+- regset it came from. If reg == -1 update all regsets. */
+- int (*regset_id) (int);
+-
+- void (*supply_gregset) (struct regcache *, char *);
+-
+- void (*supply_fpregset) (struct regcache *, char *);
+-
+- void (*supply_altregset) (struct regcache *, char *);
+-
+-/* Given a regset, tell gdb about registers stored in data. */
+- void (*supply_regset) (struct regcache *, int, char *);
+-
+-/* Given a register and regset, calculate the offset into the regset
+- and stuff it into the last argument. If regno is -1, calculate the
+- size of the entire regset. Returns length of data, -1 if unknown
+- regset, 0 if unknown register. */
+- int (*register_area) (struct gdbarch *, int, int, unsigned *);
+-
+-/* Build the Neutrino register set info into the data buffer.
+- Return -1 if unknown regset, 0 otherwise. */
+- int (*regset_fill) (const struct regcache *, int, char *);
+-
+-/* Gives the fetch_link_map_offsets function exposure outside of
+- solib-svr4.c so that we can override relocate_section_addresses(). */
+- struct link_map_offsets *(*fetch_link_map_offsets) (void);
+-
+-/* Used by nto_elf_osabi_sniffer to determine if we're connected to an
+- Neutrino target. */
+- enum gdb_osabi (*is_nto_target) (bfd *abfd);
+-};
+-
+-extern struct nto_target_ops current_nto_target;
+-
+-#define nto_cpuinfo_flags (current_nto_target.cpuinfo_flags)
+-
+-#define nto_cpuinfo_valid (current_nto_target.cpuinfo_valid)
+-
+-#define nto_regset_id (current_nto_target.regset_id)
+-
+-#define nto_supply_gregset (current_nto_target.supply_gregset)
+-
+-#define nto_supply_fpregset (current_nto_target.supply_fpregset)
+-
+-#define nto_supply_altregset (current_nto_target.supply_altregset)
+-
+-#define nto_supply_regset (current_nto_target.supply_regset)
+-
+-#define nto_register_area (current_nto_target.register_area)
+-
+-#define nto_regset_fill (current_nto_target.regset_fill)
+-
+-#define nto_fetch_link_map_offsets \
+-(current_nto_target.fetch_link_map_offsets)
+-
+-#define nto_is_nto_target (current_nto_target.is_nto_target)
+-
+-/* Keep this consistant with neutrino syspage.h. */
+-enum
+-{
+- CPUTYPE_X86,
+- CPUTYPE_PPC,
+- CPUTYPE_MIPS,
+- CPUTYPE_SPARE,
+- CPUTYPE_ARM,
+- CPUTYPE_SH,
+- CPUTYPE_UNKNOWN
+-};
+-
+-enum
+-{
+- OSTYPE_QNX4,
+- OSTYPE_NTO
+-};
+-
+-/* These correspond to the DSMSG_* versions in dsmsgs.h. */
+-enum
+-{
+- NTO_REG_GENERAL,
+- NTO_REG_FLOAT,
+- NTO_REG_SYSTEM,
+- NTO_REG_ALT,
+- NTO_REG_END
+-};
+-
+-typedef char qnx_reg64[8];
+-
+-typedef struct _debug_regs
+-{
+- qnx_reg64 padding[1024];
+-} nto_regset_t;
+-
+-struct nto_thread_info : public private_thread_info
+-{
+- short tid = 0;
+- unsigned char state = 0;
+- unsigned char flags = 0;
+- std::string name;
+-};
+-
+-static inline nto_thread_info *
+-get_nto_thread_info (thread_info *thread)
+-{
+- return gdb::checked_static_cast<nto_thread_info *> (thread->priv.get ());
+-}
+-
+-/* Per-inferior data, common for both procfs and remote. */
+-struct nto_inferior_data
+-{
+- /* Last stopped flags result from wait function */
+- unsigned int stopped_flags = 0;
+-
+- /* Last known stopped PC */
+- CORE_ADDR stopped_pc = 0;
+-};
+-
+-/* Generic functions in nto-tdep.c. */
+-
+-void nto_init_solib_absolute_prefix (void);
+-
+-char **nto_parse_redirection (char *start_argv[], const char **in,
+- const char **out, const char **err);
+-
+-void nto_relocate_section_addresses (solib &, target_section *);
+-
+-int nto_map_arch_to_cputype (const char *);
+-
+-int nto_find_and_open_solib (const char *, unsigned,
+- gdb::unique_xmalloc_ptr<char> *);
+-
+-enum gdb_osabi nto_elf_osabi_sniffer (bfd *abfd);
+-
+-void nto_initialize_signals (void);
+-
+-/* Dummy function for initializing nto_target_ops on targets which do
+- not define a particular regset. */
+-void nto_dummy_supply_regset (struct regcache *regcache, char *regs);
+-
+-int nto_in_dynsym_resolve_code (CORE_ADDR pc);
+-
+-const char *nto_extra_thread_info (struct target_ops *self, struct thread_info *);
+-
+-LONGEST nto_read_auxv_from_initial_stack (CORE_ADDR initial_stack,
+- gdb_byte *readbuf,
+- LONGEST len, size_t sizeof_auxv_t);
+-
+-struct nto_inferior_data *nto_inferior_data (struct inferior *inf);
+-
+-#endif /* NTO_TDEP_H */
+diff --git a/gdb/osabi.c b/gdb/osabi.c
+--- a/gdb/osabi.c
++++ b/gdb/osabi.c
+@@ -70,7 +70,6 @@ static const struct osabi_names gdb_osabi_names[] =
+ { "OpenBSD", NULL },
+ { "WindowsCE", NULL },
+ { "DJGPP", NULL },
+- { "QNX-Neutrino", NULL },
+ { "Cygwin", NULL },
+ { "Windows", NULL },
+ { "AIX", NULL },
+diff --git a/gdb/osabi.h b/gdb/osabi.h
+--- a/gdb/osabi.h
++++ b/gdb/osabi.h
+@@ -35,7 +35,6 @@ enum gdb_osabi
+ GDB_OSABI_OPENBSD,
+ GDB_OSABI_WINCE,
+ GDB_OSABI_GO32,
+- GDB_OSABI_QNXNTO,
+ GDB_OSABI_CYGWIN,
+ GDB_OSABI_WINDOWS,
+ GDB_OSABI_AIX,
+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
+@@ -8079,7 +8079,6 @@ gdb_caching_proc gdb_has_argv0 {} {
+ || [istarget *-wince-pe] || [istarget *-*-mingw32ce*]
+ || [istarget *-*-osf*]
+ || [istarget *-*-dicos*]
+- || [istarget *-*-nto*]
+ || [istarget *-*-*vms*]
+ || [istarget *-*-lynx*178]) } {
+ fail "argv\[0\] should be available on this target"
diff --git a/gdb-remove-use-of-py-isascii b/gdb-remove-use-of-py-isascii
deleted file mode 100644
index 367c5b3..0000000
--- a/gdb-remove-use-of-py-isascii
+++ /dev/null
@@ -1,37 +0,0 @@
-From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
-From: Andrew Burgess <aburgess@redhat.com>
-Date: Thu, 16 Nov 2023 10:53:34 +0000
-Subject: gdb-remove-use-of-py-isascii
-
-;; Backport upstream commit 7db795bc67a.
-
-gdb/python: remove use of str.isascii()
-
-This commit:
-
- commit 8f6c452b5a4e50fbb55ff1d13328b392ad1fd416
- Date: Sun Oct 15 22:48:42 2023 +0100
-
- gdb: implement missing debug handler hook for Python
-
-introduced a use of str.isascii(), which was only added in Python 3.7.
-
-This commit switches to use curses.ascii.isascii(), as this was
-available in 3.6.
-
-The same is true for str.isalnum(), which is replaced with
-curses.ascii.isalnum().
-
-There should be no user visible changes after this commit.
-
-diff --git a/gdb/python/lib/gdb/missing_debug.py b/gdb/python/lib/gdb/missing_debug.py
---- a/gdb/python/lib/gdb/missing_debug.py
-+++ b/gdb/python/lib/gdb/missing_debug.py
-@@ -20,6 +20,7 @@ MissingDebugHandler base class, and register_handler function.
- import sys
-
- import gdb
-+from curses.ascii import isascii, isalnum
-
- if sys.version_info >= (3, 7):
- # Functions str.isascii() and str.isalnum are available starting Python
diff --git a/gdb-rhbz1156192-recursive-dlopen-test.patch b/gdb-rhbz1156192-recursive-dlopen-test.patch
index e0a2270..2ff58ed 100644
--- a/gdb-rhbz1156192-recursive-dlopen-test.patch
+++ b/gdb-rhbz1156192-recursive-dlopen-test.patch
@@ -211,7 +211,7 @@ diff --git a/gdb/testsuite/gdb.base/gdb-rhbz1156192-recursive-dlopen.exp b/gdb/t
new file mode 100644
--- /dev/null
+++ b/gdb/testsuite/gdb.base/gdb-rhbz1156192-recursive-dlopen.exp
-@@ -0,0 +1,152 @@
+@@ -0,0 +1,151 @@
+# Copyright 2014 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
@@ -264,6 +264,9 @@ new file mode 100644
+
+set supported 0
+gdb_test_multiple "run" "initial trial run" {
++ -re "warning: Corrupted shared library list: \[^\r\n\]+\r\n" {
++ exp_continue
++ }
+ -re -wrap "exited normally.*" {
+ set supported 1
+ pass $gdb_test_name
@@ -333,28 +336,24 @@ new file mode 100644
+ foreach l $solib_event_order {
+ incr pass
+ with_test_prefix "pass #$pass" {
-+ set should_be_corrupted [expr 0+0[lindex $l 2]]
-+ do_test [lindex $l 0] [lindex $l 1]
-+ set test "continue"
-+ global gdb_prompt
-+ gdb_test_multiple $test $test {
-+ -re "\r\nwarning: Corrupted shared library list:.*\r\nStopped due to shared library event.*\r\n$gdb_prompt $" {
-+ set corrupted 1
-+ pass $test
-+ }
-+ -re "\r\nStopped due to shared library event.*\r\n$gdb_prompt $" {
-+ set corrupted 0
-+ pass $test
++ set should_be_corrupted [expr 0+0[lindex $l 2]]
++ do_test [lindex $l 0] [lindex $l 1]
++
++ set corrupted 0
++ gdb_test_multiple "continue" "" {
++ -re "\r\nwarning: Corrupted shared library list:\[^\r\n\]+(?=\r\n)" {
++ set corrupted 1
++ }
++ -re -wrap "\r\nStopped due to shared library event.*" {
++ pass $gdb_test_name
++ }
+ }
-+ }
-+ set test "corrupted=$corrupted but should_be_corrupted=$should_be_corrupted"
-+ if {$corrupted == $should_be_corrupted} {
-+ pass $test
-+ } else {
-+ fail $test
-+ }
++
++ gdb_assert {$corrupted == $should_be_corrupted} \
++ "corrupted=$corrupted but should_be_corrupted=$should_be_corrupted"
+ }
+ }
++
+ # In the last pass we do not expect to see libfoo or libbar.
+ incr pass
+ with_test_prefix "pass #$pass" {
diff --git a/gdb.spec b/gdb.spec
index 3bbc87f..65f950e 100644
--- a/gdb.spec
+++ b/gdb.spec
@@ -45,7 +45,7 @@ Version: 15.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: 4%{?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
# Do not provide URL for snapshots as the file lasts there only for 2 days.
@@ -928,6 +928,26 @@ fi
# endif scl
%changelog
+* Tue Dec 10 2024 Andrew Burgess <aburgess@redhat.com>
+- Remove
+ gdb-6.6-buildid-locate-misleading-warning-missing-debuginfo-rhbz981154.patch,
+ gdb-6.6-buildid-locate.patch,
+ gdb-6.6-buildid-locate-solib-missing-ids.patch, and
+ gdb-remove-use-of-py-isascii. Add new patches
+ gdb-add-deprecated-settings-py-script.patch,
+ gdb-backport-buildid-related-changes.patch,
+ gdb-remove-qnx-neutrino-support.patch, and
+ gdb-6.6-buildid-locate-tests.patch. Update
+ gdb-add-rpm-suggestion-script.patch and
+ gdb-rhbz1156192-recursive-dlopen-test.patch. All of the RPM
+ suggestion feature is now provided via a Python extension. I
+ believe that the existing functionality should be covered by the new
+ implementation, but with no real tests for the existing code, we
+ cannot be certain. There are new GDB commands added as part of this
+ change related to controlling the RPM suggestion feature. These are
+ documented within the gdb-add-rpm-suggestion-script.patch patch by
+ changes to the GDB manual.
+
* Thu Nov 14 2024 Kevin Buettner <kevinb@redhat.com>
- Remove gdb-6.5-bz243845-stale-testing-zombie-test.patch. This was a
test for a fix to a function that was named linux_test_for_tracefork,
reply other threads:[~2026-06-28 0:01 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=178260491640.1.18352866082687228845.rpms-gdb-8d5adc72d172@fedoraproject.org \
--to=aburgess@redhat.com \
--cc=git-commits@fedoraproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox