From patchwork Thu Oct 6 02:24:24 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aaron Merey X-Patchwork-Id: 58425 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 615C0384D156 for ; Thu, 6 Oct 2022 02:25:05 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 615C0384D156 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1665023105; bh=Jz7CRFnYV9bIyWJwim3/5JwUaC2epYyhFQlEtloje8w=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=dpJrXrBcVW3XtdUz5rbARTaDUuwDDXzqlsp2g3UogJM/eDJMoIjqy4c/hPS978A4b /Y9fH3M4ZZllImQxvA/L9dP8HBDJ1PC0sBQQ2qqDrwMA2JmRufZ0yEelVeD8QmECoy lZgN/eM5zd7oDvViDMeLWAdljHuOVxBOpj0Td+OI= X-Original-To: gdb-patches@sourceware.org Delivered-To: gdb-patches@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 5CEA63853835 for ; Thu, 6 Oct 2022 02:24:38 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 5CEA63853835 Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-110-8JJ83YYRP_mGJql8fqBGAQ-1; Wed, 05 Oct 2022 22:24:36 -0400 X-MC-Unique: 8JJ83YYRP_mGJql8fqBGAQ-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 7F0DC3806649 for ; Thu, 6 Oct 2022 02:24:36 +0000 (UTC) Received: from localhost.localdomain (unknown [10.22.10.119]) by smtp.corp.redhat.com (Postfix) with ESMTP id 0DAB71121314; Thu, 6 Oct 2022 02:24:36 +0000 (UTC) To: gdb-patches@sourceware.org Subject: [RFC][PATCH] gdb/debuginfod: Support on-demand downloading of debuginfo Date: Wed, 5 Oct 2022 22:24:24 -0400 Message-Id: <20221006022424.399932-1-amerey@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.3 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-17.7 required=5.0 tests=BAYES_00, DKIM_INVALID, DKIM_SIGNED, GIT_PATCH_0, KAM_DMARC_NONE, KAM_DMARC_STATUS, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Aaron Merey via Gdb-patches From: Aaron Merey Reply-To: Aaron Merey Errors-To: gdb-patches-bounces+patchwork=sourceware.org@sourceware.org Sender: "Gdb-patches" At the beginning of a session, GDB attempts to download debuginfo for every shared library used by the process being debugged. One disadvantage of this is that time may be spent downloading debuginfo that ultimately isn't needed during the current session. This patch helps address the issue by adding support for on-demand downloading and reading of debuginfo. The basic approach it takes is to use debuginfod to download just the .gdb_index of a debuginfo file as soon as the corresponding library is linked. GDB then relies on the information in the index for as long as possible. When the index isn't enough then debuginfo is downloaded and read. This helps avoid unnecessary downloads. Although this patch specifically uses .gdb_index, other indexes such as .debug_names could be supported in much the same way. Also with this patch, GDB will not build with the latest release of debuginfod, hence the RFC. The debuginfod ELF/DWARF section query function used in this patch to fetch .gdb_index is still in development and will be included in a future release. This patch implements basic on-demand functionality but more work needs to be done to fully take advantage of this feature. Currently GDB does not attempt to download debuginfo when generating backtraces. In some situations backtraces may lack information for libraries that we ought to be able to download debuginfo for. If the user attempts to print a non-existant symbol, GDB will start expanding symtabs one-by-one causing all debuginfo to be downloaded. Some uses of the 'list' command also trigger the downloading of all debuginfo, such as 'list 1' and 'list printf.c:33'. Now I'll describe the implementation in more detail. This patch adds a command 'set debuginfod enabled lazy' which enables on-demand debuginfo downloading/reading. If this 'lazy' mode is enabled and a solib's debuginfo cannot be found locally, the new function dwarf2_has_separate_index is called in elf_symfile_read. This function queries debuginfod servers for the .gdb_index matching the build-id of the solib. If it's found, a new objfile is created to hold the .gdb_index information. The new objfile flag OBJF_INDEX_READLATER is used to indicate that the objfile contains quick_symbols_functions for an index has deferred debuginfo reading. When GDB tries and fails to perform dwarf2_base_index_functions::find_pc_sect_compunit_symtab or dwarf2_gdb_index::expand_symtabs_matching, the new function read_full_dwarf_from_debuginfod downloads the actual debuginfo file and updates the objfile using the new function objfile::reinit. Symtab expansion then proceeds as if the debuginfo was present all along. Any feedback is appreciated. Aaron --- gdb/debuginfod-support.c | 56 +++++++ gdb/debuginfod-support.h | 23 +++ gdb/dwarf2/index-cache.c | 33 +++++ gdb/dwarf2/index-cache.h | 13 ++ gdb/dwarf2/public.h | 2 + gdb/dwarf2/read.c | 308 ++++++++++++++++++++++++++++++++++++--- gdb/dwarf2/section.c | 4 +- gdb/elfread.c | 2 + gdb/objfile-flags.h | 4 + gdb/objfiles.c | 16 ++ gdb/objfiles.h | 5 + gdb/symfile.c | 82 +++++++++++ gdb/symfile.h | 3 + gdb/symtab.h | 2 + 14 files changed, 532 insertions(+), 21 deletions(-) diff --git a/gdb/debuginfod-support.c b/gdb/debuginfod-support.c index 5f04a2b38ca..efcdb309522 100644 --- a/gdb/debuginfod-support.c +++ b/gdb/debuginfod-support.c @@ -33,12 +33,14 @@ static cmd_list_element *show_debuginfod_prefix_list; static const char debuginfod_on[] = "on"; static const char debuginfod_off[] = "off"; static const char debuginfod_ask[] = "ask"; +static const char debuginfod_lazy[] = "lazy"; static const char *debuginfod_enabled_enum[] = { debuginfod_on, debuginfod_off, debuginfod_ask, + debuginfod_lazy, nullptr }; @@ -79,6 +81,15 @@ debuginfod_exec_query (const unsigned char *build_id, return scoped_fd (-ENOSYS); } +scoped_fd +debuginfod_section_query (const unsigned char *build_id, + int build_id_len, + const char *filename, + const char *section_name, + gdb::unique_xmalloc_ptr *destname) +{ + return scoped_fd (-ENOSYS); +} #define NO_IMPL _("Support for debuginfod is not compiled into GDB.") #else @@ -358,6 +369,51 @@ debuginfod_exec_query (const unsigned char *build_id, return fd; } + +/* See debuginfod-support.h */ + +scoped_fd +debuginfod_section_query (const unsigned char *build_id, + int build_id_len, + const char *filename, + const char *section_name, + gdb::unique_xmalloc_ptr *destname) +{ + if (debuginfod_enabled != debuginfod_lazy || !debuginfod_is_enabled ()) + return scoped_fd (-ENOSYS); + + debuginfod_client *c = get_debuginfod_client (); + + if (c == nullptr) + return scoped_fd (-ENOMEM); + + char *dname = nullptr; + std::string desc = std::string ("section ") + section_name + " for"; + user_data data (desc.c_str (), filename); + + debuginfod_set_user_data (c, &data); + gdb::optional term_state; + if (target_supports_terminal_ours ()) + { + term_state.emplace (); + target_terminal::ours (); + } + + scoped_fd fd (debuginfod_find_section (c, build_id, build_id_len, + section_name, true, &dname)); + debuginfod_set_user_data (c, nullptr); + + if (fd.get () < 0 && fd.get () != -ENOENT) + gdb_printf (_("Download failed: %s. " \ + "Continuing without section %s for %ps.\n"), + safe_strerror (-fd.get ()), section_name, + styled_string (file_name_style.style (), filename)); + + if (fd.get () >= 0 && destname != nullptr) + destname->reset (dname); + + return fd; +} #endif /* Set callback for "set debuginfod enabled". */ diff --git a/gdb/debuginfod-support.h b/gdb/debuginfod-support.h index 5b1c1cb91f4..5294b65cac4 100644 --- a/gdb/debuginfod-support.h +++ b/gdb/debuginfod-support.h @@ -78,4 +78,27 @@ extern scoped_fd debuginfod_exec_query (const unsigned char *build_id, const char *filename, gdb::unique_xmalloc_ptr *destname); + +/* Query debuginfod servers for the binary contents of a ELF/DWARF section + from a file matching BUILD_ID. BUILD_ID can be given as a binary blob + or a null-terminated string. If given as a binary blob, BUILD_ID_LEN + should be the number of bytes. If given as a null-terminated string, + BUILD_ID_LEN should be 0. + + FILENAME should be the name or path associated with the file matching + BUILD_ID. It is used for printing messages to the user. + + SECTION_NAME should be the name of an ELF/DWARF section beginning + with '.'. + + If the file is successfully retrieved, its path on the local machine + is stored in DESTNAME. If GDB is not built with debuginfod, this + function returns -ENOSYS. */ + +extern scoped_fd debuginfod_section_query (const unsigned char *build_id, + int build_id_len, + const char *filename, + const char *section_name, + gdb::unique_xmalloc_ptr + *destname); #endif /* DEBUGINFOD_SUPPORT_H */ diff --git a/gdb/dwarf2/index-cache.c b/gdb/dwarf2/index-cache.c index 6de58592050..cc170e8abfe 100644 --- a/gdb/dwarf2/index-cache.c +++ b/gdb/dwarf2/index-cache.c @@ -222,6 +222,33 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id, return {}; } +/* See dwarf-index-cache.h. */ + +gdb::array_view +index_cache::lookup_gdb_index_debuginfod (const char *index_path, + std::unique_ptr *resource) +{ + try + { + /* Try to map that file. */ + index_cache_resource_mmap *mmap_resource + = new index_cache_resource_mmap (index_path); + + /* Hand the resource to the caller. */ + resource->reset (mmap_resource); + + return gdb::array_view + ((const gdb_byte *) mmap_resource->mapping.get (), + mmap_resource->mapping.size ()); + } + catch (const gdb_exception_error &except) + { + warning (_("Unable to read %s: %s"), index_path, except.what ()); + } + + return {}; +} + #else /* !HAVE_SYS_MMAN_H */ /* See dwarf-index-cache.h. This is a no-op on unsupported systems. */ @@ -233,6 +260,12 @@ index_cache::lookup_gdb_index (const bfd_build_id *build_id, return {}; } +gdb::array_view +index_cache::lookup_gdb_index_debuginfod (const char *index_path, + std::unique_ptr *resource) +{ + return {}; +} #endif /* See dwarf-index-cache.h. */ diff --git a/gdb/dwarf2/index-cache.h b/gdb/dwarf2/index-cache.h index 6366a9a9360..ecf74684c67 100644 --- a/gdb/dwarf2/index-cache.h +++ b/gdb/dwarf2/index-cache.h @@ -65,6 +65,19 @@ class index_cache lookup_gdb_index (const bfd_build_id *build_id, std::unique_ptr *resource); + /* Look for an index file located at INDEX_PATH in the debuginfod cache. + Unlike lookup_gdb_index, this function does not exit early if the + index cache has not been enabled. + + If found, return the contents as an array_view and store the underlying + resources (allocated memory, mapped file, etc) in RESOURCE. The returned + array_view is valid as long as RESOURCE is not destroyed. + + If no matching index file is found, return an empty array view. */ + gdb::array_view + lookup_gdb_index_debuginfod (const char *index_path, + std::unique_ptr *resource); + /* Return the number of cache hits. */ unsigned int n_hits () const { return m_n_hits; } diff --git a/gdb/dwarf2/public.h b/gdb/dwarf2/public.h index a9d4682c856..9971654c62f 100644 --- a/gdb/dwarf2/public.h +++ b/gdb/dwarf2/public.h @@ -40,4 +40,6 @@ extern void dwarf2_initialize_objfile (struct objfile *objfile); extern void dwarf2_build_frame_info (struct objfile *); +extern bool dwarf2_has_separate_index (struct objfile *); + #endif /* DWARF2_PUBLIC_H */ diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index 8e42c0f4d8d..1df63eba6f5 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -93,6 +93,9 @@ #include "split-name.h" #include "gdbsupport/parallel-for.h" #include "gdbsupport/thread-pool.h" +#include "symfile.h" +#include "inferior.h" +#include "debuginfod-support.h" /* When == 1, print basic high level tracing messages. When > 1, be more verbose. @@ -1846,6 +1849,10 @@ struct dwarf2_base_index_functions : public quick_symbol_functions CORE_ADDR pc, struct obj_section *section, int warn_if_readin) override final; + struct compunit_symtab *_find_pc_sect_compunit_symtab + (struct objfile *objfile, struct bound_minimal_symbol msymbol, + CORE_ADDR pc, struct obj_section *section, int warn_if_readin); + struct compunit_symtab *find_compunit_symtab_by_address (struct objfile *objfile, CORE_ADDR address) override { @@ -1911,6 +1918,16 @@ struct dwarf2_gdb_index : public dwarf2_base_index_functions block_search_flags search_flags, domain_enum domain, enum search_domain kind) override; + + bool _expand_symtabs_matching + (struct objfile *objfile, + gdb::function_view file_matcher, + const lookup_name_info *lookup_name, + gdb::function_view symbol_matcher, + gdb::function_view expansion_notify, + block_search_flags search_flags, + domain_enum domain, + enum search_domain kind); }; struct dwarf2_debug_names_index : public dwarf2_base_index_functions @@ -2677,28 +2694,31 @@ dwarf2_read_gdb_index /* If there is a .dwz file, read it so we can get its CU list as well. */ - dwz = dwarf2_get_dwz_file (per_bfd); - if (dwz != NULL) + if (get_gdb_index_contents_dwz != nullptr) { - struct mapped_index dwz_map; - const gdb_byte *dwz_types_ignore; - offset_type dwz_types_elements_ignore; + dwz = dwarf2_get_dwz_file (per_bfd); + if (dwz != NULL) + { + struct mapped_index dwz_map; + const gdb_byte *dwz_types_ignore; + offset_type dwz_types_elements_ignore; - gdb::array_view dwz_index_content - = get_gdb_index_contents_dwz (objfile, dwz); + gdb::array_view dwz_index_content + = get_gdb_index_contents_dwz (objfile, dwz); - if (dwz_index_content.empty ()) - return 0; + if (dwz_index_content.empty ()) + return 0; - if (!read_gdb_index_from_buffer (bfd_get_filename (dwz->dwz_bfd.get ()), - 1, dwz_index_content, &dwz_map, - &dwz_list, &dwz_list_elements, - &dwz_types_ignore, - &dwz_types_elements_ignore)) - { - warning (_("could not read '.gdb_index' section from %s; skipping"), - bfd_get_filename (dwz->dwz_bfd.get ())); - return 0; + if (!read_gdb_index_from_buffer (bfd_get_filename (dwz->dwz_bfd.get ()), + 1, dwz_index_content, &dwz_map, + &dwz_list, &dwz_list_elements, + &dwz_types_ignore, + &dwz_types_elements_ignore)) + { + warning (_("could not read '.gdb_index' section from %s; skipping"), + bfd_get_filename (dwz->dwz_bfd.get ())); + return 0; + } } } @@ -4192,8 +4212,10 @@ dw_expand_symtabs_matching_file_matcher } } +static bool read_full_dwarf_from_debuginfod (struct objfile *); + bool -dwarf2_gdb_index::expand_symtabs_matching +dwarf2_gdb_index::_expand_symtabs_matching (struct objfile *objfile, gdb::function_view file_matcher, const lookup_name_info *lookup_name, @@ -4242,6 +4264,44 @@ dwarf2_gdb_index::expand_symtabs_matching return result; } +bool +dwarf2_gdb_index::expand_symtabs_matching + (struct objfile *objfile, + gdb::function_view file_matcher, + const lookup_name_info *lookup_name, + gdb::function_view symbol_matcher, + gdb::function_view expansion_notify, + block_search_flags search_flags, + domain_enum domain, + enum search_domain kind) +{ + if (objfile->flags & OBJF_READNEVER) + return false; + + try + { + return _expand_symtabs_matching (objfile, file_matcher, lookup_name, + symbol_matcher, expansion_notify, + search_flags, domain, kind); + } + catch (gdb_exception e) + { + if ((objfile->flags & OBJF_INDEX_READLATER) == 0) + { + exception_print (gdb_stderr, e); + return false; + } + + /* Objfile is a stub holding only index information. Try to reinitialize + objfile with the full debuginfo. */ + if (!read_full_dwarf_from_debuginfod (objfile)) + return false; + return _expand_symtabs_matching (objfile, file_matcher, lookup_name, + symbol_matcher, expansion_notify, + search_flags, domain, kind); + } +} + /* A helper for dw2_find_pc_sect_compunit_symtab which finds the most specific symtab. */ @@ -4281,7 +4341,7 @@ dwarf2_base_index_functions::find_per_cu (dwarf2_per_bfd *per_bfd, } struct compunit_symtab * -dwarf2_base_index_functions::find_pc_sect_compunit_symtab +dwarf2_base_index_functions::_find_pc_sect_compunit_symtab (struct objfile *objfile, struct bound_minimal_symbol msymbol, CORE_ADDR pc, @@ -4312,6 +4372,40 @@ dwarf2_base_index_functions::find_pc_sect_compunit_symtab return result; } +struct compunit_symtab * +dwarf2_base_index_functions::find_pc_sect_compunit_symtab + (struct objfile *objfile, + struct bound_minimal_symbol msymbol, + CORE_ADDR pc, + struct obj_section *section, + int warn_if_readin) +{ + if (objfile->flags & OBJF_READNEVER) + return nullptr; + + try + { + return _find_pc_sect_compunit_symtab (objfile, msymbol, pc, + section, warn_if_readin); + } + catch (gdb_exception e) + { + if ((objfile->flags & OBJF_INDEX_READLATER) == 0) + { + exception_print (gdb_stderr, e); + return nullptr; + } + + /* Objfile is a stub holding only index information. Try to reinitialize + objfile with the full debuginfo. */ + if (!read_full_dwarf_from_debuginfod (objfile)) + return nullptr; + + return _find_pc_sect_compunit_symtab (objfile, msymbol, pc, + section, warn_if_readin); + } +} + void dwarf2_base_index_functions::map_symbol_filenames (struct objfile *objfile, @@ -5417,6 +5511,180 @@ dwarf2_initialize_objfile (struct objfile *objfile) objfile->qf.push_front (make_cooked_index_funcs ()); } +/* Query debuginfod for the .gdb_index matching OBJFILE's build-id. Return the + contents if successful. */ + +static gdb::array_view +get_gdb_index_contents_from_debuginfod (objfile *objfile, dwarf2_per_bfd *per_bfd) +{ + const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ()); + if (build_id == nullptr) + return {}; + + gdb::unique_xmalloc_ptr index_path; + scoped_fd fd = debuginfod_section_query (build_id->data, build_id->size, + bfd_get_filename + (objfile->obfd.get ()), + ".gdb_index", + &index_path); + if (fd.get () < 0) + return {}; + + return global_index_cache.lookup_gdb_index_debuginfod + (index_path.get (), &per_bfd->index_cache_res); +} + +/* If OBJFILE is a stub holding only information from a .gdb_index, then attempt + to download the full debuginfo and reinitialize OBJFILE with it. */ + +static bool +read_full_dwarf_from_debuginfod (struct objfile *objfile) +{ + if ((objfile->flags & OBJF_INDEX_READLATER) == 0) + return false; + + const struct bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ()); + if (build_id == nullptr) + return false; + + const char *filename = bfd_get_filename (objfile->obfd.get ()); + dwarf2_per_bfd *per_bfd; + dwarf2_per_objfile *per_objfile; + gdb_bfd_ref_ptr debug_bfd; + gdb::unique_xmalloc_ptr symfile_path; + + scoped_fd fd (debuginfod_debuginfo_query (build_id->data, + build_id->size, + filename, + &symfile_path)); + + if (fd.get () < 0) + goto fail; + + /* File successfully retrieved from server. Open as a bfd. */ + debug_bfd = symfile_bfd_open (symfile_path.get ()); + + if (debug_bfd == nullptr + || !build_id_verify (debug_bfd.get (), build_id->size, build_id->data)) + { + warning (_("File \"%s\" from debuginfod cannot be opened as bfd"), + filename); + goto fail; + } + + /* Fill in objfile's missing information using the debuginfo. */ + objfile->reinit (debug_bfd.release ()); + + /* Create new per_bfd and per_objfile. Placeholders based on the + separate_debug_objfile_backlink were deleted during reinit. */ + per_bfd = new dwarf2_per_bfd (objfile->obfd.get (), nullptr, false); + dwarf2_per_bfd_objfile_data_key.set (objfile, per_bfd); + per_objfile = dwarf2_objfile_data_key.emplace (objfile, objfile, per_bfd); + + objfile->flags &= ~OBJF_INDEX_READLATER; + + /* Have to attach the separate index again. The dwz will be downloaded at + this point if applicable. */ + if (dwarf2_read_gdb_index (per_objfile, + get_gdb_index_contents_from_debuginfod, + get_gdb_index_contents_from_section)) + { + dwarf_read_debug_printf ("found gdb index from debuginfod"); + objfile->qf.push_front (per_objfile->per_bfd->index_table->make_quick_functions ()); + + objfile->flags &= ~OBJF_NOT_FILENAME; + return true; + } + +fail: + objfile->flags |= OBJF_READNEVER; + return false; +} + +/* Query debuginfod for the separate .gdb_index associated with OBJFILE. If + successful, create an objfile to hold the .gdb_index information and act + as a placeholder until the full debuginfo needs to be downloaded. */ + +bool +dwarf2_has_separate_index (struct objfile *objfile) +{ + if (objfile->flags & OBJF_MAINLINE + || objfile->separate_debug_objfile_backlink != nullptr) + return 0; + + if (objfile->flags & OBJF_INDEX_READLATER) + return 1; + + gdb::unique_xmalloc_ptr index_path; + const bfd_build_id *build_id = build_id_bfd_get (objfile->obfd.get ()); + + scoped_fd fd = debuginfod_section_query (build_id->data, + build_id->size, + bfd_get_filename + (objfile->obfd.get ()), + ".gdb_index", + &index_path); + if (fd.get () >= 0) + { + /* We have a separate .gdb_index file so a separate debuginfo file + should exist. We just don't want to read it until we really + have to. Create an objfile to own the index information and to + act as a placeholder for the debuginfo that we have the option + of aquiring later. */ + gdb_bfd_ref_ptr abfd (gdb_bfd_open (objfile_filename (objfile), gnutarget)); + if (abfd == nullptr) + return false; + + dwarf2_per_bfd_objfile_data_key.clear (objfile); + dwarf2_objfile_data_key.clear (objfile); + + symbol_file_add_from_index + (abfd, current_inferior ()->symfile_flags | SYMFILE_NO_READ, objfile); + + dwarf2_per_bfd *per_bfd; + dwarf2_per_objfile *per_objfile = get_dwarf2_per_objfile (objfile); + + if (per_objfile == nullptr) + { + per_bfd = dwarf2_per_bfd_objfile_data_key.get (objfile); + if (per_bfd == nullptr) + { + per_bfd = new dwarf2_per_bfd (objfile->obfd.get (), nullptr, false); + dwarf2_per_bfd_objfile_data_key.set (objfile, per_bfd); + } + per_objfile = dwarf2_objfile_data_key.emplace (objfile, objfile, per_bfd); + } + + struct objfile *stub = objfile->separate_debug_objfile; + per_objfile = get_dwarf2_per_objfile (stub); + if (per_objfile == nullptr) + { + per_bfd = dwarf2_per_bfd_objfile_data_key.get (stub); + if (per_bfd == nullptr) + { + per_bfd = new dwarf2_per_bfd (stub->obfd.get (), nullptr, false); + dwarf2_per_bfd_objfile_data_key.set (stub, per_bfd); + } + per_objfile = dwarf2_objfile_data_key.emplace (stub, stub, per_bfd); + } + + if (dwarf2_read_gdb_index (per_objfile, + get_gdb_index_contents_from_debuginfod, + nullptr)) + { + dwarf_read_debug_printf ("found .gdb_index from debuginfod"); + stub->qf.push_front (per_bfd->index_table->make_quick_functions ()); + return 1; + } + + /* Unable to use the index. Delete the stub. */ + objfile->flags &= ~OBJF_INDEX_READLATER; + stub->unlink (); + } + + return 0; +} + /* Build a partial symbol table. */ diff --git a/gdb/dwarf2/section.c b/gdb/dwarf2/section.c index 32c86cc5d8d..4156ed2fdcb 100644 --- a/gdb/dwarf2/section.c +++ b/gdb/dwarf2/section.c @@ -54,7 +54,9 @@ dwarf2_section_info::get_bfd_owner () const section = get_containing_section (); gdb_assert (!section->is_virtual); } - gdb_assert (section->s.section != nullptr); + if (section->s.section == nullptr) + throw_error (NOT_FOUND_ERROR, + _("Can't find owner of DWARF2 section.")); return section->s.section->owner; } diff --git a/gdb/elfread.c b/gdb/elfread.c index 8aee634b44b..25bbab065b8 100644 --- a/gdb/elfread.c +++ b/gdb/elfread.c @@ -1274,6 +1274,8 @@ elf_symfile_read (struct objfile *objfile, symfile_add_flags symfile_flags) symbol_file_add_separate (debug_bfd, debugfile.c_str (), symfile_flags, objfile); } + else if (dwarf2_has_separate_index (objfile)) + return; else { has_dwarf2 = false; diff --git a/gdb/objfile-flags.h b/gdb/objfile-flags.h index b2e07110571..df9bf27d778 100644 --- a/gdb/objfile-flags.h +++ b/gdb/objfile-flags.h @@ -68,6 +68,10 @@ enum objfile_flag : unsigned /* User requested that we do not read this objfile's symbolic information. */ OBJF_READNEVER = 1 << 7, + + /* This objfile only holds information from an index. It should + be reinitialized with full debuginfo before expanding symtabs. */ + OBJF_INDEX_READLATER = 1 << 8, }; DEF_ENUM_FLAGS_TYPE (enum objfile_flag, objfile_flags); diff --git a/gdb/objfiles.c b/gdb/objfiles.c index 09aba0f80f0..c2c16c97758 100644 --- a/gdb/objfiles.c +++ b/gdb/objfiles.c @@ -53,6 +53,7 @@ #include "gdb_bfd.h" #include "btrace.h" #include "gdbsupport/pathstuff.h" +#include "symfile.h" #include #include @@ -299,6 +300,16 @@ build_objfile_section_table (struct objfile *objfile) objfile, 1); } +void +objfile::reinit (struct bfd *abfd) +{ + if ((flags & OBJF_INDEX_READLATER) == 0) + return; + + this->obfd.reset (abfd); + deferred_read_symbols (this, 0); +} + /* Given a pointer to an initialized bfd (ABFD) and some flag bits, initialize the new objfile as best we can and link it into the list of all known objfiles. @@ -455,6 +466,11 @@ objfile::make (gdb_bfd_ref_ptr bfd_, const char *name_, objfile_flags flags_, if (parent != nullptr) add_separate_debug_objfile (result, parent); + /* Objfile was initialized using only an index. Borrow offsets from the + parent until debuginfo is read. */ + if (flags_ & OBJF_INDEX_READLATER) + result->section_offsets = parent->section_offsets; + current_program_space->add_objfile (std::unique_ptr (result), parent); diff --git a/gdb/objfiles.h b/gdb/objfiles.h index 16dab0d2c69..0ebb16a30bd 100644 --- a/gdb/objfiles.h +++ b/gdb/objfiles.h @@ -500,6 +500,11 @@ struct objfile /* See quick_symbol_functions. */ struct symtab *find_last_source_symtab (); + /* Reinitialize this objfile using ABFD. Objfile should have been originally + initialized using a separate index from ABFD. Updates this objfile with + ABFD's symbols and section information. */ + void reinit (struct bfd *abfd); + /* See quick_symbol_functions. */ void forget_cached_source_info (); diff --git a/gdb/symfile.c b/gdb/symfile.c index eb27668f9d3..7edd50d92a2 100644 --- a/gdb/symfile.c +++ b/gdb/symfile.c @@ -1074,6 +1074,14 @@ symbol_file_add_with_addrs (const gdb_bfd_ref_ptr &abfd, const char *name, flags |= OBJF_MAINLINE; objfile = objfile::make (abfd, name, flags, parent); + if (objfile->flags & OBJF_INDEX_READLATER) + { + /* objfile was initialized only using a separate index so don't + try to read symbols yet. */ + gdb::observers::new_objfile.notify (objfile); + return objfile; + } + /* We either created a new mapped symbol table, mapped an existing symbol table file which has not had initial symbol reading performed, or need to read an unmapped symbol table. */ @@ -1135,6 +1143,29 @@ symbol_file_add_with_addrs (const gdb_bfd_ref_ptr &abfd, const char *name, return (objfile); } +/* We know a separate debuginfo file should exist, but we don't want to + read it yet. Infer some of it's properties from the parent objfile. */ + +void +symbol_file_add_from_index (const gdb_bfd_ref_ptr &bfd, + symfile_add_flags symfile_flags, + struct objfile *parent) +{ + section_addr_info sap = build_section_addr_info_from_objfile (parent); + + symbol_file_add_with_addrs + (bfd, "", symfile_flags, &sap, + (parent->flags & (OBJF_REORDERED | OBJF_SHARED | OBJF_READNOW + | OBJF_USERLOADED | OBJF_MAINLINE | OBJF_PSYMTABS_READ)) + | OBJF_INDEX_READLATER | OBJF_NOT_FILENAME, + parent); + + objfile *result = parent->separate_debug_objfile; + init_objfile_sect_indices (result); + + return; +} + /* Add BFD as a separate debug file for OBJFILE. For NAME description see the objfile constructor. */ @@ -2415,6 +2446,57 @@ remove_symbol_file_command (const char *args, int from_tty) clear_symtab_users (0); } +/* Read a separate debuginfo OBJFILE that was originally initialized using + only an index and section information from its parent file. */ + +void +deferred_read_symbols (struct objfile *objfile, int from_tty) +{ + gdb_assert (objfile->flags & OBJF_INDEX_READLATER); + + /* Nuke all the state that we will re-read. */ + objfile->registry_fields.clear_registry (); + + objfile->sections = NULL; + objfile->sect_index_bss = -1; + objfile->sect_index_data = -1; + objfile->sect_index_rodata = -1; + objfile->sect_index_text = -1; + objfile->compunit_symtabs = NULL; + objfile->template_symbols = NULL; + + { + gdb_bfd_ref_ptr obfd = objfile->obfd; + const char *obfd_filename; + + obfd_filename = bfd_get_filename (objfile->obfd.get ()); + /* Open the new BFD before freeing the old one, so that + the filename remains live. */ + gdb_bfd_ref_ptr temp (gdb_bfd_open (obfd_filename, gnutarget)); + objfile->obfd = std::move (temp); + if (objfile->obfd == NULL) + error (_("Can't open %s to read symbols."), obfd_filename); + } + + std::string original_name = objfile->original_name; + + /* bfd_openr sets cacheable to true, which is what we want. */ + if (!bfd_check_format (objfile->obfd.get (), bfd_object)) + error (_("Can't read symbols from %s: %s."), objfile_name (objfile), + bfd_errmsg (bfd_get_error ())); + + objfile->original_name + = obstack_strdup (&objfile->objfile_obstack, original_name); + + objfile_set_sym_fns (objfile, find_sym_fns (objfile->obfd.get ())); + build_objfile_section_table (objfile); + (*objfile->sf->sym_init) (objfile); + init_objfile_sect_indices (objfile); + + read_symbols (objfile, 0); + objfile->mtime = bfd_get_mtime (objfile->obfd.get ()); +} + /* Re-read symbols if a symbol-file has changed. */ void diff --git a/gdb/symfile.h b/gdb/symfile.h index 1d13e82502b..724e156fc4b 100644 --- a/gdb/symfile.h +++ b/gdb/symfile.h @@ -241,6 +241,9 @@ extern struct objfile *symbol_file_add_from_bfd (const gdb_bfd_ref_ptr &, extern void symbol_file_add_separate (const gdb_bfd_ref_ptr &, const char *, symfile_add_flags, struct objfile *); +extern void symbol_file_add_from_index (const gdb_bfd_ref_ptr &, + symfile_add_flags, struct objfile *); + extern std::string find_separate_debug_file_by_debuglink (struct objfile *); /* Build (allocate and populate) a section_addr_info struct from an diff --git a/gdb/symtab.h b/gdb/symtab.h index 89d7a183ff3..380387162de 100644 --- a/gdb/symtab.h +++ b/gdb/symtab.h @@ -2207,6 +2207,8 @@ extern bool find_pc_line_pc_range (CORE_ADDR, CORE_ADDR *, CORE_ADDR *); extern void reread_symbols (int from_tty); +extern void deferred_read_symbols (struct objfile *, int from_tty); + /* Look up a type named NAME in STRUCT_DOMAIN in the current language. The type returned must not be opaque -- i.e., must have at least one field defined. */