aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gold/ChangeLog109
-rw-r--r--gold/Makefile.am2
-rw-r--r--gold/Makefile.in13
-rw-r--r--gold/dwarf_reader.cc1440
-rw-r--r--gold/dwarf_reader.h828
-rw-r--r--gold/dynobj.h16
-rw-r--r--gold/gdb-index.cc1229
-rw-r--r--gold/gdb-index.h213
-rw-r--r--gold/incremental.cc72
-rw-r--r--gold/incremental.h10
-rw-r--r--gold/layout.cc89
-rw-r--r--gold/layout.h15
-rw-r--r--gold/main.cc2
-rw-r--r--gold/object.cc158
-rw-r--r--gold/object.h27
-rw-r--r--gold/options.h4
-rw-r--r--gold/plugin.cc11
-rw-r--r--gold/plugin.h5
-rw-r--r--gold/reloc.h10
-rw-r--r--gold/testsuite/Makefile.am25
-rw-r--r--gold/testsuite/Makefile.in70
-rw-r--r--gold/testsuite/gdb_index_test.cc138
-rwxr-xr-xgold/testsuite/gdb_index_test_1.sh84
-rwxr-xr-xgold/testsuite/gdb_index_test_2.sh84
24 files changed, 4486 insertions, 168 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog
index e251570..9d63bba 100644
--- a/gold/ChangeLog
+++ b/gold/ChangeLog
@@ -1,3 +1,112 @@
+2012-03-21 Cary Coutant <ccoutant@google.com>
+
+ * Makefile.am: Add gdb-index.cc, gdb-index.h.
+ * Makefile.in: Regenerate.
+ * dwarf_reader.cc (Sized_elf_reloc_mapper::do_initialize): New function.
+ (Sized_elf_reloc_mapper::symbol_section): New function.
+ (Sized_elf_reloc_mapper::do_get_reloc_target): New function.
+ (make_elf_reloc_mapper): New function.
+ (Dwarf_abbrev_table::clear_abbrev_codes): New function.
+ (Dwarf_abbrev_table::do_read_abbrevs): New function.
+ (Dwarf_abbrev_table::do_get_abbrev): New function.
+ (Dwarf_ranges_table::read_ranges_table): New function.
+ (Dwarf_ranges_table::read_range_list): New function.
+ (Dwarf_pubnames_table::read_section): New function.
+ (Dwarf_pubnames_table::read_header): New function.
+ (Dwarf_pubnames_table::next_name): New function.
+ (Dwarf_die::Dwarf_die): New function.
+ (Dwarf_die::read_attributes): New function.
+ (Dwarf_die::skip_attributes): New function.
+ (Dwarf_die::set_name): New function.
+ (Dwarf_die::set_linkage_name): New function.
+ (Dwarf_die::attribute): New function.
+ (Dwarf_die::string_attribute): New function.
+ (Dwarf_die::int_attribute): New function.
+ (Dwarf_die::uint_attribute): New function.
+ (Dwarf_die::ref_attribute): New function.
+ (Dwarf_die::child_offset): New function.
+ (Dwarf_die::sibling_offset): New function.
+ (Dwarf_info_reader::check_buffer): New function.
+ (Dwarf_info_reader::parse): New function.
+ (Dwarf_info_reader::do_parse): New function.
+ (Dwarf_info_reader::do_read_string_table): New function.
+ (Dwarf_info_reader::lookup_reloc): New function.
+ (Dwarf_info_reader::get_string): New function.
+ (Dwarf_info_reader::visit_compilation_unit): New function.
+ (Dwarf_info_reader::visit_type_unit): New function.
+ (Sized_dwarf_line_info::Sized_dwarf_line_info): Use
+ Sized_elf_reloc_mapper.
+ (Sized_dwarf_line_info::symbol_section): Remove function.
+ (Sized_dwarf_line_info::read_relocs): Use Sized_elf_reloc_mapper.
+ (Sized_dwarf_line_info::read_line_mappings): Remove object
+ parameter, adjust callers.
+ (Sized_dwarf_line_info::format_file_lineno): Fix type of cast.
+ * dwarf_reader.h: Include <sys/types.h>.
+ (class Track_relocs): Remove forward declaration.
+ (class Elf_reloc_mapper): New class.
+ (class Sized_elf_reloc_mapper): New class.
+ (class Dwarf_abbrev_table): New class.
+ (class Dwarf_range_list): New class.
+ (class Dwarf_ranges_table): New class.
+ (class Dwarf_pubnames_table): New class.
+ (class Dwarf_die): New class.
+ (class Dwarf_info_reader): New class.
+ (Sized_dwarf_line_info::read_line_mappings): Remove object parameter.
+ (Sized_dwarf_line_info::symbol_section): Remove member function.
+ * dynobj.h (Sized_dynobj::do_section_contents): Refactor code from
+ base class.
+ * gdb-index.cc: New source file.
+ * gdb-index.h: New source file.
+ * incremental.cc (Sized_relobj_incr::do_layout): Track .debug_info
+ and .debug_types sections, call Layout::add_to_gdb_index.
+ (Sized_relobj_incr::do_section_name): Implement.
+ (Sized_relobj_incr::do_section_contents): Adjust parameter list and
+ return type; Implement.
+ (Sized_incr_dynobj::do_section_contents): Adjust parameter list and
+ return type.
+ * incremental.h (Sized_relobj_incr::do_section_contents): Adjust
+ parameter list and return type.
+ (Sized_incr_dynobj::do_section_contents): Likewise.
+ * layout.cc: Include gdb-index.h.
+ (Layout::Layout): Initialize gdb_index_data_.
+ (Layout::init_fixed_output_section): Check for .gdb_index section.
+ (Layout::add_to_gdb_index): New function. Instantiate.
+ * layout.h: Add forward declaration for class Gdb_index.
+ (Layout::add_to_gdb_index): New member function.
+ (Layout::gdb_index_data_): New data member.
+ * main.cc: Include gdb-index.h.
+ (main): Print statistics for gdb index.
+ * object.cc (Object::section_contents): Move code into
+ do_section_contents.
+ (need_decompressed_section): Check for sections needed when building
+ gdb index.
+ (build_compressed_section_map): Likewise.
+ (Sized_relobj_file::do_read_symbols): Need local symbols when building
+ gdb index.
+ (Sized_relobj_file::do_layout): Track .debug_info and .debug_types
+ sections; call Layout::add_to_gdb_index.
+ (Sized_relobj_file::do_decompressed_section_contents): Call
+ do_section_contents directly.
+ * object.h (Object::do_section_contents): Adjust parameter list and
+ return type.
+ (Object::do_decompressed_section_contents): Call do_section_contents
+ directly.
+ (Sized_relobj_file::do_section_contents): Adjust parameter list and
+ return type.
+ * options.h (class General_options): Add --gdb-index option.
+ * plugin.cc (Sized_pluginobj::do_section_contents): Adjust parameter
+ list and return type.
+ * plugin.h (Sized_pluginobj::do_section_contents): Likewise.
+ * reloc.h (Track_relocs::checkpoint): New function.
+ (Track_relocs::reset): New function.
+
+ * testsuite/Makefile.am (gdb_index_test_1.sh, gdb_index_test_2.sh):
+ New test cases.
+ * testsuite/Makefile.in: Regenerate.
+ * testsuite/gdb_index_test.cc: New test source file.
+ * testsuite/gdb_index_test_1.sh: New test source file.
+ * testsuite/gdb_index_test_2.sh: New test source file.
+
2012-03-19 Doug Kwan <dougkwan@google.com>
* arm.cc (Target_arm::do_define_standard_symbols): New method.
diff --git a/gold/Makefile.am b/gold/Makefile.am
index d121ac4..7d4b725 100644
--- a/gold/Makefile.am
+++ b/gold/Makefile.am
@@ -55,6 +55,7 @@ CCFILES = \
expression.cc \
fileread.cc \
gc.cc \
+ gdb-index.cc \
gold.cc \
gold-threads.cc \
icf.cc \
@@ -102,6 +103,7 @@ HFILES = \
fileread.h \
freebsd.h \
gc.h \
+ gdb-index.h \
gold.h \
gold-threads.h \
icf.h \
diff --git a/gold/Makefile.in b/gold/Makefile.in
index 0fa739a..2998b7b 100644
--- a/gold/Makefile.in
+++ b/gold/Makefile.in
@@ -77,11 +77,11 @@ am__objects_1 = archive.$(OBJEXT) attributes.$(OBJEXT) \
descriptors.$(OBJEXT) dirsearch.$(OBJEXT) dynobj.$(OBJEXT) \
dwarf_reader.$(OBJEXT) ehframe.$(OBJEXT) errors.$(OBJEXT) \
expression.$(OBJEXT) fileread.$(OBJEXT) gc.$(OBJEXT) \
- gold.$(OBJEXT) gold-threads.$(OBJEXT) icf.$(OBJEXT) \
- incremental.$(OBJEXT) int_encoding.$(OBJEXT) layout.$(OBJEXT) \
- mapfile.$(OBJEXT) merge.$(OBJEXT) object.$(OBJEXT) \
- options.$(OBJEXT) output.$(OBJEXT) parameters.$(OBJEXT) \
- plugin.$(OBJEXT) readsyms.$(OBJEXT) \
+ gdb-index.$(OBJEXT) gold.$(OBJEXT) gold-threads.$(OBJEXT) \
+ icf.$(OBJEXT) incremental.$(OBJEXT) int_encoding.$(OBJEXT) \
+ layout.$(OBJEXT) mapfile.$(OBJEXT) merge.$(OBJEXT) \
+ object.$(OBJEXT) options.$(OBJEXT) output.$(OBJEXT) \
+ parameters.$(OBJEXT) plugin.$(OBJEXT) readsyms.$(OBJEXT) \
reduced_debug_output.$(OBJEXT) reloc.$(OBJEXT) \
resolve.$(OBJEXT) script-sections.$(OBJEXT) script.$(OBJEXT) \
stringpool.$(OBJEXT) symtab.$(OBJEXT) target.$(OBJEXT) \
@@ -399,6 +399,7 @@ CCFILES = \
expression.cc \
fileread.cc \
gc.cc \
+ gdb-index.cc \
gold.cc \
gold-threads.cc \
icf.cc \
@@ -446,6 +447,7 @@ HFILES = \
fileread.h \
freebsd.h \
gc.h \
+ gdb-index.h \
gold.h \
gold-threads.h \
icf.h \
@@ -650,6 +652,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/expression.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fileread.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdb-index.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gold-threads.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gold.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/i386.Po@am__quote@
diff --git a/gold/dwarf_reader.cc b/gold/dwarf_reader.cc
index 189e6a6..eaf35bf 100644
--- a/gold/dwarf_reader.cc
+++ b/gold/dwarf_reader.cc
@@ -1,6 +1,6 @@
// dwarf_reader.cc -- parse dwarf2/3 debug information
-// Copyright 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+// Copyright 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
// Written by Ian Lance Taylor <iant@google.com>.
// This file is part of gold.
@@ -36,6 +36,1367 @@
namespace gold {
+// Class Sized_elf_reloc_mapper
+
+// Initialize the relocation tracker for section RELOC_SHNDX.
+
+template<int size, bool big_endian>
+bool
+Sized_elf_reloc_mapper<size, big_endian>::do_initialize(
+ unsigned int reloc_shndx, unsigned int reloc_type)
+{
+ this->reloc_type_ = reloc_type;
+ return this->track_relocs_.initialize(this->object_, reloc_shndx,
+ reloc_type);
+}
+
+// Looks in the symtab to see what section a symbol is in.
+
+template<int size, bool big_endian>
+unsigned int
+Sized_elf_reloc_mapper<size, big_endian>::symbol_section(
+ unsigned int symndx, Address* value, bool* is_ordinary)
+{
+ const int symsize = elfcpp::Elf_sizes<size>::sym_size;
+ gold_assert((symndx + 1) * symsize <= this->symtab_size_);
+ elfcpp::Sym<size, big_endian> elfsym(this->symtab_ + symndx * symsize);
+ *value = elfsym.get_st_value();
+ return this->object_->adjust_sym_shndx(symndx, elfsym.get_st_shndx(),
+ is_ordinary);
+}
+
+// Return the section index and offset within the section of
+// the target of the relocation for RELOC_OFFSET.
+
+template<int size, bool big_endian>
+unsigned int
+Sized_elf_reloc_mapper<size, big_endian>::do_get_reloc_target(
+ off_t reloc_offset, off_t* target_offset)
+{
+ this->track_relocs_.advance(reloc_offset);
+ if (reloc_offset != this->track_relocs_.next_offset())
+ return 0;
+ unsigned int symndx = this->track_relocs_.next_symndx();
+ typename elfcpp::Elf_types<size>::Elf_Addr value;
+ bool is_ordinary;
+ unsigned int target_shndx = this->symbol_section(symndx, &value,
+ &is_ordinary);
+ if (!is_ordinary)
+ return 0;
+ if (this->reloc_type_ == elfcpp::SHT_RELA)
+ value += this->track_relocs_.next_addend();
+ *target_offset = value;
+ return target_shndx;
+}
+
+static inline Elf_reloc_mapper*
+make_elf_reloc_mapper(Object* object, const unsigned char* symtab,
+ off_t symtab_size)
+{
+ switch (parameters->size_and_endianness())
+ {
+#ifdef HAVE_TARGET_32_LITTLE
+ case Parameters::TARGET_32_LITTLE:
+ return new Sized_elf_reloc_mapper<32, false>(object, symtab,
+ symtab_size);
+#endif
+#ifdef HAVE_TARGET_32_BIG
+ case Parameters::TARGET_32_BIG:
+ return new Sized_elf_reloc_mapper<32, true>(object, symtab,
+ symtab_size);
+#endif
+#ifdef HAVE_TARGET_64_LITTLE
+ case Parameters::TARGET_64_LITTLE:
+ return new Sized_elf_reloc_mapper<64, false>(object, symtab,
+ symtab_size);
+#endif
+#ifdef HAVE_TARGET_64_BIG
+ case Parameters::TARGET_64_BIG:
+ return new Sized_elf_reloc_mapper<64, true>(object, symtab,
+ symtab_size);
+#endif
+ default:
+ gold_unreachable();
+ }
+}
+
+// class Dwarf_abbrev_table
+
+void
+Dwarf_abbrev_table::clear_abbrev_codes()
+{
+ for (unsigned int code = 0; code < this->low_abbrev_code_max_; ++code)
+ {
+ if (this->low_abbrev_codes_[code] != NULL)
+ {
+ delete this->low_abbrev_codes_[code];
+ this->low_abbrev_codes_[code] = NULL;
+ }
+ }
+ for (Abbrev_code_table::iterator it = this->high_abbrev_codes_.begin();
+ it != this->high_abbrev_codes_.end();
+ ++it)
+ {
+ if (it->second != NULL)
+ delete it->second;
+ }
+ this->high_abbrev_codes_.clear();
+}
+
+// Read the abbrev table from an object file.
+
+bool
+Dwarf_abbrev_table::do_read_abbrevs(
+ Relobj* object,
+ unsigned int abbrev_shndx,
+ off_t abbrev_offset)
+{
+ this->clear_abbrev_codes();
+
+ // If we don't have relocations, abbrev_shndx will be 0, and
+ // we'll have to hunt for the .debug_abbrev section.
+ if (abbrev_shndx == 0 && this->abbrev_shndx_ > 0)
+ abbrev_shndx = this->abbrev_shndx_;
+ else if (abbrev_shndx == 0)
+ {
+ for (unsigned int i = 1; i < object->shnum(); ++i)
+ {
+ std::string name = object->section_name(i);
+ if (name == ".debug_abbrev")
+ {
+ abbrev_shndx = i;
+ // Correct the offset. For incremental update links, we have a
+ // relocated offset that is relative to the output section, but
+ // here we need an offset relative to the input section.
+ abbrev_offset -= object->output_section_offset(i);
+ break;
+ }
+ }
+ if (abbrev_shndx == 0)
+ return false;
+ }
+
+ // Get the section contents and decompress if necessary.
+ if (abbrev_shndx != this->abbrev_shndx_)
+ {
+ if (this->owns_buffer_ && this->buffer_ != NULL)
+ {
+ delete[] this->buffer_;
+ this->owns_buffer_ = false;
+ }
+
+ section_size_type buffer_size;
+ this->buffer_ =
+ object->decompressed_section_contents(abbrev_shndx,
+ &buffer_size,
+ &this->owns_buffer_);
+ this->buffer_end_ = this->buffer_ + buffer_size;
+ this->abbrev_shndx_ = abbrev_shndx;
+ }
+
+ this->buffer_pos_ = this->buffer_ + abbrev_offset;
+ return true;
+}
+
+// Lookup the abbrev code entry for CODE. This function is called
+// only when the abbrev code is not in the direct lookup table.
+// It may be in the hash table, it may not have been read yet,
+// or it may not exist in the abbrev table.
+
+const Dwarf_abbrev_table::Abbrev_code*
+Dwarf_abbrev_table::do_get_abbrev(unsigned int code)
+{
+ // See if the abbrev code is already in the hash table.
+ Abbrev_code_table::const_iterator it = this->high_abbrev_codes_.find(code);
+ if (it != this->high_abbrev_codes_.end())
+ return it->second;
+
+ // Read and store abbrev code definitions until we find the
+ // one we're looking for.
+ for (;;)
+ {
+ // Read the abbrev code. A zero here indicates the end of the
+ // abbrev table.
+ size_t len;
+ if (this->buffer_pos_ >= this->buffer_end_)
+ return NULL;
+ uint64_t nextcode = read_unsigned_LEB_128(this->buffer_pos_, &len);
+ if (nextcode == 0)
+ {
+ this->buffer_pos_ = this->buffer_end_;
+ return NULL;
+ }
+ this->buffer_pos_ += len;
+
+ // Read the tag.
+ if (this->buffer_pos_ >= this->buffer_end_)
+ return NULL;
+ uint64_t tag = read_unsigned_LEB_128(this->buffer_pos_, &len);
+ this->buffer_pos_ += len;
+
+ // Read the has_children flag.
+ if (this->buffer_pos_ >= this->buffer_end_)
+ return NULL;
+ bool has_children = *this->buffer_pos_ == elfcpp::DW_CHILDREN_yes;
+ this->buffer_pos_ += 1;
+
+ // Read the list of (attribute, form) pairs.
+ Abbrev_code* entry = new Abbrev_code(tag, has_children);
+ for (;;)
+ {
+ // Read the attribute.
+ if (this->buffer_pos_ >= this->buffer_end_)
+ return NULL;
+ uint64_t attr = read_unsigned_LEB_128(this->buffer_pos_, &len);
+ this->buffer_pos_ += len;
+
+ // Read the form.
+ if (this->buffer_pos_ >= this->buffer_end_)
+ return NULL;
+ uint64_t form = read_unsigned_LEB_128(this->buffer_pos_, &len);
+ this->buffer_pos_ += len;
+
+ // A (0,0) pair terminates the list.
+ if (attr == 0 && form == 0)
+ break;
+
+ if (attr == elfcpp::DW_AT_sibling)
+ entry->has_sibling_attribute = true;
+
+ entry->add_attribute(attr, form);
+ }
+
+ this->store_abbrev(nextcode, entry);
+ if (nextcode == code)
+ return entry;
+ }
+
+ return NULL;
+}
+
+// class Dwarf_ranges_table
+
+// Read the ranges table from an object file.
+
+bool
+Dwarf_ranges_table::read_ranges_table(
+ Relobj* object,
+ const unsigned char* symtab,
+ off_t symtab_size,
+ unsigned int ranges_shndx)
+{
+ // If we've already read this abbrev table, return immediately.
+ if (this->ranges_shndx_ > 0
+ && this->ranges_shndx_ == ranges_shndx)
+ return true;
+
+ // If we don't have relocations, ranges_shndx will be 0, and
+ // we'll have to hunt for the .debug_ranges section.
+ if (ranges_shndx == 0 && this->ranges_shndx_ > 0)
+ ranges_shndx = this->ranges_shndx_;
+ else if (ranges_shndx == 0)
+ {
+ for (unsigned int i = 1; i < object->shnum(); ++i)
+ {
+ std::string name = object->section_name(i);
+ if (name == ".debug_ranges")
+ {
+ ranges_shndx = i;
+ this->output_section_offset_ = object->output_section_offset(i);
+ break;
+ }
+ }
+ if (ranges_shndx == 0)
+ return false;
+ }
+
+ // Get the section contents and decompress if necessary.
+ if (ranges_shndx != this->ranges_shndx_)
+ {
+ if (this->owns_ranges_buffer_ && this->ranges_buffer_ != NULL)
+ {
+ delete[] this->ranges_buffer_;
+ this->owns_ranges_buffer_ = false;
+ }
+
+ section_size_type buffer_size;
+ this->ranges_buffer_ =
+ object->decompressed_section_contents(ranges_shndx,
+ &buffer_size,
+ &this->owns_ranges_buffer_);
+ this->ranges_buffer_end_ = this->ranges_buffer_ + buffer_size;
+ this->ranges_shndx_ = ranges_shndx;
+ }
+
+ if (this->ranges_reloc_mapper_ != NULL)
+ {
+ delete this->ranges_reloc_mapper_;
+ this->ranges_reloc_mapper_ = NULL;
+ }
+
+ // For incremental objects, we have no relocations.
+ if (object->is_incremental())
+ return true;
+
+ // Find the relocation section for ".debug_ranges".
+ unsigned int reloc_shndx = 0;
+ unsigned int reloc_type = 0;
+ for (unsigned int i = 0; i < object->shnum(); ++i)
+ {
+ reloc_type = object->section_type(i);
+ if ((reloc_type == elfcpp::SHT_REL
+ || reloc_type == elfcpp::SHT_RELA)
+ && object->section_info(i) == ranges_shndx)
+ {
+ reloc_shndx = i;
+ break;
+ }
+ }
+
+ this->ranges_reloc_mapper_ = make_elf_reloc_mapper(object, symtab,
+ symtab_size);
+ this->ranges_reloc_mapper_->initialize(reloc_shndx, reloc_type);
+
+ return true;
+}
+
+// Read a range list from section RANGES_SHNDX at offset RANGES_OFFSET.
+
+Dwarf_range_list*
+Dwarf_ranges_table::read_range_list(
+ Relobj* object,
+ const unsigned char* symtab,
+ off_t symtab_size,
+ unsigned int addr_size,
+ unsigned int ranges_shndx,
+ off_t offset)
+{
+ Dwarf_range_list* ranges;
+
+ if (!this->read_ranges_table(object, symtab, symtab_size, ranges_shndx))
+ return NULL;
+
+ // Correct the offset. For incremental update links, we have a
+ // relocated offset that is relative to the output section, but
+ // here we need an offset relative to the input section.
+ offset -= this->output_section_offset_;
+
+ // Read the range list at OFFSET.
+ ranges = new Dwarf_range_list();
+ off_t base = 0;
+ for (;
+ this->ranges_buffer_ + offset < this->ranges_buffer_end_;
+ offset += 2 * addr_size)
+ {
+ off_t start;
+ off_t end;
+
+ // Read the raw contents of the section.
+ if (addr_size == 4)
+ {
+ start = read_from_pointer<32>(this->ranges_buffer_ + offset);
+ end = read_from_pointer<32>(this->ranges_buffer_ + offset + 4);
+ }
+ else
+ {
+ start = read_from_pointer<64>(this->ranges_buffer_ + offset);
+ end = read_from_pointer<64>(this->ranges_buffer_ + offset + 8);
+ }
+
+ // Check for relocations and adjust the values.
+ unsigned int shndx1 = 0;
+ unsigned int shndx2 = 0;
+ if (this->ranges_reloc_mapper_ != NULL)
+ {
+ shndx1 =
+ this->ranges_reloc_mapper_->get_reloc_target(offset, &start);
+ shndx2 =
+ this->ranges_reloc_mapper_->get_reloc_target(offset + addr_size,
+ &end);
+ }
+
+ // End of list is marked by a pair of zeroes.
+ if (shndx1 == 0 && start == 0 && end == 0)
+ break;
+
+ // A "base address selection entry" is identified by
+ // 0xffffffff for the first value of the pair. The second
+ // value is used as a base for subsequent range list entries.
+ if (shndx1 == 0 && start == -1)
+ base = end;
+ else if (shndx1 == shndx2)
+ {
+ if (shndx1 == 0 || object->is_section_included(shndx1))
+ ranges->add(shndx1, base + start, base + end);
+ }
+ else
+ gold_warning(_("%s: DWARF info may be corrupt; offsets in a "
+ "range list entry are in different sections"),
+ object->name().c_str());
+ }
+
+ return ranges;
+}
+
+// class Dwarf_pubnames_table
+
+// Read the pubnames section SHNDX from the object file.
+
+bool
+Dwarf_pubnames_table::read_section(Relobj* object, unsigned int shndx)
+{
+ section_size_type buffer_size;
+
+ // If we don't have relocations, shndx will be 0, and
+ // we'll have to hunt for the .debug_pubnames/pubtypes section.
+ if (shndx == 0)
+ {
+ const char* name = (this->is_pubtypes_
+ ? ".debug_pubtypes"
+ : ".debug_pubnames");
+ for (unsigned int i = 1; i < object->shnum(); ++i)
+ {
+ if (object->section_name(i) == name)
+ {
+ shndx = i;
+ this->output_section_offset_ = object->output_section_offset(i);
+ break;
+ }
+ }
+ if (shndx == 0)
+ return false;
+ }
+
+ this->buffer_ = object->decompressed_section_contents(shndx,
+ &buffer_size,
+ &this->owns_buffer_);
+ if (this->buffer_ == NULL)
+ return false;
+ this->buffer_end_ = this->buffer_ + buffer_size;
+ return true;
+}
+
+// Read the header for the set at OFFSET.
+
+bool
+Dwarf_pubnames_table::read_header(off_t offset)
+{
+ // Correct the offset. For incremental update links, we have a
+ // relocated offset that is relative to the output section, but
+ // here we need an offset relative to the input section.
+ offset -= this->output_section_offset_;
+
+ if (offset < 0 || offset + 14 >= this->buffer_end_ - this->buffer_)
+ return false;
+
+ const unsigned char* pinfo = this->buffer_ + offset;
+
+ // Read the unit_length field.
+ uint32_t unit_length = read_from_pointer<32>(pinfo);
+ pinfo += 4;
+ if (unit_length == 0xffffffff)
+ {
+ unit_length = read_from_pointer<64>(pinfo);
+ pinfo += 8;
+ this->offset_size_ = 8;
+ }
+ else
+ this->offset_size_ = 4;
+
+ // Check the version.
+ unsigned int version = read_from_pointer<16>(pinfo);
+ pinfo += 2;
+ if (version != 2)
+ return false;
+
+ // Skip the debug_info_offset and debug_info_size fields.
+ pinfo += 2 * this->offset_size_;
+
+ if (pinfo >= this->buffer_end_)
+ return false;
+
+ this->pinfo_ = pinfo;
+ return true;
+}
+
+// Read the next name from the set.
+
+const char*
+Dwarf_pubnames_table::next_name()
+{
+ const unsigned char* pinfo = this->pinfo_;
+
+ // Read the offset within the CU. If this is zero, we have reached
+ // the end of the list.
+ uint32_t offset;
+ if (this->offset_size_ == 4)
+ offset = read_from_pointer<32>(&pinfo);
+ else
+ offset = read_from_pointer<64>(&pinfo);
+ if (offset == 0)
+ return NULL;
+
+ // Return a pointer to the string at the current location,
+ // and advance the pointer to the next entry.
+ const char* ret = reinterpret_cast<const char*>(pinfo);
+ while (pinfo < this->buffer_end_ && *pinfo != '\0')
+ ++pinfo;
+ if (pinfo < this->buffer_end_)
+ ++pinfo;
+
+ this->pinfo_ = pinfo;
+ return ret;
+}
+
+// class Dwarf_die
+
+Dwarf_die::Dwarf_die(
+ Dwarf_info_reader* dwinfo,
+ off_t die_offset,
+ Dwarf_die* parent)
+ : dwinfo_(dwinfo), parent_(parent), die_offset_(die_offset),
+ child_offset_(0), sibling_offset_(0), abbrev_code_(NULL), attributes_(),
+ attributes_read_(false), name_(NULL), name_off_(-1), linkage_name_(NULL),
+ linkage_name_off_(-1), string_shndx_(0), specification_(0),
+ abstract_origin_(0)
+{
+ size_t len;
+ const unsigned char* pdie = dwinfo->buffer_at_offset(die_offset);
+ if (pdie == NULL)
+ return;
+ unsigned int code = read_unsigned_LEB_128(pdie, &len);
+ if (code == 0)
+ {
+ if (parent != NULL)
+ parent->set_sibling_offset(die_offset + len);
+ return;
+ }
+ this->attr_offset_ = len;
+
+ // Lookup the abbrev code in the abbrev table.
+ this->abbrev_code_ = dwinfo->get_abbrev(code);
+}
+
+// Read all the attributes of the DIE.
+
+bool
+Dwarf_die::read_attributes()
+{
+ if (this->attributes_read_)
+ return true;
+
+ gold_assert(this->abbrev_code_ != NULL);
+
+ const unsigned char* pdie =
+ this->dwinfo_->buffer_at_offset(this->die_offset_);
+ if (pdie == NULL)
+ return false;
+ const unsigned char* pattr = pdie + this->attr_offset_;
+
+ unsigned int nattr = this->abbrev_code_->attributes.size();
+ this->attributes_.reserve(nattr);
+ for (unsigned int i = 0; i < nattr; ++i)
+ {
+ size_t len;
+ unsigned int attr = this->abbrev_code_->attributes[i].attr;
+ unsigned int form = this->abbrev_code_->attributes[i].form;
+ if (form == elfcpp::DW_FORM_indirect)
+ {
+ form = read_unsigned_LEB_128(pattr, &len);
+ pattr += len;
+ }
+ off_t attr_off = this->die_offset_ + (pattr - pdie);
+ bool ref_form = false;
+ Attribute_value attr_value;
+ attr_value.attr = attr;
+ attr_value.form = form;
+ attr_value.aux.shndx = 0;
+ switch(form)
+ {
+ case elfcpp::DW_FORM_null:
+ attr_value.val.intval = 0;
+ break;
+ case elfcpp::DW_FORM_flag_present:
+ attr_value.val.intval = 1;
+ break;
+ case elfcpp::DW_FORM_strp:
+ {
+ off_t str_off;
+ if (this->dwinfo_->offset_size() == 4)
+ str_off = read_from_pointer<32>(&pattr);
+ else
+ str_off = read_from_pointer<64>(&pattr);
+ unsigned int shndx =
+ this->dwinfo_->lookup_reloc(attr_off, &str_off);
+ attr_value.aux.shndx = shndx;
+ attr_value.val.refval = str_off;
+ break;
+ }
+ case elfcpp::DW_FORM_sec_offset:
+ {
+ off_t sec_off;
+ if (this->dwinfo_->offset_size() == 4)
+ sec_off = read_from_pointer<32>(&pattr);
+ else
+ sec_off = read_from_pointer<64>(&pattr);
+ unsigned int shndx =
+ this->dwinfo_->lookup_reloc(attr_off, &sec_off);
+ attr_value.aux.shndx = shndx;
+ attr_value.val.refval = sec_off;
+ ref_form = true;
+ break;
+ }
+ case elfcpp::DW_FORM_addr:
+ case elfcpp::DW_FORM_ref_addr:
+ {
+ off_t sec_off;
+ if (this->dwinfo_->address_size() == 4)
+ sec_off = read_from_pointer<32>(&pattr);
+ else
+ sec_off = read_from_pointer<64>(&pattr);
+ unsigned int shndx =
+ this->dwinfo_->lookup_reloc(attr_off, &sec_off);
+ attr_value.aux.shndx = shndx;
+ attr_value.val.refval = sec_off;
+ ref_form = true;
+ break;
+ }
+ case elfcpp::DW_FORM_block1:
+ attr_value.aux.blocklen = *pattr++;
+ attr_value.val.blockval = pattr;
+ pattr += attr_value.aux.blocklen;
+ break;
+ case elfcpp::DW_FORM_block2:
+ attr_value.aux.blocklen = read_from_pointer<16>(&pattr);
+ attr_value.val.blockval = pattr;
+ pattr += attr_value.aux.blocklen;
+ break;
+ case elfcpp::DW_FORM_block4:
+ attr_value.aux.blocklen = read_from_pointer<32>(&pattr);
+ attr_value.val.blockval = pattr;
+ pattr += attr_value.aux.blocklen;
+ break;
+ case elfcpp::DW_FORM_block:
+ case elfcpp::DW_FORM_exprloc:
+ attr_value.aux.blocklen = read_unsigned_LEB_128(pattr, &len);
+ attr_value.val.blockval = pattr + len;
+ pattr += len + attr_value.aux.blocklen;
+ break;
+ case elfcpp::DW_FORM_data1:
+ case elfcpp::DW_FORM_flag:
+ attr_value.val.intval = *pattr++;
+ break;
+ case elfcpp::DW_FORM_ref1:
+ attr_value.val.refval = *pattr++;
+ ref_form = true;
+ break;
+ case elfcpp::DW_FORM_data2:
+ attr_value.val.intval = read_from_pointer<16>(&pattr);
+ break;
+ case elfcpp::DW_FORM_ref2:
+ attr_value.val.refval = read_from_pointer<16>(&pattr);
+ ref_form = true;
+ break;
+ case elfcpp::DW_FORM_data4:
+ {
+ off_t sec_off;
+ sec_off = read_from_pointer<32>(&pattr);
+ unsigned int shndx =
+ this->dwinfo_->lookup_reloc(attr_off, &sec_off);
+ attr_value.aux.shndx = shndx;
+ attr_value.val.intval = sec_off;
+ break;
+ }
+ case elfcpp::DW_FORM_ref4:
+ {
+ off_t sec_off;
+ sec_off = read_from_pointer<32>(&pattr);
+ unsigned int shndx =
+ this->dwinfo_->lookup_reloc(attr_off, &sec_off);
+ attr_value.aux.shndx = shndx;
+ attr_value.val.refval = sec_off;
+ ref_form = true;
+ break;
+ }
+ case elfcpp::DW_FORM_data8:
+ {
+ off_t sec_off;
+ sec_off = read_from_pointer<64>(&pattr);
+ unsigned int shndx =
+ this->dwinfo_->lookup_reloc(attr_off, &sec_off);
+ attr_value.aux.shndx = shndx;
+ attr_value.val.intval = sec_off;
+ break;
+ }
+ case elfcpp::DW_FORM_ref_sig8:
+ attr_value.val.uintval = read_from_pointer<64>(&pattr);
+ break;
+ case elfcpp::DW_FORM_ref8:
+ {
+ off_t sec_off;
+ sec_off = read_from_pointer<64>(&pattr);
+ unsigned int shndx =
+ this->dwinfo_->lookup_reloc(attr_off, &sec_off);
+ attr_value.aux.shndx = shndx;
+ attr_value.val.refval = sec_off;
+ ref_form = true;
+ break;
+ }
+ case elfcpp::DW_FORM_ref_udata:
+ attr_value.val.refval = read_unsigned_LEB_128(pattr, &len);
+ ref_form = true;
+ pattr += len;
+ break;
+ case elfcpp::DW_FORM_udata:
+ attr_value.val.uintval = read_unsigned_LEB_128(pattr, &len);
+ pattr += len;
+ break;
+ case elfcpp::DW_FORM_sdata:
+ attr_value.val.intval = read_signed_LEB_128(pattr, &len);
+ pattr += len;
+ break;
+ case elfcpp::DW_FORM_string:
+ attr_value.val.stringval = reinterpret_cast<const char*>(pattr);
+ len = strlen(attr_value.val.stringval);
+ pattr += len + 1;
+ break;
+ default:
+ return false;
+ }
+
+ // Cache the most frequently-requested attributes.
+ switch (attr)
+ {
+ case elfcpp::DW_AT_name:
+ if (form == elfcpp::DW_FORM_string)
+ this->name_ = attr_value.val.stringval;
+ else if (form == elfcpp::DW_FORM_strp)
+ {
+ // All indirect strings should refer to the same
+ // string section, so we just save the last one seen.
+ this->string_shndx_ = attr_value.aux.shndx;
+ this->name_off_ = attr_value.val.refval;
+ }
+ break;
+ case elfcpp::DW_AT_linkage_name:
+ case elfcpp::DW_AT_MIPS_linkage_name:
+ if (form == elfcpp::DW_FORM_string)
+ this->linkage_name_ = attr_value.val.stringval;
+ else if (form == elfcpp::DW_FORM_strp)
+ {
+ // All indirect strings should refer to the same
+ // string section, so we just save the last one seen.
+ this->string_shndx_ = attr_value.aux.shndx;
+ this->linkage_name_off_ = attr_value.val.refval;
+ }
+ break;
+ case elfcpp::DW_AT_specification:
+ if (ref_form)
+ this->specification_ = attr_value.val.refval;
+ break;
+ case elfcpp::DW_AT_abstract_origin:
+ if (ref_form)
+ this->abstract_origin_ = attr_value.val.refval;
+ break;
+ case elfcpp::DW_AT_sibling:
+ if (ref_form && attr_value.aux.shndx == 0)
+ this->sibling_offset_ = attr_value.val.refval;
+ default:
+ break;
+ }
+
+ this->attributes_.push_back(attr_value);
+ }
+
+ // Now that we know where the next DIE begins, record the offset
+ // to avoid later recalculation.
+ if (this->has_children())
+ this->child_offset_ = this->die_offset_ + (pattr - pdie);
+ else
+ this->sibling_offset_ = this->die_offset_ + (pattr - pdie);
+
+ this->attributes_read_ = true;
+ return true;
+}
+
+// Skip all the attributes of the DIE and return the offset of the next DIE.
+
+off_t
+Dwarf_die::skip_attributes()
+{
+ typedef Dwarf_abbrev_table::Attribute Attribute;
+
+ gold_assert(this->abbrev_code_ != NULL);
+
+ const unsigned char* pdie =
+ this->dwinfo_->buffer_at_offset(this->die_offset_);
+ if (pdie == NULL)
+ return 0;
+ const unsigned char* pattr = pdie + this->attr_offset_;
+
+ for (unsigned int i = 0; i < this->abbrev_code_->attributes.size(); ++i)
+ {
+ size_t len;
+ unsigned int form = this->abbrev_code_->attributes[i].form;
+ if (form == elfcpp::DW_FORM_indirect)
+ {
+ form = read_unsigned_LEB_128(pattr, &len);
+ pattr += len;
+ }
+ switch(form)
+ {
+ case elfcpp::DW_FORM_null:
+ case elfcpp::DW_FORM_flag_present:
+ break;
+ case elfcpp::DW_FORM_strp:
+ case elfcpp::DW_FORM_sec_offset:
+ pattr += this->dwinfo_->offset_size();
+ break;
+ case elfcpp::DW_FORM_addr:
+ case elfcpp::DW_FORM_ref_addr:
+ pattr += this->dwinfo_->address_size();
+ break;
+ case elfcpp::DW_FORM_block1:
+ pattr += 1 + *pattr;
+ break;
+ case elfcpp::DW_FORM_block2:
+ {
+ uint16_t block_size;
+ block_size = read_from_pointer<16>(&pattr);
+ pattr += block_size;
+ break;
+ }
+ case elfcpp::DW_FORM_block4:
+ {
+ uint32_t block_size;
+ block_size = read_from_pointer<32>(&pattr);
+ pattr += block_size;
+ break;
+ }
+ case elfcpp::DW_FORM_block:
+ case elfcpp::DW_FORM_exprloc:
+ {
+ uint64_t block_size;
+ block_size = read_unsigned_LEB_128(pattr, &len);
+ pattr += len + block_size;
+ break;
+ }
+ case elfcpp::DW_FORM_data1:
+ case elfcpp::DW_FORM_ref1:
+ case elfcpp::DW_FORM_flag:
+ pattr += 1;
+ break;
+ case elfcpp::DW_FORM_data2:
+ case elfcpp::DW_FORM_ref2:
+ pattr += 2;
+ break;
+ case elfcpp::DW_FORM_data4:
+ case elfcpp::DW_FORM_ref4:
+ pattr += 4;
+ break;
+ case elfcpp::DW_FORM_data8:
+ case elfcpp::DW_FORM_ref8:
+ case elfcpp::DW_FORM_ref_sig8:
+ pattr += 8;
+ break;
+ case elfcpp::DW_FORM_ref_udata:
+ case elfcpp::DW_FORM_udata:
+ read_unsigned_LEB_128(pattr, &len);
+ pattr += len;
+ break;
+ case elfcpp::DW_FORM_sdata:
+ read_signed_LEB_128(pattr, &len);
+ pattr += len;
+ break;
+ case elfcpp::DW_FORM_string:
+ len = strlen(reinterpret_cast<const char*>(pattr));
+ pattr += len + 1;
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ return this->die_offset_ + (pattr - pdie);
+}
+
+// Get the name of the DIE and cache it.
+
+void
+Dwarf_die::set_name()
+{
+ if (this->name_ != NULL || !this->read_attributes())
+ return;
+ if (this->name_off_ != -1)
+ this->name_ = this->dwinfo_->get_string(this->name_off_,
+ this->string_shndx_);
+}
+
+// Get the linkage name of the DIE and cache it.
+
+void
+Dwarf_die::set_linkage_name()
+{
+ if (this->linkage_name_ != NULL || !this->read_attributes())
+ return;
+ if (this->linkage_name_off_ != -1)
+ this->linkage_name_ = this->dwinfo_->get_string(this->linkage_name_off_,
+ this->string_shndx_);
+}
+
+// Return the value of attribute ATTR.
+
+const Dwarf_die::Attribute_value*
+Dwarf_die::attribute(unsigned int attr)
+{
+ if (!this->read_attributes())
+ return NULL;
+ for (unsigned int i = 0; i < this->attributes_.size(); ++i)
+ {
+ if (this->attributes_[i].attr == attr)
+ return &this->attributes_[i];
+ }
+ return NULL;
+}
+
+const char*
+Dwarf_die::string_attribute(unsigned int attr)
+{
+ const Attribute_value* attr_val = this->attribute(attr);
+ if (attr_val == NULL)
+ return NULL;
+ switch (attr_val->form)
+ {
+ case elfcpp::DW_FORM_string:
+ return attr_val->val.stringval;
+ case elfcpp::DW_FORM_strp:
+ return this->dwinfo_->get_string(attr_val->val.refval,
+ attr_val->aux.shndx);
+ default:
+ return NULL;
+ }
+}
+
+int64_t
+Dwarf_die::int_attribute(unsigned int attr)
+{
+ const Attribute_value* attr_val = this->attribute(attr);
+ if (attr_val == NULL)
+ return 0;
+ switch (attr_val->form)
+ {
+ case elfcpp::DW_FORM_null:
+ case elfcpp::DW_FORM_flag_present:
+ case elfcpp::DW_FORM_data1:
+ case elfcpp::DW_FORM_flag:
+ case elfcpp::DW_FORM_data2:
+ case elfcpp::DW_FORM_data4:
+ case elfcpp::DW_FORM_data8:
+ case elfcpp::DW_FORM_sdata:
+ return attr_val->val.intval;
+ default:
+ return 0;
+ }
+}
+
+uint64_t
+Dwarf_die::uint_attribute(unsigned int attr)
+{
+ const Attribute_value* attr_val = this->attribute(attr);
+ if (attr_val == NULL)
+ return 0;
+ switch (attr_val->form)
+ {
+ case elfcpp::DW_FORM_null:
+ case elfcpp::DW_FORM_flag_present:
+ case elfcpp::DW_FORM_data1:
+ case elfcpp::DW_FORM_flag:
+ case elfcpp::DW_FORM_data4:
+ case elfcpp::DW_FORM_data8:
+ case elfcpp::DW_FORM_ref_sig8:
+ case elfcpp::DW_FORM_udata:
+ return attr_val->val.uintval;
+ default:
+ return 0;
+ }
+}
+
+off_t
+Dwarf_die::ref_attribute(unsigned int attr, unsigned int* shndx)
+{
+ const Attribute_value* attr_val = this->attribute(attr);
+ if (attr_val == NULL)
+ return -1;
+ switch (attr_val->form)
+ {
+ case elfcpp::DW_FORM_sec_offset:
+ case elfcpp::DW_FORM_addr:
+ case elfcpp::DW_FORM_ref_addr:
+ case elfcpp::DW_FORM_ref1:
+ case elfcpp::DW_FORM_ref2:
+ case elfcpp::DW_FORM_ref4:
+ case elfcpp::DW_FORM_ref8:
+ case elfcpp::DW_FORM_ref_udata:
+ *shndx = attr_val->aux.shndx;
+ return attr_val->val.refval;
+ case elfcpp::DW_FORM_ref_sig8:
+ *shndx = attr_val->aux.shndx;
+ return attr_val->val.uintval;
+ case elfcpp::DW_FORM_data4:
+ case elfcpp::DW_FORM_data8:
+ *shndx = attr_val->aux.shndx;
+ return attr_val->val.intval;
+ default:
+ return -1;
+ }
+}
+
+// Return the offset of this DIE's first child.
+
+off_t
+Dwarf_die::child_offset()
+{
+ gold_assert(this->abbrev_code_ != NULL);
+ if (!this->has_children())
+ return 0;
+ if (this->child_offset_ == 0)
+ this->child_offset_ = this->skip_attributes();
+ return this->child_offset_;
+}
+
+// Return the offset of this DIE's next sibling.
+
+off_t
+Dwarf_die::sibling_offset()
+{
+ gold_assert(this->abbrev_code_ != NULL);
+
+ if (this->sibling_offset_ != 0)
+ return this->sibling_offset_;
+
+ if (!this->has_children())
+ {
+ this->sibling_offset_ = this->skip_attributes();
+ return this->sibling_offset_;
+ }
+
+ if (this->has_sibling_attribute())
+ {
+ if (!this->read_attributes())
+ return 0;
+ if (this->sibling_offset_ != 0)
+ return this->sibling_offset_;
+ }
+
+ // Skip over the children.
+ off_t child_offset = this->child_offset();
+ while (child_offset > 0)
+ {
+ Dwarf_die die(this->dwinfo_, child_offset, this);
+ // The Dwarf_die ctor will set this DIE's sibling offset
+ // when it reads a zero abbrev code.
+ if (die.tag() == 0)
+ break;
+ child_offset = die.sibling_offset();
+ }
+
+ // This should be set by now. If not, there was a problem reading
+ // the DWARF info, and we return 0.
+ return this->sibling_offset_;
+}
+
+// class Dwarf_info_reader
+
+// Check that the pointer P is within the current compilation unit.
+
+inline bool
+Dwarf_info_reader::check_buffer(const unsigned char* p) const
+{
+ if (p > this->buffer_ + this->cu_offset_ + this->cu_length_)
+ {
+ gold_warning(_("%s: corrupt debug info in %s"),
+ this->object_->name().c_str(),
+ this->object_->section_name(this->shndx_).c_str());
+ return false;
+ }
+ return true;
+}
+
+// Begin parsing the debug info. This calls visit_compilation_unit()
+// or visit_type_unit() for each compilation or type unit found in the
+// section, and visit_die() for each top-level DIE.
+
+void
+Dwarf_info_reader::parse()
+{
+ switch (parameters->size_and_endianness())
+ {
+#ifdef HAVE_TARGET_32_LITTLE
+ case Parameters::TARGET_32_LITTLE:
+ this->do_parse<false>();
+ break;
+#endif
+#ifdef HAVE_TARGET_32_BIG
+ case Parameters::TARGET_32_BIG:
+ this->do_parse<true>();
+ break;
+#endif
+#ifdef HAVE_TARGET_64_LITTLE
+ case Parameters::TARGET_64_LITTLE:
+ this->do_parse<false>();
+ break;
+#endif
+#ifdef HAVE_TARGET_64_BIG
+ case Parameters::TARGET_64_BIG:
+ this->do_parse<true>();
+ break;
+#endif
+ default:
+ gold_unreachable();
+ }
+}
+
+template<bool big_endian>
+void
+Dwarf_info_reader::do_parse()
+{
+ // Get the section contents and decompress if necessary.
+ section_size_type buffer_size;
+ bool buffer_is_new;
+ this->buffer_ = this->object_->decompressed_section_contents(this->shndx_,
+ &buffer_size,
+ &buffer_is_new);
+ if (this->buffer_ == NULL || buffer_size == 0)
+ return;
+ this->buffer_end_ = this->buffer_ + buffer_size;
+
+ // The offset of this input section in the output section.
+ off_t section_offset = this->object_->output_section_offset(this->shndx_);
+
+ // Start tracking relocations for this section.
+ this->reloc_mapper_ = make_elf_reloc_mapper(this->object_, this->symtab_,
+ this->symtab_size_);
+ this->reloc_mapper_->initialize(this->reloc_shndx_, this->reloc_type_);
+
+ // Loop over compilation units (or type units).
+ unsigned int abbrev_shndx = 0;
+ off_t abbrev_offset = 0;
+ const unsigned char* pinfo = this->buffer_;
+ while (pinfo < this->buffer_end_)
+ {
+ // Read the compilation (or type) unit header.
+ const unsigned char* cu_start = pinfo;
+ this->cu_offset_ = cu_start - this->buffer_;
+ this->cu_length_ = this->buffer_end_ - cu_start;
+
+ // Read unit_length (4 or 12 bytes).
+ if (!this->check_buffer(pinfo + 4))
+ break;
+ uint32_t unit_length =
+ elfcpp::Swap_unaligned<32, big_endian>::readval(pinfo);
+ pinfo += 4;
+ if (unit_length == 0xffffffff)
+ {
+ if (!this->check_buffer(pinfo + 8))
+ break;
+ unit_length = elfcpp::Swap_unaligned<64, big_endian>::readval(pinfo);
+ pinfo += 8;
+ this->offset_size_ = 8;
+ }
+ else
+ this->offset_size_ = 4;
+ if (!this->check_buffer(pinfo + unit_length))
+ break;
+ const unsigned char* cu_end = pinfo + unit_length;
+ this->cu_length_ = cu_end - cu_start;
+ if (!this->check_buffer(pinfo + 2 + this->offset_size_ + 1))
+ break;
+
+ // Read version (2 bytes).
+ this->cu_version_ =
+ elfcpp::Swap_unaligned<16, big_endian>::readval(pinfo);
+ pinfo += 2;
+
+ // Read debug_abbrev_offset (4 or 8 bytes).
+ if (this->offset_size_ == 4)
+ abbrev_offset = elfcpp::Swap_unaligned<32, big_endian>::readval(pinfo);
+ else
+ abbrev_offset = elfcpp::Swap_unaligned<64, big_endian>::readval(pinfo);
+ if (this->reloc_shndx_ > 0)
+ {
+ off_t reloc_offset = pinfo - this->buffer_;
+ off_t value;
+ abbrev_shndx =
+ this->reloc_mapper_->get_reloc_target(reloc_offset, &value);
+ if (abbrev_shndx == 0)
+ return;
+ if (this->reloc_type_ == elfcpp::SHT_REL)
+ abbrev_offset += value;
+ else
+ abbrev_offset = value;
+ }
+ pinfo += this->offset_size_;
+
+ // Read address_size (1 byte).
+ this->address_size_ = *pinfo++;
+
+ // For type units, read the two extra fields.
+ uint64_t signature = 0;
+ off_t type_offset = 0;
+ if (this->is_type_unit_)
+ {
+ if (!this->check_buffer(pinfo + 8 + this->offset_size_))
+ break;
+
+ // Read type_signature (8 bytes).
+ signature = elfcpp::Swap_unaligned<64, big_endian>::readval(pinfo);
+ pinfo += 8;
+
+ // Read type_offset (4 or 8 bytes).
+ if (this->offset_size_ == 4)
+ type_offset =
+ elfcpp::Swap_unaligned<32, big_endian>::readval(pinfo);
+ else
+ type_offset =
+ elfcpp::Swap_unaligned<64, big_endian>::readval(pinfo);
+ pinfo += this->offset_size_;
+ }
+
+ // Read the .debug_abbrev table.
+ this->abbrev_table_.read_abbrevs(this->object_, abbrev_shndx,
+ abbrev_offset);
+
+ // Visit the root DIE.
+ Dwarf_die root_die(this,
+ pinfo - (this->buffer_ + this->cu_offset_),
+ NULL);
+ if (root_die.tag() != 0)
+ {
+ // Visit the CU or TU.
+ if (this->is_type_unit_)
+ this->visit_type_unit(section_offset + this->cu_offset_,
+ type_offset, signature, &root_die);
+ else
+ this->visit_compilation_unit(section_offset + this->cu_offset_,
+ cu_end - cu_start, &root_die);
+ }
+
+ // Advance to the next CU.
+ pinfo = cu_end;
+ }
+
+ if (buffer_is_new)
+ {
+ delete[] this->buffer_;
+ this->buffer_ = NULL;
+ }
+}
+
+// Read the DWARF string table.
+
+bool
+Dwarf_info_reader::do_read_string_table(unsigned int string_shndx)
+{
+ Relobj* object = this->object_;
+
+ // If we don't have relocations, string_shndx will be 0, and
+ // we'll have to hunt for the .debug_str section.
+ if (string_shndx == 0)
+ {
+ for (unsigned int i = 1; i < this->object_->shnum(); ++i)
+ {
+ std::string name = object->section_name(i);
+ if (name == ".debug_str")
+ {
+ string_shndx = i;
+ this->string_output_section_offset_ =
+ object->output_section_offset(i);
+ break;
+ }
+ }
+ if (string_shndx == 0)
+ return false;
+ }
+
+ if (this->owns_string_buffer_ && this->string_buffer_ != NULL)
+ {
+ delete[] this->string_buffer_;
+ this->owns_string_buffer_ = false;
+ }
+
+ // Get the secton contents and decompress if necessary.
+ section_size_type buffer_size;
+ const unsigned char* buffer =
+ object->decompressed_section_contents(string_shndx,
+ &buffer_size,
+ &this->owns_string_buffer_);
+ this->string_buffer_ = reinterpret_cast<const char*>(buffer);
+ this->string_buffer_end_ = this->string_buffer_ + buffer_size;
+ this->string_shndx_ = string_shndx;
+ return true;
+}
+
+// Look for a relocation at offset ATTR_OFF in the dwarf info,
+// and return the section index and offset of the target.
+
+unsigned int
+Dwarf_info_reader::lookup_reloc(off_t attr_off, off_t* target_off)
+{
+ off_t value;
+ attr_off += this->cu_offset_;
+ unsigned int shndx = this->reloc_mapper_->get_reloc_target(attr_off, &value);
+ if (shndx == 0)
+ return 0;
+ if (this->reloc_type_ == elfcpp::SHT_REL)
+ *target_off += value;
+ else
+ *target_off = value;
+ return shndx;
+}
+
+// Return a string from the DWARF string table.
+
+const char*
+Dwarf_info_reader::get_string(off_t str_off, unsigned int string_shndx)
+{
+ if (!this->read_string_table(string_shndx))
+ return NULL;
+
+ // Correct the offset. For incremental update links, we have a
+ // relocated offset that is relative to the output section, but
+ // here we need an offset relative to the input section.
+ str_off -= this->string_output_section_offset_;
+
+ const char* p = this->string_buffer_ + str_off;
+
+ if (p < this->string_buffer_ || p >= this->string_buffer_end_)
+ return NULL;
+
+ return p;
+}
+
+// The following are default, do-nothing, implementations of the
+// hook methods normally provided by a derived class. We provide
+// default implementations rather than no implementation so that
+// a derived class needs to implement only the hooks that it needs
+// to use.
+
+// Process a compilation unit and parse its child DIE.
+
+void
+Dwarf_info_reader::visit_compilation_unit(off_t, off_t, Dwarf_die*)
+{
+}
+
+// Process a type unit and parse its child DIE.
+
+void
+Dwarf_info_reader::visit_type_unit(off_t, off_t, uint64_t, Dwarf_die*)
+{
+}
+
+// class Sized_dwarf_line_info
+
struct LineStateMachine
{
int file_num;
@@ -66,7 +1427,8 @@ Sized_dwarf_line_info<size, big_endian>::Sized_dwarf_line_info(
Object* object,
unsigned int read_shndx)
: data_valid_(false), buffer_(NULL), buffer_start_(NULL),
- symtab_buffer_(NULL), directories_(), files_(), current_header_index_(-1)
+ reloc_mapper_(NULL), symtab_buffer_(NULL), directories_(), files_(),
+ current_header_index_(-1)
{
unsigned int debug_shndx;
@@ -92,42 +1454,46 @@ Sized_dwarf_line_info<size, big_endian>::Sized_dwarf_line_info(
// Find the relocation section for ".debug_line".
// We expect these for relobjs (.o's) but not dynobjs (.so's).
- bool got_relocs = false;
- for (unsigned int reloc_shndx = 0;
- reloc_shndx < object->shnum();
- ++reloc_shndx)
+ unsigned int reloc_shndx = 0;
+ for (unsigned int i = 0; i < object->shnum(); ++i)
{
- unsigned int reloc_sh_type = object->section_type(reloc_shndx);
+ unsigned int reloc_sh_type = object->section_type(i);
if ((reloc_sh_type == elfcpp::SHT_REL
|| reloc_sh_type == elfcpp::SHT_RELA)
- && object->section_info(reloc_shndx) == debug_shndx)
+ && object->section_info(i) == debug_shndx)
{
- got_relocs = this->track_relocs_.initialize(object, reloc_shndx,
- reloc_sh_type);
+ reloc_shndx = i;
this->track_relocs_type_ = reloc_sh_type;
break;
}
}
// Finally, we need the symtab section to interpret the relocs.
- if (got_relocs)
+ if (reloc_shndx != 0)
{
unsigned int symtab_shndx;
for (symtab_shndx = 0; symtab_shndx < object->shnum(); ++symtab_shndx)
if (object->section_type(symtab_shndx) == elfcpp::SHT_SYMTAB)
{
- this->symtab_buffer_ = object->section_contents(
- symtab_shndx, &this->symtab_buffer_size_, false);
+ this->symtab_buffer_ = object->section_contents(
+ symtab_shndx, &this->symtab_buffer_size_, false);
break;
}
if (this->symtab_buffer_ == NULL)
return;
}
+ this->reloc_mapper_ =
+ new Sized_elf_reloc_mapper<size, big_endian>(object,
+ this->symtab_buffer_,
+ this->symtab_buffer_size_);
+ if (!this->reloc_mapper_->initialize(reloc_shndx, this->track_relocs_type_))
+ return;
+
// Now that we have successfully read all the data, parse the debug
// info.
this->data_valid_ = true;
- this->read_line_mappings(object, read_shndx);
+ this->read_line_mappings(read_shndx);
}
// Read the DWARF header.
@@ -504,51 +1870,28 @@ Sized_dwarf_line_info<size, big_endian>::read_lines(unsigned const char* lineptr
return lengthstart + header_.total_length;
}
-// Looks in the symtab to see what section a symbol is in.
-
-template<int size, bool big_endian>
-unsigned int
-Sized_dwarf_line_info<size, big_endian>::symbol_section(
- Object* object,
- unsigned int sym,
- typename elfcpp::Elf_types<size>::Elf_Addr* value,
- bool* is_ordinary)
-{
- const int symsize = elfcpp::Elf_sizes<size>::sym_size;
- gold_assert(sym * symsize < this->symtab_buffer_size_);
- elfcpp::Sym<size, big_endian> elfsym(this->symtab_buffer_ + sym * symsize);
- *value = elfsym.get_st_value();
- return object->adjust_sym_shndx(sym, elfsym.get_st_shndx(), is_ordinary);
-}
-
// Read the relocations into a Reloc_map.
template<int size, bool big_endian>
void
-Sized_dwarf_line_info<size, big_endian>::read_relocs(Object* object)
+Sized_dwarf_line_info<size, big_endian>::read_relocs()
{
if (this->symtab_buffer_ == NULL)
return;
- typename elfcpp::Elf_types<size>::Elf_Addr value;
+ off_t value;
off_t reloc_offset;
- while ((reloc_offset = this->track_relocs_.next_offset()) != -1)
+ while ((reloc_offset = this->reloc_mapper_->next_offset()) != -1)
{
- const unsigned int sym = this->track_relocs_.next_symndx();
-
- bool is_ordinary;
- const unsigned int shndx = this->symbol_section(object, sym, &value,
- &is_ordinary);
+ const unsigned int shndx =
+ this->reloc_mapper_->get_reloc_target(reloc_offset, &value);
// There is no reason to record non-ordinary section indexes, or
// SHN_UNDEF, because they will never match the real section.
- if (is_ordinary && shndx != elfcpp::SHN_UNDEF)
- {
- value += this->track_relocs_.next_addend();
- this->reloc_map_[reloc_offset] = std::make_pair(shndx, value);
- }
+ if (shndx != 0)
+ this->reloc_map_[reloc_offset] = std::make_pair(shndx, value);
- this->track_relocs_.advance(reloc_offset + 1);
+ this->reloc_mapper_->advance(reloc_offset + 1);
}
}
@@ -556,12 +1899,11 @@ Sized_dwarf_line_info<size, big_endian>::read_relocs(Object* object)
template<int size, bool big_endian>
void
-Sized_dwarf_line_info<size, big_endian>::read_line_mappings(Object* object,
- unsigned int shndx)
+Sized_dwarf_line_info<size, big_endian>::read_line_mappings(unsigned int shndx)
{
gold_assert(this->data_valid_ == true);
- this->read_relocs(object);
+ this->read_relocs();
while (this->buffer_ < this->buffer_end_)
{
const unsigned char* lineptr = this->buffer_;
@@ -765,7 +2107,7 @@ Sized_dwarf_line_info<size, big_endian>::format_file_lineno(
gold_assert(loc.header_num < static_cast<int>(this->files_.size()));
gold_assert(loc.file_num
- < static_cast<int>(this->files_[loc.header_num].size()));
+ < static_cast<unsigned int>(this->files_[loc.header_num].size()));
const std::pair<int, std::string>& filename_pair
= this->files_[loc.header_num][loc.file_num];
const std::string& filename = filename_pair.second;
diff --git a/gold/dwarf_reader.h b/gold/dwarf_reader.h
index 722ee64..0c3dab6 100644
--- a/gold/dwarf_reader.h
+++ b/gold/dwarf_reader.h
@@ -1,6 +1,6 @@
// dwarf_reader.h -- parse dwarf2/3 debug information for gold -*- C++ -*-
-// Copyright 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+// Copyright 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
// Written by Ian Lance Taylor <iant@google.com>.
// This file is part of gold.
@@ -26,6 +26,7 @@
#include <vector>
#include <map>
#include <limits.h>
+#include <sys/types.h>
#include "elfcpp.h"
#include "elfcpp_swap.h"
@@ -35,10 +36,815 @@
namespace gold
{
-template<int size, bool big_endian>
-class Track_relocs;
+class Dwarf_info_reader;
struct LineStateMachine;
+// This class is used to extract the section index and offset of
+// the target of a relocation for a given offset within the section.
+
+class Elf_reloc_mapper
+{
+ public:
+ Elf_reloc_mapper()
+ { }
+
+ virtual
+ ~Elf_reloc_mapper()
+ { }
+
+ // Initialize the relocation tracker for section RELOC_SHNDX.
+ bool
+ initialize(unsigned int reloc_shndx, unsigned int reloc_type)
+ { return this->do_initialize(reloc_shndx, reloc_type); }
+
+ // Return the next reloc_offset.
+ off_t
+ next_offset()
+ { return this->do_next_offset(); }
+
+ // Advance to the next relocation past OFFSET.
+ void
+ advance(off_t offset)
+ { this->do_advance(offset); }
+
+ // Return the section index and offset within the section of the target
+ // of the relocation for RELOC_OFFSET in the referring section.
+ unsigned int
+ get_reloc_target(off_t reloc_offset, off_t* target_offset)
+ { return this->do_get_reloc_target(reloc_offset, target_offset); }
+
+ // Checkpoint the current position in the reloc section.
+ uint64_t
+ checkpoint() const
+ { return this->do_checkpoint(); }
+
+ // Reset the current position to the CHECKPOINT.
+ void
+ reset(uint64_t checkpoint)
+ { this->do_reset(checkpoint); }
+
+ protected:
+ virtual bool
+ do_initialize(unsigned int, unsigned int) = 0;
+
+ // Return the next reloc_offset.
+ virtual off_t
+ do_next_offset() = 0;
+
+ // Advance to the next relocation past OFFSET.
+ virtual void
+ do_advance(off_t offset) = 0;
+
+ virtual unsigned int
+ do_get_reloc_target(off_t reloc_offset, off_t* target_offset) = 0;
+
+ // Checkpoint the current position in the reloc section.
+ virtual uint64_t
+ do_checkpoint() const = 0;
+
+ // Reset the current position to the CHECKPOINT.
+ virtual void
+ do_reset(uint64_t checkpoint) = 0;
+};
+
+template<int size, bool big_endian>
+class Sized_elf_reloc_mapper : public Elf_reloc_mapper
+{
+ public:
+ Sized_elf_reloc_mapper(Object* object, const unsigned char* symtab,
+ off_t symtab_size)
+ : object_(object), symtab_(symtab), symtab_size_(symtab_size),
+ reloc_type_(0), track_relocs_()
+ { }
+
+ protected:
+ bool
+ do_initialize(unsigned int reloc_shndx, unsigned int reloc_type);
+
+ // Return the next reloc_offset.
+ virtual off_t
+ do_next_offset()
+ { return this->track_relocs_.next_offset(); }
+
+ // Advance to the next relocation past OFFSET.
+ virtual void
+ do_advance(off_t offset)
+ { this->track_relocs_.advance(offset); }
+
+ unsigned int
+ do_get_reloc_target(off_t reloc_offset, off_t* target_offset);
+
+ // Checkpoint the current position in the reloc section.
+ uint64_t
+ do_checkpoint() const
+ { return this->track_relocs_.checkpoint(); }
+
+ // Reset the current position to the CHECKPOINT.
+ void
+ do_reset(uint64_t checkpoint)
+ { this->track_relocs_.reset(checkpoint); }
+
+ private:
+ typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
+
+ // Return the section index of symbol SYMNDX, and copy its value to *VALUE.
+ // Set *IS_ORDINARY true if the section index is an ordinary section index.
+ unsigned int
+ symbol_section(unsigned int symndx, Address* value, bool* is_ordinary);
+
+ // The object file.
+ Object* object_;
+ // The ELF symbol table.
+ const unsigned char* symtab_;
+ // The size of the ELF symbol table.
+ off_t symtab_size_;
+ // Type of the relocation section (SHT_REL or SHT_RELA).
+ unsigned int reloc_type_;
+ // Relocations for the referring section.
+ Track_relocs<size, big_endian> track_relocs_;
+};
+
+// This class is used to read the abbreviations table from the
+// .debug_abbrev section of the object file.
+
+class Dwarf_abbrev_table
+{
+ public:
+ // An attribute list entry.
+ struct Attribute
+ {
+ Attribute(unsigned int a, unsigned int f)
+ : attr(a), form(f)
+ { }
+ unsigned int attr;
+ unsigned int form;
+ };
+
+ // An abbrev code entry.
+ struct Abbrev_code
+ {
+ Abbrev_code(unsigned int t, bool hc)
+ : tag(t), has_children(hc), has_sibling_attribute(false), attributes()
+ {
+ this->attributes.reserve(10);
+ }
+
+ void
+ add_attribute(unsigned int attr, unsigned int form)
+ {
+ this->attributes.push_back(Attribute(attr, form));
+ }
+
+ // The DWARF tag.
+ unsigned int tag;
+ // True if the DIE has children.
+ bool has_children : 1;
+ // True if the DIE has a sibling attribute.
+ bool has_sibling_attribute : 1;
+ // The list of attributes and forms.
+ std::vector<Attribute> attributes;
+ };
+
+ Dwarf_abbrev_table()
+ : abbrev_shndx_(0), abbrev_offset_(0), buffer_(NULL), buffer_end_(NULL),
+ owns_buffer_(false), buffer_pos_(NULL), high_abbrev_codes_()
+ {
+ memset(this->low_abbrev_codes_, 0, sizeof(this->low_abbrev_codes_));
+ }
+
+ ~Dwarf_abbrev_table()
+ {
+ if (this->owns_buffer_ && this->buffer_ != NULL)
+ delete[] this->buffer_;
+ this->clear_abbrev_codes();
+ }
+
+ // Read the abbrev table from an object file.
+ bool
+ read_abbrevs(Relobj* object,
+ unsigned int abbrev_shndx,
+ off_t abbrev_offset)
+ {
+ // If we've already read this abbrev table, return immediately.
+ if (this->abbrev_shndx_ > 0
+ && this->abbrev_shndx_ == abbrev_shndx
+ && this->abbrev_offset_ == abbrev_offset)
+ return true;
+ return this->do_read_abbrevs(object, abbrev_shndx, abbrev_offset);
+ }
+
+ // Return the abbrev code entry for CODE. This is a fast path for
+ // abbrev codes that are in the direct lookup table. If not found
+ // there, we call do_get_abbrev() to do the hard work.
+ const Abbrev_code*
+ get_abbrev(unsigned int code)
+ {
+ if (code < this->low_abbrev_code_max_
+ && this->low_abbrev_codes_[code] != NULL)
+ return this->low_abbrev_codes_[code];
+ return this->do_get_abbrev(code);
+ }
+
+ private:
+ // Read the abbrev table from an object file.
+ bool
+ do_read_abbrevs(Relobj* object,
+ unsigned int abbrev_shndx,
+ off_t abbrev_offset);
+
+ // Lookup the abbrev code entry for CODE.
+ const Abbrev_code*
+ do_get_abbrev(unsigned int code);
+
+ // Store an abbrev code entry for CODE.
+ void
+ store_abbrev(unsigned int code, const Abbrev_code* entry)
+ {
+ if (code < this->low_abbrev_code_max_)
+ this->low_abbrev_codes_[code] = entry;
+ else
+ this->high_abbrev_codes_[code] = entry;
+ }
+
+ // Clear the abbrev code table and release the memory it uses.
+ void
+ clear_abbrev_codes();
+
+ typedef Unordered_map<unsigned int, const Abbrev_code*> Abbrev_code_table;
+
+ // The section index of the current abbrev table.
+ unsigned int abbrev_shndx_;
+ // The offset within the section of the current abbrev table.
+ off_t abbrev_offset_;
+ // The buffer containing the .debug_abbrev section.
+ const unsigned char* buffer_;
+ const unsigned char* buffer_end_;
+ // True if this object owns the buffer and needs to delete it.
+ bool owns_buffer_;
+ // Pointer to the current position in the buffer.
+ const unsigned char* buffer_pos_;
+ // The table of abbrev codes.
+ // We use a direct-lookup array for low abbrev codes,
+ // and store the rest in a hash table.
+ static const unsigned int low_abbrev_code_max_ = 256;
+ const Abbrev_code* low_abbrev_codes_[low_abbrev_code_max_];
+ Abbrev_code_table high_abbrev_codes_;
+};
+
+// A DWARF range list. The start and end offsets are relative
+// to the input section SHNDX. Each range must lie entirely
+// within a single section.
+
+class Dwarf_range_list
+{
+ public:
+ struct Range
+ {
+ Range(unsigned int a_shndx, off_t a_start, off_t a_end)
+ : shndx(a_shndx), start(a_start), end(a_end)
+ { }
+
+ unsigned int shndx;
+ off_t start;
+ off_t end;
+ };
+
+ Dwarf_range_list()
+ : range_list_()
+ { }
+
+ void
+ add(unsigned int shndx, off_t start, off_t end)
+ { this->range_list_.push_back(Range(shndx, start, end)); }
+
+ size_t
+ size() const
+ { return this->range_list_.size(); }
+
+ const Range&
+ operator[](off_t i) const
+ { return this->range_list_[i]; }
+
+ private:
+ std::vector<Range> range_list_;
+};
+
+// This class is used to read the ranges table from the
+// .debug_ranges section of the object file.
+
+class Dwarf_ranges_table
+{
+ public:
+ Dwarf_ranges_table()
+ : ranges_shndx_(0), ranges_buffer_(NULL), ranges_buffer_end_(NULL),
+ owns_ranges_buffer_(false), ranges_reloc_mapper_(NULL),
+ output_section_offset_(0)
+ { }
+
+ ~Dwarf_ranges_table()
+ {
+ if (this->owns_ranges_buffer_ && this->ranges_buffer_ != NULL)
+ delete[] this->ranges_buffer_;
+ if (this->ranges_reloc_mapper_ != NULL)
+ delete this->ranges_reloc_mapper_;
+ }
+
+ // Read the ranges table from an object file.
+ bool
+ read_ranges_table(Relobj* object,
+ const unsigned char* symtab,
+ off_t symtab_size,
+ unsigned int ranges_shndx);
+
+ // Read the range table from an object file.
+ Dwarf_range_list*
+ read_range_list(Relobj* object,
+ const unsigned char* symtab,
+ off_t symtab_size,
+ unsigned int address_size,
+ unsigned int ranges_shndx,
+ off_t ranges_offset);
+
+ private:
+ // The section index of the ranges table.
+ unsigned int ranges_shndx_;
+ // The buffer containing the .debug_ranges section.
+ const unsigned char* ranges_buffer_;
+ const unsigned char* ranges_buffer_end_;
+ // True if this object owns the buffer and needs to delete it.
+ bool owns_ranges_buffer_;
+ // Relocation mapper for the .debug_ranges section.
+ Elf_reloc_mapper* ranges_reloc_mapper_;
+ // For incremental update links, this will hold the offset of the
+ // input section within the output section. Offsets read from
+ // relocated data will be relative to the output section, and need
+ // to be corrected before reading data from the input section.
+ uint64_t output_section_offset_;
+};
+
+// This class is used to read the pubnames and pubtypes tables from the
+// .debug_pubnames and .debug_pubtypes sections of the object file.
+
+class Dwarf_pubnames_table
+{
+ public:
+ Dwarf_pubnames_table(bool is_pubtypes)
+ : buffer_(NULL), buffer_end_(NULL), owns_buffer_(false),
+ offset_size_(0), pinfo_(NULL), is_pubtypes_(is_pubtypes),
+ output_section_offset_(0)
+ { }
+
+ ~Dwarf_pubnames_table()
+ {
+ if (this->owns_buffer_ && this->buffer_ != NULL)
+ delete[] this->buffer_;
+ }
+
+ // Read the pubnames section SHNDX from the object file.
+ bool
+ read_section(Relobj* object, unsigned int shndx);
+
+ // Read the header for the set at OFFSET.
+ bool
+ read_header(off_t offset);
+
+ // Read the next name from the set.
+ const char*
+ next_name();
+
+ private:
+ // The buffer containing the .debug_ranges section.
+ const unsigned char* buffer_;
+ const unsigned char* buffer_end_;
+ // True if this object owns the buffer and needs to delete it.
+ bool owns_buffer_;
+ // The size of a DWARF offset for the current set.
+ unsigned int offset_size_;
+ // The current position within the buffer.
+ const unsigned char* pinfo_;
+ // TRUE if this is a .debug_pubtypes section.
+ bool is_pubtypes_;
+ // For incremental update links, this will hold the offset of the
+ // input section within the output section. Offsets read from
+ // relocated data will be relative to the output section, and need
+ // to be corrected before reading data from the input section.
+ uint64_t output_section_offset_;
+};
+
+// This class represents a DWARF Debug Info Entry (DIE).
+
+class Dwarf_die
+{
+ public:
+ // An attribute value.
+ struct Attribute_value
+ {
+ unsigned int attr;
+ unsigned int form;
+ union
+ {
+ int64_t intval;
+ uint64_t uintval;
+ const char* stringval;
+ const unsigned char* blockval;
+ off_t refval;
+ } val;
+ union
+ {
+ // Section index for reference forms.
+ unsigned int shndx;
+ // Block length for block forms.
+ unsigned int blocklen;
+ // Attribute offset for DW_FORM_strp.
+ unsigned int attr_off;
+ } aux;
+ };
+
+ // A list of attribute values.
+ typedef std::vector<Attribute_value> Attributes;
+
+ Dwarf_die(Dwarf_info_reader* dwinfo,
+ off_t die_offset,
+ Dwarf_die* parent);
+
+ // Return the DWARF tag for this DIE.
+ unsigned int
+ tag() const
+ {
+ if (this->abbrev_code_ == NULL)
+ return 0;
+ return this->abbrev_code_->tag;
+ }
+
+ // Return true if this DIE has children.
+ bool
+ has_children() const
+ {
+ gold_assert(this->abbrev_code_ != NULL);
+ return this->abbrev_code_->has_children;
+ }
+
+ // Return true if this DIE has a sibling attribute.
+ bool
+ has_sibling_attribute() const
+ {
+ gold_assert(this->abbrev_code_ != NULL);
+ return this->abbrev_code_->has_sibling_attribute;
+ }
+
+ // Return the value of attribute ATTR.
+ const Attribute_value*
+ attribute(unsigned int attr);
+
+ // Return the value of the DW_AT_name attribute.
+ const char*
+ name()
+ {
+ if (this->name_ == NULL)
+ this->set_name();
+ return this->name_;
+ }
+
+ // Return the value of the DW_AT_linkage_name
+ // or DW_AT_MIPS_linkage_name attribute.
+ const char*
+ linkage_name()
+ {
+ if (this->linkage_name_ == NULL)
+ this->set_linkage_name();
+ return this->linkage_name_;
+ }
+
+ // Return the value of the DW_AT_specification attribute.
+ off_t
+ specification()
+ {
+ if (!this->attributes_read_)
+ this->read_attributes();
+ return this->specification_;
+ }
+
+ // Return the value of the DW_AT_abstract_origin attribute.
+ off_t
+ abstract_origin()
+ {
+ if (!this->attributes_read_)
+ this->read_attributes();
+ return this->abstract_origin_;
+ }
+
+ // Return the value of attribute ATTR as a string.
+ const char*
+ string_attribute(unsigned int attr);
+
+ // Return the value of attribute ATTR as an integer.
+ int64_t
+ int_attribute(unsigned int attr);
+
+ // Return the value of attribute ATTR as an unsigned integer.
+ uint64_t
+ uint_attribute(unsigned int attr);
+
+ // Return the value of attribute ATTR as a reference.
+ off_t
+ ref_attribute(unsigned int attr,
+ unsigned int* shndx);
+
+ // Return the value of attribute ATTR as a flag.
+ bool
+ flag_attribute(unsigned int attr)
+ { return this->int_attribute(attr) != 0; }
+
+ // Return true if this DIE is a declaration.
+ bool
+ is_declaration()
+ { return this->flag_attribute(elfcpp::DW_AT_declaration); }
+
+ // Return the parent of this DIE.
+ Dwarf_die*
+ parent() const
+ { return this->parent_; }
+
+ // Return the offset of this DIE.
+ off_t
+ offset() const
+ { return this->die_offset_; }
+
+ // Return the offset of this DIE's first child.
+ off_t
+ child_offset();
+
+ // Set the offset of this DIE's next sibling.
+ void
+ set_sibling_offset(off_t sibling_offset)
+ { this->sibling_offset_ = sibling_offset; }
+
+ // Return the offset of this DIE's next sibling.
+ off_t
+ sibling_offset();
+
+ private:
+ typedef Dwarf_abbrev_table::Abbrev_code Abbrev_code;
+
+ // Read all the attributes of the DIE.
+ bool
+ read_attributes();
+
+ // Set the name of the DIE if present.
+ void
+ set_name();
+
+ // Set the linkage name if present.
+ void
+ set_linkage_name();
+
+ // Skip all the attributes of the DIE and return the offset
+ // of the next DIE.
+ off_t
+ skip_attributes();
+
+ // The Dwarf_info_reader, for reading attributes.
+ Dwarf_info_reader* dwinfo_;
+ // The parent of this DIE.
+ Dwarf_die* parent_;
+ // Offset of this DIE within its compilation unit.
+ off_t die_offset_;
+ // Offset of the first attribute, relative to the beginning of the DIE.
+ off_t attr_offset_;
+ // Offset of the first child, relative to the compilation unit.
+ off_t child_offset_;
+ // Offset of the next sibling, relative to the compilation unit.
+ off_t sibling_offset_;
+ // The abbreviation table entry.
+ const Abbrev_code* abbrev_code_;
+ // The list of attributes.
+ Attributes attributes_;
+ // True if the attributes have been read.
+ bool attributes_read_;
+ // The following fields hold common attributes to avoid a linear
+ // search through the attribute list.
+ // The DIE name (DW_AT_name).
+ const char* name_;
+ // Offset of the name in the string table (for DW_FORM_strp).
+ off_t name_off_;
+ // The linkage name (DW_AT_linkage_name or DW_AT_MIPS_linkage_name).
+ const char* linkage_name_;
+ // Offset of the linkage name in the string table (for DW_FORM_strp).
+ off_t linkage_name_off_;
+ // Section index of the string table (for DW_FORM_strp).
+ unsigned int string_shndx_;
+ // The value of a DW_AT_specification attribute.
+ off_t specification_;
+ // The value of a DW_AT_abstract_origin attribute.
+ off_t abstract_origin_;
+};
+
+// This class is used to read the debug info from the .debug_info
+// or .debug_types sections. This is a base class that implements
+// the generic parsing of the compilation unit header and DIE
+// structure. The parse() method parses the entire section, and
+// calls the various visit_xxx() methods for each header. Clients
+// should derive a new class from this one and implement the
+// visit_compilation_unit() and visit_type_unit() functions.
+
+class Dwarf_info_reader
+{
+ public:
+ Dwarf_info_reader(bool is_type_unit,
+ Relobj* object,
+ const unsigned char* symtab,
+ off_t symtab_size,
+ unsigned int shndx,
+ unsigned int reloc_shndx,
+ unsigned int reloc_type)
+ : is_type_unit_(is_type_unit), object_(object), symtab_(symtab),
+ symtab_size_(symtab_size), shndx_(shndx), reloc_shndx_(reloc_shndx),
+ reloc_type_(reloc_type), string_shndx_(0), buffer_(NULL),
+ buffer_end_(NULL), cu_offset_(0), cu_length_(0), offset_size_(0),
+ address_size_(0), cu_version_(0), type_signature_(0), type_offset_(0),
+ abbrev_table_(), reloc_mapper_(NULL), string_buffer_(NULL),
+ string_buffer_end_(NULL), owns_string_buffer_(false),
+ string_output_section_offset_(0)
+ { }
+
+ virtual
+ ~Dwarf_info_reader()
+ {
+ if (this->reloc_mapper_ != NULL)
+ delete this->reloc_mapper_;
+ if (this->owns_string_buffer_ && this->string_buffer_ != NULL)
+ delete[] this->string_buffer_;
+ }
+
+ // Begin parsing the debug info. This calls visit_compilation_unit()
+ // or visit_type_unit() for each compilation or type unit found in the
+ // section, and visit_die() for each top-level DIE.
+ void
+ parse();
+
+ // Return the abbrev code entry for a CODE.
+ const Dwarf_abbrev_table::Abbrev_code*
+ get_abbrev(unsigned int code)
+ { return this->abbrev_table_.get_abbrev(code); }
+
+ // Return a pointer to the DWARF info buffer at OFFSET.
+ const unsigned char*
+ buffer_at_offset(off_t offset) const
+ {
+ const unsigned char* p = this->buffer_ + this->cu_offset_ + offset;
+ if (this->check_buffer(p + 1))
+ return p;
+ return NULL;
+ }
+
+ // Look for a relocation at offset ATTR_OFF in the dwarf info,
+ // and return the section index and offset of the target.
+ unsigned int
+ lookup_reloc(off_t attr_off, off_t* target_off);
+
+ // Return a string from the DWARF string table.
+ const char*
+ get_string(off_t str_off, unsigned int string_shndx);
+
+ // Return the size of a DWARF offset.
+ unsigned int
+ offset_size() const
+ { return this->offset_size_; }
+
+ // Return the size of an address.
+ unsigned int
+ address_size() const
+ { return this->address_size_; }
+
+ protected:
+ // Begin parsing the debug info. This calls visit_compilation_unit()
+ // or visit_type_unit() for each compilation or type unit found in the
+ // section, and visit_die() for each top-level DIE.
+ template<bool big_endian>
+ void
+ do_parse();
+
+ // The following methods are hooks that are meant to be implemented
+ // by a derived class. A default, do-nothing, implementation of
+ // each is provided for this base class.
+
+ // Visit a compilation unit.
+ virtual void
+ visit_compilation_unit(off_t cu_offset, off_t cu_length, Dwarf_die* root_die);
+
+ // Visit a type unit.
+ virtual void
+ visit_type_unit(off_t tu_offset, off_t type_offset, uint64_t signature,
+ Dwarf_die* root_die);
+
+ // Read the range table.
+ Dwarf_range_list*
+ read_range_list(unsigned int ranges_shndx, off_t ranges_offset)
+ {
+ return this->ranges_table_.read_range_list(this->object_,
+ this->symtab_,
+ this->symtab_size_,
+ this->address_size_,
+ ranges_shndx,
+ ranges_offset);
+ }
+
+ // Return the object.
+ Relobj*
+ object() const
+ { return this->object_; }
+
+ // Return a pointer to the object file's ELF symbol table.
+ const unsigned char*
+ symtab() const
+ { return this->symtab_; }
+
+ // Return the size of the object file's ELF symbol table.
+ off_t
+ symtab_size() const
+ { return this->symtab_size_; }
+
+ // Checkpoint the relocation tracker.
+ uint64_t
+ get_reloc_checkpoint() const
+ { return this->reloc_mapper_->checkpoint(); }
+
+ // Reset the relocation tracker to the CHECKPOINT.
+ void
+ reset_relocs(uint64_t checkpoint)
+ { this->reloc_mapper_->reset(checkpoint); }
+
+ private:
+ // Check that P is within the bounds of the current section.
+ bool
+ check_buffer(const unsigned char* p) const;
+
+ // Read the DWARF string table.
+ bool
+ read_string_table(unsigned int string_shndx)
+ {
+ // If we've already read this string table, return immediately.
+ if (this->string_shndx_ > 0 && this->string_shndx_ == string_shndx)
+ return true;
+ if (string_shndx == 0 && this->string_shndx_ > 0)
+ return true;
+ return this->do_read_string_table(string_shndx);
+ }
+
+ bool
+ do_read_string_table(unsigned int string_shndx);
+
+ // True if this is a type unit; false for a compilation unit.
+ bool is_type_unit_;
+ // The object containing the .debug_info or .debug_types input section.
+ Relobj* object_;
+ // The ELF symbol table.
+ const unsigned char* symtab_;
+ // The size of the ELF symbol table.
+ off_t symtab_size_;
+ // Index of the .debug_info or .debug_types section.
+ unsigned int shndx_;
+ // Index of the relocation section.
+ unsigned int reloc_shndx_;
+ // Type of the relocation section (SHT_REL or SHT_RELA).
+ unsigned int reloc_type_;
+ // Index of the .debug_str section.
+ unsigned int string_shndx_;
+ // The buffer for the debug info.
+ const unsigned char* buffer_;
+ const unsigned char* buffer_end_;
+ // Offset of the current compilation unit.
+ off_t cu_offset_;
+ // Length of the current compilation unit.
+ off_t cu_length_;
+ // Size of a DWARF offset for the current compilation unit.
+ unsigned int offset_size_;
+ // Size of an address for the target architecture.
+ unsigned int address_size_;
+ // Compilation unit version number.
+ unsigned int cu_version_;
+ // Type signature (for a type unit).
+ uint64_t type_signature_;
+ // Offset from the type unit header to the type DIE (for a type unit).
+ off_t type_offset_;
+ // Abbreviations table for current compilation unit.
+ Dwarf_abbrev_table abbrev_table_;
+ // Ranges table for the current compilation unit.
+ Dwarf_ranges_table ranges_table_;
+ // Relocation mapper for the section.
+ Elf_reloc_mapper* reloc_mapper_;
+ // The buffer for the debug string table.
+ const char* string_buffer_;
+ const char* string_buffer_end_;
+ // True if this object owns the buffer and needs to delete it.
+ bool owns_string_buffer_;
+ // For incremental update links, this will hold the offset of the
+ // input .debug_str section within the output section. Offsets read
+ // from relocated data will be relative to the output section, and need
+ // to be corrected before reading data from the input section.
+ uint64_t string_output_section_offset_;
+};
+
// We can't do better than to keep the offsets in a sorted vector.
// Here, offset is the key, and file_num/line_num is the value.
struct Offset_to_lineno_entry
@@ -140,18 +946,12 @@ class Sized_dwarf_line_info : public Dwarf_line_info
// If SHNDX is non-negative, only store debug information that
// pertains to the specified section.
void
- read_line_mappings(Object*, unsigned int shndx);
+ read_line_mappings(unsigned int shndx);
// Reads the relocation section associated with .debug_line and
// stores relocation information in reloc_map_.
void
- read_relocs(Object*);
-
- // Looks in the symtab to see what section a symbol is in.
- unsigned int
- symbol_section(Object*, unsigned int sym,
- typename elfcpp::Elf_types<size>::Elf_Addr* value,
- bool* is_ordinary);
+ read_relocs();
// Reads the DWARF2/3 header for this line info. Each takes as input
// a starting buffer position, and returns the ending position.
@@ -212,7 +1012,7 @@ class Sized_dwarf_line_info : public Dwarf_line_info
const unsigned char* buffer_start_;
// This has relocations that point into buffer.
- Track_relocs<size, big_endian> track_relocs_;
+ Sized_elf_reloc_mapper<size, big_endian>* reloc_mapper_;
// The type of the reloc section in track_relocs_--SHT_REL or SHT_RELA.
unsigned int track_relocs_type_;
@@ -232,9 +1032,7 @@ class Sized_dwarf_line_info : public Dwarf_line_info
// A sorted map from offset of the relocation target to the shndx
// and addend for the relocation.
- typedef std::map<typename elfcpp::Elf_types<size>::Elf_Addr,
- std::pair<unsigned int,
- typename elfcpp::Elf_types<size>::Elf_Swxword> >
+ typedef std::map<off_t, std::pair<unsigned int, off_t> >
Reloc_map;
Reloc_map reloc_map_;
diff --git a/gold/dynobj.h b/gold/dynobj.h
index 186b67c..e027485 100644
--- a/gold/dynobj.h
+++ b/gold/dynobj.h
@@ -208,9 +208,19 @@ class Sized_dynobj : public Dynobj
// Return a view of the contents of a section. Set *PLEN to the
// size.
- Object::Location
- do_section_contents(unsigned int shndx)
- { return this->elf_file_.section_contents(shndx); }
+ const unsigned char*
+ do_section_contents(unsigned int shndx, section_size_type* plen,
+ bool cache)
+ {
+ Location loc(this->elf_file_.section_contents(shndx));
+ *plen = convert_to_section_size_type(loc.data_size);
+ if (*plen == 0)
+ {
+ static const unsigned char empty[1] = { '\0' };
+ return empty;
+ }
+ return this->get_view(loc.file_offset, *plen, true, cache);
+ }
// Return section flags.
uint64_t
diff --git a/gold/gdb-index.cc b/gold/gdb-index.cc
new file mode 100644
index 0000000..11b732a
--- /dev/null
+++ b/gold/gdb-index.cc
@@ -0,0 +1,1229 @@
+// gdb-index.cc -- generate .gdb_index section for fast debug lookup
+
+// Copyright 2012 Free Software Foundation, Inc.
+// Written by Cary Coutant <ccoutant@google.com>.
+
+// This file is part of gold.
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+// MA 02110-1301, USA.
+
+#include "gold.h"
+
+#include "gdb-index.h"
+#include "dwarf_reader.h"
+#include "dwarf.h"
+#include "object.h"
+#include "output.h"
+#include "demangle.h"
+
+namespace gold
+{
+
+const int gdb_index_version = 5;
+
+// Sizes of various records in the .gdb_index section.
+const int gdb_index_offset_size = 4;
+const int gdb_index_hdr_size = 6 * gdb_index_offset_size;
+const int gdb_index_cu_size = 16;
+const int gdb_index_tu_size = 24;
+const int gdb_index_addr_size = 16 + gdb_index_offset_size;
+const int gdb_index_sym_size = 2 * gdb_index_offset_size;
+
+// This class manages the hashed symbol table for the .gdb_index section.
+// It is essentially equivalent to the hashtab implementation in libiberty,
+// but is copied into gdb sources and here for compatibility because its
+// data structure is exposed on disk.
+
+template <typename T>
+class Gdb_hashtab
+{
+ public:
+ Gdb_hashtab()
+ : size_(0), capacity_(0), hashtab_(NULL)
+ { }
+
+ ~Gdb_hashtab()
+ {
+ for (size_t i = 0; i < this->capacity_; ++i)
+ if (this->hashtab_[i] != NULL)
+ delete this->hashtab_[i];
+ delete[] this->hashtab_;
+ }
+
+ // Add a symbol.
+ T*
+ add(T* symbol)
+ {
+ // Resize the hash table if necessary.
+ if (4 * this->size_ / 3 >= this->capacity_)
+ this->expand();
+
+ T** slot = this->find_slot(symbol);
+ if (*slot == NULL)
+ {
+ ++this->size_;
+ *slot = symbol;
+ }
+
+ return *slot;
+ }
+
+ // Return the current size.
+ size_t
+ size() const
+ { return this->size_; }
+
+ // Return the current capacity.
+ size_t
+ capacity() const
+ { return this->capacity_; }
+
+ // Return the contents of slot N.
+ T*
+ operator[](size_t n)
+ { return this->hashtab_[n]; }
+
+ private:
+ // Find a symbol in the hash table, or return an empty slot if
+ // the symbol is not in the table.
+ T**
+ find_slot(T* symbol)
+ {
+ unsigned int index = symbol->hash() & (this->capacity_ - 1);
+ unsigned int step = ((symbol->hash() * 17) & (this->capacity_ - 1)) | 1;
+
+ for (;;)
+ {
+ if (this->hashtab_[index] == NULL
+ || this->hashtab_[index]->equal(symbol))
+ return &this->hashtab_[index];
+ index = (index + step) & (this->capacity_ - 1);
+ }
+ }
+
+ // Expand the hash table.
+ void
+ expand()
+ {
+ if (this->capacity_ == 0)
+ {
+ // Allocate the hash table for the first time.
+ this->capacity_ = Gdb_hashtab::initial_size;
+ this->hashtab_ = new T*[this->capacity_];
+ memset(this->hashtab_, 0, this->capacity_ * sizeof(T*));
+ }
+ else
+ {
+ // Expand and rehash.
+ unsigned int old_cap = this->capacity_;
+ T** old_hashtab = this->hashtab_;
+ this->capacity_ *= 2;
+ this->hashtab_ = new T*[this->capacity_];
+ memset(this->hashtab_, 0, this->capacity_ * sizeof(T*));
+ for (size_t i = 0; i < old_cap; ++i)
+ {
+ if (old_hashtab[i] != NULL)
+ {
+ T** slot = this->find_slot(old_hashtab[i]);
+ *slot = old_hashtab[i];
+ }
+ }
+ delete[] old_hashtab;
+ }
+ }
+
+ // Initial size of the hash table; must be a power of 2.
+ static const int initial_size = 1024;
+ size_t size_;
+ size_t capacity_;
+ T** hashtab_;
+};
+
+// The hash function for strings in the mapped index. This is copied
+// directly from gdb/dwarf2read.c.
+
+static unsigned int
+mapped_index_string_hash(const unsigned char* str)
+{
+ unsigned int r = 0;
+ unsigned char c;
+
+ while ((c = *str++) != 0)
+ {
+ if (gdb_index_version >= 5)
+ c = tolower (c);
+ r = r * 67 + c - 113;
+ }
+
+ return r;
+}
+
+// A specialization of Dwarf_info_reader, for building the .gdb_index.
+
+class Gdb_index_info_reader : public Dwarf_info_reader
+{
+ public:
+ Gdb_index_info_reader(bool is_type_unit,
+ Relobj* object,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ unsigned int shndx,
+ unsigned int reloc_shndx,
+ unsigned int reloc_type,
+ Gdb_index* gdb_index)
+ : Dwarf_info_reader(is_type_unit, object, symbols, symbols_size, shndx,
+ reloc_shndx, reloc_type),
+ gdb_index_(gdb_index), cu_index_(0), cu_language_(0)
+ { }
+
+ ~Gdb_index_info_reader()
+ { this->clear_declarations(); }
+
+ // Print usage statistics.
+ static void
+ print_stats();
+
+ protected:
+ // Visit a compilation unit.
+ virtual void
+ visit_compilation_unit(off_t cu_offset, off_t cu_length, Dwarf_die*);
+
+ // Visit a type unit.
+ virtual void
+ visit_type_unit(off_t tu_offset, off_t type_offset, uint64_t signature,
+ Dwarf_die*);
+
+ private:
+ // A map for recording DIEs we've seen that may be referred to be
+ // later DIEs (via DW_AT_specification or DW_AT_abstract_origin).
+ // The map is indexed by a DIE offset within the compile unit.
+ // PARENT_OFFSET_ is the offset of the DIE that represents the
+ // outer context, and NAME_ is a pointer to a component of the
+ // fully-qualified name.
+ // Normally, the names we point to are in a string table, so we don't
+ // have to manage them, but when we have a fully-qualified name
+ // computed, we put it in the table, and set PARENT_OFFSET_ to -1
+ // indicate a string that we are managing.
+ struct Declaration_pair
+ {
+ Declaration_pair(off_t parent_offset, const char* name)
+ : parent_offset_(parent_offset), name_(name)
+ { }
+
+ off_t parent_offset_;
+ const char* name_;
+ };
+ typedef Unordered_map<off_t, Declaration_pair> Declaration_map;
+
+ // Visit a top-level DIE.
+ void
+ visit_top_die(Dwarf_die* die);
+
+ // Visit the children of a DIE.
+ void
+ visit_children(Dwarf_die* die, Dwarf_die* context);
+
+ // Visit a DIE.
+ void
+ visit_die(Dwarf_die* die, Dwarf_die* context);
+
+ // Visit the children of a DIE.
+ void
+ visit_children_for_decls(Dwarf_die* die);
+
+ // Visit a DIE.
+ void
+ visit_die_for_decls(Dwarf_die* die, Dwarf_die* context);
+
+ // Guess a fully-qualified name for a class type, based on member function
+ // linkage names.
+ std::string
+ guess_full_class_name(Dwarf_die* die);
+
+ // Add a declaration DIE to the table of declarations.
+ void
+ add_declaration(Dwarf_die* die, Dwarf_die* context);
+
+ // Add a declaration whose fully-qualified name is already known.
+ void
+ add_declaration_with_full_name(Dwarf_die* die, const char* full_name);
+
+ // Return the context for a DIE whose parent is at DIE_OFFSET.
+ std::string
+ get_context(off_t die_offset);
+
+ // Construct a fully-qualified name for DIE.
+ std::string
+ get_qualified_name(Dwarf_die* die, Dwarf_die* context);
+
+ // Record the address ranges for a compilation unit.
+ void
+ record_cu_ranges(Dwarf_die* die);
+
+ // Read the .debug_pubnames and .debug_pubtypes tables.
+ bool
+ read_pubnames_and_pubtypes(Dwarf_die* die);
+
+ // Clear the declarations map.
+ void
+ clear_declarations();
+
+ // The Gdb_index section.
+ Gdb_index* gdb_index_;
+ // The current CU index (negative for a TU).
+ int cu_index_;
+ // The language of the current CU or TU.
+ unsigned int cu_language_;
+ // Map from DIE offset to (parent offset, name) pair,
+ // for DW_AT_specification.
+ Declaration_map declarations_;
+
+ // Statistics.
+ // Total number of DWARF compilation units processed.
+ static unsigned int dwarf_cu_count;
+ // Number of DWARF compilation units with pubnames/pubtypes.
+ static unsigned int dwarf_cu_nopubnames_count;
+ // Total number of DWARF type units processed.
+ static unsigned int dwarf_tu_count;
+ // Number of DWARF type units with pubnames/pubtypes.
+ static unsigned int dwarf_tu_nopubnames_count;
+};
+
+// Total number of DWARF compilation units processed.
+unsigned int Gdb_index_info_reader::dwarf_cu_count = 0;
+// Number of DWARF compilation units without pubnames/pubtypes.
+unsigned int Gdb_index_info_reader::dwarf_cu_nopubnames_count = 0;
+// Total number of DWARF type units processed.
+unsigned int Gdb_index_info_reader::dwarf_tu_count = 0;
+// Number of DWARF type units without pubnames/pubtypes.
+unsigned int Gdb_index_info_reader::dwarf_tu_nopubnames_count = 0;
+
+// Process a compilation unit and parse its child DIE.
+
+void
+Gdb_index_info_reader::visit_compilation_unit(off_t cu_offset, off_t cu_length,
+ Dwarf_die* root_die)
+{
+ ++Gdb_index_info_reader::dwarf_cu_count;
+ this->cu_index_ = this->gdb_index_->add_comp_unit(cu_offset, cu_length);
+ this->visit_top_die(root_die);
+}
+
+// Process a type unit and parse its child DIE.
+
+void
+Gdb_index_info_reader::visit_type_unit(off_t tu_offset, off_t type_offset,
+ uint64_t signature, Dwarf_die* root_die)
+{
+ ++Gdb_index_info_reader::dwarf_tu_count;
+ // Use a negative index to flag this as a TU instead of a CU.
+ this->cu_index_ = -1 - this->gdb_index_->add_type_unit(tu_offset, type_offset,
+ signature);
+ this->visit_top_die(root_die);
+}
+
+// Process a top-level DIE.
+// For compile_unit DIEs, record the address ranges. For all
+// interesting tags, add qualified names to the symbol table
+// and process interesting children. We may need to process
+// certain children just for saving declarations that might be
+// referenced by later DIEs with a DW_AT_specification attribute.
+
+void
+Gdb_index_info_reader::visit_top_die(Dwarf_die* die)
+{
+ this->clear_declarations();
+
+ switch (die->tag())
+ {
+ case elfcpp::DW_TAG_compile_unit:
+ case elfcpp::DW_TAG_type_unit:
+ this->cu_language_ = die->int_attribute(elfcpp::DW_AT_language);
+ // Check for languages that require specialized knowledge to
+ // construct fully-qualified names, that we don't yet support.
+ if (this->cu_language_ == elfcpp::DW_LANG_Ada83
+ || this->cu_language_ == elfcpp::DW_LANG_Fortran77
+ || this->cu_language_ == elfcpp::DW_LANG_Fortran90
+ || this->cu_language_ == elfcpp::DW_LANG_Java
+ || this->cu_language_ == elfcpp::DW_LANG_Ada95
+ || this->cu_language_ == elfcpp::DW_LANG_Fortran95)
+ {
+ gold_warning(_("%s: --gdb-index currently supports "
+ "only C and C++ languages"),
+ this->object()->name().c_str());
+ return;
+ }
+ if (die->tag() == elfcpp::DW_TAG_compile_unit)
+ this->record_cu_ranges(die);
+ // If there is a pubnames and/or pubtypes section for this
+ // compilation unit, use those; otherwise, parse the DWARF
+ // info to extract the names.
+ if (!this->read_pubnames_and_pubtypes(die))
+ {
+ if (die->tag() == elfcpp::DW_TAG_compile_unit)
+ ++Gdb_index_info_reader::dwarf_cu_nopubnames_count;
+ else
+ ++Gdb_index_info_reader::dwarf_tu_nopubnames_count;
+ this->visit_children(die, NULL);
+ }
+ break;
+ default:
+ // The top level DIE should be one of the above.
+ gold_warning(_("%s: top level DIE is not DW_TAG_compile_unit "
+ "or DW_TAG_type_unit"),
+ this->object()->name().c_str());
+ return;
+ }
+
+}
+
+// Visit the children of PARENT, looking for symbols to add to the index.
+// CONTEXT points to the DIE to use for constructing the qualified name --
+// NULL if PARENT is the top-level DIE; otherwise it is the same as PARENT.
+
+void
+Gdb_index_info_reader::visit_children(Dwarf_die* parent, Dwarf_die* context)
+{
+ off_t next_offset = 0;
+ for (off_t die_offset = parent->child_offset();
+ die_offset != 0;
+ die_offset = next_offset)
+ {
+ Dwarf_die die(this, die_offset, parent);
+ if (die.tag() == 0)
+ break;
+ this->visit_die(&die, context);
+ next_offset = die.sibling_offset();
+ }
+}
+
+// Visit a child DIE, looking for symbols to add to the index.
+// CONTEXT is the parent DIE, used for constructing the qualified name;
+// it is NULL if the parent DIE is the top-level DIE.
+
+void
+Gdb_index_info_reader::visit_die(Dwarf_die* die, Dwarf_die* context)
+{
+ switch (die->tag())
+ {
+ case elfcpp::DW_TAG_subprogram:
+ case elfcpp::DW_TAG_constant:
+ case elfcpp::DW_TAG_variable:
+ case elfcpp::DW_TAG_enumerator:
+ case elfcpp::DW_TAG_base_type:
+ if (die->is_declaration())
+ this->add_declaration(die, context);
+ else
+ {
+ // If the DIE is not a declaration, add it to the index.
+ std::string full_name = this->get_qualified_name(die, context);
+ if (!full_name.empty())
+ this->gdb_index_->add_symbol(this->cu_index_, full_name.c_str());
+ }
+ break;
+ case elfcpp::DW_TAG_typedef:
+ case elfcpp::DW_TAG_union_type:
+ case elfcpp::DW_TAG_class_type:
+ case elfcpp::DW_TAG_interface_type:
+ case elfcpp::DW_TAG_structure_type:
+ case elfcpp::DW_TAG_enumeration_type:
+ case elfcpp::DW_TAG_subrange_type:
+ case elfcpp::DW_TAG_namespace:
+ {
+ std::string full_name;
+
+ // For classes at the top level, we need to look for a
+ // member function with a linkage name in order to get
+ // the properly-canonicalized name.
+ if (context == NULL
+ && (die->tag() == elfcpp::DW_TAG_class_type
+ || die->tag() == elfcpp::DW_TAG_structure_type
+ || die->tag() == elfcpp::DW_TAG_union_type))
+ full_name.assign(this->guess_full_class_name(die));
+
+ // Because we will visit the children, we need to add this DIE
+ // to the declarations table.
+ if (full_name.empty())
+ this->add_declaration(die, context);
+ else
+ this->add_declaration_with_full_name(die, full_name.c_str());
+
+ // If the DIE is not a declaration, add it to the index.
+ // Gdb stores a namespace in the index even when it is
+ // a declaration.
+ if (die->tag() == elfcpp::DW_TAG_namespace
+ || !die->is_declaration())
+ {
+ if (full_name.empty())
+ full_name = this->get_qualified_name(die, context);
+ if (!full_name.empty())
+ this->gdb_index_->add_symbol(this->cu_index_,
+ full_name.c_str());
+ }
+
+ // We're interested in the children only for namespaces and
+ // enumeration types. For enumeration types, we do not include
+ // the enumeration tag as part of the full name. For other tags,
+ // visit the children only to collect declarations.
+ if (die->tag() == elfcpp::DW_TAG_namespace
+ || die->tag() == elfcpp::DW_TAG_enumeration_type)
+ this->visit_children(die, die);
+ else
+ this->visit_children_for_decls(die);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+// Visit the children of PARENT, looking only for declarations that
+// may be referenced by later specification DIEs.
+
+void
+Gdb_index_info_reader::visit_children_for_decls(Dwarf_die* parent)
+{
+ off_t next_offset = 0;
+ for (off_t die_offset = parent->child_offset();
+ die_offset != 0;
+ die_offset = next_offset)
+ {
+ Dwarf_die die(this, die_offset, parent);
+ if (die.tag() == 0)
+ break;
+ this->visit_die_for_decls(&die, parent);
+ next_offset = die.sibling_offset();
+ }
+}
+
+// Visit a child DIE, looking only for declarations that
+// may be referenced by later specification DIEs.
+
+void
+Gdb_index_info_reader::visit_die_for_decls(Dwarf_die* die, Dwarf_die* context)
+{
+ switch (die->tag())
+ {
+ case elfcpp::DW_TAG_subprogram:
+ case elfcpp::DW_TAG_constant:
+ case elfcpp::DW_TAG_variable:
+ case elfcpp::DW_TAG_enumerator:
+ case elfcpp::DW_TAG_base_type:
+ {
+ if (die->is_declaration())
+ this->add_declaration(die, context);
+ }
+ break;
+ case elfcpp::DW_TAG_typedef:
+ case elfcpp::DW_TAG_union_type:
+ case elfcpp::DW_TAG_class_type:
+ case elfcpp::DW_TAG_interface_type:
+ case elfcpp::DW_TAG_structure_type:
+ case elfcpp::DW_TAG_enumeration_type:
+ case elfcpp::DW_TAG_subrange_type:
+ case elfcpp::DW_TAG_namespace:
+ {
+ if (die->is_declaration())
+ this->add_declaration(die, context);
+ this->visit_children_for_decls(die);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+// Extract the class name from the linkage name of a member function.
+// This code is adapted from ../gdb/cp-support.c.
+
+#define d_left(dc) (dc)->u.s_binary.left
+#define d_right(dc) (dc)->u.s_binary.right
+
+static char*
+class_name_from_linkage_name(const char* linkage_name)
+{
+ void* storage;
+ struct demangle_component* tree =
+ cplus_demangle_v3_components(linkage_name, DMGL_NO_OPTS, &storage);
+ if (tree == NULL)
+ return NULL;
+
+ int done = 0;
+
+ // First strip off any qualifiers, if we have a function or
+ // method.
+ while (!done)
+ switch (tree->type)
+ {
+ case DEMANGLE_COMPONENT_CONST:
+ case DEMANGLE_COMPONENT_RESTRICT:
+ case DEMANGLE_COMPONENT_VOLATILE:
+ case DEMANGLE_COMPONENT_CONST_THIS:
+ case DEMANGLE_COMPONENT_RESTRICT_THIS:
+ case DEMANGLE_COMPONENT_VOLATILE_THIS:
+ case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL:
+ tree = d_left(tree);
+ break;
+ default:
+ done = 1;
+ break;
+ }
+
+ // If what we have now is a function, discard the argument list.
+ if (tree->type == DEMANGLE_COMPONENT_TYPED_NAME)
+ tree = d_left(tree);
+
+ // If what we have now is a template, strip off the template
+ // arguments. The left subtree may be a qualified name.
+ if (tree->type == DEMANGLE_COMPONENT_TEMPLATE)
+ tree = d_left(tree);
+
+ // What we have now should be a name, possibly qualified.
+ // Additional qualifiers could live in the left subtree or the right
+ // subtree. Find the last piece.
+ done = 0;
+ struct demangle_component* prev_comp = NULL;
+ struct demangle_component* cur_comp = tree;
+ while (!done)
+ switch (cur_comp->type)
+ {
+ case DEMANGLE_COMPONENT_QUAL_NAME:
+ case DEMANGLE_COMPONENT_LOCAL_NAME:
+ prev_comp = cur_comp;
+ cur_comp = d_right(cur_comp);
+ break;
+ case DEMANGLE_COMPONENT_TEMPLATE:
+ case DEMANGLE_COMPONENT_NAME:
+ case DEMANGLE_COMPONENT_CTOR:
+ case DEMANGLE_COMPONENT_DTOR:
+ case DEMANGLE_COMPONENT_OPERATOR:
+ case DEMANGLE_COMPONENT_EXTENDED_OPERATOR:
+ done = 1;
+ break;
+ default:
+ done = 1;
+ cur_comp = NULL;
+ break;
+ }
+
+ char* ret = NULL;
+ if (cur_comp != NULL && prev_comp != NULL)
+ {
+ // We want to discard the rightmost child of PREV_COMP.
+ *prev_comp = *d_left(prev_comp);
+ size_t allocated_size;
+ ret = cplus_demangle_print(DMGL_NO_OPTS, tree, 30, &allocated_size);
+ }
+
+ free(storage);
+ return ret;
+}
+
+// Guess a fully-qualified name for a class type, based on member function
+// linkage names. This is needed for class/struct/union types at the
+// top level, because GCC does not always properly embed them within
+// the namespace. As in gdb, we look for a member function with a linkage
+// name and extract the qualified name from the demangled name.
+
+std::string
+Gdb_index_info_reader::guess_full_class_name(Dwarf_die* die)
+{
+ std::string full_name;
+ off_t next_offset = 0;
+
+ // This routine scans ahead in the DIE structure, possibly advancing
+ // the relocation tracker beyond the current DIE. We need to checkpoint
+ // the tracker and reset it when we're done.
+ uint64_t checkpoint = this->get_reloc_checkpoint();
+
+ for (off_t child_offset = die->child_offset();
+ child_offset != 0;
+ child_offset = next_offset)
+ {
+ Dwarf_die child(this, child_offset, die);
+ if (child.tag() == 0)
+ break;
+ if (child.tag() == elfcpp::DW_TAG_subprogram)
+ {
+ const char* linkage_name = child.linkage_name();
+ if (linkage_name != NULL)
+ {
+ char* guess = class_name_from_linkage_name(linkage_name);
+ if (guess != NULL)
+ {
+ full_name.assign(guess);
+ free(guess);
+ break;
+ }
+ }
+ }
+ next_offset = child.sibling_offset();
+ }
+
+ this->reset_relocs(checkpoint);
+ return full_name;
+}
+
+// Add a declaration DIE to the table of declarations.
+
+void
+Gdb_index_info_reader::add_declaration(Dwarf_die* die, Dwarf_die* context)
+{
+ const char* name = die->name();
+
+ off_t parent_offset = context != NULL ? context->offset() : 0;
+
+ // If this DIE has a DW_AT_specification or DW_AT_abstract_origin
+ // attribute, use the parent and name from the earlier declaration.
+ off_t spec = die->specification();
+ if (spec == 0)
+ spec = die->abstract_origin();
+ if (spec > 0)
+ {
+ Declaration_map::iterator it = this->declarations_.find(spec);
+ if (it != this->declarations_.end())
+ {
+ parent_offset = it->second.parent_offset_;
+ name = it->second.name_;
+ }
+ }
+
+ if (name == NULL)
+ {
+ if (die->tag() == elfcpp::DW_TAG_namespace)
+ name = "(anonymous namespace)";
+ else if (die->tag() == elfcpp::DW_TAG_union_type)
+ name = "(anonymous union)";
+ else
+ name = "(unknown)";
+ }
+
+ Declaration_pair decl(parent_offset, name);
+ this->declarations_.insert(std::make_pair(die->offset(), decl));
+}
+
+// Add a declaration whose fully-qualified name is already known.
+// In the case where we had to get the canonical name by demangling
+// a linkage name, this ensures we use that name instead of the one
+// provided in DW_AT_name.
+
+void
+Gdb_index_info_reader::add_declaration_with_full_name(
+ Dwarf_die* die,
+ const char* full_name)
+{
+ // We need to copy the name.
+ int len = strlen(full_name);
+ char* copy = new char[len + 1];
+ memcpy(copy, full_name, len + 1);
+
+ // Flag that we now manage the memory this points to.
+ Declaration_pair decl(-1, copy);
+ this->declarations_.insert(std::make_pair(die->offset(), decl));
+}
+
+// Return the context for a DIE whose parent is at DIE_OFFSET.
+
+std::string
+Gdb_index_info_reader::get_context(off_t die_offset)
+{
+ std::string context;
+ Declaration_map::iterator it = this->declarations_.find(die_offset);
+ if (it != this->declarations_.end())
+ {
+ off_t parent_offset = it->second.parent_offset_;
+ if (parent_offset > 0)
+ {
+ context = get_context(parent_offset);
+ context.append("::");
+ }
+ if (it->second.name_ != NULL)
+ context.append(it->second.name_);
+ }
+ return context;
+}
+
+// Construct the fully-qualified name for DIE.
+
+std::string
+Gdb_index_info_reader::get_qualified_name(Dwarf_die* die, Dwarf_die* context)
+{
+ std::string full_name;
+ const char* name = die->name();
+
+ off_t parent_offset = context != NULL ? context->offset() : 0;
+
+ // If this DIE has a DW_AT_specification or DW_AT_abstract_origin
+ // attribute, use the parent and name from the earlier declaration.
+ off_t spec = die->specification();
+ if (spec == 0)
+ spec = die->abstract_origin();
+ if (spec > 0)
+ {
+ Declaration_map::iterator it = this->declarations_.find(spec);
+ if (it != this->declarations_.end())
+ {
+ parent_offset = it->second.parent_offset_;
+ name = it->second.name_;
+ }
+ }
+
+ if (name == NULL && die->tag() == elfcpp::DW_TAG_namespace)
+ name = "(anonymous namespace)";
+ else if (name == NULL)
+ return full_name;
+
+ // If this is an enumerator constant, skip the immediate parent,
+ // which is the enumeration tag.
+ if (die->tag() == elfcpp::DW_TAG_enumerator)
+ {
+ Declaration_map::iterator it = this->declarations_.find(parent_offset);
+ if (it != this->declarations_.end())
+ parent_offset = it->second.parent_offset_;
+ }
+
+ if (parent_offset > 0)
+ {
+ full_name.assign(this->get_context(parent_offset));
+ full_name.append("::");
+ }
+ full_name.append(name);
+
+ return full_name;
+}
+
+// Record the address ranges for a compilation unit.
+
+void
+Gdb_index_info_reader::record_cu_ranges(Dwarf_die* die)
+{
+ unsigned int shndx;
+ unsigned int shndx2;
+
+ off_t ranges_offset = die->ref_attribute(elfcpp::DW_AT_ranges, &shndx);
+ if (ranges_offset != -1)
+ {
+ Dwarf_range_list* ranges = this->read_range_list(shndx, ranges_offset);
+ if (ranges != NULL)
+ this->gdb_index_->add_address_range_list(this->object(),
+ this->cu_index_, ranges);
+ return;
+ }
+
+ off_t low_pc = die->ref_attribute(elfcpp::DW_AT_low_pc, &shndx);
+ off_t high_pc = die->ref_attribute(elfcpp::DW_AT_high_pc, &shndx2);
+ if (low_pc != 0 && high_pc != 0 && low_pc != -1 && high_pc != -1)
+ {
+ if (shndx != shndx2)
+ {
+ gold_warning(_("%s: DWARF info may be corrupt; low_pc and high_pc "
+ "are in different sections"),
+ this->object()->name().c_str());
+ return;
+ }
+ if (shndx == 0 || this->object()->is_section_included(shndx))
+ {
+ Dwarf_range_list* ranges = new Dwarf_range_list();
+ ranges->add(shndx, low_pc, high_pc);
+ this->gdb_index_->add_address_range_list(this->object(),
+ this->cu_index_, ranges);
+ }
+ }
+}
+
+// Read the .debug_pubnames and .debug_pubtypes tables for the CU or TU.
+// Returns TRUE if either a pubnames or pubtypes section was found.
+
+bool
+Gdb_index_info_reader::read_pubnames_and_pubtypes(Dwarf_die* die)
+{
+ bool ret = false;
+
+ // If we find a DW_AT_GNU_pubnames attribute, read the pubnames table.
+ unsigned int pubnames_shndx;
+ off_t pubnames_offset = die->ref_attribute(elfcpp::DW_AT_GNU_pubnames,
+ &pubnames_shndx);
+ if (pubnames_offset != -1)
+ {
+ if (this->gdb_index_->pubnames_read(pubnames_shndx, pubnames_offset))
+ ret = true;
+ else
+ {
+ Dwarf_pubnames_table pubnames(false);
+ if (!pubnames.read_section(this->object(), pubnames_shndx))
+ return false;
+ if (!pubnames.read_header(pubnames_offset))
+ return false;
+ while (true)
+ {
+ const char* name = pubnames.next_name();
+ if (name == NULL)
+ break;
+ this->gdb_index_->add_symbol(this->cu_index_, name);
+ }
+ ret = true;
+ }
+ }
+
+ // If we find a DW_AT_GNU_pubtypes attribute, read the pubtypes table.
+ unsigned int pubtypes_shndx;
+ off_t pubtypes_offset = die->ref_attribute(elfcpp::DW_AT_GNU_pubtypes,
+ &pubtypes_shndx);
+ if (pubtypes_offset != -1)
+ {
+ if (this->gdb_index_->pubtypes_read(pubtypes_shndx, pubtypes_offset))
+ ret = true;
+ else
+ {
+ Dwarf_pubnames_table pubtypes(true);
+ if (!pubtypes.read_section(this->object(), pubtypes_shndx))
+ return false;
+ if (!pubtypes.read_header(pubtypes_offset))
+ return false;
+ while (true)
+ {
+ const char* name = pubtypes.next_name();
+ if (name == NULL)
+ break;
+ this->gdb_index_->add_symbol(this->cu_index_, name);
+ }
+ ret = true;
+ }
+ }
+
+ return ret;
+}
+
+// Clear the declarations map.
+void
+Gdb_index_info_reader::clear_declarations()
+{
+ // Free strings in memory we manage.
+ for (Declaration_map::iterator it = this->declarations_.begin();
+ it != this->declarations_.end();
+ ++it)
+ {
+ if (it->second.parent_offset_ == -1)
+ delete[] it->second.name_;
+ }
+
+ this->declarations_.clear();
+}
+
+// Print usage statistics.
+void
+Gdb_index_info_reader::print_stats()
+{
+ fprintf(stderr, _("%s: DWARF CUs: %u\n"),
+ program_name, Gdb_index_info_reader::dwarf_cu_count);
+ fprintf(stderr, _("%s: DWARF CUs without pubnames/pubtypes: %u\n"),
+ program_name, Gdb_index_info_reader::dwarf_cu_nopubnames_count);
+ fprintf(stderr, _("%s: DWARF TUs: %u\n"),
+ program_name, Gdb_index_info_reader::dwarf_tu_count);
+ fprintf(stderr, _("%s: DWARF TUs without pubnames/pubtypes: %u\n"),
+ program_name, Gdb_index_info_reader::dwarf_tu_nopubnames_count);
+}
+
+// Class Gdb_index.
+
+// Construct the .gdb_index section.
+
+Gdb_index::Gdb_index(Output_section* gdb_index_section)
+ : Output_section_data(4),
+ gdb_index_section_(gdb_index_section),
+ comp_units_(),
+ type_units_(),
+ ranges_(),
+ cu_vector_list_(),
+ cu_vector_offsets_(NULL),
+ stringpool_(),
+ tu_offset_(0),
+ addr_offset_(0),
+ symtab_offset_(0),
+ cu_pool_offset_(0),
+ stringpool_offset_(0),
+ pubnames_shndx_(0),
+ pubnames_offset_(0),
+ pubtypes_shndx_(0),
+ pubtypes_offset_(0)
+{
+ this->gdb_symtab_ = new Gdb_hashtab<Gdb_symbol>();
+}
+
+Gdb_index::~Gdb_index()
+{
+ // Free the memory used by the symbol table.
+ delete this->gdb_symtab_;
+ // Free the memory used by the CU vectors.
+ for (unsigned int i = 0; i < this->cu_vector_list_.size(); ++i)
+ delete this->cu_vector_list_[i];
+}
+
+// Scan a .debug_info or .debug_types input section.
+
+void
+Gdb_index::scan_debug_info(bool is_type_unit,
+ Relobj* object,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ unsigned int shndx,
+ unsigned int reloc_shndx,
+ unsigned int reloc_type)
+{
+ Gdb_index_info_reader dwinfo(is_type_unit, object,
+ symbols, symbols_size,
+ shndx, reloc_shndx,
+ reloc_type, this);
+ dwinfo.parse();
+}
+
+// Add a symbol.
+
+void
+Gdb_index::add_symbol(int cu_index, const char* sym_name)
+{
+ unsigned int hash = mapped_index_string_hash(
+ reinterpret_cast<const unsigned char*>(sym_name));
+ Gdb_symbol* sym = new Gdb_symbol();
+ this->stringpool_.add(sym_name, true, &sym->name_key);
+ sym->hashval = hash;
+ sym->cu_vector_index = 0;
+
+ Gdb_symbol* found = this->gdb_symtab_->add(sym);
+ if (found == sym)
+ {
+ // New symbol -- allocate a new CU index vector.
+ found->cu_vector_index = this->cu_vector_list_.size();
+ this->cu_vector_list_.push_back(new Cu_vector());
+ }
+ else
+ {
+ // Found an existing symbol -- append to the existing
+ // CU index vector.
+ delete sym;
+ }
+
+ // Add the CU index to the vector list for this symbol,
+ // if it's not already on the list. We only need to
+ // check the last added entry.
+ Cu_vector* cu_vec = this->cu_vector_list_[found->cu_vector_index];
+ if (cu_vec->size() == 0 || cu_vec->back() != cu_index)
+ cu_vec->push_back(cu_index);
+}
+
+// Return TRUE if we have already processed the pubnames set at
+// OFFSET in section SHNDX
+
+bool
+Gdb_index::pubnames_read(unsigned int shndx, off_t offset)
+{
+ bool ret = (this->pubnames_shndx_ == shndx
+ && this->pubnames_offset_ == offset);
+ this->pubnames_shndx_ = shndx;
+ this->pubnames_offset_ = offset;
+ return ret;
+}
+
+// Return TRUE if we have already processed the pubtypes set at
+// OFFSET in section SHNDX
+
+bool
+Gdb_index::pubtypes_read(unsigned int shndx, off_t offset)
+{
+ bool ret = (this->pubtypes_shndx_ == shndx
+ && this->pubtypes_offset_ == offset);
+ this->pubtypes_shndx_ = shndx;
+ this->pubtypes_offset_ = offset;
+ return ret;
+}
+
+// Set the size of the .gdb_index section.
+
+void
+Gdb_index::set_final_data_size()
+{
+ // Finalize the string pool.
+ this->stringpool_.set_string_offsets();
+
+ // Compute the total size of the CU vectors.
+ // For each CU vector, include one entry for the count at the
+ // beginning of the vector.
+ unsigned int cu_vector_count = this->cu_vector_list_.size();
+ unsigned int cu_vector_size = 0;
+ this->cu_vector_offsets_ = new off_t[cu_vector_count];
+ for (unsigned int i = 0; i < cu_vector_count; ++i)
+ {
+ Cu_vector* cu_vec = this->cu_vector_list_[i];
+ cu_vector_offsets_[i] = cu_vector_size;
+ cu_vector_size += gdb_index_offset_size * (cu_vec->size() + 1);
+ }
+
+ // Assign relative offsets to each portion of the index,
+ // and find the total size of the section.
+ section_size_type data_size = gdb_index_hdr_size;
+ data_size += this->comp_units_.size() * gdb_index_cu_size;
+ this->tu_offset_ = data_size;
+ data_size += this->type_units_.size() * gdb_index_tu_size;
+ this->addr_offset_ = data_size;
+ for (unsigned int i = 0; i < this->ranges_.size(); ++i)
+ data_size += this->ranges_[i].ranges->size() * gdb_index_addr_size;
+ this->symtab_offset_ = data_size;
+ data_size += this->gdb_symtab_->capacity() * gdb_index_sym_size;
+ this->cu_pool_offset_ = data_size;
+ data_size += cu_vector_size;
+ this->stringpool_offset_ = data_size;
+ data_size += this->stringpool_.get_strtab_size();
+
+ this->set_data_size(data_size);
+}
+
+// Write the data to the file.
+
+void
+Gdb_index::do_write(Output_file* of)
+{
+ const off_t off = this->offset();
+ const off_t oview_size = this->data_size();
+ unsigned char* const oview = of->get_output_view(off, oview_size);
+ unsigned char* pov = oview;
+
+ // Write the file header.
+ // (1) Version number.
+ elfcpp::Swap<32, false>::writeval(pov, gdb_index_version);
+ pov += 4;
+ // (2) Offset of the CU list.
+ elfcpp::Swap<32, false>::writeval(pov, gdb_index_hdr_size);
+ pov += 4;
+ // (3) Offset of the types CU list.
+ elfcpp::Swap<32, false>::writeval(pov, this->tu_offset_);
+ pov += 4;
+ // (4) Offset of the address area.
+ elfcpp::Swap<32, false>::writeval(pov, this->addr_offset_);
+ pov += 4;
+ // (5) Offset of the symbol table.
+ elfcpp::Swap<32, false>::writeval(pov, this->symtab_offset_);
+ pov += 4;
+ // (6) Offset of the constant pool.
+ elfcpp::Swap<32, false>::writeval(pov, this->cu_pool_offset_);
+ pov += 4;
+
+ gold_assert(pov - oview == gdb_index_hdr_size);
+
+ // Write the CU list.
+ unsigned int comp_units_count = this->comp_units_.size();
+ for (unsigned int i = 0; i < comp_units_count; ++i)
+ {
+ const Comp_unit& cu = this->comp_units_[i];
+ elfcpp::Swap<64, false>::writeval(pov, cu.cu_offset);
+ elfcpp::Swap<64, false>::writeval(pov + 8, cu.cu_length);
+ pov += 16;
+ }
+
+ gold_assert(pov - oview == this->tu_offset_);
+
+ // Write the types CU list.
+ for (unsigned int i = 0; i < this->type_units_.size(); ++i)
+ {
+ const Type_unit& tu = this->type_units_[i];
+ elfcpp::Swap<64, false>::writeval(pov, tu.tu_offset);
+ elfcpp::Swap<64, false>::writeval(pov + 8, tu.type_offset);
+ elfcpp::Swap<64, false>::writeval(pov + 16, tu.type_signature);
+ pov += 24;
+ }
+
+ gold_assert(pov - oview == this->addr_offset_);
+
+ // Write the address area.
+ for (unsigned int i = 0; i < this->ranges_.size(); ++i)
+ {
+ int cu_index = this->ranges_[i].cu_index;
+ // Translate negative indexes, which refer to a TU, to a
+ // logical index into a concatenated CU/TU list.
+ if (cu_index < 0)
+ cu_index = comp_units_count + (-1 - cu_index);
+ Relobj* object = this->ranges_[i].object;
+ const Dwarf_range_list& ranges = *this->ranges_[i].ranges;
+ for (unsigned int j = 0; j < ranges.size(); ++j)
+ {
+ const Dwarf_range_list::Range& range = ranges[j];
+ uint64_t base = 0;
+ if (range.shndx > 0)
+ {
+ const Output_section* os = object->output_section(range.shndx);
+ base = (os->address()
+ + object->output_section_offset(range.shndx));
+ }
+ elfcpp::Swap<64, false>::writeval(pov, base + range.start);
+ elfcpp::Swap<64, false>::writeval(pov + 8, base + range.end);
+ elfcpp::Swap<32, false>::writeval(pov + 16, cu_index);
+ pov += 20;
+ }
+ }
+
+ gold_assert(pov - oview == this->symtab_offset_);
+
+ // Write the symbol table.
+ for (unsigned int i = 0; i < this->gdb_symtab_->capacity(); ++i)
+ {
+ const Gdb_symbol* sym = (*this->gdb_symtab_)[i];
+ section_offset_type name_offset = 0;
+ unsigned int cu_vector_offset = 0;
+ if (sym != NULL)
+ {
+ name_offset = (this->stringpool_.get_offset_from_key(sym->name_key)
+ + this->stringpool_offset_ - this->cu_pool_offset_);
+ cu_vector_offset = this->cu_vector_offsets_[sym->cu_vector_index];
+ }
+ elfcpp::Swap<32, false>::writeval(pov, name_offset);
+ elfcpp::Swap<32, false>::writeval(pov + 4, cu_vector_offset);
+ pov += 8;
+ }
+
+ gold_assert(pov - oview == this->cu_pool_offset_);
+
+ // Write the CU vectors into the constant pool.
+ for (unsigned int i = 0; i < this->cu_vector_list_.size(); ++i)
+ {
+ Cu_vector* cu_vec = this->cu_vector_list_[i];
+ elfcpp::Swap<32, false>::writeval(pov, cu_vec->size());
+ pov += 4;
+ for (unsigned int j = 0; j < cu_vec->size(); ++j)
+ {
+ int cu_index = (*cu_vec)[j];
+ if (cu_index < 0)
+ cu_index = comp_units_count + (-1 - cu_index);
+ elfcpp::Swap<32, false>::writeval(pov, cu_index);
+ pov += 4;
+ }
+ }
+
+ gold_assert(pov - oview == this->stringpool_offset_);
+
+ // Write the strings into the constant pool.
+ this->stringpool_.write_to_buffer(pov, oview_size - this->stringpool_offset_);
+
+ of->write_output_view(off, oview_size, oview);
+}
+
+// Print usage statistics.
+void
+Gdb_index::print_stats()
+{
+ if (parameters->options().gdb_index())
+ Gdb_index_info_reader::print_stats();
+}
+
+} // End namespace gold.
diff --git a/gold/gdb-index.h b/gold/gdb-index.h
new file mode 100644
index 0000000..1ca38cc
--- /dev/null
+++ b/gold/gdb-index.h
@@ -0,0 +1,213 @@
+// gdb-index.h -- generate .gdb_index section for fast debug lookup -*- C++ -*-
+
+// Copyright 2012 Free Software Foundation, Inc.
+// Written by Cary Coutant <ccoutant@google.com>.
+
+// This file is part of gold.
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+// MA 02110-1301, USA.
+
+#include <sys/types.h>
+#include <vector>
+
+#include "gold.h"
+#include "output.h"
+#include "mapfile.h"
+#include "stringpool.h"
+
+#ifndef GOLD_GDB_INDEX_H
+#define GOLD_GDB_INDEX_H
+
+namespace gold
+{
+
+class Output_section;
+class Output_file;
+class Mapfile;
+template<int size, bool big_endian>
+class Sized_relobj;
+class Dwarf_range_list;
+template <typename T>
+class Gdb_hashtab;
+
+// This class manages the .gdb_index section, which is a fast
+// lookup table for DWARF information used by the gdb debugger.
+// The format of this section is described in gdb/doc/gdb.texinfo.
+
+class Gdb_index : public Output_section_data
+{
+ public:
+ Gdb_index(Output_section* gdb_index_section);
+
+ ~Gdb_index();
+
+ // Scan a .debug_info or .debug_types input section.
+ void scan_debug_info(bool is_type_unit,
+ Relobj* object,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ unsigned int shndx,
+ unsigned int reloc_shndx,
+ unsigned int reloc_type);
+
+ // Add a compilation unit.
+ int
+ add_comp_unit(off_t cu_offset, off_t cu_length)
+ {
+ this->comp_units_.push_back(Comp_unit(cu_offset, cu_length));
+ return this->comp_units_.size() - 1;
+ }
+
+ // Add a type unit.
+ int
+ add_type_unit(off_t tu_offset, off_t type_offset, uint64_t signature)
+ {
+ this->type_units_.push_back(Type_unit(tu_offset, type_offset, signature));
+ return this->type_units_.size() - 1;
+ }
+
+ // Add an address range.
+ void
+ add_address_range_list(Relobj* object, unsigned int cu_index,
+ Dwarf_range_list* ranges)
+ {
+ this->ranges_.push_back(Per_cu_range_list(object, cu_index, ranges));
+ }
+
+ // Add a symbol.
+ void
+ add_symbol(int cu_index, const char* sym_name);
+
+ // Return TRUE if we have already processed the pubnames set at
+ // OFFSET in section SHNDX
+ bool
+ pubnames_read(unsigned int shndx, off_t offset);
+
+ // Return TRUE if we have already processed the pubtypes set at
+ // OFFSET in section SHNDX
+ bool
+ pubtypes_read(unsigned int shndx, off_t offset);
+
+ // Print usage statistics.
+ static void
+ print_stats();
+
+ protected:
+ // This is called to update the section size prior to assigning
+ // the address and file offset.
+ void
+ update_data_size()
+ { this->set_final_data_size(); }
+
+ // Set the final data size.
+ void
+ set_final_data_size();
+
+ // Write the data to the file.
+ void
+ do_write(Output_file*);
+
+ // Write to a map file.
+ void
+ do_print_to_mapfile(Mapfile* mapfile) const
+ { mapfile->print_output_data(this, _("** gdb_index")); }
+
+ private:
+ // An entry in the compilation unit list.
+ struct Comp_unit
+ {
+ Comp_unit(off_t off, off_t len)
+ : cu_offset(off), cu_length(len)
+ { }
+ uint64_t cu_offset;
+ uint64_t cu_length;
+ };
+
+ // An entry in the type unit list.
+ struct Type_unit
+ {
+ Type_unit(off_t off, off_t toff, uint64_t sig)
+ : tu_offset(off), type_offset(toff), type_signature(sig)
+ { }
+ uint64_t tu_offset;
+ uint64_t type_offset;
+ uint64_t type_signature;
+ };
+
+ // An entry in the address range list.
+ struct Per_cu_range_list
+ {
+ Per_cu_range_list(Relobj* obj, uint32_t index, Dwarf_range_list* r)
+ : object(obj), cu_index(index), ranges(r)
+ { }
+ Relobj* object;
+ uint32_t cu_index;
+ Dwarf_range_list* ranges;
+ };
+
+ // A symbol table entry.
+ struct Gdb_symbol
+ {
+ Stringpool::Key name_key;
+ unsigned int hashval;
+ unsigned int cu_vector_index;
+
+ // Return the hash value.
+ unsigned int
+ hash()
+ { return this->hashval; }
+
+ // Return true if this symbol is the same as SYMBOL.
+ bool
+ equal(Gdb_symbol* symbol)
+ { return this->name_key == symbol->name_key; }
+ };
+
+ typedef std::vector<int> Cu_vector;
+
+ // The .gdb_index section.
+ Output_section* gdb_index_section_;
+ // The list of DWARF compilation units.
+ std::vector<Comp_unit> comp_units_;
+ // The list of DWARF type units.
+ std::vector<Type_unit> type_units_;
+ // The list of address ranges.
+ std::vector<Per_cu_range_list> ranges_;
+ // The symbol table.
+ Gdb_hashtab<Gdb_symbol>* gdb_symtab_;
+ // The CU vector portion of the constant pool.
+ std::vector<Cu_vector*> cu_vector_list_;
+ // An array to map from a CU vector index to an offset to the constant pool.
+ off_t* cu_vector_offsets_;
+ // The string portion of the constant pool.
+ Stringpool stringpool_;
+ // Offsets of the various pieces of the .gdb_index section.
+ off_t tu_offset_;
+ off_t addr_offset_;
+ off_t symtab_offset_;
+ off_t cu_pool_offset_;
+ off_t stringpool_offset_;
+ // Section index and offset of last read pubnames section.
+ unsigned int pubnames_shndx_;
+ off_t pubnames_offset_;
+ // Section index and offset of last read pubtypes section.
+ unsigned int pubtypes_shndx_;
+ off_t pubtypes_offset_;
+};
+
+} // End namespace gold.
+
+#endif // !defined(GOLD_GDB_INDEX_H)
diff --git a/gold/incremental.cc b/gold/incremental.cc
index 2a26573..60097a8 100644
--- a/gold/incremental.cc
+++ b/gold/incremental.cc
@@ -2002,6 +2002,11 @@ Sized_relobj_incr<size, big_endian>::do_layout(
Output_sections& out_sections(this->output_sections());
out_sections.resize(shnum);
this->section_offsets().resize(shnum);
+
+ // Keep track of .debug_info and .debug_types sections.
+ std::vector<unsigned int> debug_info_sections;
+ std::vector<unsigned int> debug_types_sections;
+
for (unsigned int i = 1; i < shnum; i++)
{
typename Input_entry_reader::Input_section_info sect =
@@ -2015,6 +2020,18 @@ Sized_relobj_incr<size, big_endian>::do_layout(
gold_assert(os != NULL);
out_sections[i] = os;
this->section_offsets()[i] = static_cast<Address>(sect.sh_offset);
+
+ // When generating a .gdb_index section, we do additional
+ // processing of .debug_info and .debug_types sections after all
+ // the other sections.
+ if (parameters->options().gdb_index())
+ {
+ const char* name = os->name();
+ if (strcmp(name, ".debug_info") == 0)
+ debug_info_sections.push_back(i);
+ else if (strcmp(name, ".debug_types") == 0)
+ debug_types_sections.push_back(i);
+ }
}
// Process the COMDAT groups.
@@ -2032,6 +2049,25 @@ Sized_relobj_incr<size, big_endian>::do_layout(
this->error(_("COMDAT group %s included twice in incremental link"),
signature);
}
+
+ // When building a .gdb_index section, scan the .debug_info and
+ // .debug_types sections.
+ for (std::vector<unsigned int>::const_iterator p
+ = debug_info_sections.begin();
+ p != debug_info_sections.end();
+ ++p)
+ {
+ unsigned int i = *p;
+ layout->add_to_gdb_index(false, this, NULL, 0, i, 0, 0);
+ }
+ for (std::vector<unsigned int>::const_iterator p
+ = debug_types_sections.begin();
+ p != debug_types_sections.end();
+ ++p)
+ {
+ unsigned int i = *p;
+ layout->add_to_gdb_index(true, this, 0, 0, i, 0, 0);
+ }
}
// Layout sections whose layout was deferred while waiting for
@@ -2193,22 +2229,39 @@ Sized_relobj_incr<size, big_endian>::do_section_size(unsigned int)
gold_unreachable();
}
-// Get the name of a section.
+// Get the name of a section. This returns the name of the output
+// section, because we don't usually track the names of the input
+// sections.
template<int size, bool big_endian>
std::string
-Sized_relobj_incr<size, big_endian>::do_section_name(unsigned int)
+Sized_relobj_incr<size, big_endian>::do_section_name(unsigned int shndx)
{
- gold_unreachable();
+ Output_sections& out_sections(this->output_sections());
+ Output_section* os = out_sections[shndx];
+ if (os == NULL)
+ return NULL;
+ return os->name();
}
// Return a view of the contents of a section.
template<int size, bool big_endian>
-Object::Location
-Sized_relobj_incr<size, big_endian>::do_section_contents(unsigned int)
+const unsigned char*
+Sized_relobj_incr<size, big_endian>::do_section_contents(
+ unsigned int shndx,
+ section_size_type* plen,
+ bool)
{
- gold_unreachable();
+ Output_sections& out_sections(this->output_sections());
+ Output_section* os = out_sections[shndx];
+ gold_assert(os != NULL);
+ off_t section_offset = os->offset();
+ typename Input_entry_reader::Input_section_info sect =
+ this->input_reader_.get_input_section(shndx - 1);
+ section_offset += sect.sh_offset;
+ *plen = sect.sh_size;
+ return this->ibase_->view(section_offset, sect.sh_size).data();
}
// Return section flags.
@@ -2780,8 +2833,11 @@ Sized_incr_dynobj<size, big_endian>::do_section_name(unsigned int)
// Return a view of the contents of a section.
template<int size, bool big_endian>
-Object::Location
-Sized_incr_dynobj<size, big_endian>::do_section_contents(unsigned int)
+const unsigned char*
+Sized_incr_dynobj<size, big_endian>::do_section_contents(
+ unsigned int,
+ section_size_type*,
+ bool)
{
gold_unreachable();
}
diff --git a/gold/incremental.h b/gold/incremental.h
index 0edb190..b631ae2 100644
--- a/gold/incremental.h
+++ b/gold/incremental.h
@@ -1880,8 +1880,9 @@ class Sized_relobj_incr : public Sized_relobj<size, big_endian>
do_section_name(unsigned int shndx);
// Return a view of the contents of a section.
- Object::Location
- do_section_contents(unsigned int shndx);
+ const unsigned char*
+ do_section_contents(unsigned int shndx, section_size_type* plen,
+ bool cache);
// Return section flags.
uint64_t
@@ -2086,8 +2087,9 @@ class Sized_incr_dynobj : public Dynobj
do_section_name(unsigned int shndx);
// Return a view of the contents of a section.
- Object::Location
- do_section_contents(unsigned int shndx);
+ const unsigned char*
+ do_section_contents(unsigned int shndx, section_size_type* plen,
+ bool cache);
// Return section flags.
uint64_t
diff --git a/gold/layout.cc b/gold/layout.cc
index c120376..65d1432 100644
--- a/gold/layout.cc
+++ b/gold/layout.cc
@@ -44,6 +44,7 @@
#include "symtab.h"
#include "dynobj.h"
#include "ehframe.h"
+#include "gdb-index.h"
#include "compressed_output.h"
#include "reduced_debug_output.h"
#include "object.h"
@@ -390,6 +391,7 @@ Layout::Layout(int number_of_input_files, Script_options* script_options)
eh_frame_data_(NULL),
added_eh_frame_data_(false),
eh_frame_hdr_section_(NULL),
+ gdb_index_data_(NULL),
build_id_note_(NULL),
debug_abbrev_(NULL),
debug_info_(NULL),
@@ -905,6 +907,13 @@ Layout::init_fixed_output_section(const char* name,
if (!can_incremental_update(sh_type))
return NULL;
+ // If we're generating a .gdb_index section, we need to regenerate
+ // it from scratch.
+ if (parameters->options().gdb_index()
+ && sh_type == elfcpp::SHT_PROGBITS
+ && strcmp(name, ".gdb_index") == 0)
+ return NULL;
+
typename elfcpp::Elf_types<size>::Elf_Addr sh_addr = shdr.get_sh_addr();
typename elfcpp::Elf_types<size>::Elf_Off sh_offset = shdr.get_sh_offset();
typename elfcpp::Elf_types<size>::Elf_WXword sh_size = shdr.get_sh_size();
@@ -1292,6 +1301,38 @@ Layout::add_eh_frame_for_plt(Output_data* plt, const unsigned char* cie_data,
}
}
+// Scan a .debug_info or .debug_types section, and add summary
+// information to the .gdb_index section.
+
+template<int size, bool big_endian>
+void
+Layout::add_to_gdb_index(bool is_type_unit,
+ Sized_relobj<size, big_endian>* object,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ unsigned int shndx,
+ unsigned int reloc_shndx,
+ unsigned int reloc_type)
+{
+ if (this->gdb_index_data_ == NULL)
+ {
+ Output_section* os = this->choose_output_section(NULL, ".gdb_index",
+ elfcpp::SHT_PROGBITS, 0,
+ false, ORDER_INVALID,
+ false);
+ if (os == NULL)
+ return;
+
+ this->gdb_index_data_ = new Gdb_index(os);
+ os->add_output_section_data(this->gdb_index_data_);
+ os->set_after_input_sections();
+ }
+
+ this->gdb_index_data_->scan_debug_info(is_type_unit, object, symbols,
+ symbols_size, shndx, reloc_shndx,
+ reloc_type);
+}
+
// Add POSD to an output section using NAME, TYPE, and FLAGS. Return
// the output section.
@@ -5297,4 +5338,52 @@ Layout::layout_eh_frame<64, true>(Sized_relobj_file<64, true>* object,
off_t* off);
#endif
+#ifdef HAVE_TARGET_32_LITTLE
+template
+void
+Layout::add_to_gdb_index(bool is_type_unit,
+ Sized_relobj<32, false>* object,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ unsigned int shndx,
+ unsigned int reloc_shndx,
+ unsigned int reloc_type);
+#endif
+
+#ifdef HAVE_TARGET_32_BIG
+template
+void
+Layout::add_to_gdb_index(bool is_type_unit,
+ Sized_relobj<32, true>* object,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ unsigned int shndx,
+ unsigned int reloc_shndx,
+ unsigned int reloc_type);
+#endif
+
+#ifdef HAVE_TARGET_64_LITTLE
+template
+void
+Layout::add_to_gdb_index(bool is_type_unit,
+ Sized_relobj<64, false>* object,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ unsigned int shndx,
+ unsigned int reloc_shndx,
+ unsigned int reloc_type);
+#endif
+
+#ifdef HAVE_TARGET_64_BIG
+template
+void
+Layout::add_to_gdb_index(bool is_type_unit,
+ Sized_relobj<64, true>* object,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ unsigned int shndx,
+ unsigned int reloc_shndx,
+ unsigned int reloc_type);
+#endif
+
} // End namespace gold.
diff --git a/gold/layout.h b/gold/layout.h
index d76fc96..f81ea3b 100644
--- a/gold/layout.h
+++ b/gold/layout.h
@@ -58,6 +58,7 @@ class Output_symtab_xindex;
class Output_reduced_debug_abbrev_section;
class Output_reduced_debug_info_section;
class Eh_frame;
+class Gdb_index;
class Target;
struct Timespec;
@@ -601,6 +602,18 @@ class Layout
size_t cie_length, const unsigned char* fde_data,
size_t fde_length);
+ // Scan a .debug_info or .debug_types section, and add summary
+ // information to the .gdb_index section.
+ template<int size, bool big_endian>
+ void
+ add_to_gdb_index(bool is_type_unit,
+ Sized_relobj<size, big_endian>* object,
+ const unsigned char* symbols,
+ off_t symbols_size,
+ unsigned int shndx,
+ unsigned int reloc_shndx,
+ unsigned int reloc_type);
+
// Handle a GNU stack note. This is called once per input object
// file. SEEN_GNU_STACK is true if the object file has a
// .note.GNU-stack section. GNU_STACK_FLAGS is the section flags
@@ -1281,6 +1294,8 @@ class Layout
bool added_eh_frame_data_;
// The exception frame header output section if there is one.
Output_section* eh_frame_hdr_section_;
+ // The data for the .gdb_index section.
+ Gdb_index* gdb_index_data_;
// The space for the build ID checksum if there is one.
Output_section_data* build_id_note_;
// The output section containing dwarf abbreviations
diff --git a/gold/main.cc b/gold/main.cc
index 0484541..d329298 100644
--- a/gold/main.cc
+++ b/gold/main.cc
@@ -47,6 +47,7 @@
#include "gc.h"
#include "icf.h"
#include "incremental.h"
+#include "gdb-index.h"
#include "timer.h"
using namespace gold;
@@ -301,6 +302,7 @@ main(int argc, char** argv)
program_name, static_cast<long long>(layout.output_file_size()));
symtab.print_stats();
layout.print_stats();
+ Gdb_index::print_stats();
Free_list::print_stats();
}
diff --git a/gold/object.cc b/gold/object.cc
index 2b4fe02..15e5d05 100644
--- a/gold/object.cc
+++ b/gold/object.cc
@@ -178,16 +178,7 @@ Object::error(const char* format, ...) const
const unsigned char*
Object::section_contents(unsigned int shndx, section_size_type* plen,
bool cache)
-{
- Location loc(this->do_section_contents(shndx));
- *plen = convert_to_section_size_type(loc.data_size);
- if (*plen == 0)
- {
- static const unsigned char empty[1] = { '\0' };
- return empty;
- }
- return this->get_view(loc.file_offset, *plen, true, cache);
-}
+{ return this->do_section_contents(shndx, plen, cache); }
// Read the section data into SD. This is code common to Sized_relobj_file
// and Sized_dynobj, so we put it into Object.
@@ -550,24 +541,55 @@ Sized_relobj_file<size, big_endian>::find_eh_frame(
return false;
}
-#ifdef ENABLE_THREADS
-
// Return TRUE if this is a section whose contents will be needed in the
-// Add_symbols task.
+// Add_symbols task. This function is only called for sections that have
+// already passed the test in is_compressed_debug_section(), so we know
+// that the section name begins with ".zdebug".
static bool
need_decompressed_section(const char* name)
{
- // We will need .zdebug_str if this is not an incremental link
- // (i.e., we are processing string merge sections).
- if (!parameters->incremental() && strcmp(name, ".zdebug_str") == 0)
+ // Skip over the ".zdebug" and a quick check for the "_".
+ name += 7;
+ if (*name++ != '_')
+ return false;
+
+#ifdef ENABLE_THREADS
+ // Decompressing these sections now will help only if we're
+ // multithreaded.
+ if (parameters->options().threads())
+ {
+ // We will need .zdebug_str if this is not an incremental link
+ // (i.e., we are processing string merge sections) or if we need
+ // to build a gdb index.
+ if ((!parameters->incremental() || parameters->options().gdb_index())
+ && strcmp(name, "str") == 0)
+ return true;
+
+ // We will need these other sections when building a gdb index.
+ if (parameters->options().gdb_index()
+ && (strcmp(name, "info") == 0
+ || strcmp(name, "types") == 0
+ || strcmp(name, "pubnames") == 0
+ || strcmp(name, "pubtypes") == 0
+ || strcmp(name, "ranges") == 0
+ || strcmp(name, "abbrev") == 0))
+ return true;
+ }
+#endif
+
+ // Even when single-threaded, we will need .zdebug_str if this is
+ // not an incremental link and we are building a gdb index.
+ // Otherwise, we would decompress the section twice: once for
+ // string merge processing, and once for building the gdb index.
+ if (!parameters->incremental()
+ && parameters->options().gdb_index()
+ && strcmp(name, "str") == 0)
return true;
return false;
}
-#endif
-
// Build a table for any compressed debug sections, mapping each section index
// to the uncompressed size and (if needed) the decompressed contents.
@@ -604,33 +626,22 @@ build_compressed_section_map(
const unsigned char* contents =
obj->section_contents(i, &len, false);
uint64_t uncompressed_size = get_uncompressed_size(contents, len);
+ Compressed_section_info info;
+ info.size = convert_to_section_size_type(uncompressed_size);
+ info.contents = NULL;
if (uncompressed_size != -1ULL)
{
- Compressed_section_info info;
- info.size = convert_to_section_size_type(uncompressed_size);
- info.contents = NULL;
-
-#ifdef ENABLE_THREADS
- // If we're multi-threaded, it will help to decompress
- // any sections that will be needed during the Add_symbols
- // task, so that several decompressions can run in
- // parallel.
- if (parameters->options().threads())
+ unsigned char* uncompressed_data = NULL;
+ if (need_decompressed_section(name))
{
- unsigned char* uncompressed_data = NULL;
- if (need_decompressed_section(name))
- {
- uncompressed_data = new unsigned char[uncompressed_size];
- if (decompress_input_section(contents, len,
- uncompressed_data,
- uncompressed_size))
- info.contents = uncompressed_data;
- else
- delete[] uncompressed_data;
- }
+ uncompressed_data = new unsigned char[uncompressed_size];
+ if (decompress_input_section(contents, len,
+ uncompressed_data,
+ uncompressed_size))
+ info.contents = uncompressed_data;
+ else
+ delete[] uncompressed_data;
}
-#endif
-
(*uncompressed_map)[i] = info;
}
}
@@ -645,6 +656,8 @@ template<int size, bool big_endian>
void
Sized_relobj_file<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
{
+ bool need_local_symbols = false;
+
this->read_section_data(&this->elf_file_, sd);
const unsigned char* const pshdrs = sd->section_headers->data();
@@ -663,6 +676,14 @@ Sized_relobj_file<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
build_compressed_section_map(pshdrs, this->shnum(), names,
sd->section_names_size, this);
+ if (this->has_eh_frame_
+ || (!parameters->options().relocatable()
+ && parameters->options().gdb_index()
+ && (memmem(names, sd->section_names_size, "debug_info", 12) == 0
+ || memmem(names, sd->section_names_size, "debug_types",
+ 13) == 0)))
+ need_local_symbols = true;
+
sd->symbols = NULL;
sd->symbols_size = 0;
sd->external_symbols_offset = 0;
@@ -680,7 +701,8 @@ Sized_relobj_file<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
+ this->symtab_shndx_ * This::shdr_size);
gold_assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB);
- // If this object has a .eh_frame section, we need all the symbols.
+ // If this object has a .eh_frame section, or if building a .gdb_index
+ // section and there is debug info, we need all the symbols.
// Otherwise we only need the external symbols. While it would be
// simpler to just always read all the symbols, I've seen object
// files with well over 2000 local symbols, which for a 64-bit
@@ -698,8 +720,8 @@ Sized_relobj_file<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
off_t extoff = dataoff + locsize;
section_size_type extsize = datasize - locsize;
- off_t readoff = this->has_eh_frame_ ? dataoff : extoff;
- section_size_type readsize = this->has_eh_frame_ ? datasize : extsize;
+ off_t readoff = need_local_symbols ? dataoff : extoff;
+ section_size_type readsize = need_local_symbols ? datasize : extsize;
if (readsize == 0)
{
@@ -731,7 +753,7 @@ Sized_relobj_file<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
sd->symbols = fvsymtab;
sd->symbols_size = readsize;
- sd->external_symbols_offset = this->has_eh_frame_ ? locsize : 0;
+ sd->external_symbols_offset = need_local_symbols ? locsize : 0;
sd->symbol_names = fvstrtab;
sd->symbol_names_size =
convert_to_section_size_type(strtabshdr.get_sh_size());
@@ -1318,6 +1340,10 @@ Sized_relobj_file<size, big_endian>::do_layout(Symbol_table* symtab,
// Keep track of .eh_frame sections.
std::vector<unsigned int> eh_frame_sections;
+ // Keep track of .debug_info and .debug_types sections.
+ std::vector<unsigned int> debug_info_sections;
+ std::vector<unsigned int> debug_types_sections;
+
// Skip the first, dummy, section.
pshdrs = shdrs + This::shdr_size;
for (unsigned int i = 1; i < shnum; ++i, pshdrs += This::shdr_size)
@@ -1558,6 +1584,21 @@ Sized_relobj_file<size, big_endian>::do_layout(Symbol_table* symtab,
// only happens in the second call.
this->layout_section(layout, i, name, shdr, reloc_shndx[i],
reloc_type[i]);
+
+ // When generating a .gdb_index section, we do additional
+ // processing of .debug_info and .debug_types sections after all
+ // the other sections for the same reason as above.
+ if (!relocatable
+ && parameters->options().gdb_index()
+ && !(shdr.get_sh_flags() & elfcpp::SHF_ALLOC))
+ {
+ if (strcmp(name, ".debug_info") == 0
+ || strcmp(name, ".zdebug_info") == 0)
+ debug_info_sections.push_back(i);
+ else if (strcmp(name, ".debug_types") == 0
+ || strcmp(name, ".zdebug_types") == 0)
+ debug_types_sections.push_back(i);
+ }
}
}
@@ -1638,6 +1679,29 @@ Sized_relobj_file<size, big_endian>::do_layout(Symbol_table* symtab,
reloc_type[i]);
}
+ // When building a .gdb_index section, scan the .debug_info and
+ // .debug_types sections.
+ gold_assert(!is_gc_pass_one
+ || (debug_info_sections.empty() && debug_types_sections.empty()));
+ for (std::vector<unsigned int>::const_iterator p
+ = debug_info_sections.begin();
+ p != debug_info_sections.end();
+ ++p)
+ {
+ unsigned int i = *p;
+ layout->add_to_gdb_index(false, this, symbols_data, symbols_size,
+ i, reloc_shndx[i], reloc_type[i]);
+ }
+ for (std::vector<unsigned int>::const_iterator p
+ = debug_types_sections.begin();
+ p != debug_types_sections.end();
+ ++p)
+ {
+ unsigned int i = *p;
+ layout->add_to_gdb_index(true, this, symbols_data, symbols_size,
+ i, reloc_shndx[i], reloc_type[i]);
+ }
+
if (is_gc_pass_two)
{
delete[] gc_sd->section_headers_data;
@@ -2614,8 +2678,8 @@ Sized_relobj_file<size, big_endian>::do_decompressed_section_contents(
bool* is_new)
{
section_size_type buffer_size;
- const unsigned char* buffer = this->section_contents(shndx, &buffer_size,
- false);
+ const unsigned char* buffer = this->do_section_contents(shndx, &buffer_size,
+ false);
if (this->compressed_sections_ == NULL)
{
diff --git a/gold/object.h b/gold/object.h
index 9d3e1d7..82517d5 100644
--- a/gold/object.h
+++ b/gold/object.h
@@ -725,7 +725,7 @@ class Object
section_size_type* uncompressed_size) const
{ return this->do_section_is_compressed(shndx, uncompressed_size); }
- // Return a view of the uncompressed contents of a section. Set *PLEN
+ // Return a view of the decompressed contents of a section. Set *PLEN
// to the size. Set *IS_NEW to true if the contents need to be freed
// by the caller.
const unsigned char*
@@ -805,8 +805,9 @@ class Object
// Return the location of the contents of a section. Implemented by
// child class.
- virtual Location
- do_section_contents(unsigned int shndx) = 0;
+ virtual const unsigned char*
+ do_section_contents(unsigned int shndx, section_size_type* plen,
+ bool cache) = 0;
// Get the size of a section--implemented by child class.
virtual uint64_t
@@ -918,7 +919,7 @@ class Object
bool* is_new)
{
*is_new = false;
- return this->section_contents(shndx, plen, false);
+ return this->do_section_contents(shndx, plen, false);
}
// Discard any buffers of decompressed sections. This is done
@@ -2237,9 +2238,19 @@ class Sized_relobj_file : public Sized_relobj<size, big_endian>
{ return this->elf_file_.section_name(shndx); }
// Return the location of the contents of a section.
- Object::Location
- do_section_contents(unsigned int shndx)
- { return this->elf_file_.section_contents(shndx); }
+ const unsigned char*
+ do_section_contents(unsigned int shndx, section_size_type* plen,
+ bool cache)
+ {
+ Object::Location loc(this->elf_file_.section_contents(shndx));
+ *plen = convert_to_section_size_type(loc.data_size);
+ if (*plen == 0)
+ {
+ static const unsigned char empty[1] = { '\0' };
+ return empty;
+ }
+ return this->get_view(loc.file_offset, *plen, true, cache);
+ }
// Return section flags.
uint64_t
@@ -2373,7 +2384,7 @@ class Sized_relobj_file : public Sized_relobj<size, big_endian>
section_size_type* plen,
bool* is_new);
- // Discard any buffers of uncompressed sections. This is done
+ // Discard any buffers of decompressed sections. This is done
// at the end of the Add_symbols task.
void
do_discard_decompressed_sections();
diff --git a/gold/options.h b/gold/options.h
index 534df26..b5df3eb 100644
--- a/gold/options.h
+++ b/gold/options.h
@@ -791,6 +791,10 @@ class General_options
DEFINE_bool(g, options::EXACTLY_ONE_DASH, '\0', false,
N_("Ignored"), NULL);
+ DEFINE_bool(gdb_index, options::TWO_DASHES, '\0', false,
+ N_("Generate .gdb_index section"),
+ N_("Do not generate .gdb_index section"));
+
DEFINE_bool(gnu_unique, options::TWO_DASHES, '\0', true,
N_("Enable STB_GNU_UNIQUE symbol binding (default)"),
N_("Disable STB_GNU_UNIQUE symbol binding"));
diff --git a/gold/plugin.cc b/gold/plugin.cc
index 637613c..63d0974 100644
--- a/gold/plugin.cc
+++ b/gold/plugin.cc
@@ -1159,13 +1159,14 @@ Sized_pluginobj<size, big_endian>::do_section_name(unsigned int)
// Return a view of the contents of a section. Not used for plugin objects.
template<int size, bool big_endian>
-Object::Location
-Sized_pluginobj<size, big_endian>::do_section_contents(unsigned int)
+const unsigned char*
+Sized_pluginobj<size, big_endian>::do_section_contents(
+ unsigned int,
+ section_size_type*,
+ bool)
{
- Location loc(0, 0);
-
gold_unreachable();
- return loc;
+ return NULL;
}
// Return section flags. Not used for plugin objects.
diff --git a/gold/plugin.h b/gold/plugin.h
index 32ffe35..1891d6b 100644
--- a/gold/plugin.h
+++ b/gold/plugin.h
@@ -493,8 +493,9 @@ class Sized_pluginobj : public Pluginobj
do_section_name(unsigned int shndx);
// Return a view of the contents of a section.
- Object::Location
- do_section_contents(unsigned int shndx);
+ const unsigned char*
+ do_section_contents(unsigned int shndx, section_size_type* plen,
+ bool cache);
// Return section flags.
uint64_t
diff --git a/gold/reloc.h b/gold/reloc.h
index ec448ac..4827600 100644
--- a/gold/reloc.h
+++ b/gold/reloc.h
@@ -873,6 +873,16 @@ class Track_relocs
int
advance(off_t offset);
+ // Checkpoint the current position in the reloc section.
+ section_size_type
+ checkpoint() const
+ { return this->pos_; }
+
+ // Reset the position to CHECKPOINT.
+ void
+ reset(section_size_type checkpoint)
+ { this->pos_ = checkpoint; }
+
private:
// The contents of the input object's reloc section.
const unsigned char* prelocs_;
diff --git a/gold/testsuite/Makefile.am b/gold/testsuite/Makefile.am
index a7fd06d..6f74f71 100644
--- a/gold/testsuite/Makefile.am
+++ b/gold/testsuite/Makefile.am
@@ -1967,6 +1967,31 @@ memory_test: memory_test.o gcctestdir/ld $(srcdir)/memory_test.t
memory_test.stdout: memory_test
$(TEST_READELF) -lWS $< > $@
+# Test that --gdb-index functions correctly.
+check_SCRIPTS += gdb_index_test_1.sh
+check_DATA += gdb_index_test_1.stdout
+MOSTLYCLEANFILES += gdb_index_test_1.stdout gdb_index_test_1
+gdb_index_test.o: gdb_index_test.cc
+ $(CXXCOMPILE) -O0 -g -c -o $@ $<
+gdb_index_test_1: gdb_index_test.o gcctestdir/ld
+ $(CXXLINK) -Bgcctestdir/ -Wl,--gdb-index $<
+gdb_index_test_1.stdout: gdb_index_test_1
+ $(TEST_READELF) --debug-dump=gdb_index $< > $@
+
+if HAVE_ZLIB
+
+check_SCRIPTS += gdb_index_test_2.sh
+check_DATA += gdb_index_test_2.stdout
+MOSTLYCLEANFILES += gdb_index_test_2.stdout gdb_index_test_2
+gdb_index_test_cdebug.o: gdb_index_test.cc
+ $(CXXCOMPILE) -Bgcctestdir/ -O0 -g -Wa,--compress-debug-sections -c -o $@ $<
+gdb_index_test_2: gdb_index_test_cdebug.o gcctestdir/ld
+ $(CXXLINK) -Bgcctestdir/ -Wl,--gdb-index $<
+gdb_index_test_2.stdout: gdb_index_test_2
+ $(TEST_READELF) --debug-dump=gdb_index $< > $@
+
+endif HAVE_ZLIB
+
# End-to-end incremental linking tests.
# Incremental linking is currently supported only on the x86_64 target.
diff --git a/gold/testsuite/Makefile.in b/gold/testsuite/Makefile.in
index 251bdce..844aa67 100644
--- a/gold/testsuite/Makefile.in
+++ b/gold/testsuite/Makefile.in
@@ -360,13 +360,16 @@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
# weak reference in a DSO.
# Test that MEMORY region support works.
+
+# Test that --gdb-index functions correctly.
@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_38 = exclude_libs_test.sh \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ discard_locals_test.sh \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ hidden_test.sh \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ retain_symbols_file_test.sh \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ no_version_test.sh \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ strong_ref_weak_def.sh \
-@GCC_TRUE@@NATIVE_LINKER_TRUE@ dyn_weak_ref.sh memory_test.sh
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ dyn_weak_ref.sh memory_test.sh \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ gdb_index_test_1.sh
@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_39 = exclude_libs_test.syms \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ discard_locals_test.syms \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ discard_locals_relocatable_test1.syms \
@@ -376,7 +379,8 @@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ no_version_test.stdout \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ strong_ref_weak_def.stdout \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ dyn_weak_ref.stdout \
-@GCC_TRUE@@NATIVE_LINKER_TRUE@ memory_test.stdout
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ memory_test.stdout \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ gdb_index_test_1.stdout
@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_40 = exclude_libs_test.syms \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ libexclude_libs_test_1.a \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ libexclude_libs_test_2.a \
@@ -402,7 +406,9 @@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ dyn_weak_ref_2.so \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ dyn_weak_ref.stdout \
@GCC_TRUE@@NATIVE_LINKER_TRUE@ memory_test.stdout memory_test \
-@GCC_TRUE@@NATIVE_LINKER_TRUE@ memory_test.o
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ memory_test.o \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ gdb_index_test_1.stdout \
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ gdb_index_test_1
@GCC_TRUE@@MCMODEL_MEDIUM_TRUE@@NATIVE_LINKER_TRUE@am__append_41 = large
@GCC_FALSE@large_DEPENDENCIES =
@MCMODEL_MEDIUM_FALSE@large_DEPENDENCIES =
@@ -477,10 +483,13 @@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
# Test that --start-lib and --end-lib function correctly.
@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_53 = start_lib_test
+@GCC_TRUE@@HAVE_ZLIB_TRUE@@NATIVE_LINKER_TRUE@am__append_54 = gdb_index_test_2.sh
+@GCC_TRUE@@HAVE_ZLIB_TRUE@@NATIVE_LINKER_TRUE@am__append_55 = gdb_index_test_2.stdout
+@GCC_TRUE@@HAVE_ZLIB_TRUE@@NATIVE_LINKER_TRUE@am__append_56 = gdb_index_test_2.stdout gdb_index_test_2
# Test the --incremental-unchanged flag with an archive library.
# The second link should not update the library.
-@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_54 = incremental_test_2 \
+@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_57 = incremental_test_2 \
@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ incremental_test_3 \
@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ incremental_test_4 \
@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ incremental_test_5 \
@@ -488,7 +497,7 @@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ incremental_copy_test \
@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ incremental_common_test_1 \
@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ incremental_comdat_test_1
-@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_55 = two_file_test_tmp_2.o \
+@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_58 = two_file_test_tmp_2.o \
@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_test_tmp_3.o \
@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ incremental_test_4.base \
@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_test_tmp_4.o \
@@ -498,22 +507,22 @@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
# These tests work with native and cross linkers.
# Test script section order.
-@NATIVE_OR_CROSS_LINKER_TRUE@am__append_56 = script_test_10.sh
-@NATIVE_OR_CROSS_LINKER_TRUE@am__append_57 = script_test_10.stdout
+@NATIVE_OR_CROSS_LINKER_TRUE@am__append_59 = script_test_10.sh
+@NATIVE_OR_CROSS_LINKER_TRUE@am__append_60 = script_test_10.stdout
# These tests work with cross linkers only.
-@DEFAULT_TARGET_I386_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_58 = split_i386.sh
-@DEFAULT_TARGET_I386_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_59 = split_i386_1.stdout split_i386_2.stdout \
+@DEFAULT_TARGET_I386_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_61 = split_i386.sh
+@DEFAULT_TARGET_I386_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_62 = split_i386_1.stdout split_i386_2.stdout \
@DEFAULT_TARGET_I386_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@ split_i386_3.stdout split_i386_4.stdout split_i386_r.stdout
-@DEFAULT_TARGET_I386_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_60 = split_i386_1 split_i386_2 split_i386_3 \
+@DEFAULT_TARGET_I386_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_63 = split_i386_1 split_i386_2 split_i386_3 \
@DEFAULT_TARGET_I386_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@ split_i386_4 split_i386_r
-@DEFAULT_TARGET_X86_64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_61 = split_x86_64.sh
-@DEFAULT_TARGET_X86_64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_62 = split_x86_64_1.stdout split_x86_64_2.stdout \
+@DEFAULT_TARGET_X86_64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_64 = split_x86_64.sh
+@DEFAULT_TARGET_X86_64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_65 = split_x86_64_1.stdout split_x86_64_2.stdout \
@DEFAULT_TARGET_X86_64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@ split_x86_64_3.stdout split_x86_64_4.stdout split_x86_64_r.stdout
-@DEFAULT_TARGET_X86_64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_63 = split_x86_64_1 split_x86_64_2 split_x86_64_3 \
+@DEFAULT_TARGET_X86_64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_66 = split_x86_64_1 split_x86_64_2 split_x86_64_3 \
@DEFAULT_TARGET_X86_64_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@ split_x86_64_4 split_x86_64_r
@@ -528,7 +537,7 @@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
# Check Thumb to Thumb farcall veneers
# Check Thumb to ARM farcall veneers
-@DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_64 = arm_abs_global.sh \
+@DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_67 = arm_abs_global.sh \
@DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@ arm_branch_in_range.sh \
@DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@ arm_branch_out_of_range.sh \
@DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@ arm_fix_v4bx.sh \
@@ -542,7 +551,7 @@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
@DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@ arm_farcall_arm_thumb.sh \
@DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@ arm_farcall_thumb_thumb.sh \
@DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@ arm_farcall_thumb_arm.sh
-@DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_65 = arm_abs_global.stdout \
+@DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_68 = arm_abs_global.stdout \
@DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@ arm_bl_in_range.stdout \
@DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@ arm_bl_out_of_range.stdout \
@DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@ thumb_bl_in_range.stdout \
@@ -587,7 +596,7 @@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \
@DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@ arm_farcall_thumb_thumb_6m.stdout \
@DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@ arm_farcall_thumb_arm.stdout \
@DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@ arm_farcall_thumb_arm_5t.stdout
-@DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_66 = arm_abs_global \
+@DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@am__append_69 = arm_abs_global \
@DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@ arm_bl_in_range \
@DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@ arm_bl_out_of_range \
@DEFAULT_TARGET_ARM_TRUE@@NATIVE_OR_CROSS_LINKER_TRUE@ thumb_bl_in_range \
@@ -1997,18 +2006,19 @@ TEST_AS = $(top_builddir)/../gas/as-new
MOSTLYCLEANFILES = *.so *.syms *.stdout $(am__append_4) \
$(am__append_17) $(am__append_26) $(am__append_28) \
$(am__append_30) $(am__append_36) $(am__append_40) \
- $(am__append_55) $(am__append_60) $(am__append_63) \
- $(am__append_66)
+ $(am__append_56) $(am__append_58) $(am__append_63) \
+ $(am__append_66) $(am__append_69)
# We will add to these later, for each individual test. Note
# that we add each test under check_SCRIPTS or check_PROGRAMS;
# the TESTS variable is automatically populated from these.
check_SCRIPTS = $(am__append_2) $(am__append_34) $(am__append_38) \
- $(am__append_56) $(am__append_58) $(am__append_61) \
- $(am__append_64)
+ $(am__append_54) $(am__append_59) $(am__append_61) \
+ $(am__append_64) $(am__append_67)
check_DATA = $(am__append_3) $(am__append_27) $(am__append_29) \
- $(am__append_35) $(am__append_39) $(am__append_57) \
- $(am__append_59) $(am__append_62) $(am__append_65)
+ $(am__append_35) $(am__append_39) $(am__append_55) \
+ $(am__append_60) $(am__append_62) $(am__append_65) \
+ $(am__append_68)
BUILT_SOURCES = $(am__append_25)
TESTS = $(check_SCRIPTS) $(check_PROGRAMS)
@@ -3721,6 +3731,10 @@ dyn_weak_ref.sh.log: dyn_weak_ref.sh
@p='dyn_weak_ref.sh'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post)
memory_test.sh.log: memory_test.sh
@p='memory_test.sh'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post)
+gdb_index_test_1.sh.log: gdb_index_test_1.sh
+ @p='gdb_index_test_1.sh'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post)
+gdb_index_test_2.sh.log: gdb_index_test_2.sh
+ @p='gdb_index_test_2.sh'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post)
script_test_10.sh.log: script_test_10.sh
@p='script_test_10.sh'; $(am__check_pre) $(LOG_COMPILE) "$$tst" $(am__check_post)
split_i386.sh.log: split_i386.sh
@@ -5042,6 +5056,18 @@ uninstall-am:
@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(LINK) -Bgcctestdir/ -nostartfiles -nostdlib -T $(srcdir)/memory_test.t -o $@ memory_test.o
@GCC_TRUE@@NATIVE_LINKER_TRUE@memory_test.stdout: memory_test
@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(TEST_READELF) -lWS $< > $@
+@GCC_TRUE@@NATIVE_LINKER_TRUE@gdb_index_test.o: gdb_index_test.cc
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -O0 -g -c -o $@ $<
+@GCC_TRUE@@NATIVE_LINKER_TRUE@gdb_index_test_1: gdb_index_test.o gcctestdir/ld
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -Wl,--gdb-index $<
+@GCC_TRUE@@NATIVE_LINKER_TRUE@gdb_index_test_1.stdout: gdb_index_test_1
+@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(TEST_READELF) --debug-dump=gdb_index $< > $@
+@GCC_TRUE@@HAVE_ZLIB_TRUE@@NATIVE_LINKER_TRUE@gdb_index_test_cdebug.o: gdb_index_test.cc
+@GCC_TRUE@@HAVE_ZLIB_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -Bgcctestdir/ -O0 -g -Wa,--compress-debug-sections -c -o $@ $<
+@GCC_TRUE@@HAVE_ZLIB_TRUE@@NATIVE_LINKER_TRUE@gdb_index_test_2: gdb_index_test_cdebug.o gcctestdir/ld
+@GCC_TRUE@@HAVE_ZLIB_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -Wl,--gdb-index $<
+@GCC_TRUE@@HAVE_ZLIB_TRUE@@NATIVE_LINKER_TRUE@gdb_index_test_2.stdout: gdb_index_test_2
+@GCC_TRUE@@HAVE_ZLIB_TRUE@@NATIVE_LINKER_TRUE@ $(TEST_READELF) --debug-dump=gdb_index $< > $@
# End-to-end incremental linking tests.
# Incremental linking is currently supported only on the x86_64 target.
diff --git a/gold/testsuite/gdb_index_test.cc b/gold/testsuite/gdb_index_test.cc
new file mode 100644
index 0000000..342d47c
--- /dev/null
+++ b/gold/testsuite/gdb_index_test.cc
@@ -0,0 +1,138 @@
+// gdb_index_test.cc -- a test case for the --gdb-index option.
+
+// Copyright 2012 Free Software Foundation, Inc.
+// Written by Cary Coutant <ccoutant@google.com>.
+
+// This file is part of gold.
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+// MA 02110-1301, USA.
+
+// This source file defines a number of symbols of different forms
+// to exercise the DWARF scanner in gold.
+
+namespace
+{
+int c1_count;
+int c2_count;
+};
+
+namespace one
+{
+
+enum G
+{
+ G_A,
+ G_B,
+ G_C
+};
+
+class c1
+{
+ public:
+ static int count;
+
+ c1()
+ { ++c1_count; }
+
+ ~c1()
+ {
+ --c1_count;
+ }
+
+ enum E
+ {
+ E_A,
+ E_B,
+ E_C,
+ };
+
+ int
+ val()
+ { return E_A; }
+};
+
+c1 c1v;
+};
+
+namespace two
+{
+const int ci = 3;
+
+template <typename T>
+class c2
+{
+ public:
+ c2(T t)
+ : t_(t)
+ {
+ ++c2_count;
+ }
+
+ ~c2()
+ { --c2_count; }
+
+ T
+ val()
+ { return this->t_; }
+
+ T t_;
+};
+
+c2<int> c2v1(1);
+c2<double> c2v2(2.0);
+c2<int const*> c2v3(&ci);
+};
+
+enum F
+{
+ F_A,
+ F_B,
+ F_C
+};
+
+template <class C>
+bool
+check(C* c)
+{ return c->val() == 0; }
+
+bool
+check_enum(int i)
+{ return i > 0; }
+
+struct anonymous_union_container {
+ union {
+ struct astruct {
+ int a;
+ };
+ int b;
+ } u;
+};
+
+anonymous_union_container anonymous_union_var;
+
+int
+main()
+{
+ F f = F_A;
+ one::G g = one::G_A;
+ check_enum(f);
+ check_enum(g);
+ check(&one::c1v);
+ check(&two::c2v1);
+ check(&two::c2v2);
+ check(&two::c2v3);
+ return anonymous_union_var.u.b;
+}
diff --git a/gold/testsuite/gdb_index_test_1.sh b/gold/testsuite/gdb_index_test_1.sh
new file mode 100755
index 0000000..9221f7f
--- /dev/null
+++ b/gold/testsuite/gdb_index_test_1.sh
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+# gdb_index_test_1.sh -- a test case for the --gdb-index option.
+
+# Copyright 2012 Free Software Foundation, Inc.
+# Written by Cary Coutant <ccoutant@google.com>.
+
+# This file is part of gold.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+check()
+{
+ if ! grep -q "$2" "$1"
+ then
+ echo "Did not find expected output:"
+ echo " $2"
+ echo ""
+ echo "Actual error output below:"
+ cat "$1"
+ exit 1
+ fi
+}
+
+STDOUT=gdb_index_test_1.stdout
+
+check $STDOUT "^Version [45]"
+
+# Look for the symbols we know should be in the symbol table.
+
+check $STDOUT "^\[ *[0-9]*\] (anonymous namespace): "
+check $STDOUT "^\[ *[0-9]*\] (anonymous namespace)::c1_count: "
+check $STDOUT "^\[ *[0-9]*\] (anonymous namespace)::c2_count: "
+check $STDOUT "^\[ *[0-9]*\] bool: "
+check $STDOUT "^\[ *[0-9]*\] check<one::c1>: "
+check $STDOUT "^\[ *[0-9]*\] check<two::c2<double> >: "
+check $STDOUT "^\[ *[0-9]*\] check<two::c2<int> >: "
+# check $STDOUT "^\[ *[0-9]*\] check<two::c2<int const\*> >: "
+check $STDOUT "^\[ *[0-9]*\] double: "
+check $STDOUT "^\[ *[0-9]*\] F_A: "
+check $STDOUT "^\[ *[0-9]*\] F_B: "
+check $STDOUT "^\[ *[0-9]*\] F_C: "
+check $STDOUT "^\[ *[0-9]*\] int: "
+check $STDOUT "^\[ *[0-9]*\] main: "
+check $STDOUT "^\[ *[0-9]*\] one: "
+check $STDOUT "^\[ *[0-9]*\] one::c1: "
+check $STDOUT "^\[ *[0-9]*\] one::c1::~c1: "
+check $STDOUT "^\[ *[0-9]*\] one::c1::c1: "
+check $STDOUT "^\[ *[0-9]*\] one::c1::val: "
+check $STDOUT "^\[ *[0-9]*\] one::c1v: "
+check $STDOUT "^\[ *[0-9]*\] one::G_A: "
+check $STDOUT "^\[ *[0-9]*\] one::G_B: "
+check $STDOUT "^\[ *[0-9]*\] one::G_B: "
+check $STDOUT "^\[ *[0-9]*\] two: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<double>::~c2: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<double>::c2: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<double>::val: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<double>: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<int const\*>: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<int const\*>::~c2: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<int const\*>::c2: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<int const\*>::val: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<int>::~c2: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<int>::c2: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<int>::val: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<int>: "
+check $STDOUT "^\[ *[0-9]*\] two::c2v1: "
+check $STDOUT "^\[ *[0-9]*\] two::c2v2: "
+check $STDOUT "^\[ *[0-9]*\] anonymous_union_var: "
+
+exit 0
diff --git a/gold/testsuite/gdb_index_test_2.sh b/gold/testsuite/gdb_index_test_2.sh
new file mode 100755
index 0000000..99928bf
--- /dev/null
+++ b/gold/testsuite/gdb_index_test_2.sh
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+# gdb_index_test_2.sh -- a test case for the --gdb-index option.
+
+# Copyright 2012 Free Software Foundation, Inc.
+# Written by Cary Coutant <ccoutant@google.com>.
+
+# This file is part of gold.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+check()
+{
+ if ! grep -q "$2" "$1"
+ then
+ echo "Did not find expected output:"
+ echo " $2"
+ echo ""
+ echo "Actual error output below:"
+ cat "$1"
+ exit 1
+ fi
+}
+
+STDOUT=gdb_index_test_2.stdout
+
+check $STDOUT "^Version [45]"
+
+# Look for the symbols we know should be in the symbol table.
+
+check $STDOUT "^\[ *[0-9]*\] (anonymous namespace): "
+check $STDOUT "^\[ *[0-9]*\] (anonymous namespace)::c1_count: "
+check $STDOUT "^\[ *[0-9]*\] (anonymous namespace)::c2_count: "
+check $STDOUT "^\[ *[0-9]*\] bool: "
+check $STDOUT "^\[ *[0-9]*\] check<one::c1>: "
+check $STDOUT "^\[ *[0-9]*\] check<two::c2<double> >: "
+check $STDOUT "^\[ *[0-9]*\] check<two::c2<int> >: "
+# check $STDOUT "^\[ *[0-9]*\] check<two::c2<int const\*> >: "
+check $STDOUT "^\[ *[0-9]*\] double: "
+check $STDOUT "^\[ *[0-9]*\] F_A: "
+check $STDOUT "^\[ *[0-9]*\] F_B: "
+check $STDOUT "^\[ *[0-9]*\] F_C: "
+check $STDOUT "^\[ *[0-9]*\] int: "
+check $STDOUT "^\[ *[0-9]*\] main: "
+check $STDOUT "^\[ *[0-9]*\] one: "
+check $STDOUT "^\[ *[0-9]*\] one::c1: "
+check $STDOUT "^\[ *[0-9]*\] one::c1::~c1: "
+check $STDOUT "^\[ *[0-9]*\] one::c1::c1: "
+check $STDOUT "^\[ *[0-9]*\] one::c1::val: "
+check $STDOUT "^\[ *[0-9]*\] one::c1v: "
+check $STDOUT "^\[ *[0-9]*\] one::G_A: "
+check $STDOUT "^\[ *[0-9]*\] one::G_B: "
+check $STDOUT "^\[ *[0-9]*\] one::G_B: "
+check $STDOUT "^\[ *[0-9]*\] two: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<double>::~c2: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<double>::c2: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<double>::val: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<double>: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<int const\*>: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<int const\*>::~c2: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<int const\*>::c2: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<int const\*>::val: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<int>::~c2: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<int>::c2: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<int>::val: "
+check $STDOUT "^\[ *[0-9]*\] two::c2<int>: "
+check $STDOUT "^\[ *[0-9]*\] two::c2v1: "
+check $STDOUT "^\[ *[0-9]*\] two::c2v2: "
+check $STDOUT "^\[ *[0-9]*\] anonymous_union_var: "
+
+exit 0