diff mbox

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

Message ID 149582315340.15869.12350305944876352632.stgit@host1.jankratochvil.net
State New
Headers show

Commit Message

Jan Kratochvil May 26, 2017, 6:25 p.m. UTC
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             | 1002 +++++++++++++++++++++++++++++++++++++-----
 3 files changed, 958 insertions(+), 119 deletions(-)
diff mbox

Patch

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..4f5eb08 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 <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,874 @@  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::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_back (std::move (hashitpair));
+      }
+    for (size_t bucket_ix = 0; bucket_ix < bucket_hash.size (); ++bucket_ix)
+      {
+	const std::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 +24655,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 +24692,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 +24826,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);