315 lines
12 KiB
Diff
315 lines
12 KiB
Diff
|
From FEDORA_PATCHES Mon Sep 17 00:00:00 2001
|
|||
|
From: Andrew Burgess <aburgess@redhat.com>
|
|||
|
Date: Fri, 13 Oct 2023 16:48:36 +0100
|
|||
|
Subject: gdb-add-missing-debug-ext-lang-hook.patch
|
|||
|
|
|||
|
;; Backport upstream commit 661d98a3331.
|
|||
|
|
|||
|
gdb: add an extension language hook for missing debug info
|
|||
|
|
|||
|
This commit adds a new extension_language_ops hook which allows an
|
|||
|
extension to handle the case where GDB can't find a separate debug
|
|||
|
information file for a particular objfile.
|
|||
|
|
|||
|
This commit doesn't actually implement the hook for any of GDB's
|
|||
|
extension languages, the next commit will do that. This commit just
|
|||
|
adds support for the hook to extension-priv.h and extension.[ch], and
|
|||
|
then reworks symfile-debug.c to call the hook.
|
|||
|
|
|||
|
Right now the hook will always return its default value, which means
|
|||
|
GDB should do nothing different. As such, there should be no user
|
|||
|
visible changes after this commit.
|
|||
|
|
|||
|
I'll give a brief description of what the hook does here so that we
|
|||
|
can understand the changes in symfile-debug.c. The next commit adds a
|
|||
|
Python implementation for this new hook, and gives a fuller
|
|||
|
description of the new functionality.
|
|||
|
|
|||
|
Currently, when looking for separate debug information GDB tries three
|
|||
|
things, in this order:
|
|||
|
|
|||
|
1. Use the build-id to find the required debug information,
|
|||
|
|
|||
|
2. Check for .gnu_debuglink section and use that to look up the
|
|||
|
required debug information,
|
|||
|
|
|||
|
3. Check with debuginfod to see if it can supply the required
|
|||
|
information.
|
|||
|
|
|||
|
The new extension_language_ops::handle_missing_debuginfo hook is
|
|||
|
called if all three steps fail to find any debug information. The
|
|||
|
hook has three possible return values:
|
|||
|
|
|||
|
a. Nothing, no debug information is found, GDB continues without the
|
|||
|
debug information for this objfile. This matches the current
|
|||
|
behaviour of GDB, and is the default if nothing is implementing this
|
|||
|
new hook,
|
|||
|
|
|||
|
b. Install debug information into a location that step #1 or #2
|
|||
|
above would normally check, and then request that GDB repeats steps
|
|||
|
#1 and #2 in the hope that GDB will now find the debug information.
|
|||
|
If the debug information is still not found then GDB carries on
|
|||
|
without the debug information. If the debug information is found
|
|||
|
the GDB loads it and carries on,
|
|||
|
|
|||
|
c. Return a filename for a file containing the required debug
|
|||
|
information. GDB loads the contents of this file and carries on.
|
|||
|
|
|||
|
The changes in this commit mostly involve placing the core of
|
|||
|
objfile::find_and_add_separate_symbol_file into a loop which allows
|
|||
|
for steps #1 and #2 to be repeated.
|
|||
|
|
|||
|
We take care to ensure that debuginfod is only queried once, the first
|
|||
|
time through. The assumption is that no extension is going to be able
|
|||
|
to control the replies from debuginfod, so there's no point making a
|
|||
|
second request -- and as these requests go over the network, they
|
|||
|
could potentially be slow.
|
|||
|
|
|||
|
The warnings that find_and_add_separate_symbol_file collects are
|
|||
|
displayed only once assuming that no debug information is found. If
|
|||
|
debug information is found, even after the extension has operated,
|
|||
|
then the warnings are not shown; remember, these are warnings from GDB
|
|||
|
about failure to find any suitable debug information, so it makes
|
|||
|
sense to hide these if debug information is found.
|
|||
|
|
|||
|
Approved-By: Tom Tromey <tom@tromey.com>
|
|||
|
|
|||
|
diff --git a/gdb/extension-priv.h b/gdb/extension-priv.h
|
|||
|
--- a/gdb/extension-priv.h
|
|||
|
+++ b/gdb/extension-priv.h
|
|||
|
@@ -279,6 +279,13 @@ struct extension_language_ops
|
|||
|
gdb::optional<int> (*print_insn) (struct gdbarch *gdbarch,
|
|||
|
CORE_ADDR address,
|
|||
|
struct disassemble_info *info);
|
|||
|
+
|
|||
|
+ /* 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
|
|||
|
+ (*handle_missing_debuginfo) (const struct extension_language_defn *,
|
|||
|
+ struct objfile *objfile);
|
|||
|
};
|
|||
|
|
|||
|
/* 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
|
|||
|
@@ -997,6 +997,25 @@ ext_lang_print_insn (struct gdbarch *gdbarch, CORE_ADDR address,
|
|||
|
return {};
|
|||
|
}
|
|||
|
|
|||
|
+/* See extension.h. */
|
|||
|
+
|
|||
|
+ext_lang_missing_debuginfo_result
|
|||
|
+ext_lang_handle_missing_debuginfo (struct objfile *objfile)
|
|||
|
+{
|
|||
|
+ for (const struct extension_language_defn *extlang : extension_languages)
|
|||
|
+ {
|
|||
|
+ if (extlang->ops == nullptr
|
|||
|
+ || extlang->ops->handle_missing_debuginfo == nullptr)
|
|||
|
+ continue;
|
|||
|
+ ext_lang_missing_debuginfo_result result
|
|||
|
+ = extlang->ops->handle_missing_debuginfo (extlang, objfile);
|
|||
|
+ 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
|
|||
|
@@ -337,6 +337,68 @@ extern gdb::optional<std::string> ext_lang_colorize_disasm
|
|||
|
extern gdb::optional<int> ext_lang_print_insn
|
|||
|
(struct gdbarch *gdbarch, CORE_ADDR address, struct disassemble_info *info);
|
|||
|
|
|||
|
+/* When GDB calls into an extension language because an objfile was
|
|||
|
+ discovered for which GDB couldn't find any debug information, this
|
|||
|
+ structure holds the result that the extension language returns.
|
|||
|
+
|
|||
|
+ There are three possible actions that might be returned by an extension;
|
|||
|
+ first an extension can return a filename, this is the path to the file
|
|||
|
+ containing the required debug information. The second possibility is
|
|||
|
+ to return a flag indicating that GDB should check again for the missing
|
|||
|
+ debug information, this would imply that the extension has installed
|
|||
|
+ the debug information into a location where GDB can be expected to find
|
|||
|
+ 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
|
|||
|
+{
|
|||
|
+ /* Default result. The extension was unable to provide the missing debug
|
|||
|
+ info. */
|
|||
|
+ ext_lang_missing_debuginfo_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)
|
|||
|
+ : m_try_again (try_again)
|
|||
|
+ { /* Nothing. */ }
|
|||
|
+
|
|||
|
+ /* Look in FILENAME for the missing debug info. */
|
|||
|
+ ext_lang_missing_debuginfo_result (std::string &&filename)
|
|||
|
+ : m_filename (std::move (filename))
|
|||
|
+ { /* Nothing. */ }
|
|||
|
+
|
|||
|
+ /* The filename where GDB can find the missing debuginfo. This is empty
|
|||
|
+ if the extension didn't suggest a file that can be used. */
|
|||
|
+ const std::string &
|
|||
|
+ filename () const
|
|||
|
+ {
|
|||
|
+ return m_filename;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+ /* Returns true if GDB should look again for the debug information. */
|
|||
|
+ const bool
|
|||
|
+ try_again () const
|
|||
|
+ {
|
|||
|
+ return m_try_again;
|
|||
|
+ }
|
|||
|
+
|
|||
|
+private:
|
|||
|
+ /* The filename where the missing debuginfo can now be found. */
|
|||
|
+ std::string m_filename;
|
|||
|
+
|
|||
|
+ /* When true GDB will search again for the debuginfo using its standard
|
|||
|
+ techniques. When false GDB will not search again. */
|
|||
|
+ bool m_try_again = false;
|
|||
|
+};
|
|||
|
+
|
|||
|
+/* Called when GDB failed to find any debug information for OBJFILE. */
|
|||
|
+
|
|||
|
+extern ext_lang_missing_debuginfo_result ext_lang_handle_missing_debuginfo
|
|||
|
+ (struct objfile *objfile);
|
|||
|
+
|
|||
|
#if GDB_SELF_TEST
|
|||
|
namespace selftests {
|
|||
|
extern void (*hook_set_active_ext_lang) ();
|
|||
|
diff --git a/gdb/symfile-debug.c b/gdb/symfile-debug.c
|
|||
|
--- a/gdb/symfile-debug.c
|
|||
|
+++ b/gdb/symfile-debug.c
|
|||
|
@@ -631,38 +631,88 @@ debuginfod_find_and_open_separate_symbol_file (struct objfile * objfile)
|
|||
|
bool
|
|||
|
objfile::find_and_add_separate_symbol_file (symfile_add_flags symfile_flags)
|
|||
|
{
|
|||
|
- bool has_dwarf = false;
|
|||
|
-
|
|||
|
- deferred_warnings warnings;
|
|||
|
-
|
|||
|
- gdb_bfd_ref_ptr debug_bfd;
|
|||
|
- std::string filename;
|
|||
|
-
|
|||
|
- std::tie (debug_bfd, filename) = simple_find_and_open_separate_symbol_file
|
|||
|
- (this, find_separate_debug_file_by_buildid, &warnings);
|
|||
|
-
|
|||
|
- if (debug_bfd == nullptr)
|
|||
|
- std::tie (debug_bfd, filename)
|
|||
|
- = simple_find_and_open_separate_symbol_file
|
|||
|
- (this, find_separate_debug_file_by_debuglink, &warnings);
|
|||
|
+ bool has_dwarf2 = false;
|
|||
|
+
|
|||
|
+ /* Usually we only make a single pass when looking for separate debug
|
|||
|
+ information. However, it is possible for an extension language hook
|
|||
|
+ to request that GDB make a second pass, in which case max_attempts
|
|||
|
+ will be updated, and the loop restarted. */
|
|||
|
+ for (unsigned attempt = 0, max_attempts = 1;
|
|||
|
+ attempt < max_attempts && !has_dwarf2;
|
|||
|
+ ++attempt)
|
|||
|
+ {
|
|||
|
+ gdb_assert (max_attempts <= 2);
|
|||
|
+
|
|||
|
+ deferred_warnings warnings;
|
|||
|
+ gdb_bfd_ref_ptr debug_bfd;
|
|||
|
+ std::string filename;
|
|||
|
+
|
|||
|
+ std::tie (debug_bfd, filename)
|
|||
|
+ = simple_find_and_open_separate_symbol_file
|
|||
|
+ (this, find_separate_debug_file_by_buildid, &warnings);
|
|||
|
+
|
|||
|
+ if (debug_bfd == nullptr)
|
|||
|
+ std::tie (debug_bfd, filename)
|
|||
|
+ = simple_find_and_open_separate_symbol_file
|
|||
|
+ (this, find_separate_debug_file_by_debuglink, &warnings);
|
|||
|
+
|
|||
|
+ /* Only try debuginfod on the first attempt. Sure, we could imagine
|
|||
|
+ an extension that somehow adds the required debug info to the
|
|||
|
+ debuginfod server but, at least for now, we don't support this
|
|||
|
+ scenario. Better for the extension to return new debug info
|
|||
|
+ directly to GDB. Plus, going to the debuginfod server might be
|
|||
|
+ slow, so that's a good argument for only doing this once. */
|
|||
|
+ if (debug_bfd == nullptr && attempt == 0)
|
|||
|
+ std::tie (debug_bfd, filename)
|
|||
|
+ = debuginfod_find_and_open_separate_symbol_file (this);
|
|||
|
+
|
|||
|
+ if (debug_bfd != nullptr)
|
|||
|
+ {
|
|||
|
+ /* We found a separate debug info symbol file. If this is our
|
|||
|
+ first attempt then setting HAS_DWARF2 will cause us to break
|
|||
|
+ from the attempt loop. */
|
|||
|
+ symbol_file_add_separate (debug_bfd, filename.c_str (),
|
|||
|
+ symfile_flags, this);
|
|||
|
+ has_dwarf2 = true;
|
|||
|
+ }
|
|||
|
+ else if (attempt == 0)
|
|||
|
+ {
|
|||
|
+ /* Failed to find a separate debug info symbol file. Call out to
|
|||
|
+ the extension languages. The user might have registered an
|
|||
|
+ extension that can find the debug info for us, or maybe give
|
|||
|
+ the user a system specific message that guides them to finding
|
|||
|
+ the missing debug info. */
|
|||
|
+
|
|||
|
+ ext_lang_missing_debuginfo_result ext_result
|
|||
|
+ = ext_lang_handle_missing_debuginfo (this);
|
|||
|
+ if (!ext_result.filename ().empty ())
|
|||
|
+ {
|
|||
|
+ /* Extension found a suitable debug file for us. */
|
|||
|
+ debug_bfd
|
|||
|
+ = symfile_bfd_open_no_error (ext_result.filename ().c_str ());
|
|||
|
|
|||
|
- if (debug_bfd == nullptr)
|
|||
|
- std::tie (debug_bfd, filename)
|
|||
|
- = debuginfod_find_and_open_separate_symbol_file (this);
|
|||
|
+ if (debug_bfd != nullptr)
|
|||
|
+ {
|
|||
|
+ symbol_file_add_separate (debug_bfd,
|
|||
|
+ ext_result.filename ().c_str (),
|
|||
|
+ symfile_flags, this);
|
|||
|
+ has_dwarf2 = true;
|
|||
|
+ }
|
|||
|
+ }
|
|||
|
+ else if (ext_result.try_again ())
|
|||
|
+ {
|
|||
|
+ max_attempts = 2;
|
|||
|
+ continue;
|
|||
|
+ }
|
|||
|
+ }
|
|||
|
|
|||
|
- if (debug_bfd != nullptr)
|
|||
|
- {
|
|||
|
- symbol_file_add_separate (debug_bfd, filename.c_str (), symfile_flags,
|
|||
|
- this);
|
|||
|
- has_dwarf = true;
|
|||
|
+ /* If we still have not got a separate debug symbol file, then
|
|||
|
+ emit any warnings we've collected so far. */
|
|||
|
+ if (!has_dwarf2)
|
|||
|
+ warnings.emit ();
|
|||
|
}
|
|||
|
|
|||
|
- /* If we still have not got a separate debug symbol file, then
|
|||
|
- emit any warnings we've collected so far. */
|
|||
|
- if (!has_dwarf)
|
|||
|
- warnings.emit ();
|
|||
|
-
|
|||
|
- return has_dwarf;
|
|||
|
+ return has_dwarf2;
|
|||
|
}
|
|||
|
|
|||
|
|