diff --git a/gdb/extension-priv.h b/gdb/extension-priv.h index 3442302a0be..e71eac20d4e 100644 --- a/gdb/extension-priv.h +++ b/gdb/extension-priv.h @@ -279,6 +279,13 @@ struct extension_language_ops gdb::optional (*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 index 65f3bab32a7..9cb393e1d50 100644 --- 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 index 28f9e3bc028..282d591be43 100644 --- a/gdb/extension.h +++ b/gdb/extension.h @@ -337,6 +337,68 @@ extern gdb::optional ext_lang_colorize_disasm extern gdb::optional 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 index 0b6dc4752df..85c43719dee 100644 --- 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; }