[PATCH 3.1/6] DWARF-5: .debug_names index producer

Jan Kratochvil
2017-06-09T05:58:58Z

[-- Attachment #1: Type: text/plain, Size: 31 bytes --]

s/std::list/std::forward_list/

[-- Attachment #2: Type: message/rfc822, Size: 41918 bytes --]

From: Jan Kratochvil <jan.kratochvil@redhat.com>
Subject: [PATCH] DWARF-5: .debug_names index producer
Date: Thu, 25 May 2017 15:47:08 +0200

Hi,

there are two FIXME lines I do not have a real answer for.

Also I am not sure if the -dwarf-4 option for former .gdb_index should be named
that way. And contrib/gdb-add-index.sh (incl. cc-with-tweaks.sh) has no
commandline option for that -dwarf-4 GDB option.

Jan

gdb/ChangeLog
2017-05-26 Jan Kratochvil <jan.kratochvil@redhat.com>

* contrib/gdb-add-index.sh (index): Rename to ...
(index4): ... here.
(index5, debugstr, debugstrmerge, debugstrerr): New variables.
Support also .debug_names and .debug_str.
* dwarf2read.c: Include cmath, locale, set, list.
(INDEX_SUFFIX): Rename to ...
(INDEX4_SUFFIX): ... here.
(INDEX5_SUFFIX, DEBUG_STR_SUFFIX): New.
(DataBuf::append_unsigned_leb128, DataBuf::empty)
(DataBuf::operator const char *, DebugNamesNameTable, check_dwarf64_offsets): New.
(write_gdbindex): New from write_psymtabs_to_index code.
(write_debug_names): New.
(write_psymtabs_to_index): New parameter is_dwarf5. Support
filename_str and out_file_str. Move code to write_gdbindex, possibly
call write_debug_names.
(save_gdb_index_command): New parameter -dwarf-4.
(_initialize_dwarf2_read): Document the new parameter -dwarf-4.

gdb/doc/ChangeLog
2017-05-26 Jan Kratochvil <jan.kratochvil@redhat.com>

* gdb.texinfo (Index Files): Document .debug_names and -dwarf-4.
(Index Section Format): Mention .debug_names.
---
gdb/contrib/gdb-add-index.sh | 53 ++-
gdb/doc/gdb.texinfo | 22 +-
gdb/dwarf2read.c | 1004 +++++++++++++++++++++++++++++++++++++-----
3 files changed, 960 insertions(+), 119 deletions(-)

diff --git a/gdb/contrib/gdb-add-index.sh b/gdb/contrib/gdb-add-index.sh
index 0cd4ce3..02b17f0 100755
--- a/gdb/contrib/gdb-add-index.sh
+++ b/gdb/contrib/gdb-add-index.sh
@@ -37,11 +37,15 @@ fi

dir="${file%/*}"
test "$dir" = "$file" && dir="."
-index="${file}.gdb-index"
+index4="${file}.gdb-index"
+index5="${file}.debug_names"
+debugstr="${file}.debug_str"
+debugstrmerge="${file}.debug_str.merge"
+debugstrerr="${file}.debug_str.err"

-rm -f $index
+rm -f $index4 $index5 $debugstr $debugstrmerge $debugstrerr
# Ensure intermediate index file is removed when we exit.
-trap "rm -f $index" 0
+trap "rm -f $index4 $index5 $debugstr $debugstrmerge $debugstrerr" 0

$GDB --batch -nx -iex 'set auto-load no' \
-ex "file $file" -ex "save gdb-index $dir" || {
@@ -57,9 +61,46 @@ $GDB --batch -nx -iex 'set auto-load no' \
# already stripped binary, it's a no-op.
status=0

-if test -f "$index"; then
- $OBJCOPY --add-section .gdb_index="$index" \
- --set-section-flags .gdb_index=readonly "$file" "$file"
+if test -f "$index4" -a -f "$index5"; then
+ echo "$myname: Both index types were created for $file" 1>&2
+ status=1
+elif test -f "$index4" -o -f "$index5"; then
+ if test -f "$index4"; then
+ index="$index4"
+ section=".gdb_index"
+ else
+ index="$index5"
+ section=".debug_names"
+ fi
+ debugstradd=false
+ debugstrupdate=false
+ if test -s "$debugstr"; then
+ if ! $OBJCOPY --dump-section .debug_str="$debugstrmerge" "$file" /dev/null \
+ 2>$debugstrerr; then
+ cat >&2 $debugstrerr
+ exit 1
+ fi
+ if grep -q "can't dump section '.debug_str' - it does not exist" \
+ $debugstrerr; then
+ debugstradd=true
+ else
+ debugstrupdate=true
+ cat >&2 $debugstrerr
+ fi
+ cat "$debugstr" >>"$debugstrmerge"
+ fi
+
+ $OBJCOPY --add-section $section="$index" \
+ --set-section-flags $section=readonly \
+ $(if $debugstradd; then \
+ echo --add-section .debug_str="$debugstrmerge"; \
+ echo --set-section-flags .debug_str=readonly; \
+ fi; \
+ if $debugstrupdate; then \
+ echo --update-section .debug_str="$debugstrmerge"; \
+ fi) \
+ "$file" "$file"
+
status=$?
else
echo "$myname: No index was created for $file" 1>&2
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 9fb70f6..17c0ec8 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -19243,18 +19243,29 @@ using @command{objcopy}.
To create an index file, use the @code{save gdb-index} command:

@table @code
-@item save gdb-index @var{directory}
+@item save gdb-index [-dwarf-4] @var{directory}
@kindex save gdb-index
Create an index file for each symbol file currently known by
@value{GDBN}. Each file is named after its corresponding symbol file,
-with @samp{.gdb-index} appended, and is written into the given
-@var{directory}.
+with @samp{.debug_names} and @samp{.str} (or @samp{.gdb-index}
+from @code{-dwarf-4} option for older index consumers) appended, and is written
+into the given @var{directory}.
@end table

Once you have created an index file you can merge it into your symbol
file, here named @file{symfile}, using @command{objcopy}:

@smallexample
+$ objcopy --dump-section .debug_str=symfile.str.new symfile
+$ cat symfile.str >>symfile.str.new
+$ objcopy --add-section .debug_names=symfile.gdb-index \
+ --set-section-flags .debug_names=readonly \
+ --update-section .debug_str=symfile.str.new symfile symfile
+@end smallexample
+
+Or for @code{-dwarf-4}:
+
+@smallexample
$ objcopy --add-section .gdb_index=symfile.gdb-index \
--set-section-flags .gdb_index=readonly symfile symfile
@end smallexample
@@ -41657,7 +41668,10 @@ of blocks.
@cindex index section format

This section documents the index section that is created by @code{save
-gdb-index} (@pxref{Index Files}). The index section is
+gdb-index -dwarf-4} (@pxref{Index Files}). Currently preferred section is
+@code{.debug_names} as produced by the default @code{save gdb-index} command.
+@code{.debug_names} section is documented in official @code{DWARF-5}
+specification outside of this @value{GDBN} manual. The index section is
DWARF-specific; some knowledge of DWARF is assumed in this
description.

diff --git a/gdb/dwarf2read.c b/gdb/dwarf2read.c
index 12a194a..b7413f3 100644
--- a/gdb/dwarf2read.c
+++ b/gdb/dwarf2read.c
@@ -78,6 +78,10 @@
#include <algorithm>
#include <unordered_set>
#include <unordered_map>
+#include <cmath>
+#include <locale>
+#include <set>
+#include <forward_list>

typedef struct symbol *symbolp;
DEF_VEC_P (symbolp);
@@ -2181,7 +2185,9 @@ attr_value_as_address (struct attribute *attr)
}

/* The suffix for an index file. */
-#define INDEX_SUFFIX ".gdb-index"
+#define INDEX4_SUFFIX ".gdb-index"
+#define INDEX5_SUFFIX ".debug_names"
+#define DEBUG_STR_SUFFIX ".debug_str"

/* Try to locate the sections we need for DWARF 2 debugging
information and return true if we have enough to do something.
@@ -23248,6 +23254,23 @@ public:
std::copy (cstr, cstr + size, append_space (size));
}

+ // Store INPUT as ULEB128 to the end of buffer.
+
+ void
+ append_unsigned_leb128 (ULONGEST input)
+ {
+ for (;;)
+ {
+ gdb_byte output (input & 0x7f);
+ input >>= 7;
+ if (input)
+ output |= 0x80;
+ append_data (output);
+ if (!input)
+ break;
+ }
+ }
+
// Return size of the buffer.

size_t
@@ -23256,6 +23279,14 @@ public:
return vec.size ();
}

+ // Return true iff the buffer has size zero.
+
+ bool
+ empty () const
+ {
+ return vec.empty ();
+ }
+
/* Write the buffer to FILE. */

void
@@ -23413,6 +23444,13 @@ public:
{
return !strcmp (cstr, other.cstr);
}
+
+ // Returned string is only a reference with lifetime of this object.
+
+ operator const char * () const
+ {
+ return cstr;
+ }
};

// Provide std::unordered_map::hasher for CstrView.
@@ -23741,142 +23779,876 @@ recursively_write_psymbols (struct objfile *objfile,
1);
}

-/* Create an index file for OBJFILE in the directory DIR. */
+// DWARF-5 .debug_names builder.
+class DebugNamesNameTable
+{
+private:

-static void
-write_psymtabs_to_index (struct objfile *objfile, const char *dir)
+ // Storage for symbol names mapping them to their .debug_str section offsets.
+ class DebugStrLookup
+ {
+ private:
+ std::unordered_map<CstrView, size_t, CstrViewHasher> str_table;
+ bfd *const abfd;
+
+ // Data to add at the end of .debug_str for new needed symbol names.
+ DataBuf str_add_buf;
+ public:
+
+ // Object costructor to be called for current DWARF2_PER_OBJFILE.
+ // All .debug_str section strings are automatically stored.
+
+ DebugStrLookup () : abfd (dwarf2_per_objfile->objfile->obfd)
+ {
+ dwarf2_read_section (dwarf2_per_objfile->objfile,
+ &dwarf2_per_objfile->str);
+ if (dwarf2_per_objfile->str.buffer == NULL)
+ return;
+ for (const gdb_byte *data = dwarf2_per_objfile->str.buffer;
+ data < (dwarf2_per_objfile->str.buffer
+ + dwarf2_per_objfile->str.size);)
+ {
+ const char *const s (reinterpret_cast<const char *> (data));
+ const auto insertpair
+ (str_table.emplace (CstrView (s),
+ data - dwarf2_per_objfile->str.buffer));
+ if (!insertpair.second)
+ complaint (&symfile_complaints,
+ _("Duplicate string \"%s\" in "
+ ".debug_str section [in module %s]"),
+ s, bfd_get_filename (abfd));
+ data += strlen (s) + 1;
+ }
+ }
+
+ // Return offset of symbol name S in .debug_str section. Add such
+ // symbol to the section end if it does not exist there yet.
+
+ size_t
+ lookup (const char *s)
+ {
+ const auto it (str_table.find (CstrView (s)));
+ if (it != str_table.end ())
+ return it->second;
+ const size_t offset (dwarf2_per_objfile->str.size + str_add_buf.size ());
+ str_table.emplace (CstrView (s), offset);
+ str_add_buf.append_cstr0 (s);
+ return offset;
+ }
+
+ /* Write appended end of .debug_str section to FILE. */
+
+ void
+ file_write (FILE *file) const
+ {
+ str_add_buf.file_write (file);
+ }
+ };
+
+ // Container to map used DWARF tags to their .debug_names abbreviation tags.
+ class IndexKey
+ {
+ public:
+ const int dwarf_tag;
+ const bool is_static;
+ IndexKey (int dwarf_tag_, bool is_static_)
+ : dwarf_tag (dwarf_tag_), is_static (is_static_)
+ {
+ }
+ bool
+ operator == (const IndexKey &other) const
+ {
+ return dwarf_tag == other.dwarf_tag && is_static == other.is_static;
+ }
+ };
+
+ // Provide std::unordered_map::hasher for IndexKey.
+ class IndexKeyHasher
+ {
+ public:
+ size_t
+ operator () (const IndexKey &key) const
+ {
+ return (std::hash<int>() (key.dwarf_tag) << 1) | key.is_static;
+ }
+ };
+
+ // Parameters of one symbol entry.
+ class SymbolValue
+ {
+ public:
+ const int dwarf_tag, cu_index;
+ const bool is_static;
+ SymbolValue (int dwarf_tag_, int cu_index_, bool is_static_)
+ : dwarf_tag (dwarf_tag_), cu_index (cu_index_), is_static (is_static_)
+ {
+ }
+ bool
+ operator < (const SymbolValue &other) const
+ {
+#define X(n) \
+ do \
+ { \
+ if (n < other.n) \
+ return true; \
+ if (n > other.n) \
+ return false; \
+ } \
+ while (0)
+ X (dwarf_tag);
+ X (is_static);
+ X (cu_index);
+#undef X
+ return false;
+ }
+ };
+
+ // Store value of each symbol.
+ std::unordered_map<CstrView, std::set<SymbolValue>, CstrViewHasher>
+ name_to_value_set;
+
+ // Tables of DWARF-5 .debug_names. They are in object file byte order.
+ std::vector<uint32_t> bucket_table;
+ std::vector<uint32_t> hash_table;
+
+ // Abstract base class to unify DWARF-32 and DWARF-64 name table output.
+ class OffsetVec
+ {
+ protected:
+ const bfd_endian dwarf5_byte_order;
+ public:
+ OffsetVec (bfd_endian dwarf5_byte_order_)
+ : dwarf5_byte_order (dwarf5_byte_order_)
+ {
+ }
+
+ // Call std::vector::reserve for NELEM elements.
+ virtual void reserve (size_t nelem) = 0;
+
+ // Call std::vector::push_back with store_unsigned_integer byte
+ // reordering for ELEM.
+ virtual void push_back_reorder (size_t elem) = 0;
+
+ // Return expected output size in bytes.
+ virtual size_t bytes () const = 0;
+
+ // Write name table to FILE.
+ virtual void file_write (FILE *file) const = 0;
+ };
+
+ // Template to unify DWARF-32 and DWARF-64 output.
+ template<class OffsetSize> class OffsetVecTmpl:public OffsetVec
+ {
+ private:
+ std::vector<OffsetSize> vec;
+ public:
+ OffsetVecTmpl (bfd_endian dwarf5_byte_order_)
+ : OffsetVec (dwarf5_byte_order_)
+ {
+ }
+
+ // Implement OffsetVec::reserve.
+ virtual void
+ reserve (size_t nelem) override
+ {
+ vec.reserve (nelem);
+ }
+
+ // Implement OffsetVec::push_back_reorder.
+ virtual void
+ push_back_reorder (size_t elem) override
+ {
+ vec.push_back (elem);
+ // Check for overflow.
+ gdb_assert (vec.back () == elem);
+ store_unsigned_integer (reinterpret_cast<gdb_byte *> (&vec.back ()),
+ sizeof (vec.back ()), dwarf5_byte_order, elem);
+ }
+
+ // Implement OffsetVec::bytes.
+ virtual size_t
+ bytes () const override
+ {
+ return vec.size () * sizeof (vec[0]);
+ }
+
+ // Implement OffsetVec::file_write.
+ virtual void
+ file_write (FILE *file) const override
+ {
+ ::file_write (file, vec);
+ }
+ };
+
+ // Base class to unify DWARF-32 and DWARF-64 .debug_names output
+ // respecting name table width.
+ class Dwarf
+ {
+ public:
+ OffsetVec &name_table_string_offs, &name_table_entry_offs;
+ Dwarf (OffsetVec &name_table_string_offs_,
+ OffsetVec &name_table_entry_offs_)
+ : name_table_string_offs (name_table_string_offs_),
+ name_table_entry_offs (name_table_entry_offs_)
+ {
+ }
+ };
+
+ // Template to unify DWARF-32 and DWARF-64 .debug_names output
+ // respecting name table width.
+ template<class OffsetSize> class DwarfTmpl:public Dwarf
+ {
+ private:
+ OffsetVecTmpl<OffsetSize> name_table_string_offs, name_table_entry_offs;
+ public:
+ DwarfTmpl (bfd_endian dwarf5_byte_order_)
+ : Dwarf(name_table_string_offs, name_table_entry_offs),
+ name_table_string_offs (dwarf5_byte_order_),
+ name_table_entry_offs (dwarf5_byte_order_)
+ {
+ }
+ };
+
+ const bfd_endian dwarf5_byte_order;
+ DwarfTmpl<uint32_t> dwarf32;
+ DwarfTmpl<uint64_t> dwarf64;
+ Dwarf &dwarf;
+ OffsetVec &name_table_string_offs, &name_table_entry_offs;
+ DebugStrLookup debugstrlookup;
+
+ // Map each used .debug_names abbreviation tag parameters to its index value.
+ std::unordered_map<IndexKey, int, IndexKeyHasher> indexkey_to_idx;
+
+ // Next unused .debug_names abbreviation tag for indexkey_to_idx.
+ int idx_next = 1;
+
+ // .debug_names abbreviation table.
+ DataBuf abbrev_table;
+
+ // .debug_names entry pool.
+ DataBuf entry_pool;
+
+ // Symbol name hashing function as specified by DWARF-5.
+ static uint32_t
+ djb_hash (const unsigned char *str)
+ {
+ uint32_t hash (5381);
+ while (int c = *str++)
+ {
+ // FIXME: std::bad_cast for: std::tolower (c, std::locale::classic ())
+ // FIXME: Is unicode supported for symbol names by GDB?
+ hash = hash * 33 + tolower (c);
+ }
+ return hash;
+ }
+
+ // Try to reconstruct original DWARF tag for given partial_symbol.
+ // This function is not DWARF-5 compliant but it is sufficient for GDB
+ // as a DWARF-5 index consumer.
+ static int
+ psymbol_tag (const struct partial_symbol *psym)
+ {
+ domain_enum domain = PSYMBOL_DOMAIN (psym);
+ enum address_class aclass = PSYMBOL_CLASS (psym);
+
+ switch (domain)
+ {
+ case VAR_DOMAIN:
+ switch (aclass)
+ {
+ case LOC_BLOCK:
+ return DW_TAG_subprogram;
+ case LOC_TYPEDEF:
+ return DW_TAG_typedef;
+ case LOC_COMPUTED:
+ case LOC_CONST_BYTES:
+ case LOC_OPTIMIZED_OUT:
+ case LOC_STATIC:
+ return DW_TAG_variable;
+ case LOC_CONST:
+ /* Note: It's currently impossible to recognize psyms as enum values
+ short of reading the type info. For now punt. */
+ return DW_TAG_variable;
+ default:
+ /* There are other LOC_FOO values that one might want to classify
+ as variables, but dwarf2read.c doesn't currently use them. */
+ return DW_TAG_variable;
+ }
+ case STRUCT_DOMAIN:
+ return DW_TAG_structure_type;
+ default:
+ return 0;
+ }
+ }
+
+public:
+ DebugNamesNameTable (bool is_dwarf64, bfd_endian dwarf5_byte_order_)
+ : dwarf5_byte_order (dwarf5_byte_order_), dwarf32 (dwarf5_byte_order_),
+ dwarf64 (dwarf5_byte_order_),
+ dwarf (is_dwarf64 ? static_cast<Dwarf &> (dwarf64)
+ : static_cast<Dwarf &> (dwarf32)),
+ name_table_string_offs (dwarf.name_table_string_offs),
+ name_table_entry_offs (dwarf.name_table_entry_offs)
+ {
+ }
+
+ // Insert one symbol.
+
+ void
+ insert (const partial_symbol *psym, int cu_index, bool is_static)
+ {
+ const int dwarf_tag (psymbol_tag (psym));
+ if (!dwarf_tag)
+ return;
+ const char *const name (SYMBOL_SEARCH_NAME (psym));
+ const auto insertpair (name_to_value_set.emplace (CstrView (name),
+ std::set<SymbolValue> ()));
+ std::set<SymbolValue> &value_set (insertpair.first->second);
+ value_set.emplace (SymbolValue (dwarf_tag, cu_index, is_static));
+ }
+
+ // Build all the tables. All symbols must be already inserted.
+ // This function does not call file_write, caller has to do it
+ // afterwards.
+
+ void
+ build ()
+ {
+ // Verify the build method has not be called twice.
+ gdb_assert (abbrev_table.empty ());
+ const size_t name_count (name_to_value_set.size ());
+ bucket_table.resize
+ (std::pow (2, std::ceil (std::log2 (name_count * 4 / 3))));
+ hash_table.reserve (name_count);
+ name_table_string_offs.reserve (name_count);
+ name_table_entry_offs.reserve (name_count);
+
+ // Map each hash of symbol to its name and value.
+ class HashItPair
+ {
+ public:
+ uint32_t hash;
+ decltype (name_to_value_set)::const_iterator it;
+ };
+ std::vector<std::forward_list<HashItPair>> bucket_hash;
+ bucket_hash.resize (bucket_table.size ());
+ for (decltype (name_to_value_set)::const_iterator it =
+ name_to_value_set.cbegin ();
+ it != name_to_value_set.cend (); ++it)
+ {
+ const char *const name (it->first);
+ const unsigned char *const nameuc
+ (reinterpret_cast<const unsigned char *> (name));
+ const uint32_t hash (djb_hash (nameuc));
+ HashItPair hashitpair;
+ hashitpair.hash = hash;
+ hashitpair.it = it;
+ bucket_hash[hash % bucket_hash.size()].push_front
+ (std::move (hashitpair));
+ }
+ for (size_t bucket_ix = 0; bucket_ix < bucket_hash.size (); ++bucket_ix)
+ {
+ const std::forward_list<HashItPair> &hashitlist
+ (bucket_hash[bucket_ix]);
+ if (hashitlist.empty ())
+ continue;
+ uint32_t &bucket_slot (bucket_table[bucket_ix]);
+ // The hashes array is indexed starting at 1.
+ store_unsigned_integer (reinterpret_cast<gdb_byte *> (&bucket_slot),
+ sizeof (bucket_slot), dwarf5_byte_order,
+ hash_table.size () + 1);
+ for (const HashItPair &hashitpair:hashitlist)
+ {
+ hash_table.push_back (0);
+ store_unsigned_integer (reinterpret_cast<gdb_byte *>
+ (&hash_table.back ()),
+ sizeof (hash_table.back ()), dwarf5_byte_order,
+ hashitpair.hash);
+ const CstrView &name (hashitpair.it->first);
+ const std::set<SymbolValue> &value_set (hashitpair.it->second);
+ name_table_string_offs.push_back_reorder (debugstrlookup.lookup (name));
+ name_table_entry_offs.push_back_reorder (entry_pool.size ());
+ gdb_assert (!value_set.empty ());
+ for (const SymbolValue &value:value_set)
+ {
+ int &idx (indexkey_to_idx[IndexKey (value.dwarf_tag,
+ value.is_static)]);
+ if (!idx) {
+ idx = idx_next++;
+ abbrev_table.append_unsigned_leb128 (idx);
+ abbrev_table.append_unsigned_leb128 (value.dwarf_tag);
+ abbrev_table.append_unsigned_leb128 (DW_IDX_compile_unit);
+ abbrev_table.append_unsigned_leb128 (DW_FORM_udata);
+ abbrev_table.append_unsigned_leb128 (value.is_static
+ ? DW_IDX_GNU_static
+ : DW_IDX_GNU_external);
+ abbrev_table.append_unsigned_leb128 (DW_FORM_flag_present);
+
+ // Terminate attributes list.
+ abbrev_table.append_unsigned_leb128 (0);
+ abbrev_table.append_unsigned_leb128 (0);
+ }
+
+ entry_pool.append_unsigned_leb128 (idx);
+ entry_pool.append_unsigned_leb128 (value.cu_index);
+ }
+
+ // Terminate the list of CUs.
+ entry_pool.append_unsigned_leb128 (0);
+ }
+ }
+ gdb_assert (hash_table.size () == name_count);
+
+ // Terminate tags list.
+ abbrev_table.append_unsigned_leb128 (0);
+ }
+
+ // Return .debug_names bucket count.
+ // It must be called only after calling build method.
+
+ uint32_t
+ bucket_count () const
+ {
+ // Verify the build method has been already called.
+ gdb_assert (!abbrev_table.empty ());
+ const uint32_t retval (bucket_table.size ());
+
+ // Check for overflow; to use boost::numeric_cast.
+ gdb_assert (retval == bucket_table.size ());
+ return retval;
+ }
+
+ // Return .debug_names names count.
+ // It must be called only after calling build method.
+
+ uint32_t
+ name_count () const
+ {
+ // Verify the build method has been already called.
+ gdb_assert (!abbrev_table.empty ());
+ const uint32_t retval (hash_table.size ());
+
+ // Check for overflow; to use boost::numeric_cast.
+ gdb_assert (retval == hash_table.size ());
+ return retval;
+ }
+
+ // Return number of bytes of .debug_names abbreviation table.
+ // It must be called only after calling build method.
+
+ uint32_t
+ abbrev_table_bytes () const
+ {
+ gdb_assert (!abbrev_table.empty ());
+ return abbrev_table.size ();
+ }
+
+private:
+
+ // Call insert for all partial symbols and mark them in PSYMS_SEEN.
+
+ void
+ write_psymbols (std::unordered_set<partial_symbol *> &psyms_seen,
+ struct partial_symbol **psymp, int count, int cu_index,
+ bool is_static)
+ {
+ for (; count-- > 0; ++psymp)
+ {
+ struct partial_symbol *psym = *psymp;
+
+ if (SYMBOL_LANGUAGE (psym) == language_ada)
+ error (_("Ada is not currently supported by the index"));
+
+ /* Only add a given psymbol once. */
+ if (psyms_seen.insert (psym).second)
+ insert (psym, cu_index, is_static);
+ }
+ }
+
+public:
+
+ // Recurse into all "included" dependencies and store their symbols as
+ // if they appeared in this psymtab.
+
+ void
+ recursively_write_psymbols (struct objfile *objfile,
+ struct partial_symtab *psymtab,
+ std::unordered_set<partial_symbol *> &psyms_seen,
+ int cu_index)
+ {
+ int i;
+
+ for (i = 0; i < psymtab->number_of_dependencies; ++i)
+ if (psymtab->dependencies[i]->user != NULL)
+ recursively_write_psymbols (objfile, psymtab->dependencies[i],
+ psyms_seen, cu_index);
+
+ write_psymbols (psyms_seen,
+ objfile->global_psymbols.list + psymtab->globals_offset,
+ psymtab->n_global_syms, cu_index, false);
+ write_psymbols (psyms_seen,
+ objfile->static_psymbols.list + psymtab->statics_offset,
+ psymtab->n_static_syms, cu_index, true);
+ }
+
+public:
+
+ // Return number of bytes the .debug_names section will have.
+ // It must be called only after calling build method.
+
+ size_t
+ bytes () const
+ {
+ // Verify the build method has been already called.
+ gdb_assert (!abbrev_table.empty ());
+ size_t expected_bytes (0);
+ expected_bytes += bucket_table.size () * sizeof (bucket_table[0]);
+ expected_bytes += hash_table.size () * sizeof (hash_table[0]);
+ expected_bytes += name_table_string_offs.bytes ();
+ expected_bytes += name_table_entry_offs.bytes ();
+ expected_bytes += abbrev_table.size ();
+ expected_bytes += entry_pool.size ();
+ return expected_bytes;
+ }
+
+ // Write .debug_names to FILE and .debug_str addition to FILE_STR.
+ // It must be called only after calling build method.
+
+ void
+ file_write (FILE *file, FILE *file_str) const
+ {
+ // Verify the build method has been already called.
+ gdb_assert (!abbrev_table.empty ());
+ ::file_write (file, bucket_table);
+ ::file_write (file, hash_table);
+ name_table_string_offs.file_write (file);
+ name_table_entry_offs.file_write (file);
+ abbrev_table.file_write (file);
+ entry_pool.file_write (file);
+ debugstrlookup.file_write (file_str);
+ }
+};
+
+// Return iff any of the needed offsets does not fit into 32-bit
+// .debug_names section.
+
+static bool
+check_dwarf64_offsets ()
{
- if (dwarf2_per_objfile->using_index)
- error (_("Cannot use an index to create the index"));
+ for (int i = 0; i < dwarf2_per_objfile->n_comp_units; ++i)
+ {
+ const dwarf2_per_cu_data &per_cu (*dwarf2_per_objfile->all_comp_units[i]);

- if (VEC_length (dwarf2_section_info_def, dwarf2_per_objfile->types) > 1)
- error (_("Cannot make an index when the file has multiple .debug_types sections"));
+ if (to_underlying (per_cu.sect_off) >= (static_cast<uint64_t> (1) << 32))
+ return true;
+ }
+ for (int i = 0; i < dwarf2_per_objfile->n_type_units; ++i)
+ {
+ const signatured_type &sigtype (*dwarf2_per_objfile->all_type_units[i]);
+ const dwarf2_per_cu_data &per_cu (sigtype.per_cu);

- if (!objfile->psymtabs || !objfile->psymtabs_addrmap)
- return;
+ if (to_underlying (per_cu.sect_off) >= (static_cast<uint64_t> (1) << 32))
+ return true;
+ }
+ return false;
+}

- struct stat st;
- if (stat (objfile_name (objfile), &st) < 0)
- perror_with_name (objfile_name (objfile));
+// Write new .gdb_index section for OBJFILE into OUT_FILE.
+// OUT_FILE_STR is unused.
+// Return how many bytes were expected to be written into OUT_FILE.

- std::string filename (std::string (dir) + SLASH_STRING
- + lbasename (objfile_name (objfile)) + INDEX_SUFFIX);
+static size_t
+write_gdbindex (struct objfile *objfile, FILE *out_file, FILE *out_file_str)
+{
+ mapped_symtab symtab;
+ DataBuf cu_list;
+ std::unordered_set<partial_symbol *> psyms_seen;

- FILE *out_file (gdb_fopen_cloexec (filename.c_str (), "wb"));
- if (!out_file)
- error (_("Can't open `%s' for writing"), filename.c_str ());
+ /* While we're scanning CU's create a table that maps a psymtab pointer
+ (which is what addrmap records) to its index (which is what is recorded
+ in the index file). This will later be needed to write the address
+ table. */
+ std::unordered_map<struct partial_symtab *, unsigned int> cu_index_htab;
+ cu_index_htab.reserve (dwarf2_per_objfile->n_comp_units);

- TRY
+ /* The CU list is already sorted, so we don't need to do additional
+ work here. Also, the debug_types entries do not appear in
+ all_comp_units, but only in their own hash table. */
+ for (int i = 0; i < dwarf2_per_objfile->n_comp_units; ++i)
{
- mapped_symtab symtab;
- DataBuf cu_list;
- std::unordered_set<partial_symbol *> psyms_seen;
-
- /* While we're scanning CU's create a table that maps a psymtab pointer
- (which is what addrmap records) to its index (which is what is recorded
- in the index file). This will later be needed to write the address
- table. */
- std::unordered_map<struct partial_symtab *, unsigned int> cu_index_htab;
- cu_index_htab.reserve (dwarf2_per_objfile->n_comp_units);
-
- /* The CU list is already sorted, so we don't need to do additional
- work here. Also, the debug_types entries do not appear in
- all_comp_units, but only in their own hash table. */
- for (int i = 0; i < dwarf2_per_objfile->n_comp_units; ++i)
- {
- struct dwarf2_per_cu_data *per_cu
- = dwarf2_per_objfile->all_comp_units[i];
- struct partial_symtab *psymtab = per_cu->v.psymtab;
-
- /* CU of a shared file from 'dwz -m' may be unused by this main file.
- It may be referenced from a local scope but in such case it does
- not need to be present in .gdb_index. */
- if (psymtab == NULL)
- continue;
+ struct dwarf2_per_cu_data *per_cu
+ = dwarf2_per_objfile->all_comp_units[i];
+ struct partial_symtab *psymtab = per_cu->v.psymtab;
+
+ /* CU of a shared file from 'dwz -m' may be unused by this main file.
+ It may be referenced from a local scope but in such case it does
+ not need to be present in .gdb_index. */
+ if (psymtab == NULL)
+ continue;

- if (psymtab->user == NULL)
- recursively_write_psymbols (objfile, psymtab, &symtab, psyms_seen,
- i);
+ if (psymtab->user == NULL)
+ recursively_write_psymbols (objfile, psymtab, &symtab, psyms_seen,
+ i);

- const auto insertpair (cu_index_htab.emplace (psymtab, i));
- gdb_assert (insertpair.second);
+ const auto insertpair (cu_index_htab.emplace (psymtab, i));
+ gdb_assert (insertpair.second);

- store_unsigned_integer (cu_list.append_space (8), 8,
- BFD_ENDIAN_LITTLE,
- to_underlying (per_cu->sect_off));
- store_unsigned_integer (cu_list.append_space (8), 8,
- BFD_ENDIAN_LITTLE, per_cu->length);
- }
+ store_unsigned_integer (cu_list.append_space (8), 8,
+ BFD_ENDIAN_LITTLE,
+ to_underlying (per_cu->sect_off));
+ store_unsigned_integer (cu_list.append_space (8), 8,
+ BFD_ENDIAN_LITTLE, per_cu->length);
+ }

- /* Dump the address map. */
- DataBuf addr_vec;
- write_address_map (objfile, addr_vec, cu_index_htab);
+ /* Dump the address map. */
+ DataBuf addr_vec;
+ write_address_map (objfile, addr_vec, cu_index_htab);

- /* Write out the .debug_type entries, if any. */
- DataBuf types_cu_list;
- if (dwarf2_per_objfile->signatured_types)
- {
- struct signatured_type_index_data sig_data (types_cu_list,
- psyms_seen);
-
- sig_data.objfile = objfile;
- sig_data.symtab = &symtab;
- sig_data.cu_index = dwarf2_per_objfile->n_comp_units;
- htab_traverse_noresize (dwarf2_per_objfile->signatured_types,
- write_one_signatured_type, &sig_data);
- }
+ /* Write out the .debug_type entries, if any. */
+ DataBuf types_cu_list;
+ if (dwarf2_per_objfile->signatured_types)
+ {
+ struct signatured_type_index_data sig_data (types_cu_list,
+ psyms_seen);
+
+ sig_data.objfile = objfile;
+ sig_data.symtab = &symtab;
+ sig_data.cu_index = dwarf2_per_objfile->n_comp_units;
+ htab_traverse_noresize (dwarf2_per_objfile->signatured_types,
+ write_one_signatured_type, &sig_data);
+ }
+
+ /* Now that we've processed all symbols we can shrink their cu_indices
+ lists. */
+ uniquify_cu_indices (&symtab);
+
+ DataBuf symtab_vec, constant_pool;
+ write_hash_table (&symtab, symtab_vec, constant_pool);
+
+ const offset_type size_of_header (6 * sizeof (offset_type));
+ size_t total_len (size_of_header);
+ DataBuf header;
+
+ /* The version number. */
+ header.append_data (MAYBE_SWAP (8));
+
+ /* The offset of the CU list from the start of the file. */
+ header.append_data (MAYBE_SWAP (total_len));
+ total_len += cu_list.size ();
+
+ /* The offset of the types CU list from the start of the file. */
+ header.append_data (MAYBE_SWAP (total_len));
+ total_len += types_cu_list.size ();
+
+ /* The offset of the address table from the start of the file. */
+ header.append_data (MAYBE_SWAP (total_len));
+ total_len += addr_vec.size ();
+
+ /* The offset of the symbol table from the start of the file. */
+ header.append_data (MAYBE_SWAP (total_len));
+ total_len += symtab_vec.size ();
+
+ /* The offset of the constant pool from the start of the file. */
+ header.append_data (MAYBE_SWAP (total_len));
+ total_len += constant_pool.size ();
+
+ gdb_assert (header.size () == size_of_header);
+
+ header.file_write (out_file);
+ cu_list.file_write (out_file);
+ types_cu_list.file_write (out_file);
+ addr_vec.file_write (out_file);
+ symtab_vec.file_write (out_file);
+ constant_pool.file_write (out_file);
+
+ return total_len;
+}
+
+// Write new .debug_names section for OBJFILE into OUT_FILE,
+// write needed addition to .debug_str section to OUT_FILE_STR.
+// Return how many bytes were expected to be written into OUT_FILE.
+
+static size_t
+write_debug_names (struct objfile *objfile, FILE *out_file, FILE *out_file_str)
+{
+ const bool dwarf5_is_dwarf64 (check_dwarf64_offsets ());
+ const int dwarf5_offset_size (dwarf5_is_dwarf64 ? 8 : 4);
+ const enum bfd_endian dwarf5_byte_order
+ (gdbarch_byte_order (get_objfile_arch (objfile)));
+
+ /* The CU list is already sorted, so we don't need to do additional
+ work here. Also, the debug_types entries do not appear in
+ all_comp_units, but only in their own hash table. */
+ DataBuf cu_list;
+ DebugNamesNameTable nametable (dwarf5_is_dwarf64, dwarf5_byte_order);
+ std::unordered_set<partial_symbol *> psyms_seen;
+ for (int i = 0; i < dwarf2_per_objfile->n_comp_units; ++i)
+ {
+ const dwarf2_per_cu_data *per_cu (dwarf2_per_objfile->all_comp_units[i]);
+ partial_symtab *psymtab = per_cu->v.psymtab;
+
+ /* CU of a shared file from 'dwz -m' may be unused by this main file.
+ It may be referenced from a local scope but in such case it does
+ not need to be present in .gdb_index. */
+ if (psymtab == NULL)
+ continue;
+
+ if (psymtab->user == NULL)
+ nametable.recursively_write_psymbols (objfile, psymtab, psyms_seen, i);
+
+ store_unsigned_integer (cu_list.append_space (dwarf5_offset_size),
+ dwarf5_offset_size, dwarf5_byte_order,
+ to_underlying (per_cu->sect_off));
+ }
+ nametable.build ();
+
+ /* No addr_vec - DWARF-5 uses .debug_aranges genereated by GCC. */
+
+ DataBuf types_cu_list;
+ for (int i = 0; i < dwarf2_per_objfile->n_type_units; ++i)
+ {
+ const signatured_type &sigtype
+ (*dwarf2_per_objfile->all_type_units[i]);
+ const dwarf2_per_cu_data &per_cu (sigtype.per_cu);
+
+ store_unsigned_integer (types_cu_list.append_space (dwarf5_offset_size),
+ dwarf5_offset_size, dwarf5_byte_order,
+ to_underlying (per_cu.sect_off));
+ }
+
+ const gdb_byte augmentation[] = { 'G', 'D', 'B', 0 };
+ const offset_type bytes_of_header ((dwarf5_is_dwarf64 ? 12 : 4)
+ + 2 + 2 + 7 * 4 + sizeof (augmentation));
+ size_t expected_bytes (0);
+ expected_bytes += bytes_of_header;
+ expected_bytes += cu_list.size ();
+ expected_bytes += types_cu_list.size ();
+ expected_bytes += nametable.bytes ();
+ DataBuf header;
+
+ if (!dwarf5_is_dwarf64)
+ {
+ const uint64_t size64 (expected_bytes - 4);
+ gdb_assert (size64 < 0xfffffff0);
+ store_unsigned_integer (header.append_space (4), 4, dwarf5_byte_order,
+ size64);
+ }
+ else
+ {
+ store_unsigned_integer (header.append_space (4), 4, dwarf5_byte_order,
+ 0xffffffff);
+ store_unsigned_integer (header.append_space (8), 8, dwarf5_byte_order,
+ expected_bytes - 12);
+ }
+
+ /* The version number. */
+ store_unsigned_integer (header.append_space (2), 2, dwarf5_byte_order, 5);
+
+ /* Padding. */
+ store_unsigned_integer (header.append_space (2), 2, dwarf5_byte_order, 0);
+
+ /* comp_unit_count - The number of CUs in the CU list. */
+ store_unsigned_integer (header.append_space (4), 4, dwarf5_byte_order,
+ dwarf2_per_objfile->n_comp_units);
+
+ /* local_type_unit_count - The number of TUs
+ in the local TU list. */
+ store_unsigned_integer (header.append_space (4), 4, dwarf5_byte_order,
+ dwarf2_per_objfile->n_type_units);

- /* Now that we've processed all symbols we can shrink their cu_indices
- lists. */
- uniquify_cu_indices (&symtab);
+ /* foreign_type_unit_count - The number of TUs
+ in the foreign TU list. */
+ store_unsigned_integer (header.append_space (4), 4, dwarf5_byte_order, 0);

- DataBuf symtab_vec, constant_pool;
- write_hash_table (&symtab, symtab_vec, constant_pool);
+ /* bucket_count - The number of hash buckets
+ in the hash lookup table. */
+ store_unsigned_integer (header.append_space (4), 4, dwarf5_byte_order,
+ nametable.bucket_count ());
+
+ /* name_count - The number of unique names in the index. */
+ store_unsigned_integer (header.append_space (4), 4, dwarf5_byte_order,
+ nametable.name_count ());
+
+ /* abbrev_table_size - The size in bytes
+ of the abbreviations table. */
+ store_unsigned_integer (header.append_space (4), 4, dwarf5_byte_order,
+ nametable.abbrev_table_bytes ());
+
+ /* augmentation_string_size - The size in bytes of the augmentation
+ string. This value is rounded up to a multiple of 4. */
+ static_assert (sizeof (augmentation) % 4 == 0);
+ store_unsigned_integer (header.append_space (4), 4, dwarf5_byte_order,
+ sizeof (augmentation));
+ header.append_data (augmentation);
+
+ gdb_assert (header.size () == bytes_of_header);
+
+ header.file_write (out_file);
+ cu_list.file_write (out_file);
+ types_cu_list.file_write (out_file);
+ nametable.file_write (out_file, out_file_str);
+
+ return expected_bytes;
+}
+
+/* Create an index file for OBJFILE in the directory DIR. */

- DataBuf contents;
- const offset_type size_of_contents (6 * sizeof (offset_type));
- offset_type total_len (size_of_contents);
+static void
+write_psymtabs_to_index (struct objfile *objfile, const char *dir, bool is_dwarf5)
+{
+ if (dwarf2_per_objfile->using_index)
+ error (_("Cannot use an index to create the index"));

- /* The version number. */
- contents.append_data (MAYBE_SWAP (8));
+ if (VEC_length (dwarf2_section_info_def, dwarf2_per_objfile->types) > 1)
+ error (_("Cannot make an index when the file has multiple .debug_types sections"));

- /* The offset of the CU list from the start of the file. */
- contents.append_data (MAYBE_SWAP (total_len));
- total_len += cu_list.size ();
+ if (!objfile->psymtabs || !objfile->psymtabs_addrmap)
+ return;

- /* The offset of the types CU list from the start of the file. */
- contents.append_data (MAYBE_SWAP (total_len));
- total_len += types_cu_list.size ();
+ struct stat st;
+ if (stat (objfile_name (objfile), &st) < 0)
+ perror_with_name (objfile_name (objfile));

- /* The offset of the address table from the start of the file. */
- contents.append_data (MAYBE_SWAP (total_len));
- total_len += addr_vec.size ();
+ std::string filename (std::string (dir) + SLASH_STRING
+ + lbasename (objfile_name (objfile))
+ + (is_dwarf5 ? INDEX5_SUFFIX : INDEX4_SUFFIX));
+ std::string filename_str (std::string (dir) + SLASH_STRING
+ + lbasename (objfile_name (objfile))
+ + DEBUG_STR_SUFFIX);

- /* The offset of the symbol table from the start of the file. */
- contents.append_data (MAYBE_SWAP (total_len));
- total_len += symtab_vec.size ();
+ FILE *out_file (NULL);
+ FILE *out_file_str (NULL);

- /* The offset of the constant pool from the start of the file. */
- contents.append_data (MAYBE_SWAP (total_len));
- total_len += constant_pool.size ();
+ TRY
+ {
+ out_file = (gdb_fopen_cloexec (filename.c_str (), "wb"));
+ if (!out_file)
+ error (_("Can't open `%s' for writing"), filename.c_str ());

- gdb_assert (contents.size () == size_of_contents);
+ out_file_str = (gdb_fopen_cloexec (filename_str.c_str (), "wb"));
+ if (!out_file_str)
+ error (_("Can't open `%s' for writing"), filename_str.c_str ());

- contents.file_write (out_file);
- cu_list.file_write (out_file);
- types_cu_list.file_write (out_file);
- addr_vec.file_write (out_file);
- symtab_vec.file_write (out_file);
- constant_pool.file_write (out_file);
+ const size_t total_len ((is_dwarf5 ? write_debug_names : write_gdbindex)
+ (objfile, out_file, out_file_str));
+ const auto out_file_size (ftell (out_file));
+ if (out_file_size == -1)
+ error (_("Can't get `%s' size"), filename.c_str ());
+ gdb_assert (out_file_size == total_len);
}
CATCH (except, RETURN_MASK_ALL)
{
- fclose (out_file);
+ if (out_file != NULL)
+ fclose (out_file);
unlink (filename.c_str ());
+ if (out_file_str != NULL)
+ fclose (out_file_str);
+ unlink (filename_str.c_str ());
throw_exception (except);
}
END_CATCH
fclose (out_file);
+ fclose (out_file_str);
}

/* Implementation of the `save gdb-index' command.
@@ -23885,12 +24657,26 @@ write_psymtabs_to_index (struct objfile *objfile, const char *dir)
GDB manual. Any changes here must be documented there. */

static void
-save_gdb_index_command (char *arg, int from_tty)
+save_gdb_index_command (char *arg_entry, int from_tty)
{
struct objfile *objfile;
+ const char dwarf4space[] = "-dwarf-4 ";
+ bool is_dwarf5 (true);
+ const char *arg = arg_entry;
+
+ if (!arg)
+ arg = "";
+
+ arg = skip_spaces_const (arg);
+ if (strncmp (arg, dwarf4space, strlen (dwarf4space)) == 0)
+ {
+ is_dwarf5 = false;
+ arg += strlen (dwarf4space);
+ arg = skip_spaces_const (arg);
+ }

- if (!arg || !*arg)
- error (_("usage: save gdb-index DIRECTORY"));
+ if (!*arg)
+ error (_("usage: save gdb-index [-dwarf-4] DIRECTORY"));

ALL_OBJFILES (objfile)
{
@@ -23908,7 +24694,7 @@ save_gdb_index_command (char *arg, int from_tty)

TRY
{
- write_psymtabs_to_index (objfile, arg);
+ write_psymtabs_to_index (objfile, arg, is_dwarf5);
}
CATCH (except, RETURN_MASK_ERROR)
{
@@ -24042,7 +24828,7 @@ Warning: This option must be enabled before gdb reads the file."),
c = add_cmd ("gdb-index", class_files, save_gdb_index_command,
_("\
Save a gdb-index file.\n\
-Usage: save gdb-index DIRECTORY"),
+Usage: save gdb-index [-dwarf-4] DIRECTORY"),
&save_cmdlist);
set_cmd_completer (c, filename_completer);

--
2.9.4