From patchwork Fri May 26 18:25:53 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jan Kratochvil X-Patchwork-Id: 20607 Received: (qmail 89707 invoked by alias); 26 May 2017 18:26:09 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 89548 invoked by uid 89); 26 May 2017 18:26:07 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-25.8 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_LAZY_DOMAIN_SECURITY, KAM_STOCKGEN, RP_MATCHES_RCVD, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=Tables X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 26 May 2017 18:25:53 +0000 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 74F6A4E35B; Fri, 26 May 2017 18:25:55 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 74F6A4E35B Authentication-Results: ext-mx09.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx09.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=jan.kratochvil@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com 74F6A4E35B Received: from host1.jankratochvil.net (ovpn-117-182.ams2.redhat.com [10.36.117.182]) by smtp.corp.redhat.com (Postfix) with ESMTP id 46CC251DEB; Fri, 26 May 2017 18:25:54 +0000 (UTC) Subject: [PATCH 3/6] DWARF-5: .debug_names index producer From: Jan Kratochvil To: gdb-patches@sourceware.org Cc: Victor Leschuk Date: Fri, 26 May 2017 20:25:53 +0200 Message-ID: <149582315340.15869.12350305944876352632.stgit@host1.jankratochvil.net> In-Reply-To: <149582312757.15869.18345460438195439402.stgit@host1.jankratochvil.net> References: <149582312757.15869.18345460438195439402.stgit@host1.jankratochvil.net> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 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 * 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 * 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 --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 #include #include +#include +#include +#include +#include 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 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 (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() (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, CstrViewHasher> + name_to_value_set; + + // Tables of DWARF-5 .debug_names. They are in object file byte order. + std::vector bucket_table; + std::vector 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 OffsetVecTmpl:public OffsetVec + { + private: + std::vector 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 (&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 DwarfTmpl:public Dwarf + { + private: + OffsetVecTmpl 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 dwarf32; + DwarfTmpl 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_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 (dwarf64) + : static_cast (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 ())); + std::set &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> 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 (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 &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 (&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 + (&hash_table.back ()), + sizeof (hash_table.back ()), dwarf5_byte_order, + hashitpair.hash); + const CstrView &name (hashitpair.it->first); + const std::set &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 &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 &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 (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 (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 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 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 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 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 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);