Speed up relocations against local symbols in merged sections.
authorIan Lance Taylor <iant@google.com>
Fri, 21 Dec 2007 21:19:45 +0000 (21:19 +0000)
committerIan Lance Taylor <iant@google.com>
Fri, 21 Dec 2007 21:19:45 +0000 (21:19 +0000)
gold/ehframe.cc
gold/ehframe.h
gold/layout.cc
gold/merge.cc
gold/merge.h
gold/object.cc
gold/object.h
gold/output.cc
gold/output.h
gold/reloc.cc

index b77148b43989a39c40f50449b42ba6ae4ce1c551..b2406b4f7e1f4da356f4d6927dbebb84f3993e8a 100644 (file)
@@ -1041,6 +1041,15 @@ Eh_frame::do_output_offset(const Relobj* object, unsigned int shndx,
   return this->merge_map_.get_output_offset(object, shndx, offset, poutput);
 }
 
+// Return whether this is the merge section for an input section.
+
+bool
+Eh_frame::do_is_merge_section_for(const Relobj* object,
+                                 unsigned int shndx) const
+{
+  return this->merge_map_.is_merge_section_for(object, shndx);
+}
+
 // Write the data to the output file.
 
 void
index ce4f6df5879ee5714b3d25f21f82bd4d963f9544..8e09657e948dcbb078c90dfd3b3a933a56547486 100644 (file)
@@ -326,6 +326,10 @@ class Eh_frame : public Output_section_data
                   section_offset_type offset,
                   section_offset_type* poutput) const;
 
+  // Return whether this is the merge section for an input section.
+  bool
+  do_is_merge_section_for(const Relobj*, unsigned int shndx) const;
+
   // Write the data to the file.
   void
   do_write(Output_file*);
index 231e2c9f4146875b2516a589608ac940ab08e0bc..3897ec7c24c98436da3bf96f833f15181715b51a 100644 (file)
@@ -732,6 +732,12 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab,
   // they contain.
   off_t off = this->set_segment_offsets(target, load_seg, &shndx);
 
+  // Set the file offsets of all the non-data sections we've seen so
+  // far which don't have to wait for the input sections.  We need
+  // this in order to finalize local symbols in non-allocated
+  // sections.
+  off = this->set_section_offsets(off, BEFORE_INPUT_SECTIONS_PASS);
+
   // Create the symbol table sections.
   this->create_symtab_sections(input_objects, symtab, task, &off);
   if (!parameters->doing_static_link())
@@ -740,8 +746,8 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab,
   // Create the .shstrtab section.
   Output_section* shstrtab_section = this->create_shstrtab();
 
-  // Set the file offsets of all the non-data sections which don't
-  // have to wait for the input sections.
+  // Set the file offsets of the rest of the non-data sections which
+  // don't have to wait for the input sections.
   off = this->set_section_offsets(off, BEFORE_INPUT_SECTIONS_PASS);
 
   // Now that all sections have been created, set the section indexes.
@@ -1113,6 +1119,10 @@ Layout::set_section_offsets(off_t off, Layout::Section_offset_pass pass)
       if (*p == this->symtab_section_)
        continue;
 
+      // If we've already set the data size, don't set it again.
+      if ((*p)->is_offset_valid() && (*p)->is_data_size_valid())
+       continue;
+
       if (pass == BEFORE_INPUT_SECTIONS_PASS
          && (*p)->requires_postprocessing())
        {
index beb0906c0e443722df94d812e2d1a5c0cd9a9a79..75a3eeea924f48978174dfc52e0d63a5e96c6ef8 100644 (file)
 namespace gold
 {
 
-// For each object with merge sections, we store an Object_merge_map.
-// This is used to map locations in input sections to a merged output
-// section.  The output section itself is not recorded here--it can be
-// found in the map_to_output_ field of the Object.
-
-class Object_merge_map
-{
- public:
-  Object_merge_map()
-    : first_shnum_(-1U), first_map_(),
-      second_shnum_(-1U), second_map_(),
-      section_merge_maps_()
-  { }
-
-  ~Object_merge_map();
-
-  // Add a mapping for MERGE_MAP, for the bytes from OFFSET to OFFSET
-  // + LENGTH in the input section SHNDX to OUTPUT_OFFSET in the
-  // output section.  An OUTPUT_OFFSET of -1 means that the bytes are
-  // discarded.  OUTPUT_OFFSET is relative to the start of the merged
-  // data in the output section.
-  void
-  add_mapping(const Merge_map*, unsigned int shndx, section_offset_type offset,
-             section_size_type length, section_offset_type output_offset);
-
-  // Get the output offset for an input address in MERGE_MAP.  The
-  // input address is at offset OFFSET in section SHNDX.  This sets
-  // *OUTPUT_OFFSET to the offset in the output section; this will be
-  // -1 if the bytes are not being copied to the output.  This returns
-  // true if the mapping is known, false otherwise.  *OUTPUT_OFFSET is
-  // relative to the start of the merged data in the output section.
-  bool
-  get_output_offset(const Merge_map*, unsigned int shndx,
-                   section_offset_type offset,
-                   section_offset_type *output_offset);
-
- private:
-  // Map input section offsets to a length and an output section
-  // offset.  An output section offset of -1 means that this part of
-  // the input section is being discarded.
-  struct Input_merge_entry
-  {
-    // The offset in the input section.
-    section_offset_type input_offset;
-    // The length.
-    section_size_type length;
-    // The offset in the output section.
-    section_offset_type output_offset;
-  };
-
-  // A less-than comparison routine for Input_merge_entry.
-  struct Input_merge_compare
-  {
-    bool
-    operator()(const Input_merge_entry& i1, const Input_merge_entry& i2) const
-    { return i1.input_offset < i2.input_offset; }
-  };
-
-  // A list of entries for a particular input section.
-  struct Input_merge_map
-  {
-    // We store these with the Relobj, and we look them up by input
-    // section.  It is possible to have two different merge maps
-    // associated with a single output section.  For example, this
-    // happens routinely with .rodata, when merged string constants
-    // and merged fixed size constants are both put into .rodata.  The
-    // output offset that we store is not the offset from the start of
-    // the output section; it is the offset from the start of the
-    // merged data in the output section.  That means that the caller
-    // is going to add the offset of the merged data within the output
-    // section, which means that the caller needs to know which set of
-    // merged data it found the entry in.  So it's not enough to find
-    // this data based on the input section and the output section; we
-    // also have to find it based on a set of merged data in the
-    // output section.  In order to verify that we are looking at the
-    // right data, we store a pointer to the Merge_map here, and we
-    // pass in a pointer when looking at the data.  If we are asked to
-    // look up information for a different Merge_map, we report that
-    // we don't have it, rather than trying a lookup and returning an
-    // answer which will receive the wrong offset.
-    const Merge_map* merge_map;
-    // The list of mappings.
-    std::vector<Input_merge_entry> entries;
-    // Whether the ENTRIES field is sorted by input_offset.
-    bool sorted;
-
-    Input_merge_map()
-      : merge_map(NULL), entries(), sorted(true)
-    { }
-  };
-
-  // Map input section indices to merge maps.
-  typedef std::map<unsigned int, Input_merge_map*> Section_merge_maps;
-
-  // Return a pointer to the Input_merge_map to use for the input
-  // section SHNDX, or NULL.
-  Input_merge_map*
-  get_input_merge_map(unsigned int shndx);
-
-  // Get or make the the Input_merge_map to use for the section SHNDX
-  // with MERGE_MAP.
-  Input_merge_map*
-  get_or_make_input_merge_map(const Merge_map* merge_map, unsigned int shndx);
-
-  // Any given object file will normally only have a couple of input
-  // sections with mergeable contents.  So we keep the first two input
-  // section numbers inline, and push any further ones into a map.  A
-  // value of -1U in first_shnum_ or second_shnum_ means that we don't
-  // have a corresponding entry.
-  unsigned int first_shnum_;
-  Input_merge_map first_map_;
-  unsigned int second_shnum_;
-  Input_merge_map second_map_;
-  Section_merge_maps section_merge_maps_;
-};
+// Class Object_merge_map.
 
 // Destructor.
 
@@ -261,7 +147,8 @@ Object_merge_map::get_output_offset(const Merge_map* merge_map,
                                    section_offset_type *output_offset)
 {
   Input_merge_map* map = this->get_input_merge_map(shndx);
-  if (map == NULL || map->merge_map != merge_map)
+  if (map == NULL
+      || (merge_map != NULL && map->merge_map != merge_map))
     return false;
 
   if (!map->sorted)
@@ -294,6 +181,49 @@ Object_merge_map::get_output_offset(const Merge_map* merge_map,
   return true;
 }
 
+// Return whether this is the merge map for section SHNDX.
+
+inline bool
+Object_merge_map::is_merge_section_for(const Merge_map* merge_map,
+                                      unsigned int shndx)
+{
+  Input_merge_map* map = this->get_input_merge_map(shndx);
+  return map != NULL && map->merge_map == merge_map;
+}
+
+// Initialize a mapping from input offsets to output addresses.
+
+template<int size>
+void
+Object_merge_map::initialize_input_to_output_map(
+    unsigned int shndx,
+    typename elfcpp::Elf_types<size>::Elf_Addr starting_address,
+    Unordered_map<section_offset_type,
+                 typename elfcpp::Elf_types<size>::Elf_Addr>* initialize_map)
+{
+  Input_merge_map* map = this->get_input_merge_map(shndx);
+  gold_assert(map != NULL);
+
+  for (Input_merge_map::Entries::const_iterator p = map->entries.begin();
+       p != map->entries.end();
+       ++p)
+    {
+      section_offset_type output_offset = p->output_offset;
+      if (output_offset != -1)
+       output_offset += starting_address;
+      else
+       {
+         // If we see a relocation against an address we have chosen
+         // to discard, we relocate to zero.  FIXME: We could also
+         // issue a warning in this case; that would require
+         // reporting this somehow and checking it in the routines in
+         // reloc.h.
+         output_offset = 0;
+       }
+      initialize_map->insert(std::make_pair(p->input_offset, output_offset));
+    }
+}
+
 // Class Merge_map.
 
 // Add a mapping for the bytes from OFFSET to OFFSET + LENGTH in input
@@ -333,6 +263,17 @@ Merge_map::get_output_offset(const Relobj* object, unsigned int shndx,
                                             output_offset);
 }
 
+// Return whether this is the merge section for SHNDX in OBJECT.
+
+bool
+Merge_map::is_merge_section_for(const Relobj* object, unsigned int shndx) const
+{
+  Object_merge_map* object_merge_map = object->merge_map();
+  if (object_merge_map == NULL)
+    return false;
+  return object_merge_map->is_merge_section_for(this, shndx);
+}
+
 // Class Output_merge_base.
 
 // Return the output offset for an input offset.  The input address is
@@ -348,6 +289,15 @@ Output_merge_base::do_output_offset(const Relobj* object,
   return this->merge_map_.get_output_offset(object, shndx, offset, poutput);
 }
 
+// Return whether this is the merge section for SHNDX in OBJECT.
+
+bool
+Output_merge_base::do_is_merge_section_for(const Relobj* object,
+                                          unsigned int shndx) const
+{
+  return this->merge_map_.is_merge_section_for(object, shndx);
+}
+
 // Class Output_merge_data.
 
 // Compute the hash code for a fixed-size constant.
@@ -666,4 +616,22 @@ class Output_merge_string<uint16_t>;
 template
 class Output_merge_string<uint32_t>;
 
+#if defined(HAVE_TARGET_32_LITTLE) || defined(HAVE_TARGET_32_BIG)
+template
+void
+Object_merge_map::initialize_input_to_output_map<32>(
+    unsigned int shndx,
+    elfcpp::Elf_types<32>::Elf_Addr starting_address,
+    Unordered_map<section_offset_type, elfcpp::Elf_types<32>::Elf_Addr>*);
+#endif
+
+#if defined(HAVE_TARGET_64_LITTLE) || defined(HAVE_TARGET_64_BIG)
+template
+void
+Object_merge_map::initialize_input_to_output_map<64>(
+    unsigned int shndx,
+    elfcpp::Elf_types<64>::Elf_Addr starting_address,
+    Unordered_map<section_offset_type, elfcpp::Elf_types<64>::Elf_Addr>*);
+#endif
+
 } // End namespace gold.
index c232ab0787cf3da21cdc4b0913b308a7677ed218..c385a44e6674d12595c0a65d03df132bf950c26a 100644 (file)
@@ -24,6 +24,8 @@
 #define GOLD_MERGE_H
 
 #include <climits>
+#include <map>
+#include <vector>
 
 #include "stringpool.h"
 #include "output.h"
 namespace gold
 {
 
+class Merge_map;
+
+// For each object with merge sections, we store an Object_merge_map.
+// This is used to map locations in input sections to a merged output
+// section.  The output section itself is not recorded here--it can be
+// found in the map_to_output_ field of the Object.
+
+class Object_merge_map
+{
+ public:
+  Object_merge_map()
+    : first_shnum_(-1U), first_map_(),
+      second_shnum_(-1U), second_map_(),
+      section_merge_maps_()
+  { }
+
+  ~Object_merge_map();
+
+  // Add a mapping for MERGE_MAP, for the bytes from OFFSET to OFFSET
+  // + LENGTH in the input section SHNDX to OUTPUT_OFFSET in the
+  // output section.  An OUTPUT_OFFSET of -1 means that the bytes are
+  // discarded.  OUTPUT_OFFSET is relative to the start of the merged
+  // data in the output section.
+  void
+  add_mapping(const Merge_map*, unsigned int shndx, section_offset_type offset,
+             section_size_type length, section_offset_type output_offset);
+
+  // Get the output offset for an input address.  MERGE_MAP is the map
+  // we are looking for, or NULL if we don't care.  The input address
+  // is at offset OFFSET in section SHNDX.  This sets *OUTPUT_OFFSET
+  // to the offset in the output section; this will be -1 if the bytes
+  // are not being copied to the output.  This returns true if the
+  // mapping is known, false otherwise.  *OUTPUT_OFFSET is relative to
+  // the start of the merged data in the output section.
+  bool
+  get_output_offset(const Merge_map*, unsigned int shndx,
+                   section_offset_type offset,
+                   section_offset_type *output_offset);
+
+  // Return whether this is the merge map for section SHNDX.
+  bool
+  is_merge_section_for(const Merge_map*, unsigned int shndx);
+
+  // Initialize an mapping from input offsets to output addresses for
+  // section SHNDX.  STARTING_ADDRESS is the output address of the
+  // merged section.
+  template<int size>
+  void
+  initialize_input_to_output_map(
+      unsigned int shndx,
+      typename elfcpp::Elf_types<size>::Elf_Addr starting_address,
+      Unordered_map<section_offset_type,
+                   typename elfcpp::Elf_types<size>::Elf_Addr>*);
+
+ private:
+  // Map input section offsets to a length and an output section
+  // offset.  An output section offset of -1 means that this part of
+  // the input section is being discarded.
+  struct Input_merge_entry
+  {
+    // The offset in the input section.
+    section_offset_type input_offset;
+    // The length.
+    section_size_type length;
+    // The offset in the output section.
+    section_offset_type output_offset;
+  };
+
+  // A less-than comparison routine for Input_merge_entry.
+  struct Input_merge_compare
+  {
+    bool
+    operator()(const Input_merge_entry& i1, const Input_merge_entry& i2) const
+    { return i1.input_offset < i2.input_offset; }
+  };
+
+  // A list of entries for a particular input section.
+  struct Input_merge_map
+  {
+    typedef std::vector<Input_merge_entry> Entries;
+
+    // We store these with the Relobj, and we look them up by input
+    // section.  It is possible to have two different merge maps
+    // associated with a single output section.  For example, this
+    // happens routinely with .rodata, when merged string constants
+    // and merged fixed size constants are both put into .rodata.  The
+    // output offset that we store is not the offset from the start of
+    // the output section; it is the offset from the start of the
+    // merged data in the output section.  That means that the caller
+    // is going to add the offset of the merged data within the output
+    // section, which means that the caller needs to know which set of
+    // merged data it found the entry in.  So it's not enough to find
+    // this data based on the input section and the output section; we
+    // also have to find it based on a set of merged data in the
+    // output section.  In order to verify that we are looking at the
+    // right data, we store a pointer to the Merge_map here, and we
+    // pass in a pointer when looking at the data.  If we are asked to
+    // look up information for a different Merge_map, we report that
+    // we don't have it, rather than trying a lookup and returning an
+    // answer which will receive the wrong offset.
+    const Merge_map* merge_map;
+    // The list of mappings.
+    Entries entries;
+    // Whether the ENTRIES field is sorted by input_offset.
+    bool sorted;
+
+    Input_merge_map()
+      : merge_map(NULL), entries(), sorted(true)
+    { }
+  };
+
+  // Map input section indices to merge maps.
+  typedef std::map<unsigned int, Input_merge_map*> Section_merge_maps;
+
+  // Return a pointer to the Input_merge_map to use for the input
+  // section SHNDX, or NULL.
+  Input_merge_map*
+  get_input_merge_map(unsigned int shndx);
+
+  // Get or make the the Input_merge_map to use for the section SHNDX
+  // with MERGE_MAP.
+  Input_merge_map*
+  get_or_make_input_merge_map(const Merge_map* merge_map, unsigned int shndx);
+
+  // Any given object file will normally only have a couple of input
+  // sections with mergeable contents.  So we keep the first two input
+  // section numbers inline, and push any further ones into a map.  A
+  // value of -1U in first_shnum_ or second_shnum_ means that we don't
+  // have a corresponding entry.
+  unsigned int first_shnum_;
+  Input_merge_map first_map_;
+  unsigned int second_shnum_;
+  Input_merge_map second_map_;
+  Section_merge_maps section_merge_maps_;
+};
+
 // This class manages mappings from input sections to offsets in an
 // output section.  This is used where input sections are merged.  The
 // actual data is stored in fields in Object.
@@ -63,6 +201,12 @@ class Merge_map
   get_output_offset(const Relobj* object, unsigned int shndx,
                    section_offset_type offset,
                    section_offset_type *output_offset) const;
+
+  // Return whether this is the merge mapping for section SHNDX in
+  // OBJECT.  This should return true when get_output_offset would
+  // return true for some input offset.
+  bool
+  is_merge_section_for(const Relobj* object, unsigned int shndx) const;
 };
 
 // A general class for SHF_MERGE data, to hold functions shared by
@@ -75,13 +219,17 @@ class Output_merge_base : public Output_section_data
     : Output_section_data(addralign), merge_map_(), entsize_(entsize)
   { }
 
+ protected:
   // Return the output offset for an input offset.
   bool
   do_output_offset(const Relobj* object, unsigned int shndx,
                   section_offset_type offset,
                   section_offset_type* poutput) const;
 
- protected:
+  // Return whether this is the merge section for an input section.
+  bool
+  do_is_merge_section_for(const Relobj*, unsigned int shndx) const;
+
   // Return the entry size.
   uint64_t
   entsize() const
index 5216049e3d1838cd603ce02357c691692044d9c5..e92a66d2e4948a175d2ab542358f721b6531413a 100644 (file)
@@ -889,14 +889,30 @@ Sized_relobj<size, big_endian>::do_finalize_local_symbols(unsigned int index,
            }
          else if (mo[shndx].offset == -1)
            {
-             // Leave the input value in place for SHF_MERGE sections.
+             // This is a SHF_MERGE section or one which otherwise
+             // requires special handling.  We get the output address
+             // of the start of the merged section.  If this is not a
+             // section symbol, we can then determine the final
+             // value.  If it is a section symbol, we can not, as in
+             // that case we have to consider the addend to determine
+             // the value to use in a relocation.
+             section_offset_type start =
+               os->starting_output_address(this, shndx);
+             if (!lv.is_section_symbol())
+               lv.set_output_value(lv.input_value() + start);
+             else
+               {
+                 Merged_symbol_value<size>* msv =
+                   new Merged_symbol_value<size>(lv.input_value(), start);
+                 lv.set_merged_symbol_value(msv);
+               }
            }
           else if (lv.is_tls_symbol())
-           lv.set_output_value(mo[shndx].output_section->tls_offset()
+           lv.set_output_value(os->tls_offset()
                                + mo[shndx].offset
                                + lv.input_value());
          else
-           lv.set_output_value(mo[shndx].output_section->address()
+           lv.set_output_value(os->address()
                                + mo[shndx].offset
                                + lv.input_value());
        }
@@ -953,40 +969,6 @@ Sized_relobj<size, big_endian>::local_symbol_value(unsigned int symndx) const
   return lv.value(this, 0);
 }
 
-// Return the value of a local symbol defined in input section SHNDX,
-// with value VALUE, adding addend ADDEND.  IS_SECTION_SYMBOL
-// indicates whether the symbol is a section symbol.  This handles
-// SHF_MERGE sections.
-template<int size, bool big_endian>
-typename elfcpp::Elf_types<size>::Elf_Addr
-Sized_relobj<size, big_endian>::local_value(unsigned int shndx,
-                                           Address value,
-                                           bool is_section_symbol,
-                                           Address addend) const
-{
-  const std::vector<Map_to_output>& mo(this->map_to_output());
-  Output_section* os = mo[shndx].output_section;
-  if (os == NULL)
-    return addend;
-  gold_assert(mo[shndx].offset == -1);
-
-  // Do the mapping required by the output section.  If this is not a
-  // section symbol, then we want to map the symbol value, and then
-  // include the addend.  If this is a section symbol, then we need to
-  // include the addend to figure out where in the section we are,
-  // before we do the mapping.  This will do the right thing provided
-  // the assembler is careful to only convert a relocation in a merged
-  // section to a section symbol if there is a zero addend.  If the
-  // assembler does not do this, then in general we can't know what to
-  // do, because we can't distinguish the addend for the instruction
-  // format from the addend for the section offset.
-
-  if (is_section_symbol)
-    return os->output_address(this, shndx, value + addend);
-  else
-    return addend + os->output_address(this, shndx, value);
-}
-
 // Write out the local symbols.
 
 template<int size, bool big_endian>
index 1ad9796a42536932ae31d75bc14fda3a02e35def..f5e4ab62482b2210216eb276fff7c33a45eee393 100644 (file)
@@ -622,6 +622,71 @@ Relobj::output_section(unsigned int shndx, section_offset_type* poff) const
   return mo.output_section;
 }
 
+// This class is used to handle relocations against a section symbol
+// in an SHF_MERGE section.  For such a symbol, we need to know the
+// addend of the relocation before we can determine the final value.
+// The addend gives us the location in the input section, and we can
+// determine how it is mapped to the output section.  For a
+// non-section symbol, we apply the addend to the final value of the
+// symbol; that is done in finalize_local_symbols, and does not use
+// this class.
+
+template<int size>
+class Merged_symbol_value
+{
+ public:
+  typedef typename elfcpp::Elf_types<size>::Elf_Addr Value;
+
+  // We use a hash table to map offsets in the input section to output
+  // addresses.
+  typedef Unordered_map<section_offset_type, Value> Output_addresses;
+
+  Merged_symbol_value(Value input_value, Value output_start_address)
+    : input_value_(input_value), output_start_address_(output_start_address),
+      output_addresses_()
+  { }
+
+  // Initialize the hash table.
+  void
+  initialize_input_to_output_map(const Relobj*, unsigned int input_shndx);
+
+  // Release the hash table to save space.
+  void
+  free_input_to_output_map()
+  { this->output_addresses_.clear(); }
+
+  // Get the output value corresponding to an addend.  The object and
+  // input section index are passed in because the caller will have
+  // them; otherwise we could store them here.
+  Value
+  value(const Relobj* object, unsigned int input_shndx, Value addend) const
+  {
+    Value input_offset = this->input_value_ + addend;
+    typename Output_addresses::const_iterator p =
+      this->output_addresses_.find(input_offset);
+    if (p != this->output_addresses_.end())
+      return p->second;
+
+    return this->value_from_output_section(object, input_shndx, input_offset);
+  }
+
+ private:
+  // Get the output value for an input offset if we couldn't find it
+  // in the hash table.
+  Value
+  value_from_output_section(const Relobj*, unsigned int input_shndx,
+                           Value input_offset) const;
+
+  // The value of the section symbol in the input file.  This is
+  // normally zero, but could in principle be something else.
+  Value input_value_;
+  // The start address of this merged section in the output file.
+  Value output_start_address_;
+  // A hash table which maps offsets in the input section to output
+  // addresses.  This only maps specific offsets, not all offsets.
+  Output_addresses output_addresses_;
+};
+
 // This POD class is holds the value of a symbol.  This is used for
 // local symbols, and for all symbols during relocation processing.
 // For special sections, such as SHF_MERGE sections, this calls a
@@ -636,8 +701,8 @@ class Symbol_value
   Symbol_value()
     : output_symtab_index_(0), output_dynsym_index_(-1U), input_shndx_(0),
       is_section_symbol_(false), is_tls_symbol_(false),
-      needs_output_address_(false), value_(0)
-  { }
+      has_output_value_(true)
+  { this->u_.value = 0; }
 
   // Get the value of this symbol.  OBJECT is the object in which this
   // symbol is defined, and ADDEND is an addend to add to the value.
@@ -645,41 +710,64 @@ class Symbol_value
   Value
   value(const Sized_relobj<size, big_endian>* object, Value addend) const
   {
-    if (!this->needs_output_address_)
-      return this->value_ + addend;
-    return object->local_value(this->input_shndx_, this->value_,
-                              this->is_section_symbol_, addend);
+    if (this->has_output_value_)
+      return this->u_.value + addend;
+    else
+      return this->u_.merged_symbol_value->value(object, this->input_shndx_,
+                                                addend);
   }
 
   // Set the value of this symbol in the output symbol table.
   void
   set_output_value(Value value)
+  { this->u_.value = value; }
+
+  // For a section symbol in a merged section, we need more
+  // information.
+  void
+  set_merged_symbol_value(Merged_symbol_value<size>* msv)
   {
-    this->value_ = value;
-    this->needs_output_address_ = false;
+    gold_assert(this->is_section_symbol_);
+    this->has_output_value_ = false;
+    this->u_.merged_symbol_value = msv;
   }
 
-  // Set the value of the symbol from the input file.  This value
-  // will usually be replaced during finalization with the output
-  // value, but if the symbol is mapped to an output section which
-  // requires special handling to determine the output value, we
-  // leave the input value in place until later.  This is used for
-  // SHF_MERGE sections.
+  // Initialize the input to output map for a section symbol in a
+  // merged section.  We also initialize the value of a non-section
+  // symbol in a merged section.
   void
-  set_input_value(Value value)
+  initialize_input_to_output_map(const Relobj* object)
   {
-    this->value_ = value;
-    this->needs_output_address_ = true;
+    if (!this->has_output_value_)
+      {
+       gold_assert(this->is_section_symbol_);
+       Merged_symbol_value<size>* msv = this->u_.merged_symbol_value;
+       msv->initialize_input_to_output_map(object, this->input_shndx_);
+      }
   }
 
-  // Return the input value.
-  Value
-  input_value() const
+  // Free the input to output map for a section symbol in a merged
+  // section.
+  void
+  free_input_to_output_map()
   {
-    gold_assert(this->needs_output_address_);
-    return this->value_;
+    if (!this->has_output_value_)
+      this->u_.merged_symbol_value->free_input_to_output_map();
   }
 
+  // Set the value of the symbol from the input file.  This is only
+  // called by count_local_symbols, to communicate the value to
+  // finalize_local_symbols.
+  void
+  set_input_value(Value value)
+  { this->u_.value = value; }
+
+  // Return the input value.  This is only called by
+  // finalize_local_symbols.
+  Value
+  input_value() const
+  { return this->u_.value; }
+
   // Return whether this symbol should go into the output symbol
   // table.
   bool
@@ -757,6 +845,11 @@ class Symbol_value
   input_shndx() const
   { return this->input_shndx_; }
 
+  // Whether this is a section symbol.
+  bool
+  is_section_symbol() const
+  { return this->is_section_symbol_; }
+
   // Record that this is a section symbol.
   void
   set_is_section_symbol()
@@ -786,14 +879,23 @@ class Symbol_value
   bool is_section_symbol_ : 1;
   // Whether this is a STT_TLS symbol.
   bool is_tls_symbol_ : 1;
-  // Whether getting the value of this symbol requires calling an
-  // Output_section method.  For example, this will be true of a
-  // symbol in a SHF_MERGE section.
-  bool needs_output_address_ : 1;
-  // The value of the symbol.  If !needs_output_address_, this is the
-  // value in the output file.  If needs_output_address_, this is the
-  // value in the input file.
-  Value value_;
+  // Whether this symbol has a value for the output file.  This is
+  // normally set to true during Layout::finalize, by
+  // finalize_local_symbols.  It will be false for a section symbol in
+  // a merge section, as for such symbols we can not determine the
+  // value to use in a relocation until we see the addend.
+  bool has_output_value_ : 1;
+  union
+  {
+    // This is used if has_output_value_ is true.  Between
+    // count_local_symbols and finalize_local_symbols, this is the
+    // value in the input file.  After finalize_local_symbols, it is
+    // the value in the output file.
+    Value value;
+    // This is used if has_output_value_ is false.  It points to the
+    // information we need to get the value for a merge section.
+    Merged_symbol_value<size>* merged_symbol_value;
+  } u_;
 };
 
 // A regular object file.  This is size and endian specific.
@@ -876,14 +978,6 @@ class Sized_relobj : public Relobj
   Address
   local_symbol_value(unsigned int symndx) const;
 
-  // Return the value of a local symbol defined in input section
-  // SHNDX, with value VALUE, adding addend ADDEND.  IS_SECTION_SYMBOL
-  // indicates whether the symbol is a section symbol.  This handles
-  // SHF_MERGE sections.
-  Address
-  local_value(unsigned int shndx, Address value, bool is_section_symbol,
-             Address addend) const;
-
   void
   set_needs_output_dynsym_entry(unsigned int sym)
   {
@@ -1119,6 +1213,16 @@ class Sized_relobj : public Relobj
   relocate_sections(const General_options& options, const Symbol_table*,
                    const Layout*, const unsigned char* pshdrs, Views*);
 
+  // Initialize input to output maps for section symbols in merged
+  // sections.
+  void
+  initialize_input_to_output_maps();
+
+  // Free the input to output maps for section symbols in merged
+  // sections.
+  void
+  free_input_to_output_maps();
+
   // Write out the local symbols.
   void
   write_local_symbols(Output_file*,
index 793d6a3a4a46477cb70df635c77dc83487a3d021..3d1d27e644acb631f2c72cc9d5f9f0ced51c43e4 100644 (file)
@@ -1369,6 +1369,18 @@ Output_section::Input_section::output_offset(
     }
 }
 
+// Return whether this is the merge section for the input section
+// SHNDX in OBJECT.
+
+inline bool
+Output_section::Input_section::is_merge_section_for(const Relobj* object,
+                                                   unsigned int shndx) const
+{
+  if (this->is_input_section())
+    return false;
+  return this->u2_.posd->is_merge_section_for(object, shndx);
+}
+
 // Write out the data.  We don't have to do anything for an input
 // section--they are handled via Object::relocate--but this is where
 // we write out the data for an Output_section_data.
@@ -1710,6 +1722,34 @@ Output_section::output_address(const Relobj* object, unsigned int shndx,
   gold_unreachable();
 }
 
+// Return the output address of the start of the merged section for
+// input section SHNDX in object OBJECT.
+
+uint64_t
+Output_section::starting_output_address(const Relobj* object,
+                                       unsigned int shndx) const
+{
+  gold_assert(object->is_section_specially_mapped(shndx));
+
+  uint64_t addr = this->address() + this->first_input_offset_;
+  for (Input_section_list::const_iterator p = this->input_sections_.begin();
+       p != this->input_sections_.end();
+       ++p)
+    {
+      addr = align_address(addr, p->addralign());
+
+      // It would be nice if we could use the existing output_offset
+      // method to get the output offset of input offset 0.
+      // Unfortunately we don't know for sure that input offset 0 is
+      // mapped at all.
+      if (p->is_merge_section_for(object, shndx))
+       return addr;
+
+      addr += p->data_size();
+    }
+  gold_unreachable();
+}
+
 // Set the data size of an Output_section.  This is where we handle
 // setting the addresses of any Output_section_data objects.
 
index 895d70378cc04db4b5a1c0ed2e8033e4e486e4dc..3d1a44d9d545ab87b83c633cbda2c97794c73239 100644 (file)
@@ -198,6 +198,21 @@ class Output_data
   dynamic_reloc_count() const
   { return this->dynamic_reloc_count_; }
 
+  // Whether the address is valid.
+  bool
+  is_address_valid() const
+  { return this->is_address_valid_; }
+
+  // Whether the file offset is valid.
+  bool
+  is_offset_valid() const
+  { return this->is_offset_valid_; }
+
+  // Whether the data size is valid.
+  bool
+  is_data_size_valid() const
+  { return this->is_data_size_valid_; }
+
  protected:
   // Functions that child classes may or in some cases must implement.
 
@@ -256,21 +271,6 @@ class Output_data
 
   // Functions that child classes may call.
 
-  // Whether the address is valid.
-  bool
-  is_address_valid() const
-  { return this->is_address_valid_; }
-
-  // Whether the file offset is valid.
-  bool
-  is_offset_valid() const
-  { return this->is_offset_valid_; }
-
-  // Whether the data size is valid.
-  bool
-  is_data_size_valid() const
-  { return this->is_data_size_valid_; }
-
   // Set the size of the data.
   void
   set_data_size(off_t data_size)
@@ -466,6 +466,13 @@ class Output_section_data : public Output_data
                section_offset_type *poutput) const
   { return this->do_output_offset(object, shndx, offset, poutput); }
 
+  // Return whether this is the merge section for the input section
+  // SHNDX in OBJECT.  This should return true when output_offset
+  // would return true for some values of OFFSET.
+  bool
+  is_merge_section_for(const Relobj* object, unsigned int shndx) const
+  { return this->do_is_merge_section_for(object, shndx); }
+
   // Write the contents to a buffer.  This is used for sections which
   // require postprocessing, such as compression.
   void
@@ -499,6 +506,11 @@ class Output_section_data : public Output_data
                   section_offset_type*) const
   { return false; }
 
+  // The child class may implement is_merge_section_for.
+  virtual bool
+  do_is_merge_section_for(const Relobj*, unsigned int) const
+  { return false; }
+
   // The child class may implement write_to_buffer.  Most child
   // classes can not appear in a compressed section, and they do not
   // implement this.
@@ -1683,6 +1695,13 @@ class Output_section : public Output_data
   output_address(const Relobj* object, unsigned int shndx,
                 off_t offset) const;
 
+  // Return the output address of the start of the merged section for
+  // input section SHNDX in object OBJECT.  This is not necessarily
+  // the offset corresponding to input offset 0 in the section, since
+  // the section may be mapped arbitrarily.
+  uint64_t
+  starting_output_address(const Relobj* object, unsigned int shndx) const;
+
   // Write the section header into *OPHDR.
   template<int size, bool big_endian>
   void
@@ -1904,6 +1923,11 @@ class Output_section : public Output_data
                  section_offset_type offset,
                  section_offset_type *poutput) const;
 
+    // Return whether this is the merge section for the input section
+    // SHNDX in OBJECT.
+    bool
+    is_merge_section_for(const Relobj* object, unsigned int shndx) const;
+
     // Write out the data.  This does nothing for an input section.
     void
     write(Output_file*);
index 37a9a85591a1df84f4d222ffd1a39c25ead7fe5a..e9c13266549b3f061292145bc7f3732464520ae8 100644 (file)
 #include "gold.h"
 
 #include "workqueue.h"
-#include "object.h"
 #include "symtab.h"
 #include "output.h"
+#include "merge.h"
+#include "object.h"
 #include "reloc.h"
 
 namespace gold
@@ -343,10 +344,18 @@ Sized_relobj<size, big_endian>::do_relocate(const General_options& options,
 
   this->write_sections(pshdrs, of, &views);
 
+  // To speed up relocations, we set up hash tables for fast lookup of
+  // input offsets to output addresses.
+  this->initialize_input_to_output_maps();
+
   // Apply relocations.
 
   this->relocate_sections(options, symtab, layout, pshdrs, &views);
 
+  // After we've done the relocations, we release the hash tables,
+  // since we no longer need them.
+  this->free_input_to_output_maps();
+
   // Write out the accumulated views.
   for (unsigned int i = 1; i < shnum; ++i)
     {
@@ -584,6 +593,76 @@ Sized_relobj<size, big_endian>::relocate_sections(
     }
 }
 
+// Create merge hash tables for the local symbols.  These are used to
+// speed up relocations.
+
+template<int size, bool big_endian>
+void
+Sized_relobj<size, big_endian>::initialize_input_to_output_maps()
+{
+  const unsigned int loccount = this->local_symbol_count_;
+  for (unsigned int i = 1; i < loccount; ++i)
+    {
+      Symbol_value<size>& lv(this->local_values_[i]);
+      lv.initialize_input_to_output_map(this);
+    }
+}
+
+// Free merge hash tables for the local symbols.
+
+template<int size, bool big_endian>
+void
+Sized_relobj<size, big_endian>::free_input_to_output_maps()
+{
+  const unsigned int loccount = this->local_symbol_count_;
+  for (unsigned int i = 1; i < loccount; ++i)
+    {
+      Symbol_value<size>& lv(this->local_values_[i]);
+      lv.free_input_to_output_map();
+    }
+}
+
+// Class Merged_symbol_value.
+
+template<int size>
+void
+Merged_symbol_value<size>::initialize_input_to_output_map(
+    const Relobj* object,
+    unsigned int input_shndx)
+{
+  Object_merge_map* map = object->merge_map();
+  map->initialize_input_to_output_map<size>(input_shndx,
+                                           this->output_start_address_,
+                                           &this->output_addresses_);
+}
+
+// Get the output value corresponding to an input offset if we
+// couldn't find it in the hash table.
+
+template<int size>
+typename elfcpp::Elf_types<size>::Elf_Addr
+Merged_symbol_value<size>::value_from_output_section(
+    const Relobj* object,
+    unsigned int input_shndx,
+    typename elfcpp::Elf_types<size>::Elf_Addr input_offset) const
+{
+  section_offset_type output_offset;
+  bool found = object->merge_map()->get_output_offset(NULL, input_shndx,
+                                                     input_offset,
+                                                     &output_offset);
+
+  // If this assertion fails, it means that some relocation was
+  // against a portion of an input merge section which we didn't map
+  // to the output file and we didn't explicitly discard.  We should
+  // always map all portions of input merge sections.
+  gold_assert(found);
+
+  if (output_offset == -1)
+    return 0;
+  else
+    return this->output_start_address_ + output_offset;
+}
+
 // Copy_relocs::Copy_reloc_entry methods.
 
 // Return whether we should emit this reloc.  We should emit it if the
@@ -917,6 +996,26 @@ Sized_relobj<64, true>::do_relocate(const General_options& options,
                                    Output_file* of);
 #endif
 
+#if defined(HAVE_TARGET_32_LITTLE) || defined(HAVE_TARGET_32_BIG)
+template
+class Merged_symbol_value<32>;
+#endif
+
+#if defined(HAVE_TARGET_64_LITTLE) || defined(HAVE_TARGET_64_BIG)
+template
+class Merged_symbol_value<64>;
+#endif
+
+#if defined(HAVE_TARGET_32_LITTLE) || defined(HAVE_TARGET_32_BIG)
+template
+class Symbol_value<32>;
+#endif
+
+#if defined(HAVE_TARGET_64_LITTLE) || defined(HAVE_TARGET_64_BIG)
+template
+class Symbol_value<64>;
+#endif
+
 #ifdef HAVE_TARGET_32_LITTLE
 template
 class Copy_relocs<32, false>;
This page took 0.039502 seconds and 4 git commands to generate.