]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Handle inline functions with dwz
authorTom Tromey <tom@tromey.com>
Tue, 22 Oct 2024 22:29:46 +0000 (16:29 -0600)
committerTom Tromey <tom@tromey.com>
Tue, 7 Apr 2026 17:36:10 +0000 (11:36 -0600)
Currently, gdb does not properly handle inline functions when dwz is
used.  This can be seen by running gdb.cp/breakpoint-locs.exp with the
cc-with-dwz target board.

The problem here is that inline functions need special handling in the
dwz case.

First, recall that a DWARF partial unit cannot, in general, be read in
isolation, as it may not have a language.  To handle this, gdb defers
scanning partial units directly, and instead scans them in the context
of some including CU.

Entries coming from the PU are attributed to this reading CU.  If
multiple CUs import a PU, gdb has the reader threads race to see which
one does the actual reading.

However, if an inline function is moved into a partial unit, then that
means it has potentially been inlined somewhere in every including CU.

Thus, when linespec searches for this function, each such including CU
should be expanded.  But because gdb only attributed the function's
index entry to one CU, only that particular one is expanded.

This patch fixes this bug.  All inclusions of a PU are recorded.
Entries coming from a PU are attributed to that PU.  For most entries
coming from the PU, a single "canonical" outer CU is chosen to expand.
However, when an inline function is found, all such CUs are expanded.

A change was also needed to the index writer to handle this case.
There, entries coming from a PU should note the correct including CU.
This must be done because, with .debug_names or .gdb_index, gdb does
not have information about unit imports.  Handling inline functions
here means writing out a separate entry for each outermost CU that
includes the function's PU.

I did consider changing the cooked indexer to create an internal table
more similar to what would be created by the .debug_names (e.g.)
reader: that is, multiple entries for each inline function.  However,
this seemed more expensive at read time, and a main goal of the cooked
indexer is to be fast.

This version updates an assert in the .debug_names writer, and adds a
regression test for that.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30728
Acked-By: Tom de Vries <tdevries@suse.de>
gdb/dwarf2/cooked-index-entry.c
gdb/dwarf2/cooked-index-entry.h
gdb/dwarf2/cooked-index-worker.c
gdb/dwarf2/cooked-index-worker.h
gdb/dwarf2/cooked-indexer.c
gdb/dwarf2/cooked-indexer.h
gdb/dwarf2/index-write.c
gdb/dwarf2/read.c
gdb/dwarf2/read.h
gdb/testsuite/gdb.cp/breakpoint-locs.exp

index c24dcf445fe0d5defd478b0e4943003ef2491f78..2f61e5c4c57bbf91ef4de00b652a3f468e0c0ddc 100644 (file)
@@ -18,6 +18,7 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "dwarf2/cooked-index-entry.h"
+#include "dwarf2/read.h"
 #include "dwarf2/tag.h"
 #include "gdbsupport/selftest.h"
 
@@ -33,6 +34,7 @@ to_string (cooked_index_flag flags)
     MAP_ENUM_FLAG (IS_TYPE_DECLARATION),
     MAP_ENUM_FLAG (IS_PARENT_DEFERRED),
     MAP_ENUM_FLAG (IS_SYNTHESIZED),
+    MAP_ENUM_FLAG (IS_INLINED),
   };
 
   return flags.to_string (mapping);
@@ -240,6 +242,28 @@ cooked_index_entry::write_scope (struct obstack *storage,
   obstack_grow (storage, sep, strlen (sep));
 }
 
+/* See cooked_index_entry.h.  */
+
+void
+cooked_index_entry::force_set_language () const
+{
+  gdb_assert (lang == language_unknown);
+  lang = per_cu->lang ();
+}
+
+/* See cooked-index-entry.h.  */
+
+bool
+cooked_index_entry::visit_defining_cus (per_cu_callback callback) const
+{
+  if ((flags & IS_INLINED) == 0)
+    return callback (per_cu->canonical_outermost_cu ());
+
+  return per_cu->recursively_visit_cus (callback);
+}
+
+/* See cooked-index-entry.h.  */
+
 INIT_GDB_FILE (dwarf2_entry)
 {
 #if GDB_SELF_TEST
index a64b267405600a3754fb5fffc21cbcb2f6353613..9eed2ca9c46c1f84f702356f258884f74d060a76 100644 (file)
@@ -44,6 +44,9 @@ enum cooked_index_flag_enum : unsigned char
   /* True if this entry was synthesized by gdb (as opposed to coming
      directly from the DWARF).  */
   IS_SYNTHESIZED = 32,
+  /* True if this is a function that has DW_AT_inline set in a way
+     that indicates it was inlined.  */
+  IS_INLINED = 64,
 };
 DEF_ENUM_FLAGS_TYPE (enum cooked_index_flag_enum, cooked_index_flag);
 
@@ -222,6 +225,31 @@ struct cooked_index_entry : public allocate_on_obstack<cooked_index_entry>
     return m_parent_entry.deferred;
   }
 
+  /* Force the language to be set to the CU's language.  This may only
+     be called when the language is unknown, which can only happen
+     with .gdb_index.  This method is const because it is called from
+     a location that normally handles const entries.  */
+  void force_set_language () const;
+
+  /* Type of callback used when visiting defining CUs.  */
+  using per_cu_callback = gdb::function_view<bool (dwarf2_per_cu *)>;
+
+  /* Calls CALLBACK for each CU that "defines" this entry.
+
+     If any call to CALLBACK returns false, this immediately returns
+     false (skipping the remaining calls); otherwise returns true.
+
+     For most entries, there is a single defining CU, which is the
+     canonical outermost includer of the CU holding the entry.  In the
+     most typical case (i.e., where "dwz" has not been used), this is
+     just the entry's CU.
+
+     However, conceptually an inlined subroutine is defined in each
+     such outermost includer -- if a subroutine is inlined in many
+     places, and then "dwz" is run, the IS_INLINED subroutine may be
+     defined in some CU that is included by many other CUs.  */
+  bool visit_defining_cus (per_cu_callback callback) const;
+
   /* The name as it appears in DWARF.  This always points into one of
      the mapped DWARF sections.  Note that this may be the name or the
      linkage name -- two entries are created for DIEs which have both
@@ -233,11 +261,16 @@ struct cooked_index_entry : public allocate_on_obstack<cooked_index_entry>
   enum dwarf_tag tag;
   /* Any flags attached to this entry.  */
   cooked_index_flag flags;
-  /* The language of this symbol.  */
-  ENUM_BITFIELD (language) lang : LANGUAGE_BITS;
+  /* The language of this symbol.  This is mutable because there is a
+     situation where the language is initially unknown, and then only
+     filled in later.  In particular this can happen when using
+     .gdb_index.  See also cooked_index_functions::search.  */
+  mutable ENUM_BITFIELD (language) lang : LANGUAGE_BITS;
   /* The offset of this DIE.  */
   sect_offset die_offset;
-  /* The CU from which this entry originates.  */
+  /* The CU from which this entry originates.  This may point to a
+     partial CU, which can't be expanded in isolation; see
+     visit_defining_cus.  */
   dwarf2_per_cu *per_cu;
 
 private:
index ef2427d0a71c96f83d373961bea91c89187cca8b..723e027172e80c3b4deb942663999d61b782a4b2 100644 (file)
@@ -105,6 +105,16 @@ cooked_index_worker_result::emit_complaints_and_exceptions
 
 /* See cooked-index-worker.h.  */
 
+void
+cooked_index_worker_result::invert_cu_inclusions ()
+{
+  for (auto &iter : m_inclusions)
+    for (dwarf2_per_cu *included : iter.second)
+      included->add_includer (iter.first);
+}
+
+/* See cooked-index-worker.h.  */
+
 void
 cooked_index_worker::start ()
 {
@@ -239,6 +249,10 @@ cooked_index_worker::done_reading ()
       m_all_parents_map.add_map (*one_result.get_parent_map ());
   }
 
+  /* Update all the CU inclusion information.  */
+  for (auto &item : m_results)
+    item.invert_cu_inclusions ();
+
   dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd;
   cooked_index *table
     = (gdb::checked_static_cast<cooked_index *>
index d46415bf8bcba045bb23eb51d112d44bc90ccfd3..1799c0b9e08b3f9677c3c23169b7f47204763dce 100644 (file)
@@ -66,17 +66,6 @@ public:
 
   /* Add an entry to the index.  The arguments describe the entry; see
      cooked-index.h.  The new entry is returned.  */
-  cooked_index_entry *add (sect_offset die_offset, enum dwarf_tag tag,
-                          cooked_index_flag flags,
-                          const char *name,
-                          cooked_index_entry_ref parent_entry,
-                          dwarf2_per_cu *per_cu)
-  {
-    return m_shard->add (die_offset, tag, flags, per_cu->lang (),
-                        name, parent_entry, per_cu);
-  }
-
-  /* Overload that allows the language to be specified.  */
   cooked_index_entry *add (sect_offset die_offset, enum dwarf_tag tag,
                           cooked_index_flag flags, enum language lang,
                           const char *name,
@@ -154,6 +143,16 @@ public:
   void emit_complaints_and_exceptions
        (gdb::unordered_set<gdb_exception> &seen_exceptions);
 
+  /* Record that the FROM CU included the TO CU.  */
+  void record_inclusion (dwarf2_per_cu *from, dwarf2_per_cu *to)
+  {
+    m_inclusions[from].push_back (to);
+  }
+
+  /* Update CUs with all the inclusion information that we've
+     discovered.  */
+  void invert_cu_inclusions ();
+
 private:
   /* The abbrev table cache used by this indexer.  */
   abbrev_table_cache m_abbrev_table_cache;
@@ -198,6 +197,11 @@ private:
 
   /* Exceptions that we're storing to emit later.  */
   std::vector<gdb_exception> m_exceptions;
+
+  /* Map from a CU to a list of all the CUs that it directly
+     includes.  */
+  gdb::unordered_map<dwarf2_per_cu *,
+                    std::vector<dwarf2_per_cu *>> m_inclusions;
 };
 
 /* The possible states of the index.  See the explanatory comment
index 230f6408ddbb611ee8670dac3d621e2e23f248dd..f55efc5e3a0f64ef5c35c015f209cce88ebc7d10 100644 (file)
@@ -27,9 +27,8 @@
 /* See cooked-indexer.h.  */
 
 cooked_indexer::cooked_indexer (cooked_index_worker_result *storage,
-                               dwarf2_per_cu *per_cu, enum language language)
+                               enum language language)
   : m_index_storage (storage),
-    m_per_cu (per_cu),
     m_language (language),
     m_die_range_map (storage->get_parent_map ())
 {
@@ -102,6 +101,17 @@ cooked_indexer::ensure_cu_exists (cutu_reader *reader,
      Doing this check here avoids self-imports as well.  */
   if (for_scanning)
     {
+      /* Record the inclusion precisely here.  Inclusions are done to
+        ensure that imported units cause the correct expansions when
+        searched; and in particular that all including units are
+        expanded when finding an inline function.  So, this must be
+        done for all units that are importing "for scanning".
+        However, doing this any later than this spot would be too
+        late -- because we want to do this for all importing units,
+        not just the one that happens to win the race to do the
+        scanning.  */
+      m_index_storage->record_inclusion (reader->cu ()->per_cu, per_cu);
+
       bool nope = false;
       if (!per_cu->scanned.compare_exchange_strong (nope, true))
        return nullptr;
@@ -146,6 +156,7 @@ cooked_indexer::scan_attributes (dwarf2_per_cu *scanning_per_cu,
                                 const cooked_index_entry **parent_entry,
                                 parent_map::addr_type *maybe_defer,
                                 bool *is_enum_class,
+                                bool *is_inlined,
                                 bool for_specification)
 {
   bool is_declaration = false;
@@ -281,6 +292,16 @@ cooked_indexer::scan_attributes (dwarf2_per_cu *scanning_per_cu,
                                  scanning_per_cu, abbrev->tag);
            }
          break;
+
+       case DW_AT_inline:
+         if (abbrev->tag == DW_TAG_subprogram)
+           {
+             std::optional<ULONGEST> val = attr.unsigned_constant ();
+             if (val.has_value () && (*val == DW_INL_inlined
+                                      || *val == DW_INL_declared_inlined))
+               *is_inlined = true;
+           }
+         break;
        }
     }
 
@@ -359,7 +380,7 @@ cooked_indexer::scan_attributes (dwarf2_per_cu *scanning_per_cu,
        scan_attributes (scanning_per_cu, new_reader, new_info_ptr,
                         new_info_ptr, new_abbrev, name, linkage_name,
                         flags, nullptr, parent_entry, maybe_defer,
-                        is_enum_class, true);
+                        is_enum_class, is_inlined, true);
     }
 
   if (!for_specification)
@@ -427,8 +448,7 @@ cooked_indexer::index_imported_unit (cutu_reader *reader,
   if (!target.has_value ())
     return info_ptr;
 
-  cutu_reader *new_reader
-    = ensure_cu_exists (reader, *target, true);
+  cutu_reader *new_reader = ensure_cu_exists (reader, *target, true);
   if (new_reader != nullptr)
     index_dies (new_reader, new_reader->info_ptr (), nullptr, false);
 
@@ -526,6 +546,7 @@ cooked_indexer::index_dies (cutu_reader *reader,
       sect_offset sibling {};
       const cooked_index_entry *this_parent_entry = parent_entry;
       bool is_enum_class = false;
+      bool is_inlined = false;
 
       /* The scope of a DW_TAG_entry_point cooked_index_entry is the one of
         its surrounding subroutine.  */
@@ -534,7 +555,8 @@ cooked_indexer::index_dies (cutu_reader *reader,
       info_ptr
        = scan_attributes (reader->cu ()->per_cu, reader, info_ptr, info_ptr,
                           abbrev, &name, &linkage_name, &flags, &sibling,
-                          &this_parent_entry, &defer, &is_enum_class, false);
+                          &this_parent_entry, &defer, &is_enum_class,
+                          &is_inlined, false);
       /* A DW_TAG_entry_point inherits its static/extern property from
         the enclosing subroutine.  */
       if (abbrev->tag == DW_TAG_entry_point)
@@ -583,18 +605,25 @@ cooked_indexer::index_dies (cutu_reader *reader,
            }
        }
 
+      if (is_inlined)
+       flags |= IS_INLINED;
+
       cooked_index_entry *this_entry = nullptr;
+      /* Always use the reader's CU for the entry CU.  */
+      dwarf2_per_cu *cu_for_entry = reader->cu ()->per_cu;
       if (name != nullptr)
        {
          if (defer != 0)
            this_entry
              = m_index_storage->add (this_die, abbrev->tag,
-                                     flags | IS_PARENT_DEFERRED, name,
-                                     defer, m_per_cu);
+                                     flags | IS_PARENT_DEFERRED,
+                                     m_language, name,
+                                     defer, cu_for_entry);
          else
            this_entry
-             = m_index_storage->add (this_die, abbrev->tag, flags, name,
-                                     this_parent_entry, m_per_cu);
+             = m_index_storage->add (this_die, abbrev->tag, flags,
+                                     m_language, name,
+                                     this_parent_entry, cu_for_entry);
        }
 
       if (linkage_name != nullptr)
@@ -607,11 +636,10 @@ cooked_indexer::index_dies (cutu_reader *reader,
             have linkage name present but name is absent.  */
          if (name != nullptr
              || (abbrev->tag != DW_TAG_subprogram
-                 && abbrev->tag != DW_TAG_inlined_subroutine
                  && abbrev->tag != DW_TAG_entry_point))
            flags = flags | IS_LINKAGE;
-         m_index_storage->add (this_die, abbrev->tag, flags,
-                               linkage_name, nullptr, m_per_cu);
+         m_index_storage->add (this_die, abbrev->tag, flags, m_language,
+                               linkage_name, nullptr, cu_for_entry);
        }
 
       if (abbrev->has_children)
index 5cb5bae33f11c77cca88bd313b073e256f28e810..563742c90f6a56924dbc36da8a0456a3013a413c 100644 (file)
@@ -37,7 +37,7 @@ struct section_and_offset;
 class cooked_indexer
 {
 public:
-  cooked_indexer (cooked_index_worker_result *storage, dwarf2_per_cu *per_cu,
+  cooked_indexer (cooked_index_worker_result *storage,
                  enum language language);
 
   DISABLE_COPY_AND_ASSIGN (cooked_indexer);
@@ -85,6 +85,7 @@ private:
                                   const cooked_index_entry **parent_entry,
                                   parent_map::addr_type *maybe_defer,
                                   bool *is_enum_class,
+                                  bool *is_inlined,
                                   bool for_specification);
 
   /* Handle DW_TAG_imported_unit, by scanning the DIE to find
@@ -104,11 +105,6 @@ private:
 
   /* The storage object, where the results are kept.  */
   cooked_index_worker_result *m_index_storage;
-  /* The CU that we are reading on behalf of.  This object might be
-     asked to index one CU but to treat the results as if they come
-     from some including CU; in this case the including CU would be
-     recorded here.  */
-  dwarf2_per_cu *m_per_cu;
   /* The language that we're assuming when reading.  */
   enum language m_language;
 
index f40e9636a0b2dcb7e9f5429b586d31214040e5a4..82c4645a26549de0653c51910e510fb734dd8351 100644 (file)
@@ -682,6 +682,10 @@ public:
     /* The next available abbrev number.  */
     int next_abbrev = 1;
 
+    /* Storage for the CU list for inline entries.  This is outside
+       the loops to try to avoid repeated allocations.  */
+    std::vector<dwarf2_per_cu *> per_cus;
+
     for (auto &[name, these_entries] : m_name_to_value_set)
       {
        /* Sort the items within each bucket.  This ensures that the
@@ -710,108 +714,137 @@ public:
 
        for (const cooked_index_entry *entry : these_entries)
          {
-           unit_kind kind = (entry->per_cu->is_debug_types ()
-                             ? unit_kind::tu
-                             : unit_kind::cu);
-           /* Some Ada parentage is synthesized by the reader and so
-              must be ignored here.  */
-           const cooked_index_entry *parent = entry->get_parent ();
-           if (parent != nullptr && (parent->flags & IS_SYNTHESIZED) != 0)
-             parent = nullptr;
-
-           int &abbrev = m_indexkey_to_abbrev[index_key (entry->tag,
-                                                         kind,
-                                                         entry->flags,
-                                                         entry->lang,
-                                                         parent != nullptr)];
-           if (abbrev == 0)
+           /* Accumulate all the relevant CUs.  This is needed for
+              the DW_TAG_inlined_subroutine special case.  */
+           per_cus.clear ();
+           entry->visit_defining_cus ([&] (dwarf2_per_cu *one_cu)
+             {
+               per_cus.push_back (one_cu);
+               return true;
+             });
+           /* Make sure the output is stable.  */
+           std::sort (per_cus.begin (), per_cus.end (),
+                      [] (const dwarf2_per_cu *lhs, const dwarf2_per_cu *rhs)
+                      {
+                        return lhs->index < rhs->index;
+                      });
+
+           for (dwarf2_per_cu *one_cu : per_cus)
              {
-               abbrev = next_abbrev++;
+               unit_kind kind = (entry->per_cu->is_debug_types ()
+                                 ? unit_kind::tu
+                                 : unit_kind::cu);
+               /* Some Ada parentage is synthesized by the reader and so
+                  must be ignored here.  */
+               const cooked_index_entry *parent = entry->get_parent ();
+               if (parent != nullptr && (parent->flags & IS_SYNTHESIZED) != 0)
+                 parent = nullptr;
+
+               int &abbrev = m_indexkey_to_abbrev[index_key (entry->tag,
+                                                             kind,
+                                                             entry->flags,
+                                                             entry->lang,
+                                                             parent != nullptr)];
+               if (abbrev == 0)
+                 {
+                   abbrev = next_abbrev++;
+
+                   /* Abbrev number and tag.  */
+                   m_abbrev_table.append_unsigned_leb128 (abbrev);
+                   m_abbrev_table.append_unsigned_leb128 (entry->tag);
+
+                   /* Unit index.  */
+                   m_abbrev_table.append_unsigned_leb128
+                     (kind == unit_kind::cu
+                      ? DW_IDX_compile_unit
+                      : DW_IDX_type_unit);
+                   m_abbrev_table.append_unsigned_leb128 (DW_FORM_udata);
+
+                   /* DIE offset.  */
+                   m_abbrev_table.append_unsigned_leb128 (DW_IDX_die_offset);
+                   m_abbrev_table.append_unsigned_leb128 (DW_FORM_ref_addr);
+
+                   /* Language.  */
+                   m_abbrev_table.append_unsigned_leb128
+                     (DW_IDX_GNU_language);
+                   m_abbrev_table.append_unsigned_leb128 (DW_FORM_udata);
+
+                   /* Internal linkage flag.  */
+                   if (!tag_is_type (entry->tag)
+                       && (entry->flags & IS_STATIC) != 0)
+                     {
+                       m_abbrev_table.append_unsigned_leb128
+                         (DW_IDX_GNU_internal);
+                       m_abbrev_table.append_unsigned_leb128
+                         (DW_FORM_flag_present);
+                     }
+
+                   /* Main subprogram flag.  */
+                   if ((entry->flags & IS_MAIN) != 0)
+                     {
+                       m_abbrev_table.append_unsigned_leb128
+                         (DW_IDX_GNU_main);
+                       m_abbrev_table.append_unsigned_leb128
+                         (DW_FORM_flag_present);
+                     }
+
+                   /* Linkage name flag.  */
+                   if ((entry->flags & IS_LINKAGE) != 0)
+                     {
+                       m_abbrev_table.append_unsigned_leb128
+                         (DW_IDX_GNU_linkage_name);
+                       m_abbrev_table.append_unsigned_leb128
+                         (DW_FORM_flag_present);
+                     }
+
+                   /* Parent offset.  */
+                   if (parent != nullptr)
+                     {
+                       m_abbrev_table.append_unsigned_leb128 (DW_IDX_parent);
+                       m_abbrev_table.append_unsigned_leb128 (DW_FORM_data4);
+                     }
+
+                   /* Terminate attributes list.  */
+                   m_abbrev_table.append_unsigned_leb128 (0);
+                   m_abbrev_table.append_unsigned_leb128 (0);
+                 }
 
-               /* Abbrev number and tag.  */
-               m_abbrev_table.append_unsigned_leb128 (abbrev);
-               m_abbrev_table.append_unsigned_leb128 (entry->tag);
+               /* Record the offset in the pool at which this entry will
+                  reside.  */
+               const auto offset_inserted
+                 = (m_entry_pool_offsets.emplace (entry, m_entry_pool.size ())
+                    .second);
+               gdb_assert (offset_inserted
+                           || (entry->flags & IS_INLINED) != 0);
+
+               /* Write the entry to the pool, starting with the
+                  abbrev number.  */
+               m_entry_pool.append_unsigned_leb128 (abbrev);
 
                /* Unit index.  */
-               m_abbrev_table.append_unsigned_leb128
-                 (kind == unit_kind::cu
-                  ? DW_IDX_compile_unit
-                  : DW_IDX_type_unit);
-               m_abbrev_table.append_unsigned_leb128 (DW_FORM_udata);
+               const auto it = m_cu_index_htab.find (one_cu);
+               gdb_assert (it != m_cu_index_htab.cend ());
+               m_entry_pool.append_unsigned_leb128 (it->second);
 
                /* DIE offset.  */
-               m_abbrev_table.append_unsigned_leb128 (DW_IDX_die_offset);
-               m_abbrev_table.append_unsigned_leb128 (DW_FORM_ref_addr);
+               m_entry_pool.append_uint (dwarf5_offset_size (),
+                                         m_dwarf5_byte_order,
+                                         to_underlying (entry->die_offset));
 
                /* Language.  */
-               m_abbrev_table.append_unsigned_leb128 (DW_IDX_GNU_language);
-               m_abbrev_table.append_unsigned_leb128 (DW_FORM_udata);
-
-               /* Internal linkage flag.  */
-               if (!tag_is_type (entry->tag)
-                   && (entry->flags & IS_STATIC) != 0)
-                 {
-                   m_abbrev_table.append_unsigned_leb128 (DW_IDX_GNU_internal);
-                   m_abbrev_table.append_unsigned_leb128 (DW_FORM_flag_present);
-                 }
-
-               /* Main subprogram flag.  */
-               if ((entry->flags & IS_MAIN) != 0)
-                 {
-                   m_abbrev_table.append_unsigned_leb128 (DW_IDX_GNU_main);
-                   m_abbrev_table.append_unsigned_leb128 (DW_FORM_flag_present);
-                 }
-
-               /* Linkage name flag.  */
-               if ((entry->flags & IS_LINKAGE) != 0)
-                 {
-                   m_abbrev_table.append_unsigned_leb128 (DW_IDX_GNU_linkage_name);
-                   m_abbrev_table.append_unsigned_leb128 (DW_FORM_flag_present);
-                 }
+               m_entry_pool.append_unsigned_leb128
+                 (entry->per_cu->dw_lang ());
 
                /* Parent offset.  */
                if (parent != nullptr)
                  {
-                   m_abbrev_table.append_unsigned_leb128 (DW_IDX_parent);
-                   m_abbrev_table.append_unsigned_leb128 (DW_FORM_data4);
-                 }
-
-               /* Terminate attributes list.  */
-               m_abbrev_table.append_unsigned_leb128 (0);
-               m_abbrev_table.append_unsigned_leb128 (0);
-             }
-
-           /* Record the offset in the pool at which this entry will
-              reside.  */
-           const auto offset_inserted
-             = (m_entry_pool_offsets.emplace (entry, m_entry_pool.size ())
-                .second);
-           gdb_assert (offset_inserted);
-
-           /* Write the entry to the pool, starting with the abbrev number.  */
-           m_entry_pool.append_unsigned_leb128 (abbrev);
-
-           /* Unit index.  */
-           const auto it = m_cu_index_htab.find (entry->per_cu);
-           gdb_assert (it != m_cu_index_htab.cend ());
-           m_entry_pool.append_unsigned_leb128 (it->second);
-
-           /* DIE offset.  */
-           m_entry_pool.append_uint (dwarf5_offset_size (),
-                                     m_dwarf5_byte_order,
-                                     to_underlying (entry->die_offset));
+                   m_offsets_to_patch.emplace_back
+                     (m_entry_pool.size (), parent);
 
-           /* Language.  */
-           m_entry_pool.append_unsigned_leb128 (entry->per_cu->dw_lang ());
-
-           /* Parent offset.  */
-           if (parent != nullptr)
-             {
-               m_offsets_to_patch.emplace_back (m_entry_pool.size (), parent);
-
-               /* Write a dummy number, this gets patched later.  */
-               m_entry_pool.append_uint (4, m_dwarf5_byte_order,
-                                         0xfafafafa);
+                   /* Write a dummy number, this gets patched later.  */
+                   m_entry_pool.append_uint (4, m_dwarf5_byte_order,
+                                             0xfafafafa);
+                 }
              }
          }
 
@@ -1238,9 +1271,6 @@ write_cooked_index (cooked_index *table,
 
   for (const cooked_index_entry *entry : table->all_entries ())
     {
-      const auto it = cu_index_htab.find (entry->per_cu);
-      gdb_assert (it != cu_index_htab.cend ());
-
       const char *name = entry->full_name (symtab->obstack ());
 
       if (entry->lang == language_ada)
@@ -1280,8 +1310,14 @@ write_cooked_index (cooked_index *table,
       else
        kind = GDB_INDEX_SYMBOL_KIND_OTHER;
 
-      symtab->add_index_entry (name, (entry->flags & IS_STATIC) != 0,
-                              kind, it->second);
+      entry->visit_defining_cus ([&] (dwarf2_per_cu *per_cu)
+       {
+         const auto it = cu_index_htab.find (per_cu);
+         gdb_assert (it != cu_index_htab.cend ());
+         symtab->add_index_entry (name, (entry->flags & IS_STATIC) != 0,
+                                  kind, it->second);
+         return true;
+       });
     }
 }
 
index ebf6133e38fcddec6742c57cbc47239826f8074a..4035bba6e45bc98c408c46c59dec39afdd02b25e 100644 (file)
@@ -1578,6 +1578,11 @@ static struct compunit_symtab *
 dw2_instantiate_symtab (dwarf2_per_cu *per_cu, dwarf2_per_objfile *per_objfile,
                        bool skip_partial)
 {
+  /* Expand the corresponding canonical outermost CU.  This will
+     expand the desired CU as a side effect when following the
+     DW_TAG_imported_unit.  */
+  per_cu = per_cu->canonical_outermost_cu ();
+
   if (!per_objfile->compunit_symtab_set_p (per_cu))
     {
       free_cached_comp_units freer (per_objfile);
@@ -3327,7 +3332,7 @@ cooked_index_worker_debug_info::process_unit
       if (this_cu->scanned.compare_exchange_strong (nope, true))
        {
          gdb_assert (storage != nullptr);
-         cooked_indexer indexer (storage, this_cu, reader->cu ()->lang ());
+         cooked_indexer indexer (storage, reader->cu ()->lang ());
          indexer.make_index (reader);
        }
     }
@@ -3347,7 +3352,7 @@ cooked_index_worker_debug_info::process_type_unit
     return;
 
   gdb_assert (storage != nullptr);
-  cooked_indexer indexer (storage, per_cu, cu->lang ());
+  cooked_indexer indexer (storage, cu->lang ());
   indexer.make_index (reader);
 }
 
@@ -14144,15 +14149,15 @@ cooked_index_functions::search
             we see a symbol with an unknown language we find the CU's
             language.  Only the .gdb_index reader creates such
             symbols.  */
-         enum language entry_lang = entry->lang;
-         if (lang_matcher != nullptr || entry_lang == language_unknown)
+         if (lang_matcher != nullptr || entry->lang == language_unknown)
            {
              entry->per_cu->ensure_lang (per_objfile);
              if (lang_matcher != nullptr
                  && !entry->per_cu->maybe_multi_language ()
                  && !lang_matcher (entry->per_cu->lang ()))
                continue;
-             entry_lang = entry->per_cu->lang ();
+             else if (entry->lang == language_unknown)
+               entry->force_set_language ();
            }
 
          /* We've found the base name of the symbol; now walk its
@@ -14161,7 +14166,7 @@ cooked_index_functions::search
          bool found = true;
 
          const cooked_index_entry *parent = entry->get_parent ();
-         const language_defn *lang_def = language_def (entry_lang);
+         const language_defn *lang_def = language_def (entry->lang);
          for (int i = name_vec.size () - 1; i > 0; --i)
            {
              /* If we ran out of entries, or if this segment doesn't
@@ -14217,8 +14222,12 @@ cooked_index_functions::search
          else if (!symbol_matcher (full_name))
            continue;
 
-         if (!search_one (entry->per_cu, per_objfile, cus_to_skip,
-                          file_matcher, listener, nullptr))
+         bool check = entry->visit_defining_cus ([&] (dwarf2_per_cu *per_cu)
+           {
+             return search_one (per_cu, per_objfile, cus_to_skip,
+                                file_matcher, listener, nullptr);
+           });
+         if (!check)
            return false;
        }
     }
@@ -17978,6 +17987,30 @@ dwarf2_per_cu::ensure_lang (dwarf2_per_objfile *per_objfile)
                      true, std::nullopt, abbrev_table_cache);
 }
 
+/* See read.h.  */
+
+bool
+dwarf2_per_cu::recursively_visit_cus (per_cu_callback callback)
+{
+  if (including_cus.empty ())
+    return callback (this);
+  for (dwarf2_per_cu *iter : including_cus)
+    if (!iter->recursively_visit_cus (callback))
+      return false;
+  return true;
+}
+
+/* See read.h.  */
+
+dwarf2_per_cu *
+dwarf2_per_cu::canonical_outermost_cu ()
+{
+  dwarf2_per_cu *iter = this;
+  while (!iter->including_cus.empty ())
+    iter = *iter->including_cus.begin ();
+  return iter;
+}
+
 /* Return the unit from ALL_UNITS that potentially contains TARGET.
 
    Since the unit lengths may not be known yet, this function doesn't check that
index 86f97e7ccf4a39dd528b6410170f870a2434ef47..1150c3cfdcad8a4d6ebaf67f75d4b391b827a555 100644 (file)
@@ -224,6 +224,15 @@ private:
   /* Backlink to the owner of this.  */
   dwarf2_per_bfd *m_per_bfd;
 
+  /* Compare two CUs.  */
+  struct compare
+  {
+    bool operator() (const dwarf2_per_cu *lhs, const dwarf2_per_cu *rhs) const
+    {
+      return lhs->index < rhs->index;
+    }
+  };
+
 public:
   /* The file and directory for this CU.  This is cached so that we
      don't need to re-examine the DWO in some situations.  This may be
@@ -253,6 +262,13 @@ public:
      http://sourceware.org/bugzilla/show_bug.cgi?id=15021.  */
   std::vector<dwarf2_per_cu *> imported_symtabs;
 
+  /* Pointer to all the CUs that include this CU via
+     DW_TAG_imported_unit.  This is used for the special handling of
+     inline functions appearing in partial units.  An ordered set is
+     used to ensure that the canonical CU is the same across
+     invocations of gdb.  */
+  std::set<dwarf2_per_cu *, compare> including_cus;
+
   bool is_debug_types () const
   { return m_is_debug_types; }
 
@@ -404,6 +420,27 @@ public:
 
   /* Free any cached file names.  */
   void free_cached_file_names ();
+
+  /* Indicate that INCLUDER includes this CU via
+     DW_TAG_imported_unit.  */
+  void add_includer (dwarf2_per_cu *includer)
+  {
+    including_cus.emplace (includer);
+  }
+
+  /* Type of callback used when visiting defining CUs.  */
+  using per_cu_callback = gdb::function_view<bool (dwarf2_per_cu *)>;
+
+  /* Calls CALLBACK for each CU that is the outermost includer of
+     ONE_CU.  If ONE_CU has no includers, it calls CALLBACK on ONE_CU.
+     If any call to CALLBACK returns false, this immediately returns
+     false (skipping the remaining calls); otherwise returns true.  */
+  bool recursively_visit_cus (per_cu_callback callback);
+
+  /* Return a canonical outermost CU corresponding to this CU.  If
+     this CU is standalone (not included by other CUs), then this
+     method will simply return 'this'.  */
+  dwarf2_per_cu *canonical_outermost_cu ();
 };
 
 /* Entry in the signatured_types hash table.  */
index 5895cd4549dfa3e1e900127afb1fbcb1c679e9f6..2ab046d3be89c2cb6b671e9035754b43d59c16cf 100644 (file)
@@ -27,3 +27,11 @@ if { [prepare_for_testing "failed to prepare" $testfile "$srcfile $srcfile2"\
 }
 
 gdb_test "break N1::C1::baz" "\\(2 locations\\)"
+
+if {[have_index $binfile] == ""} {
+    set index_dir [host_standard_output_file ""]
+    # This is a regression test for a crash that occurred in the
+    # dwz+inlining series.  See PR symtab/30728.
+    gdb_test_no_output "save gdb-index -dwarf-5 $index_dir" \
+       "save gdb-index"
+}