diff options
-rw-r--r-- | gdb/doc/gdb.texinfo | 41 | ||||
-rw-r--r-- | gdb/dwarf2/index-write.c | 95 | ||||
-rw-r--r-- | gdb/dwarf2/read-debug-names.c | 116 | ||||
-rw-r--r-- | gdb/dwarf2/read-debug-names.h | 25 |
4 files changed, 205 insertions, 72 deletions
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 507598c..88b6c68 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -22976,16 +22976,48 @@ The DWARF specification documents an optional index section called section. However, in order to work with @value{GDBN}, some extensions were necessary. -@value{GDBN} uses the augmentation string @samp{GDB2}. Earlier -versions used the string @samp{GDB}, but these versions of the index -are no longer supported. +@value{GDBN} uses an augmentation string to specify which extensions +are in use and to allow support of backwards-incompatible changes in +this functionality. The augmentation string has the form +@samp{GDB@var{n}}, where @var{n} is an integral version number of the +extensions, which is incremented when the extensions are added or +modified. The smallest @var{n} is 2; earlier versions of +@value{GDBN} used just @samp{GDB} with no version number, but these +versions of the index are no longer supported. + +Here is a list of augmentation string versions along with the changes +introduced with each version, compared to the previous version. + +@table @samp + +@item GDB2 +Specifies the use of attributes @code{DW_IDX_GNU_internal}, +@code{DW_IDX_GNU_main}, @code{DW_IDX_GNU_language} and +@code{DW_IDX_GNU_linkage_name}, described below. + +@item GDB3 +Changes the semantic of the @code{DW_IDX_parent} attribute. +With @samp{GDB2}, @code{DW_IDX_parent} provided an offset into the name +table. With @samp{GDB3}, it now provides an offset to the index entry +of the parent, relative to the start of the entry pool region. + +@end table + +@value{GDBN} produces indexes with the augmentation string @samp{GDB3}. + +@value{GDBN} can read indexes with augmentation strings @samp{GDB2} or +@samp{GDB3}. @value{GDBN} does not support reading indexes with any +other augmentation strings. @value{GDBN} does not use the specified hash table. Therefore, because this hash table is optional, @value{GDBN} also does not write it. -@value{GDBN} also generates and uses some extra index attributes: +@value{GDBN} generates and uses the following non-standard index +attributes: + @table @code + @item DW_IDX_GNU_internal This has the value @samp{0x2000}. It is a flag that, when set, indicates that the associated entry has @code{static} linkage. @@ -23002,6 +23034,7 @@ indicating the language of the associated entry. This has the value @samp{0x2004}. It is a flag that, when set, indicates that the associated entry is a linkage name, and not a source name. + @end table @node Symbol Errors diff --git a/gdb/dwarf2/index-write.c b/gdb/dwarf2/index-write.c index e82d38a..9087add 100644 --- a/gdb/dwarf2/index-write.c +++ b/gdb/dwarf2/index-write.c @@ -130,6 +130,15 @@ public: ::store_unsigned_integer (grow (len), len, byte_order, val); } + /* Accept a host-format integer in VAL and write it in the buffer at offset + OFFSET as a target-format integer which is LEN bytes long. */ + void write_uint (size_t offset, size_t len, bfd_endian byte_order, + ULONGEST val) + { + gdb_assert (offset + len <= m_vec.size ()); + ::store_unsigned_integer (&m_vec[offset], len, byte_order, val); + } + /* Copy VALUE to the end of the buffer, little-endian. */ void append_offset (offset_type value) { @@ -675,10 +684,7 @@ public: if (entry->lang == language_ada && entry->tag == DW_TAG_namespace) return; - const auto insertpair - = m_name_to_value_set.try_emplace (c_str_view (entry->name)); - entry_list &elist = insertpair.first->second; - elist.entries.push_back (entry); + m_name_to_value_set[entry->name].emplace_back (entry); } /* Build all the tables. All symbols must be already inserted. @@ -692,25 +698,16 @@ public: m_name_table_string_offs.reserve (name_count); m_name_table_entry_offs.reserve (name_count); - /* The name table is indexed from 1. The numbers are needed here - so that parent entries can be handled correctly. */ - int next_name = 1; - for (auto &item : m_name_to_value_set) - item.second.index = next_name++; - /* The next available abbrev number. */ int next_abbrev = 1; - for (auto &item : m_name_to_value_set) + for (auto &[name, these_entries] : m_name_to_value_set) { - const c_str_view &name = item.first; - entry_list &these_entries = item.second; - /* Sort the items within each bucket. This ensures that the generated index files will be the same no matter the order in which symbols were added into the index. */ - std::sort (these_entries.entries.begin (), - these_entries.entries.end (), + std::sort (these_entries.begin (), + these_entries.end (), [] (const cooked_index_entry *a, const cooked_index_entry *b) { @@ -730,7 +727,7 @@ public: (m_debugstrlookup.lookup (name.c_str ())); /* ??? */ m_name_table_entry_offs.push_back_reorder (m_entry_pool.size ()); - for (const cooked_index_entry *entry : these_entries.entries) + for (const cooked_index_entry *entry : these_entries) { unit_kind kind = (entry->per_cu->is_debug_types ? unit_kind::tu @@ -778,7 +775,7 @@ public: if (parent != nullptr) { m_abbrev_table.append_unsigned_leb128 (DW_IDX_parent); - m_abbrev_table.append_unsigned_leb128 (DW_FORM_udata); + m_abbrev_table.append_unsigned_leb128 (DW_FORM_data4); } /* Terminate attributes list. */ @@ -786,6 +783,14 @@ public: 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. */ m_entry_pool.append_unsigned_leb128 (idx); const auto it = m_cu_index_htab.find (entry->per_cu); @@ -800,11 +805,11 @@ public: if (parent != nullptr) { - c_str_view par_name (parent->name); - auto name_iter = m_name_to_value_set.find (par_name); - gdb_assert (name_iter != m_name_to_value_set.end ()); - gdb_assert (name_iter->second.index != 0); - m_entry_pool.append_unsigned_leb128 (name_iter->second.index); + 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); } } @@ -814,6 +819,15 @@ public: /* Terminate tags list. */ m_abbrev_table.append_unsigned_leb128 (0); + + /* Write the parent offset values. */ + for (const auto &[reloc_offset, parent] : m_offsets_to_patch) + { + const auto parent_offset_it = m_entry_pool_offsets.find (parent); + gdb_assert (parent_offset_it != m_entry_pool_offsets.cend ()); + m_entry_pool.write_uint (reloc_offset, 4, m_dwarf5_byte_order, + parent_offset_it->second); + } } /* Return .debug_names names count. This must be called only after @@ -1059,15 +1073,26 @@ private: offset_vec_tmpl<OffsetSize> m_name_table_entry_offs; }; - struct entry_list - { - unsigned index = 0; - std::vector<const cooked_index_entry *> entries; - }; + /* Store the index entries for each name. + + Note that we rely on the sorting behavior of map to make the output + stable. */ + std::map<c_str_view, std::vector<const cooked_index_entry *>> + m_name_to_value_set; + + /* Offset at which each entry is written in the entry pool. */ + gdb::unordered_map<const cooked_index_entry *, offset_type> + m_entry_pool_offsets; + + /* The locations where we need to patch offset to entries. + + The first element of the pair is the offset into the pool that needs to + be patched. - /* Store value of each symbol. Note that we rely on the sorting - behavior of map to make the output stable. */ - std::map<c_str_view, entry_list> m_name_to_value_set; + The second element is the entry the offset to which needs to be + patched in. */ + std::vector<std::pair<offset_type, const cooked_index_entry *>> + m_offsets_to_patch; const bfd_endian m_dwarf5_byte_order; dwarf_tmpl<uint32_t> m_dwarf32; @@ -1406,7 +1431,7 @@ write_debug_names (dwarf2_per_bfd *per_bfd, cooked_index *table, const offset_type bytes_of_header = ((dwarf5_is_dwarf64 ? 12 : 4) + 2 + 2 + 7 * 4 - + sizeof (dwarf5_augmentation)); + + sizeof (dwarf5_augmentation_3)); size_t expected_bytes = 0; expected_bytes += bytes_of_header; expected_bytes += cu_list.size (); @@ -1458,9 +1483,9 @@ write_debug_names (dwarf2_per_bfd *per_bfd, cooked_index *table, /* augmentation_string_size - The size in bytes of the augmentation string. This value is rounded up to a multiple of 4. */ - static_assert (sizeof (dwarf5_augmentation) % 4 == 0); - header.append_uint (4, dwarf5_byte_order, sizeof (dwarf5_augmentation)); - header.append_array (dwarf5_augmentation); + static_assert (sizeof (dwarf5_augmentation_3) % 4 == 0); + header.append_uint (4, dwarf5_byte_order, sizeof (dwarf5_augmentation_3)); + header.append_array (dwarf5_augmentation_3); gdb_assert (header.size () == bytes_of_header); diff --git a/gdb/dwarf2/read-debug-names.c b/gdb/dwarf2/read-debug-names.c index 3d73bcd..aeddab66b 100644 --- a/gdb/dwarf2/read-debug-names.c +++ b/gdb/dwarf2/read-debug-names.c @@ -74,7 +74,15 @@ struct mapped_debug_names_reader bfd *abfd = nullptr; bfd_endian dwarf5_byte_order {}; bool dwarf5_is_dwarf64 = false; + + /* True if the augmentation string indicates the index was produced by + GDB. */ bool augmentation_is_gdb = false; + + /* If AUGMENTATION_IS_GDB is true, this indicates the version. Otherwise, + this value is meaningless. */ + unsigned int gdb_augmentation_version = 0; + uint8_t offset_size = 0; uint32_t cu_count = 0; uint32_t tu_count = 0, bucket_count = 0, name_count = 0; @@ -106,7 +114,25 @@ struct mapped_debug_names_reader std::unordered_map<ULONGEST, index_val> abbrev_map; std::unique_ptr<cooked_index_shard> shard; + + /* Maps entry pool offsets to cooked index entries. */ + gdb::unordered_map<ULONGEST, cooked_index_entry *> + entry_pool_offsets_to_entries; + + /* Cooked index entries for which the parent needs to be resolved. + + The second value of the pair is the DW_IDX_parent value. Its meaning + depends on the augmentation string: + + - GDB2: an index in the name table + - GDB3: an offset offset into the entry pool */ std::vector<std::pair<cooked_index_entry *, ULONGEST>> needs_parent; + + /* All the cooked index entries created, in the same order and groups as + listed in the name table. + + The size of the outer vector is equal to the number of entries in the name + table (NAME_COUNT). */ std::vector<std::vector<cooked_index_entry *>> all_entries; }; @@ -121,6 +147,7 @@ mapped_debug_names_reader::scan_one_entry (const char *name, std::optional<ULONGEST> &parent) { unsigned int bytes_read; + const auto offset_in_entry_pool = entry - entry_pool; const ULONGEST abbrev = read_unsigned_leb128 (abfd, entry, &bytes_read); entry += bytes_read; if (abbrev == 0) @@ -239,8 +266,12 @@ mapped_debug_names_reader::scan_one_entry (const char *name, /* Skip if we couldn't find a valid CU/TU index. */ if (per_cu != nullptr) - *result = shard->add (die_offset, (dwarf_tag) indexval.dwarf_tag, flags, - lang, name, nullptr, per_cu); + { + *result = shard->add (die_offset, (dwarf_tag) indexval.dwarf_tag, flags, + lang, name, nullptr, per_cu); + entry_pool_offsets_to_entries.emplace (offset_in_entry_pool, *result); + } + return entry; } @@ -296,19 +327,43 @@ mapped_debug_names_reader::scan_all_names () scan_entries (i, name, entry); } - /* Now update the parent pointers for all entries. This has to be - done in a funny way because DWARF specifies the parent entry to - point to a name -- but we don't know which specific one. */ - for (auto [entry, parent_idx] : needs_parent) + /* Resolve the parent pointers for all entries that have a parent. + + If the augmentation string is "GDB2", the DW_IDX_parent value is an index + into the name table. Since there may be multiple index entries associated + to that name, we have a little heuristic to figure out which is the right + one. + + Otherwise, the DW_IDX_parent value is an offset into the entry pool, which + is not ambiguous. */ + for (auto &[entry, parent_val] : needs_parent) { - /* Name entries are indexed from 1 in DWARF. */ - std::vector<cooked_index_entry *> &entries = all_entries[parent_idx - 1]; - for (const auto &parent : entries) - if (parent->lang == entry->lang) - { - entry->set_parent (parent); - break; - } + if (augmentation_is_gdb && gdb_augmentation_version == 2) + { + /* Name entries are indexed from 1 in DWARF. */ + std::vector<cooked_index_entry *> &entries + = all_entries[parent_val - 1]; + + for (const auto &parent : entries) + if (parent->lang == entry->lang) + { + entry->set_parent (parent); + break; + } + } + else + { + const auto parent_it + = entry_pool_offsets_to_entries.find (parent_val); + + if (parent_it == entry_pool_offsets_to_entries.cend ()) + { + complaint (_ ("Parent entry not found for .debug_names entry")); + continue; + } + + entry->set_parent (parent_it->second); + } } } @@ -404,16 +459,6 @@ check_signatured_type_table_from_debug_names /* DWARF-5 debug_names reader. */ -/* The old, no-longer-supported GDB augmentation. */ -static const gdb_byte old_gdb_augmentation[] - = { 'G', 'D', 'B', 0 }; -static_assert (sizeof (old_gdb_augmentation) % 4 == 0); - -/* DWARF-5 augmentation string for GDB's DW_IDX_GNU_* extension. This - must have a size that is a multiple of 4. */ -const gdb_byte dwarf5_augmentation[8] = { 'G', 'D', 'B', '2', 0, 0, 0, 0 }; -static_assert (sizeof (dwarf5_augmentation) % 4 == 0); - /* A helper function that reads the .debug_names section in SECTION and fills in MAP. FILENAME is the name of the file containing the section; it is used for error reporting. @@ -525,18 +570,24 @@ read_debug_names_from_section (dwarf2_per_objfile *per_objfile, addr += 4; augmentation_string_size += (-augmentation_string_size) & 3; - if (augmentation_string_size == sizeof (old_gdb_augmentation) - && memcmp (addr, old_gdb_augmentation, - sizeof (old_gdb_augmentation)) == 0) + const auto augmentation_string + = gdb::make_array_view (addr, augmentation_string_size); + + if (augmentation_string == gdb::make_array_view (dwarf5_augmentation_1)) { warning (_(".debug_names created by an old version of gdb; ignoring")); return false; } - - map.augmentation_is_gdb = ((augmentation_string_size - == sizeof (dwarf5_augmentation)) - && memcmp (addr, dwarf5_augmentation, - sizeof (dwarf5_augmentation)) == 0); + else if (augmentation_string == gdb::make_array_view (dwarf5_augmentation_2)) + { + map.augmentation_is_gdb = true; + map.gdb_augmentation_version = 2; + } + else if (augmentation_string == gdb::make_array_view (dwarf5_augmentation_3)) + { + map.augmentation_is_gdb = true; + map.gdb_augmentation_version = 3; + } if (!map.augmentation_is_gdb) { @@ -544,6 +595,7 @@ read_debug_names_from_section (dwarf2_per_objfile *per_objfile, return false; } + /* Skip past augmentation string. */ addr += augmentation_string_size; /* List of CUs */ diff --git a/gdb/dwarf2/read-debug-names.h b/gdb/dwarf2/read-debug-names.h index 729fe89..ef91a70 100644 --- a/gdb/dwarf2/read-debug-names.h +++ b/gdb/dwarf2/read-debug-names.h @@ -22,7 +22,30 @@ struct dwarf2_per_objfile; -extern const gdb_byte dwarf5_augmentation[8]; +/* DWARF-5 augmentation strings. + + They must have a size that is a multiple of 4. + + "GDB" is the old, no-longer-supported GDB augmentation. + + The "GDB2" augmentation string specifies the use of the DW_IDX_GNU_* + attributes. + + The meaning of the "GDB3" augmentation string is identical to "GDB2", except + for the meaning of DW_IDX_parent. With "GDB2", DW_IDX_parent represented an + index in the name table. With "GDB3", it represents an offset into the entry + pool. */ + +constexpr gdb_byte dwarf5_augmentation_1[4] = { 'G', 'D', 'B', 0 }; +static_assert (sizeof (dwarf5_augmentation_1) % 4 == 0); + +constexpr gdb_byte dwarf5_augmentation_2[8] + = { 'G', 'D', 'B', '2', 0, 0, 0, 0 }; +static_assert (sizeof (dwarf5_augmentation_2) % 4 == 0); + +constexpr gdb_byte dwarf5_augmentation_3[8] + = { 'G', 'D', 'B', '3', 0, 0, 0, 0 }; +static_assert (sizeof (dwarf5_augmentation_3) % 4 == 0); /* Read .debug_names. If everything went ok, initialize the "quick" elements of all the CUs and return true. Otherwise, return false. */ |