From: Cary Coutant Date: Wed, 21 Mar 2012 19:02:22 +0000 (+0000) Subject: 2012-03-21 Cary Coutant X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=c1027032c2fec15b7a334a09bab8882984716764;p=binutils-gdb.git 2012-03-21 Cary Coutant * 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 . (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. --- diff --git a/gold/ChangeLog b/gold/ChangeLog index e2515707ac5..9d63bba07ff 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,112 @@ +2012-03-21 Cary Coutant + + * 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 . + (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 * arm.cc (Target_arm::do_define_standard_symbols): New method. diff --git a/gold/Makefile.am b/gold/Makefile.am index d121ac444c4..7d4b7254b26 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 0fa739ad3f4..2998b7bf61c 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 189e6a6740f..eaf35bfdfae 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 . // 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 +bool +Sized_elf_reloc_mapper::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 +unsigned int +Sized_elf_reloc_mapper::symbol_section( + unsigned int symndx, Address* value, bool* is_ordinary) +{ + const int symsize = elfcpp::Elf_sizes::sym_size; + gold_assert((symndx + 1) * symsize <= this->symtab_size_); + elfcpp::Sym 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 +unsigned int +Sized_elf_reloc_mapper::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::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(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(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(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(); + break; +#endif +#ifdef HAVE_TARGET_32_BIG + case Parameters::TARGET_32_BIG: + this->do_parse(); + break; +#endif +#ifdef HAVE_TARGET_64_LITTLE + case Parameters::TARGET_64_LITTLE: + this->do_parse(); + break; +#endif +#ifdef HAVE_TARGET_64_BIG + case Parameters::TARGET_64_BIG: + this->do_parse(); + break; +#endif + default: + gold_unreachable(); + } +} + +template +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(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::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::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(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::read_lines(unsigned const char* lineptr return lengthstart + header_.total_length; } -// Looks in the symtab to see what section a symbol is in. - -template -unsigned int -Sized_dwarf_line_info::symbol_section( - Object* object, - unsigned int sym, - typename elfcpp::Elf_types::Elf_Addr* value, - bool* is_ordinary) -{ - const int symsize = elfcpp::Elf_sizes::sym_size; - gold_assert(sym * symsize < this->symtab_buffer_size_); - elfcpp::Sym 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 void -Sized_dwarf_line_info::read_relocs(Object* object) +Sized_dwarf_line_info::read_relocs() { if (this->symtab_buffer_ == NULL) return; - typename elfcpp::Elf_types::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::read_relocs(Object* object) template void -Sized_dwarf_line_info::read_line_mappings(Object* object, - unsigned int shndx) +Sized_dwarf_line_info::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::format_file_lineno( gold_assert(loc.header_num < static_cast(this->files_.size())); gold_assert(loc.file_num - < static_cast(this->files_[loc.header_num].size())); + < static_cast(this->files_[loc.header_num].size())); const std::pair& 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 722ee647afd..0c3dab6eece 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 . // This file is part of gold. @@ -26,6 +26,7 @@ #include #include #include +#include #include "elfcpp.h" #include "elfcpp_swap.h" @@ -35,10 +36,815 @@ namespace gold { -template -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 +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::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 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 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 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_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 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 + 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::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 track_relocs_; + Sized_elf_reloc_mapper* 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::Elf_Addr, - std::pair::Elf_Swxword> > + typedef std::map > Reloc_map; Reloc_map reloc_map_; diff --git a/gold/dynobj.h b/gold/dynobj.h index 186b67cf7ec..e027485f386 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 00000000000..11b732a21c8 --- /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 . + +// 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 +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 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_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(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 00000000000..1ca38cc9288 --- /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 . + +// 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 +#include + +#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 +class Sized_relobj; +class Dwarf_range_list; +template +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 Cu_vector; + + // The .gdb_index section. + Output_section* gdb_index_section_; + // The list of DWARF compilation units. + std::vector comp_units_; + // The list of DWARF type units. + std::vector type_units_; + // The list of address ranges. + std::vector ranges_; + // The symbol table. + Gdb_hashtab* gdb_symtab_; + // The CU vector portion of the constant pool. + std::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 2a265736a79..60097a8ebc0 100644 --- a/gold/incremental.cc +++ b/gold/incremental.cc @@ -2002,6 +2002,11 @@ Sized_relobj_incr::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 debug_info_sections; + std::vector 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::do_layout( gold_assert(os != NULL); out_sections[i] = os; this->section_offsets()[i] = static_cast
(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::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::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::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::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 std::string -Sized_relobj_incr::do_section_name(unsigned int) +Sized_relobj_incr::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 -Object::Location -Sized_relobj_incr::do_section_contents(unsigned int) +const unsigned char* +Sized_relobj_incr::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::do_section_name(unsigned int) // Return a view of the contents of a section. template -Object::Location -Sized_incr_dynobj::do_section_contents(unsigned int) +const unsigned char* +Sized_incr_dynobj::do_section_contents( + unsigned int, + section_size_type*, + bool) { gold_unreachable(); } diff --git a/gold/incremental.h b/gold/incremental.h index 0edb190885d..b631ae225f6 100644 --- a/gold/incremental.h +++ b/gold/incremental.h @@ -1880,8 +1880,9 @@ class Sized_relobj_incr : public Sized_relobj 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 c12037671fd..65d1432d7b3 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::Elf_Addr sh_addr = shdr.get_sh_addr(); typename elfcpp::Elf_types::Elf_Off sh_offset = shdr.get_sh_offset(); typename elfcpp::Elf_types::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 +void +Layout::add_to_gdb_index(bool is_type_unit, + Sized_relobj* 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 d76fc96bee1..f81ea3b9a73 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 + void + add_to_gdb_index(bool is_type_unit, + Sized_relobj* 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 048454186f8..d3292981f80 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(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 2b4fe022791..15e5d054078 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::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 void Sized_relobj_file::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::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::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::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::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::do_layout(Symbol_table* symtab, // Keep track of .eh_frame sections. std::vector eh_frame_sections; + // Keep track of .debug_info and .debug_types sections. + std::vector debug_info_sections; + std::vector 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::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::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::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::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::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 9d3e1d736a9..82517d57039 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 { 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 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 534df26623f..b5df3ebf2af 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 637613c897a..63d0974c6ad 100644 --- a/gold/plugin.cc +++ b/gold/plugin.cc @@ -1159,13 +1159,14 @@ Sized_pluginobj::do_section_name(unsigned int) // Return a view of the contents of a section. Not used for plugin objects. template -Object::Location -Sized_pluginobj::do_section_contents(unsigned int) +const unsigned char* +Sized_pluginobj::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 32ffe35fc33..1891d6ba402 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 ec448acc152..4827600b382 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 a7fd06d79c9..6f74f710978 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 251bdceb9b1..844aa67226d 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 00000000000..342d47c5049 --- /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 . + +// 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 +class c2 +{ + public: + c2(T t) + : t_(t) + { + ++c2_count; + } + + ~c2() + { --c2_count; } + + T + val() + { return this->t_; } + + T t_; +}; + +c2 c2v1(1); +c2 c2v2(2.0); +c2 c2v3(&ci); +}; + +enum F +{ + F_A, + F_B, + F_C +}; + +template +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 00000000000..9221f7ff8d3 --- /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 . + +# 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: " +check $STDOUT "^\[ *[0-9]*\] check >: " +check $STDOUT "^\[ *[0-9]*\] check >: " +# check $STDOUT "^\[ *[0-9]*\] check >: " +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::~c2: " +check $STDOUT "^\[ *[0-9]*\] two::c2::c2: " +check $STDOUT "^\[ *[0-9]*\] two::c2::val: " +check $STDOUT "^\[ *[0-9]*\] two::c2: " +check $STDOUT "^\[ *[0-9]*\] two::c2: " +check $STDOUT "^\[ *[0-9]*\] two::c2::~c2: " +check $STDOUT "^\[ *[0-9]*\] two::c2::c2: " +check $STDOUT "^\[ *[0-9]*\] two::c2::val: " +check $STDOUT "^\[ *[0-9]*\] two::c2::~c2: " +check $STDOUT "^\[ *[0-9]*\] two::c2::c2: " +check $STDOUT "^\[ *[0-9]*\] two::c2::val: " +check $STDOUT "^\[ *[0-9]*\] two::c2: " +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 00000000000..99928bfa44c --- /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 . + +# 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: " +check $STDOUT "^\[ *[0-9]*\] check >: " +check $STDOUT "^\[ *[0-9]*\] check >: " +# check $STDOUT "^\[ *[0-9]*\] check >: " +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::~c2: " +check $STDOUT "^\[ *[0-9]*\] two::c2::c2: " +check $STDOUT "^\[ *[0-9]*\] two::c2::val: " +check $STDOUT "^\[ *[0-9]*\] two::c2: " +check $STDOUT "^\[ *[0-9]*\] two::c2: " +check $STDOUT "^\[ *[0-9]*\] two::c2::~c2: " +check $STDOUT "^\[ *[0-9]*\] two::c2::c2: " +check $STDOUT "^\[ *[0-9]*\] two::c2::val: " +check $STDOUT "^\[ *[0-9]*\] two::c2::~c2: " +check $STDOUT "^\[ *[0-9]*\] two::c2::c2: " +check $STDOUT "^\[ *[0-9]*\] two::c2::val: " +check $STDOUT "^\[ *[0-9]*\] two::c2: " +check $STDOUT "^\[ *[0-9]*\] two::c2v1: " +check $STDOUT "^\[ *[0-9]*\] two::c2v2: " +check $STDOUT "^\[ *[0-9]*\] anonymous_union_var: " + +exit 0