From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id dvfcJaP69GkzdwkAWB0awg (envelope-from ) for ; Fri, 01 May 2026 15:10:27 -0400 Authentication-Results: simark.ca; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=Ey1Deq4L; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 896A91E067; Fri, 01 May 2026 15:10:27 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-3.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,RCVD_IN_MSPIKE_H2,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id F02491E067 for ; Fri, 01 May 2026 15:10:24 -0400 (EDT) Received: from vm01.sourceware.org (localhost [127.0.0.1]) by sourceware.org (Postfix) with ESMTP id 1A32F437423A for ; Fri, 1 May 2026 19:10:24 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 1A32F437423A Authentication-Results: sourceware.org; dkim=pass (1024-bit key, unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=Ey1Deq4L Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id D3F0F4BA798B for ; Fri, 1 May 2026 19:09:46 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org D3F0F4BA798B Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org D3F0F4BA798B Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1777662587; cv=none; b=mSEzXpIcF2zhsSs5biG0mgKqwua4IGasKLVnVZ/JrnmWwNsk/U+UeVj6DChv6rFnzwplOM0T6nx4EAWP03DCzglHxWPNUDotTODrH/B6Xj9hNGG5NHtgQK1QYB6nJgfAYG8Zaf78nhq9m8ZTEKjDgOUnl8Kd2ZIcj/h5stQgi1E= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1777662587; c=relaxed/simple; bh=OiTC2CMOSBeEFemQCuvXMcaxZ7yk/8INg3Rs6nrrWGQ=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=a1sZuUSmRXoT2R+GKKNCqXoKeXsf5udwVapKbSfPYoI9+0wra2hXE81lzRU+w7rMMSU7w+KoxccZyUDHoEFxEGLTUnMVbpEZxQfDzh6KtVKljRQWYBowShFznswFT7NYca7WV/8QVgltGOwnVoOh0W9x5N13mFWhPBKK+Ey8nwQ= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D3F0F4BA798B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1777662586; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=LBfdZOaFJjURMMXoBylGcoIY4feEj7RRvw71jJgBfm8=; b=Ey1Deq4L9pHhN9QxVJMwTug0RLkxmOdZ6j8qVhPXs23HQUsoK7VUngTjzACZYxegz9XfE/ /YOq/xi7jjO+21h2NfJTGvoTMi6QXbuPObt1LMqgJPn2q4oDlLUs4tyulgsHYZADkzp6mO USn7ce5RFpnmJ4a2t7fdeCuCLzqF5vU= Received: from mail-wr1-f70.google.com (mail-wr1-f70.google.com [209.85.221.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-488-SPGHMT10N22snurmDeDv4w-1; Fri, 01 May 2026 15:09:45 -0400 X-MC-Unique: SPGHMT10N22snurmDeDv4w-1 X-Mimecast-MFC-AGG-ID: SPGHMT10N22snurmDeDv4w_1777662584 Received: by mail-wr1-f70.google.com with SMTP id ffacd0b85a97d-43cfedb10a8so1258825f8f.1 for ; Fri, 01 May 2026 12:09:44 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1777662583; x=1778267383; h=mime-version:message-id:date:references:in-reply-to:subject:to:from :x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=LBfdZOaFJjURMMXoBylGcoIY4feEj7RRvw71jJgBfm8=; b=rFbuT0z920Vz4KM7JOe1QoH0cUJZ/vxuaoz6mlCugC8G0IGzqM+jG4Avfs6kypXUdH pIwj2eCyjNLq1iONZSTVU32rIZSnQRX2ZpnG7CHqWbM26LHOGRhJSTQTBqMKe1+GsrLh RBsatsxuWf+gJgZO9OVrbgMpQRxreBUHxJI4nkAo1I96JCMc8v4Dz5fvTdpxEgBMkQnp cnph2zQB1BY/eJmAKLw8Sxvol78vCK8wFeQ85qLVmHSPMd5kLIkNLFts6RkBopvTeSL3 QKMvVaukqXywgilqXriz+MF5GbbtPNhpTXNtUOd2a0HKd1iZrZ0PGNXq4kayW2W4iuy4 VFxg== X-Forwarded-Encrypted: i=1; AFNElJ81itSPqKzxivV22Cu0abLsi+MpkN5VvFWJV6sb0j0qoGEwc6k6Su0xti8fMF9Y/xC2WSngEdxlG1V4eQ==@sourceware.org X-Gm-Message-State: AOJu0YyvPmLBpB8OBemciM/EDUqPbL/yV/Rq1uNsTqAE1YNR51sTBUnF dfWExlZEpkOuRV4ypmBTVoq5o2O4kJljJv92eWqHeJXEvD6FzHtVzsRfZVF3T8rJfoASmNPnbVJ Wm6xdwJmezThMJoPHl7GwioKn+CY+QnuuFOeTE1iK2OpPcUvjWK36VxLor7x5U45I0++ZPEo= X-Gm-Gg: AeBDietxX8YiNnIZMOJ0akwWV0OSrOhooclyIPXoMMGcCDXuv00XXfDkqNelGQhHGxh eDC7ZhyFPeH4n8QD0eK1lPpW1JFCleJMAKUpaWJc6yvhrFwDAZ/NZjD9It+UijRcj6gj/ct8G4F 4tD4JGgAg9tc3l+W6RnbPokSGgqKsTiDS1I62WbEImziJREbENJTH+FnOnrBxPYpOh2KiQxtJUI S5JsolTOL10f6JYQMcDf9evSkpZK9KFF0GX+nSwcLHln3tLlz5w0qRhWIjbH55JpGdcau7mdH0y nQKH4il2aGN6BpphRvjdtrFWjwCU9/T2onbfqHxLQ1qSEnTR56sVa+VSCfZky6PMyqv9TevGKW7 t6Cs6TOcwam+E4Be+jHTS8/0/ksM28ya9oncCvrpL1t6jV6Xu0MMKn3kE X-Received: by 2002:a05:600c:154b:b0:488:936a:6220 with SMTP id 5b1f17b1804b1-48a988a705dmr4856455e9.21.1777662583169; Fri, 01 May 2026 12:09:43 -0700 (PDT) X-Received: by 2002:a05:600c:154b:b0:488:936a:6220 with SMTP id 5b1f17b1804b1-48a988a705dmr4855815e9.21.1777662582488; Fri, 01 May 2026 12:09:42 -0700 (PDT) Received: from localhost (92.40.169.181.threembb.co.uk. [92.40.169.181]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-48a82301ad1sm237734935e9.9.2026.05.01.12.09.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 01 May 2026 12:09:42 -0700 (PDT) From: Andrew Burgess To: Keith Seitz , gdb-patches@sourceware.org Subject: Re: [PATCH 5/5] gdb/linux: handle missing NT_FILE note when opening core files In-Reply-To: <4ff57d97-02a3-4e10-b6f7-7203372ebda4@redhat.com> References: <84829f29d55cd681c1829c60109c9eb140353795.1773498341.git.aburgess@redhat.com> <4ff57d97-02a3-4e10-b6f7-7203372ebda4@redhat.com> Date: Fri, 01 May 2026 20:09:40 +0100 Message-ID: <87340beykr.fsf@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: 95OnHNImhgWNQmtPTQKjdYKH7-7UnFtqsCAgrRqlC4k_1777662584 X-Mimecast-Originator: redhat.com Content-Type: text/plain X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org Keith Seitz writes: > On 3/14/26 7:32 AM, Andrew Burgess wrote: >> Then in corelow.c various functions are updated in order to handle >> anonymous mappings. > > Just one note below, otherwise LGTM. > >> diff --git a/gdb/corelow.c b/gdb/corelow.c >> index d63dd5f8f0d..d1412c4903a 100644 >> --- a/gdb/corelow.c >> +++ b/gdb/corelow.c >> @@ -1963,7 +1972,6 @@ mapped_file_info::add (const char *soname, >> const bfd_build_id *build_id) >> { >> gdb_assert (build_id != nullptr); >> - gdb_assert (expected_filename != nullptr); >> >> if (soname != nullptr) >> { > > The comment at the beginning of this method (mapped_file_info::add) > contains: > > 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. > > This needs to be updated, since null `expected_filename' file names > will now be ignored. > Thanks for pointing this out. I fixed this. I also realised there was another small improvement that could be made in gdb_read_core_file_mappings that would better handle the case where an NT_FILE note was corrupted, so I made that fix and then pushed this series. The version of this patch that I pushed can be found below. Thanks, Andrew --- commit 37cb3cd8f896b8d0aa95ced818c6c7b1fb9ddc99 Author: Andrew Burgess Date: Wed Mar 11 11:45:42 2026 +0000 gdb/linux: handle missing NT_FILE note when opening core files This patch originated from this mailing list discussion: https://inbox.sourceware.org/gdb-patches/b9b5bf03c59b58e02ca27b522338c6103d5ae49f.camel@gnu.org The user has some core files which lack an NT_FILE note. They wondered why GDB was still unable to find the shared libraries based on their build-id. The reason right now is that GDB only records the build-id information for mappings based on the entries in the NT_FILE note. With the entries in this note we build several lookup tables; a filename to build-id table, a soname (extracted from the file if it is a shared library) to build-id table, and an address range to build-id table. When a shared library is being loaded we perform a lookup using two pieces of information; the shared library's filename, and an address that we know is within the shared library. If either of these give a build-id, then we can use that build-id to ensure GDB loads the shared library that matches the core file. If the NT_FILE note is missing then none of the lookup tables are created, and so the shared library build-id lookup fails, meaning that all GDB can do is look for the shared library by name on the local file system. This often results in the wrong library version being loaded, or the library not being found at all. However, Linux core files also have the segment table. This table gives address ranges. The segment table doesn't tell us what file was mapped in, or the offset within the file that was mapped in. But if we go back to the three lookup tables, we can use the segment table to build the address to build-id lookup table, and that would be enough to allow GDB to find the build-id for a shared library in most cases. So, here's what this patch does: linux_read_core_file_mappings (in linux-tdep.c) is updated to first parse the NT_FILE note as it currently does. But after this we also walk the segment table (BFD actually converts these into sections with the LOAD flag set), and if a segment has a build-id, and doesn't correspond to an entry found in the NT_FILE note, we create an anonymous mapping. An anonymous mapping is just like a mapping from the NT_FILE note, but without a filename and file offset. This mapping is passed through the callback just like the traditional, non-anonymous, mappings. Then in corelow.c various functions are updated in order to handle anonymous mappings. Back in linux-tdep.c, function linux_core_info_proc_mappings gets a small update to handle anonymous mappings. The corefile-buildid.exp test is updated to remove the NT_FILE notes and rerun the tests. This should make no difference as all this test is checking is that GDB is able to find and load the shared libraries and executable based on their build-ids; this is something we can do fine now without the NT_FILE note. I have also had to update the Python core file API documentation after this commit. Previously we claimed that CorefileMappedFile.filename would never be empty, but this is now possible. Luckily, this API has not yet been in a released version of GDB, so this minor tweak isn't going to break any existing user code. I did consider having CorefileMappedFile.filename be a non-empty string or None, but I couldn't see much value in this, so I just documented that the string could be empty, and what this means. The py-corefile.exp test needed a minor update to filter out anonymous mappings (those without a filename), this matches the behaviour of the builtin 'info proc mappings' command. Reviewed-By: Keith Seitz diff --git a/gdb/corelow.c b/gdb/corelow.c index 407515ee45a..008da68155f 100644 --- a/gdb/corelow.c +++ b/gdb/corelow.c @@ -406,12 +406,25 @@ core_target::build_file_mappings () gdb::unordered_string_map bfd_map; gdb::unordered_set unavailable_paths; - /* All files mapped into the core file. The key is the filename. */ + /* All files mapped into the core file. */ std::vector mapped_files = gdb_read_core_file_mappings (m_core_gdbarch, this->core_bfd ()); for (const core_mapped_file &file_data : mapped_files) { + /* A mapping without a filename can still have a build-id. Tracking + these mappings is useful as when the shared libraries are loaded, + if the shared library is within this anonymous region, we can + validate the build-id of the shared library being loaded. */ + if (file_data.filename.empty ()) + { + gdb_assert (file_data.build_id != nullptr); + std::vector ranges = file_data.mem_ranges (); + m_mapped_file_info.add (nullptr, nullptr, nullptr, + std::move (ranges), file_data.build_id); + continue; + } + /* If this mapped file is marked as the main executable then record the filename as we can use this later. */ if (file_data.is_main_exec && m_expected_exec_filename.empty ()) @@ -477,10 +490,6 @@ core_target::build_file_mappings () } } - std::vector ranges; - for (const core_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)) @@ -593,7 +602,7 @@ core_target::build_file_mappings () libraries. */ if (file_data.build_id != nullptr) { - normalize_mem_ranges (&ranges); + std::vector ranges = file_data.mem_ranges (); const char *actual_filename = nullptr; gdb::unique_xmalloc_ptr soname; @@ -1977,7 +1986,10 @@ maintenance_print_core_file_backed_mappings (const char *args, int from_tty) 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. + inferior as extracted from the core file, this can be nullptr if the + filename is unknown. This can happen on Linux if there is no NT_FILE + note, and we are building the mapped_file_info using just the segment + table from the core file. 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 @@ -2001,7 +2013,6 @@ mapped_file_info::add (const char *soname, const bfd_build_id *build_id) { gdb_assert (build_id != nullptr); - gdb_assert (expected_filename != nullptr); if (soname != nullptr) { @@ -2021,13 +2032,17 @@ mapped_file_info::add (const char *soname, 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 inserted - = m_filename_to_build_id_map.emplace (expected_filename, build_id).second; - gdb_assert (inserted); + /* Ignore empty filenames. */ + if (expected_filename != nullptr) + { + /* 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 inserted + = m_filename_to_build_id_map.emplace (expected_filename, build_id).second; + gdb_assert (inserted); + } /* Setup the reverse build-id to file name map. */ if (actual_filename != nullptr) @@ -2173,9 +2188,19 @@ gdb_read_core_file_mappings (struct gdbarch *gdbarch, struct bfd *cbfd) [&] (ULONGEST start, ULONGEST end, ULONGEST file_ofs, const char *filename, const bfd_build_id *build_id) { - /* Architecture-specific read_core_mapping methods are expected to - weed out non-file-backed mappings. */ - gdb_assert (filename != nullptr); + if (filename == nullptr) + { + /* A mapping without a filename MUST have a build-id, and the + file_ofs MUST be 0, but the file_ofs is actually meaningless + in this case. */ + gdb_assert (build_id != nullptr); + gdb_assert (file_ofs == 0); + + results.emplace_back (); + results.back ().build_id = build_id; + results.back ().regions.emplace_back (start, end, file_ofs); + return; + } /* Add this mapped region to the data for FILENAME. */ auto iter = mapped_files.find (filename); diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index 96b46a98091..6cafab78d58 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -9040,8 +9040,10 @@ Core Files In Python A @code{gdb.CorefileMappedFile} object has the following attributes: @defvar CorefileMappedFile.filename -This read only attribute contains a non-empty string, the file name of -the mapped file. +This read only attribute contains a string, the file name of the +mapped file. This string can be empty if @value{GDBN} was unable to +find the name of the mapped file. If this string is empty then +@code{CorefileMappedFile.build_id} will not be @code{None}. @end defvar @defvar CorefileMappedFile.build_id @@ -9081,7 +9083,9 @@ Core Files In Python @defvar CorefileMappedFileRegion.file_offset This read only attribute contains the offset within the mapped file -for this mapping. +for this mapping. This attribute will be @code{0} if the containing +@code{CorefileMappedFile.filename} is empty, in which case this +attribute has no meaning. @end defvar @node Python Auto-loading diff --git a/gdb/gdbcore.h b/gdb/gdbcore.h index 89586e0c6ec..3fd91770c54 100644 --- a/gdb/gdbcore.h +++ b/gdb/gdbcore.h @@ -261,7 +261,9 @@ core_target_find_mapped_file (const char *filename, /* 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. */ + with the list of regions the file is mapped into. It is acceptable to + have a core_mapped_file with an empty filename, so long as we have a + build-id for the mapping. */ struct core_mapped_file { /* Type for a region of a file that was mapped into the inferior when @@ -281,15 +283,22 @@ struct core_mapped_file /* The inferior address immediately after the mapped region. */ CORE_ADDR end; - /* The offset within the mapped file for this content. */ + /* The offset within the mapped file for this content. If the + filename of the mapping is empty then this field will be 0, but has + no meaning. */ CORE_ADDR file_ofs; }; - /* The filename as recorded in the core file. */ + /* The filename as recorded in the core file. This can be empty meaning + that GDB was unable to find a filename for this mapping. If the + filename is empty then the build_id field MUST be non-NULL. If this + is empty then the region::file_ofs fields will all be 0, and have no + meaning. */ std::string filename; /* If not nullptr, then this is the build-id associated with this - file. */ + mapping. If the filename field is empty, then this MUST be + non-NULL. */ const bfd_build_id *build_id = nullptr; /* All the mapped regions of this file. */ @@ -297,6 +306,17 @@ struct core_mapped_file /* True if this is the main executable. */ bool is_main_exec = false; + + /* Convert the REGIONS to a vector of mem_range objects. */ + std::vector + mem_ranges () const + { + std::vector ranges; + for (const core_mapped_file::region ®ion : this->regions) + ranges.emplace_back (region.start, region.end - region.start); + normalize_mem_ranges (&ranges); + return ranges; + } }; extern std::vector gdb_read_core_file_mappings diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c index 43df05d1529..4bc5a2b6948 100644 --- a/gdb/linux-tdep.c +++ b/gdb/linux-tdep.c @@ -1091,6 +1091,33 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args, } } +/* Return a map from the start address of any LOAD segment in CBFD to the + associated build-id. The map only contains entries for those segments + where a build-id is found, so the build-id will never be NULL, but not + every segment will have an entry in the map. */ + +static gdb::unordered_map +linux_read_build_ids_from_core_file_mappings (struct bfd *cbfd) +{ + gdb::unordered_map vma_to_build_id_map; + + auto restore_cbfd_build_id = make_scoped_restore (&cbfd->build_id); + + /* Search for solib build-ids in the core file. Each time one is found, + map the start vma of the corresponding elf header to the build-id. */ + for (bfd_section *sec = cbfd->sections; sec != nullptr; sec = sec->next) + { + cbfd->build_id = nullptr; + + if (sec->flags & SEC_LOAD + && (get_elf_backend_data (cbfd)->elf_backend_core_find_build_id + (cbfd, (bfd_vma) sec->filepos))) + vma_to_build_id_map[sec->vma] = cbfd->build_id; + } + + return vma_to_build_id_map; +} + /* Implementation of `gdbarch_read_core_file_mappings', as defined in gdbarch.h. @@ -1107,103 +1134,39 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args, long file_ofs followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL... + In addition to reading the NT_FILE note, this function also considers + all of the LOADable segments (which BFD turns into sections). If a + segment is loadable, has a build-id, and isn't covered by an NT_FILE + entry, then we consider it an anonymous mapping. Tracking these is + useful when we load shared libraries. If a shared library corresponds + to an anonymous mapping then we can validate the build-id of the shared + library. This is useful when core files are created without an NT_FILE + note. + CBFD is the BFD of the core file. LOOP_CB is the callback function that will be executed once for each mapping. */ static void -linux_read_core_file_mappings - (struct gdbarch *gdbarch, - struct bfd *cbfd, - read_core_file_mappings_loop_ftype loop_cb) +linux_read_core_file_mappings (struct gdbarch *gdbarch, struct bfd *cbfd, + read_core_file_mappings_loop_ftype loop_cb) { /* Ensure that ULONGEST is big enough for reading 64-bit core files. */ static_assert (sizeof (ULONGEST) >= 8); - /* It's not required that the NT_FILE note exists, so return silently - if it's not found. Beyond this point though, we'll complain - if problems are found. */ - asection *section = bfd_get_section_by_name (cbfd, ".note.linuxcore.file"); - if (section == nullptr) - return; + /* Map from the start address of LOAD segments to the associated + build-id, but only for segments that have a build-id. */ + gdb::unordered_map vma_map + = linux_read_build_ids_from_core_file_mappings (cbfd); - unsigned int addr_size_bits = gdbarch_addr_bit (gdbarch); - unsigned int addr_size = addr_size_bits / 8; - size_t note_size = bfd_section_size (section); + /* Track the start address of each mapping found. This allows us to + quickly drop duplicates when processing the NT_FILE note then the LOAD + segments. */ + gdb::unordered_set mapping_start_addresses; - if (note_size < 2 * addr_size) - { - warning (_("malformed core note - too short for header")); - return; - } - - gdb::byte_vector contents (note_size); - if (!bfd_get_section_contents (cbfd, section, contents.data (), 0, - note_size)) - { - warning (_("could not get core note contents")); - return; - } - - gdb_byte *descdata = contents.data (); - char *descend = (char *) descdata + note_size; - - if (descdata[note_size - 1] != '\0') - { - warning (_("malformed note - does not end with \\0")); - return; - } - - ULONGEST count = bfd_get (addr_size_bits, cbfd, descdata); - descdata += addr_size; - - ULONGEST page_size = bfd_get (addr_size_bits, cbfd, descdata); - descdata += addr_size; - - if (note_size < 2 * addr_size + count * 3 * addr_size) - { - warning (_("malformed note - too short for supplied file count")); - return; - } - - char *filenames = (char *) descdata + count * 3 * addr_size; - - /* Make sure that the correct number of filenames exist. Complain - if there aren't enough or are too many. */ - char *f = filenames; - for (int i = 0; i < count; i++) - { - if (f >= descend) - { - warning (_("malformed note - filename area is too small")); - return; - } - f += strnlen (f, descend - f) + 1; - } - /* Complain, but don't return early if the filename area is too big. */ - if (f != descend) - warning (_("malformed note - filename area is too big")); - - const bfd_build_id *orig_build_id = cbfd->build_id; - gdb::unordered_map vma_map; - - /* Search for solib build-ids in the core file. Each time one is found, - map the start vma of the corresponding elf header to the build-id. */ - for (bfd_section *sec = cbfd->sections; sec != nullptr; sec = sec->next) - { - cbfd->build_id = nullptr; - - if (sec->flags & SEC_LOAD - && (get_elf_backend_data (cbfd)->elf_backend_core_find_build_id - (cbfd, (bfd_vma) sec->filepos))) - vma_map[sec->vma] = cbfd->build_id; - } - - cbfd->build_id = orig_build_id; - - /* Vector to collect proc mappings. */ - struct proc_mapping + /* Vector to collect all mappings. */ + struct a_mapping { ULONGEST start; ULONGEST end; @@ -1211,38 +1174,165 @@ linux_read_core_file_mappings const char *filename; const bfd_build_id *build_id; }; - std::vector proc_mappings; + std::vector all_mappings; - /* Collect proc mappings. */ - for (int i = 0; i < count; i++) + /* If we find the NT_FILE section in the lambda below then we need + to load its contents, and those contents need to remain live + until the end of this function. This vector will hold the + contents. */ + gdb::byte_vector contents; + + /* Read mapping from the NT_FILE note if it exists. It is not + required that this note exist so wrap the parsing within a lambda + function, this allows us to return if the note doesn't exist, or + is malformed. + + After parsing the NT_FILE note we will also parse the segment + table to fill in any additional mappings that the NT_FILE didn't + cover, e.g. if NT_FILE was missing or unreadable. */ + auto read_mappings_from_nt_file_note = [&] () { - struct proc_mapping m; - m.start = bfd_get (addr_size_bits, cbfd, descdata); - descdata += addr_size; - m.end = bfd_get (addr_size_bits, cbfd, descdata); - descdata += addr_size; - m.file_ofs = bfd_get (addr_size_bits, cbfd, descdata) * page_size; - descdata += addr_size; - m.filename = filenames; - filenames += strlen ((char *) filenames) + 1; + asection *section = bfd_get_section_by_name (cbfd, ".note.linuxcore.file"); + if (section == nullptr) + return; - m.build_id = nullptr; - auto vma_map_it = vma_map.find (m.start); + unsigned int addr_size_bits = gdbarch_addr_bit (gdbarch); + unsigned int addr_size = addr_size_bits / 8; + size_t note_size = bfd_section_size (section); + + if (note_size < 2 * addr_size) + { + warning (_("malformed core note - too short for header")); + return; + } + + contents.resize (note_size); + if (!bfd_get_section_contents (cbfd, section, contents.data (), 0, + note_size)) + { + warning (_("could not get core note contents")); + return; + } + + gdb_byte *descdata = contents.data (); + char *descend = (char *) descdata + note_size; + + if (descdata[note_size - 1] != '\0') + { + warning (_("malformed note - does not end with \\0")); + return; + } + + ULONGEST count = bfd_get (addr_size_bits, cbfd, descdata); + descdata += addr_size; + + ULONGEST page_size = bfd_get (addr_size_bits, cbfd, descdata); + descdata += addr_size; + + if (note_size < 2 * addr_size + count * 3 * addr_size) + { + warning (_("malformed note - too short for supplied file count")); + return; + } + + char *filenames = (char *) descdata + count * 3 * addr_size; + + /* Make sure that the correct number of filenames exist. Complain + if there aren't enough or are too many. */ + char *f = filenames; + for (int i = 0; i < count; i++) + { + if (f >= descend) + { + warning (_("malformed note - filename area is too small")); + return; + } + f += strnlen (f, descend - f) + 1; + } + /* Complain, but don't return early if the filename area is too big. */ + if (f != descend) + warning (_("malformed note - filename area is too big")); + + /* Collect proc mappings. */ + for (int i = 0; i < count; i++) + { + struct a_mapping m; + m.start = bfd_get (addr_size_bits, cbfd, descdata); + descdata += addr_size; + m.end = bfd_get (addr_size_bits, cbfd, descdata); + descdata += addr_size; + m.file_ofs = bfd_get (addr_size_bits, cbfd, descdata) * page_size; + descdata += addr_size; + m.filename = filenames; + filenames += strlen ((char *) filenames) + 1; + + m.build_id = nullptr; + auto vma_map_it = vma_map.find (m.start); + if (vma_map_it != vma_map.end ()) + m.build_id = vma_map_it->second; + + all_mappings.push_back (m); + mapping_start_addresses.insert (m.start); + } + }; + read_mappings_from_nt_file_note (); + + /* Now look through the LOAD segments. These have all been converted to + sections with the SEC_LOAD flag by BFD. If we find a section that + contains a build-id, and which wasn't covered by an NT_FILE note, then + we create a mapping entry with no filename. */ + for (bfd_section *sec = cbfd->sections; sec != nullptr; sec = sec->next) + { + /* Skip non LOAD segments. */ + if ((sec->flags & SEC_LOAD) == 0) + continue; + + CORE_ADDR start = bfd_section_vma (sec); + + /* If there is already a mapping at this address (likely from the + NT_FILE processing above) then skip this LOAD segment. Currently + this is assuming that the mapping from NT_FILE will be the same + size as the LOAD segment, if this ever turns out not to be the + case then we might need to be smarter here and figure out + non-overlapping regions. But that seems like unnecessary + complexity for now. */ + auto mapping_start_addresses_it = mapping_start_addresses.find (start); + if (mapping_start_addresses_it != mapping_start_addresses.end ()) + continue; + + /* This LOAD segment is not covered by an NT_FILE entry, so we are + not going to have a filename associated with the mapping. Still, + we might be able to find a build-id, and if we can, then tracking + this mapping is still useful as, when we load shared libraries, if + a library sits in this mapping we can validate the library's + build-id. If we cannot find a build-id for this mapping then + there's no point tracking it, as it tells us nothing of value; no + filename, no build-id. */ + const bfd_build_id *build_id = nullptr; + auto vma_map_it = vma_map.find (start); if (vma_map_it != vma_map.end ()) - m.build_id = vma_map_it->second; + build_id = vma_map_it->second; + if (build_id == nullptr) + continue; - proc_mappings.push_back (m); + /* Create an anonymous mapping object, one without a filename. */ + CORE_ADDR end = start + bfd_section_size (sec); + + struct a_mapping m { .start = start, .end = end, + .file_ofs = 0, .filename = nullptr, .build_id = build_id }; + all_mappings.push_back (m); + mapping_start_addresses.insert (start); } - /* Sort proc mappings. */ - std::sort (proc_mappings.begin (), proc_mappings.end (), - [] (const proc_mapping &a, const proc_mapping &b) - { - return a.start < b.start; - }); + /* Sort the mappings. */ + std::sort (all_mappings.begin (), all_mappings.end (), + [] (const a_mapping &a, const a_mapping &b) + { + return a.start < b.start; + }); - /* Call loop_cb with sorted proc mappings. */ - for (const auto &m : proc_mappings) + /* Call loop_cb with sorted mappings. */ + for (const a_mapping &m : all_mappings) loop_cb (m.start, m.end, m.file_ofs, m.filename, m.build_id); } @@ -1258,6 +1348,10 @@ linux_core_info_proc_mappings (struct gdbarch *gdbarch, struct bfd *cbfd, [&] (ULONGEST start, ULONGEST end, ULONGEST file_ofs, const char *filename, const bfd_build_id *build_id) { + /* Ignore anonymous mappings. */ + if (filename == nullptr) + return; + if (!emitter.has_value ()) { gdb_printf (_("Mapped address spaces:\n\n")); diff --git a/gdb/testsuite/gdb.base/corefile-buildid.exp b/gdb/testsuite/gdb.base/corefile-buildid.exp index 09935c2a86e..5207cc15816 100644 --- a/gdb/testsuite/gdb.base/corefile-buildid.exp +++ b/gdb/testsuite/gdb.base/corefile-buildid.exp @@ -21,6 +21,10 @@ standard_testfile .c -shlib-shr.c -shlib.c +# Reuse the Python script from another test. This provides a command +# to remove a particular note from a core file. +set pyfile [gdb_remote_download host ${srcdir}/${subdir}/corefile-no-threads.py] + # Create a corefile from PROGNAME. Return the name of the generated # corefile, or the empty string if anything goes wrong. # @@ -345,6 +349,21 @@ foreach_with_prefix mode { exec shared } { return } + if { [allow_python_tests] } { + # Create a corefile with no NT_FILE notes. + gdb_start + set corefile_no_nt_file [standard_output_file ${corefile}-no-nt_file] + remote_exec build "cp $corefile $corefile_no_nt_file" + gdb_test_no_output "source $::pyfile" "import python scripts" + gdb_test "modify-core-file \"$corefile_no_nt_file\" NT_FILE" \ + [multi_line \ + "Located PT_NOTE segment: \[^\r\n\]+" \ + "\\s+Found note with type $hex at file offset $hex\\..*" \ + "Successfully updated $decimal note\\(s\\) in \[^\r\n\]+"] \ + "update core file" + gdb_exit + } + # Create a directory for the non-stripped test, copy every build # artefact into this directory. set combined_dirname [standard_output_file ${mode}_not-stripped] @@ -394,6 +413,14 @@ foreach_with_prefix mode { exec shared } { locate_exec_from_core_build_id $corefile $dirname \ $build_artefacts $sepdebug $symlink \ [expr {$mode eq "shared"}] + + if { [allow_python_tests] } { + with_test_prefix "NT_FILE removed" { + locate_exec_from_core_build_id $corefile_no_nt_file $dirname \ + $build_artefacts $sepdebug $symlink \ + [expr {$mode eq "shared"}] + } + } } } } diff --git a/gdb/testsuite/gdb.python/py-corefile.py b/gdb/testsuite/gdb.python/py-corefile.py index aadcfc4acb2..de6d85fffc1 100644 --- a/gdb/testsuite/gdb.python/py-corefile.py +++ b/gdb/testsuite/gdb.python/py-corefile.py @@ -56,6 +56,10 @@ def info_proc_mappings(): result = [] for m in mappings: + # Ignore anonymous mappings. + if m.filename == "": + continue + for r in m.regions: result.append(Mapping(m, r))