Add support for SHF_MERGE sections.
authorIan Lance Taylor <iant@google.com>
Wed, 16 May 2007 17:42:48 +0000 (17:42 +0000)
committerIan Lance Taylor <iant@google.com>
Wed, 16 May 2007 17:42:48 +0000 (17:42 +0000)
18 files changed:
gold/Makefile.am
gold/Makefile.in
gold/dynobj.h
gold/i386.cc
gold/layout.cc
gold/merge.cc [new file with mode: 0644]
gold/merge.h [new file with mode: 0644]
gold/object.cc
gold/object.h
gold/output.cc
gold/output.h
gold/po/POTFILES.in
gold/po/gold.pot
gold/reloc.cc
gold/reloc.h
gold/stringpool.cc
gold/stringpool.h
gold/target-reloc.h

index 02a6da1..a411127 100644 (file)
@@ -30,6 +30,7 @@ CCFILES = \
        gold.cc \
        gold-threads.cc \
        layout.cc \
+       merge.cc \
        object.cc \
        options.cc \
        output.cc \
@@ -52,6 +53,7 @@ HFILES = \
        gold.h \
        gold-threads.h \
        layout.h \
+       merge.h \
        object.h \
        options.h \
        output.h \
index 36994c3..884b1a5 100644 (file)
@@ -71,10 +71,11 @@ libgold_a_LIBADD =
 am__objects_1 = archive.$(OBJEXT) common.$(OBJEXT) defstd.$(OBJEXT) \
        dirsearch.$(OBJEXT) dynobj.$(OBJEXT) fileread.$(OBJEXT) \
        gold.$(OBJEXT) gold-threads.$(OBJEXT) layout.$(OBJEXT) \
-       object.$(OBJEXT) options.$(OBJEXT) output.$(OBJEXT) \
-       readsyms.$(OBJEXT) reloc.$(OBJEXT) resolve.$(OBJEXT) \
-       script.$(OBJEXT) symtab.$(OBJEXT) stringpool.$(OBJEXT) \
-       target-select.$(OBJEXT) workqueue.$(OBJEXT)
+       merge.$(OBJEXT) object.$(OBJEXT) options.$(OBJEXT) \
+       output.$(OBJEXT) readsyms.$(OBJEXT) reloc.$(OBJEXT) \
+       resolve.$(OBJEXT) script.$(OBJEXT) symtab.$(OBJEXT) \
+       stringpool.$(OBJEXT) target-select.$(OBJEXT) \
+       workqueue.$(OBJEXT)
 am__objects_2 =
 am__objects_3 = yyscript.$(OBJEXT)
 am_libgold_a_OBJECTS = $(am__objects_1) $(am__objects_2) \
@@ -256,6 +257,7 @@ CCFILES = \
        gold.cc \
        gold-threads.cc \
        layout.cc \
+       merge.cc \
        object.cc \
        options.cc \
        output.cc \
@@ -278,6 +280,7 @@ HFILES = \
        gold.h \
        gold-threads.h \
        layout.h \
+       merge.h \
        object.h \
        options.h \
        output.h \
@@ -398,6 +401,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/i386.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/layout.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/merge.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/object.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/output.Po@am__quote@
index 476b602..d63aa6a 100644 (file)
@@ -12,7 +12,6 @@ namespace gold
 {
 
 class General_options;
-class Stringpool;
 
 // A dynamic object (ET_DYN).  This is an abstract base class itself.
 // The implementations is the template class Sized_dynobj.
index dbbd2c0..448453a 100644 (file)
@@ -109,7 +109,7 @@ class Target_i386 : public Sized_target<32, false>
     relocate(const Relocate_info<32, false>*, Target_i386*, size_t relnum,
             const elfcpp::Rel<32, false>&,
             unsigned int r_type, const Sized_symbol<32>*,
-            elfcpp::Elf_types<32>::Elf_Addr,
+            const Symbol_value<32>*,
             unsigned char*, elfcpp::Elf_types<32>::Elf_Addr,
             off_t);
 
@@ -119,7 +119,7 @@ class Target_i386 : public Sized_target<32, false>
     relocate_tls(const Relocate_info<32, false>*, size_t relnum,
                 const elfcpp::Rel<32, false>&,
                 unsigned int r_type, const Sized_symbol<32>*,
-                elfcpp::Elf_types<32>::Elf_Addr,
+                const Symbol_value<32>*,
                 unsigned char*, elfcpp::Elf_types<32>::Elf_Addr, off_t);
 
     // Do a TLS Initial-Exec to Local-Exec transition.
@@ -1027,7 +1027,7 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
                                const elfcpp::Rel<32, false>& rel,
                                unsigned int r_type,
                                const Sized_symbol<32>* gsym,
-                               elfcpp::Elf_types<32>::Elf_Addr value,
+                               const Symbol_value<32>* psymval,
                                unsigned char* view,
                                elfcpp::Elf_types<32>::Elf_Addr address,
                                off_t view_size)
@@ -1050,14 +1050,19 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
     }
 
   // Pick the value to use for symbols defined in shared objects.
+  Symbol_value<32> symval;
   if (gsym != NULL && gsym->is_from_dynobj())
     {
-      if (gsym->has_plt_offset())
-       value = target->plt_section()->address() + gsym->plt_offset();
-      else
+      if (!gsym->has_plt_offset())
        gold_unreachable();
+
+      symval.set_output_value(target->plt_section()->address()
+                             + gsym->plt_offset());
+      psymval = &symval;
     }
 
+  const Sized_relobj<32, false>* object = relinfo->object;
+
   switch (r_type)
     {
     case elfcpp::R_386_NONE:
@@ -1066,51 +1071,57 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
       break;
 
     case elfcpp::R_386_32:
-      Relocate_functions<32, false>::rel32(view, value);
+      Relocate_functions<32, false>::rel32(view, object, psymval);
       break;
 
     case elfcpp::R_386_PC32:
-      Relocate_functions<32, false>::pcrel32(view, value, address);
+      Relocate_functions<32, false>::pcrel32(view, object, psymval, address);
       break;
 
     case elfcpp::R_386_16:
-      Relocate_functions<32, false>::rel16(view, value);
+      Relocate_functions<32, false>::rel16(view, object, psymval);
       break;
 
     case elfcpp::R_386_PC16:
-      Relocate_functions<32, false>::pcrel16(view, value, address);
+      Relocate_functions<32, false>::pcrel16(view, object, psymval, address);
       break;
 
     case elfcpp::R_386_8:
-      Relocate_functions<32, false>::rel8(view, value);
+      Relocate_functions<32, false>::rel8(view, object, psymval);
       break;
 
     case elfcpp::R_386_PC8:
-      Relocate_functions<32, false>::pcrel8(view, value, address);
+      Relocate_functions<32, false>::pcrel8(view, object, psymval, address);
       break;
 
     case elfcpp::R_386_PLT32:
       gold_assert(gsym->has_plt_offset()
                  || gsym->final_value_is_known(relinfo->options));
-      Relocate_functions<32, false>::pcrel32(view, value, address);
+      Relocate_functions<32, false>::pcrel32(view, object, psymval, address);
       break;
 
     case elfcpp::R_386_GOT32:
       // Local GOT offsets not yet supported.
       gold_assert(gsym);
       gold_assert(gsym->has_got_offset());
-      value = gsym->got_offset();
-      Relocate_functions<32, false>::rel32(view, value);
+      Relocate_functions<32, false>::rel32(view, gsym->got_offset());
       break;
 
     case elfcpp::R_386_GOTOFF:
-      value -= target->got_section(NULL, NULL, NULL)->address();
-      Relocate_functions<32, false>::rel32(view, value);
+      {
+       elfcpp::Elf_types<32>::Elf_Addr value;
+       value = (psymval->value(object, 0)
+                - target->got_section(NULL, NULL, NULL)->address());
+       Relocate_functions<32, false>::rel32(view, value);
+      }
       break;
 
     case elfcpp::R_386_GOTPC:
-      value = target->got_section(NULL, NULL, NULL)->address();
-      Relocate_functions<32, false>::pcrel32(view, value, address);
+      {
+       elfcpp::Elf_types<32>::Elf_Addr value;
+       value = target->got_section(NULL, NULL, NULL)->address();
+       Relocate_functions<32, false>::pcrel32(view, value, address);
+      }
       break;
 
     case elfcpp::R_386_COPY:
@@ -1139,7 +1150,7 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo,
     case elfcpp::R_386_TLS_LE_32:
     case elfcpp::R_386_TLS_GOTDESC:
     case elfcpp::R_386_TLS_DESC_CALL:
-      this->relocate_tls(relinfo, relnum, rel, r_type, gsym, value, view,
+      this->relocate_tls(relinfo, relnum, rel, r_type, gsym, psymval, view,
                         address, view_size);
       break;
 
@@ -1173,7 +1184,7 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
                                    const elfcpp::Rel<32, false>& rel,
                                    unsigned int r_type,
                                    const Sized_symbol<32>* gsym,
-                                   elfcpp::Elf_types<32>::Elf_Addr value,
+                                   const Symbol_value<32>* psymval,
                                    unsigned char* view,
                                    elfcpp::Elf_types<32>::Elf_Addr,
                                    off_t view_size)
@@ -1187,6 +1198,8 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo,
       gold_exit(false);
     }
 
+  elfcpp::Elf_types<32>::Elf_Addr value = psymval->value(relinfo->object, 0);
+
   const bool is_final = (gsym == NULL
                         ? !relinfo->options->is_shared()
                         : gsym->final_value_is_known(relinfo->options));
index c150707..62ba5f3 100644 (file)
@@ -133,7 +133,9 @@ Layout::get_output_section(const char* name, Stringpool::Key name_key,
   // We should ignore some flags.
   flags &= ~ (elfcpp::SHF_INFO_LINK
              | elfcpp::SHF_LINK_ORDER
-             | elfcpp::SHF_GROUP);
+             | elfcpp::SHF_GROUP
+             | elfcpp::SHF_MERGE
+             | elfcpp::SHF_STRINGS);
 
   const Key key(name_key, std::make_pair(type, flags));
   const std::pair<Key, Output_section*> v(key, NULL);
@@ -224,7 +226,7 @@ Output_section*
 Layout::make_output_section(const char* name, elfcpp::Elf_Word type,
                            elfcpp::Elf_Xword flags)
 {
-  Output_section* os = new Output_section(name, type, flags, true);
+  Output_section* os = new Output_section(name, type, flags);
   this->section_list_.push_back(os);
 
   if ((flags & elfcpp::SHF_ALLOC) == 0)
@@ -466,7 +468,6 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab)
   off_t off = this->set_segment_offsets(target, load_seg, &shndx);
 
   // Create the symbol table sections.
-  // FIXME: We don't need to do this if we are stripping symbols.
   this->create_symtab_sections(size, input_objects, symtab, &off);
 
   // Create the .shstrtab section.
@@ -690,7 +691,9 @@ Layout::set_section_offsets(off_t off, unsigned int* pshndx)
   return off;
 }
 
-// Create the symbol table sections.
+// Create the symbol table sections.  Here we also set the final
+// values of the symbols.  At this point all the loadable sections are
+// fully laid out.
 
 void
 Layout::create_symtab_sections(int size, const Input_objects* input_objects,
diff --git a/gold/merge.cc b/gold/merge.cc
new file mode 100644 (file)
index 0000000..7af4faa
--- /dev/null
@@ -0,0 +1,333 @@
+// merge.cc -- handle section merging for gold
+
+#include "gold.h"
+
+#include <cstdlib>
+
+#include "merge.h"
+
+namespace gold
+{
+
+// Sort the entries in a merge mapping.  The key is an input object, a
+// section index in that object, and an offset in that section.
+
+bool
+Output_merge_base::Merge_key_less::operator()(const Merge_key& mk1,
+                                             const Merge_key& mk2) const
+{
+  // The order of different objects and different sections doesn't
+  // matter.  We want to get consistent results across links so we
+  // don't use pointer comparison.
+  if (mk1.object != mk2.object)
+    return mk1.object->name() < mk2.object->name();
+  if (mk1.shndx != mk2.shndx)
+    return mk1.shndx < mk2.shndx;
+  return mk1.offset < mk2.offset;
+}
+
+// Add a mapping from an OFFSET in input section SHNDX in object
+// OBJECT to an OUTPUT_OFFSET in a merged output section.  This
+// manages the mapping used to resolve relocations against merged
+// sections.
+
+void
+Output_merge_base::add_mapping(Relobj* object, unsigned int shndx,
+                              off_t offset, off_t output_offset)
+{
+  Merge_key mk;
+  mk.object = object;
+  mk.shndx = shndx;
+  mk.offset = offset;
+  std::pair<Merge_map::iterator, bool> ins =
+    this->merge_map_.insert(std::make_pair(mk, output_offset));
+  gold_assert(ins.second);
+}
+
+// Return the output address for an input address.  The input address
+// is at offset OFFSET in section SHNDX in OBJECT.
+// OUTPUT_SECTION_ADDRESS is the address of the output section.  If we
+// know the address, set *POUTPUT and return true.  Otherwise return
+// false.
+
+bool
+Output_merge_base::do_output_address(const Relobj* object, unsigned int shndx,
+                                    off_t offset,
+                                    uint64_t output_section_address,
+                                    uint64_t* poutput) const
+{
+  gold_assert(output_section_address == this->address());
+
+  Merge_key mk;
+  mk.object = object;
+  mk.shndx = shndx;
+  mk.offset = offset;
+  Merge_map::const_iterator p = this->merge_map_.lower_bound(mk);
+
+  // If MK is not in the map, lower_bound returns the next iterator
+  // larger than it.
+  if (p->first.object != object
+      || p->first.shndx != shndx
+      || p->first.offset != offset)
+    {
+      if (p == this->merge_map_.begin())
+       return false;
+      --p;
+    }
+
+  if (p->first.object != object || p->first.shndx != shndx)
+    return false;
+
+  // Any input section is fully mapped: we don't need to know the size
+  // of the range starting at P->FIRST.OFFSET.
+  *poutput = output_section_address + p->second + (offset - p->first.offset);
+  return true;
+}
+
+// Compute the hash code for a fixed-size constant.
+
+size_t
+Output_merge_data::Merge_data_hash::operator()(Merge_data_key k) const
+{
+  const unsigned char* p = this->pomd_->constant(k);
+  uint64_t entsize = this->pomd_->entsize();
+
+  // Fowler/Noll/Vo (FNV) hash (type FNV-1a).
+  if (sizeof(size_t) == 8)
+    {
+      size_t result = static_cast<size_t>(14695981039346656037ULL);
+      for (uint64_t i = 0; i < entsize; ++i)
+       {
+         result &= (size_t) *p++;
+         result *= 1099511628211ULL;
+       }
+      return result;
+    }
+  else
+    {
+      size_t result = 2166136261UL;
+      for (uint64_t i = 0; i < entsize; ++i)
+       {
+         result ^= (size_t) *p++;
+         result *= 16777619UL;
+       }
+      return result;
+    }
+}
+
+// Return whether one hash table key equals another.
+
+bool
+Output_merge_data::Merge_data_eq::operator()(Merge_data_key k1,
+                                            Merge_data_key k2) const
+{
+  const unsigned char* p1 = this->pomd_->constant(k1);
+  const unsigned char* p2 = this->pomd_->constant(k2);
+  return memcmp(p1, p2, this->pomd_->entsize()) == 0;
+}
+
+// Add a constant to the end of the section contents.
+
+void
+Output_merge_data::add_constant(const unsigned char* p)
+{
+  uint64_t entsize = this->entsize();
+  if (this->len_ + entsize > this->alc_)
+    {
+      if (this->alc_ == 0)
+       this->alc_ = 128 * entsize;
+      else
+       this->alc_ *= 2;
+      this->p_ = static_cast<unsigned char*>(realloc(this->p_, this->alc_));
+      if (this->p_ == NULL)
+       gold_fatal("out of memory", true);
+    }
+
+  memcpy(this->p_ + this->len_, p, entsize);
+  this->len_ += entsize;
+}
+
+// Add the input section SHNDX in OBJECT to a merged output section
+// which holds fixed length constants.  Return whether we were able to
+// handle the section; if not, it will be linked as usual without
+// constant merging.
+
+bool
+Output_merge_data::do_add_input_section(Relobj* object, unsigned int shndx)
+{
+  off_t len;
+  const unsigned char* p = object->section_contents(shndx, &len);
+
+  uint64_t entsize = this->entsize();
+
+  if (len % entsize != 0)
+    return false;
+
+  for (off_t i = 0; i < len; i += entsize, p += entsize)
+    {
+      // Add the constant to the section contents.  If we find that it
+      // is already in the hash table, we will remove it again.
+      Merge_data_key k = this->len_;
+      this->add_constant(p);
+
+      std::pair<Merge_data_hashtable::iterator, bool> ins =
+       this->hashtable_.insert(k);
+
+      if (!ins.second)
+       {
+         // Key was already present.  Remove the copy we just added.
+         this->len_ -= entsize;
+         k = *ins.first;
+       }
+
+      // Record the offset of this constant in the output section.
+      this->add_mapping(object, shndx, i, k);
+    }
+
+  return true;
+}
+
+// Set the final data size in a merged output section with fixed size
+// constants.
+
+void
+Output_merge_data::do_set_address(uint64_t, off_t)
+{
+  // Release the memory we don't need.
+  this->p_ = static_cast<unsigned char*>(realloc(this->p_, this->len_));
+  gold_assert(this->p_ != NULL);
+  this->set_data_size(this->len_);
+}
+
+// Write the data of a merged output section with fixed size constants
+// to the file.
+
+void
+Output_merge_data::do_write(Output_file* of)
+{
+  of->write(this->offset(), this->p_, this->len_);
+}
+
+// Compute a hash code for a Merge_string_key, which is an object, a
+// section index, and an offset.
+
+template<typename Char_type>
+size_t
+Output_merge_string<Char_type>::Merge_string_key_hash::operator()(
+    const Merge_string_key& key) const
+{
+  // This is a very simple minded hash code.  Fix it if it we get too
+  // many collisions.
+  const std::string& oname(key.object->name());
+  return oname[0] + oname.length() + key.shndx + key.offset;
+}
+
+// Compare two Merge_string_keys for equality.
+
+template<typename Char_type>
+bool
+Output_merge_string<Char_type>::Merge_string_key_eq::operator()(
+    const Merge_string_key& k1, const Merge_string_key& k2) const
+{
+  return (k1.object == k2.object
+         && k1.shndx == k2.shndx
+         && k1.offset == k2.offset);
+}
+
+// Add an input section to a merged string section.
+
+template<typename Char_type>
+bool
+Output_merge_string<Char_type>::do_add_input_section(Relobj* object,
+                                                    unsigned int shndx)
+{
+  off_t len;
+  const unsigned char* pdata = object->section_contents(shndx, &len);
+
+  const Char_type* p = reinterpret_cast<const Char_type*>(pdata);
+
+  if (len % sizeof(Char_type) != 0)
+    {
+      fprintf(stderr,
+             _("%s: %s: mergeable string section length not multiple of "
+               "character size\n"),
+             program_name, object->name().c_str());
+      gold_exit(false);
+    }
+  len /= sizeof(Char_type);
+
+  off_t i = 0;
+  while (i < len)
+    {
+      off_t plen = 0;
+      for (const Char_type* pl = p; *pl != 0; ++pl)
+       {
+         ++plen;
+         if (i + plen >= len)
+           {
+             fprintf(stderr,
+                     _("%s: %s: entry in mergeable string section "
+                       "not null terminated\n"),
+                     program_name, object->name().c_str());
+             gold_exit(false);
+           }
+       }
+
+      const Char_type* str = this->stringpool_.add(p, NULL);
+
+      Merge_string_key k(object, shndx, i);
+      typename Merge_string_hashtable::value_type v(k, str);
+      bool b = this->hashtable_.insert(v).second;
+      gold_assert(b);
+
+      p += plen + 1;
+      i += plen + 1;
+    }
+
+  return true;
+}
+
+// Set the final data size of a merged string section.  This is where
+// we finalize the mappings from the input sections to the output
+// section.
+
+template<typename Char_type>
+void
+Output_merge_string<Char_type>::do_set_address(uint64_t, off_t)
+{
+  this->stringpool_.set_string_offsets();
+
+  for (typename Merge_string_hashtable::const_iterator p =
+        this->hashtable_.begin();
+       p != this->hashtable_.end();
+       ++p)
+    this->add_mapping(p->first.object, p->first.shndx, p->first.offset,
+                     this->stringpool_.get_offset(p->second));
+
+  this->set_data_size(this->stringpool_.get_strtab_size());
+
+  // Save some memory.
+  this->hashtable_.clear();
+}
+
+// Write out a merged string section.
+
+template<typename Char_type>
+void
+Output_merge_string<Char_type>::do_write(Output_file* of)
+{
+  this->stringpool_.write(of, this->offset());
+}
+
+// Instantiate the templates we need.
+
+template
+class Output_merge_string<char>;
+
+template
+class Output_merge_string<uint16_t>;
+
+template
+class Output_merge_string<uint32_t>;
+
+} // End namespace gold.
diff --git a/gold/merge.h b/gold/merge.h
new file mode 100644 (file)
index 0000000..dd97bf2
--- /dev/null
@@ -0,0 +1,226 @@
+// merge.h -- handle section merging for gold  -*- C++ -*-
+
+#ifndef GOLD_MERGE_H
+#define GOLD_MERGE_H
+
+#include <climits>
+
+#include "stringpool.h"
+#include "output.h"
+
+namespace gold
+{
+
+// A general class for SHF_MERGE data, to hold functions shared by
+// fixed-size constant data and string data.
+
+class Output_merge_base : public Output_section_data
+{
+ public:
+  Output_merge_base(uint64_t entsize)
+    : Output_section_data(1), merge_map_(), entsize_(entsize)
+  { }
+
+  // Return the output address for an input address.
+  bool
+  do_output_address(const Relobj* object, unsigned int shndx, off_t offset,
+                   uint64_t output_section_address, uint64_t* poutput) const;
+
+ protected:
+  // Return the entry size.
+  uint64_t
+  entsize() const
+  { return this->entsize_; }
+
+  // Add a mapping from an OFFSET in input section SHNDX in object
+  // OBJECT to an OUTPUT_OFFSET in the output section.
+  void
+  add_mapping(Relobj* object, unsigned int shndx, off_t offset,
+             off_t output_offset);
+
+ private:
+  // We build a mapping from OBJECT/SHNDX/OFFSET to an offset in the
+  // output section.
+  struct Merge_key
+  {
+    const Relobj* object;
+    unsigned int shndx;
+    off_t offset;
+  };
+
+  struct Merge_key_less
+  {
+    bool
+    operator()(const Merge_key&, const Merge_key&) const;
+  };
+
+  typedef std::map<Merge_key, off_t, Merge_key_less> Merge_map;
+
+  // A mapping from input object/section/offset to offset in output
+  // section.
+  Merge_map merge_map_;
+
+  // The entry size.  For fixed-size constants, this is the size of
+  // the constants.  For strings, this is the size of a character.
+  uint64_t entsize_;
+};
+
+// Handle SHF_MERGE sections with fixed-size constant data.
+
+class Output_merge_data : public Output_merge_base
+{
+ public:
+  Output_merge_data(uint64_t entsize)
+    : Output_merge_base(entsize), p_(NULL), len_(0), alc_(0),
+      hashtable_(128, Merge_data_hash(this), Merge_data_eq(this))
+  { }
+
+  // Add an input section.
+  bool
+  do_add_input_section(Relobj* object, unsigned int shndx);
+
+  // Set the final data size.
+  void
+  do_set_address(uint64_t, off_t);
+
+  // Write the data to the file.
+  void
+  do_write(Output_file*);
+
+ private:
+  // We build a hash table of the fixed-size constants.  Each constant
+  // is stored as a pointer into the section data we are accumulating.
+
+  // A key in the hash table.  This is an offset in the section
+  // contents we are building.
+  typedef off_t Merge_data_key;
+
+  // Compute the hash code.  To do this we need a pointer back to the
+  // object holding the data.
+  class Merge_data_hash
+  {
+   public:
+    Merge_data_hash(const Output_merge_data* pomd)
+      : pomd_(pomd)
+    { }
+
+    size_t
+    operator()(Merge_data_key) const;
+
+   private:
+    const Output_merge_data* pomd_;
+  };
+
+  friend class Merge_data_hash;
+
+  // Compare two entries in the hash table for equality.  To do this
+  // we need a pointer back to the object holding the data.  Note that
+  // we now have a pointer to the object stored in two places in the
+  // hash table.  Fixing this would require specializing the hash
+  // table, which would be hard to do portably.
+  class Merge_data_eq
+  {
+   public:
+    Merge_data_eq(const Output_merge_data* pomd)
+      : pomd_(pomd)
+    { }
+
+    bool
+    operator()(Merge_data_key k1, Merge_data_key k2) const;
+
+   private:
+    const Output_merge_data* pomd_;
+  };
+
+  friend class Merge_data_eq;
+
+  // The type of the hash table.
+  typedef Unordered_set<Merge_data_key, Merge_data_hash, Merge_data_eq>
+    Merge_data_hashtable;
+
+  // Given a hash table key, which is just an offset into the section
+  // data, return a pointer to the corresponding constant.
+  const unsigned char*
+  constant(Merge_data_key k) const
+  {
+    gold_assert(k >= 0 && k < this->len_);
+    return this->p_ + k;
+  }
+
+  // Add a constant to the output.
+  void
+  add_constant(const unsigned char*);
+
+  // The accumulated data.
+  unsigned char* p_;
+  // The length of the accumulated data.
+  off_t len_;
+  // The size of the allocated buffer.
+  size_t alc_;
+  // The hash table.
+  Merge_data_hashtable hashtable_;
+};
+
+// Handle SHF_MERGE sections with string data.  This is a template
+// based on the type of the characters in the string.
+
+template<typename Char_type>
+class Output_merge_string : public Output_merge_base
+{
+ public:
+  Output_merge_string()
+    : Output_merge_base(sizeof(Char_type)), stringpool_(false), hashtable_()
+  { }
+
+  // Add an input section.
+  bool
+  do_add_input_section(Relobj* object, unsigned int shndx);
+
+  // Set the final data size.
+  void
+  do_set_address(uint64_t, off_t);
+
+  // Write the data to the file.
+  void
+  do_write(Output_file*);
+
+ private:
+  // As we see input sections, we build a mapping from object, section
+  // index and offset to strings.
+  struct Merge_string_key
+  {
+    Relobj* object;
+    unsigned int shndx;
+    off_t offset;
+
+    Merge_string_key(Relobj *objecta, unsigned int shndxa, off_t offseta)
+      : object(objecta), shndx(shndxa), offset(offseta)
+    { }
+  };
+
+  struct Merge_string_key_hash
+  {
+    size_t
+    operator()(const Merge_string_key&) const;
+  };
+
+  struct Merge_string_key_eq
+  {
+    bool
+    operator()(const Merge_string_key&, const Merge_string_key&) const;
+  };
+
+  typedef Unordered_map<Merge_string_key, const Char_type*,
+                       Merge_string_key_hash, Merge_string_key_eq>
+    Merge_string_hashtable;
+
+  // As we see the strings, we add them to a Stringpool.
+  Stringpool_template<Char_type> stringpool_;
+  // Map from a location in an input object to an entry in the
+  // Stringpool.
+  Merge_string_hashtable hashtable_;
+};
+
+} // End namespace gold.
+
+#endif // !defined(GOLD_MERGE_H)
index 01c4c16..eb975c6 100644 (file)
@@ -127,8 +127,7 @@ Sized_relobj<size, big_endian>::Sized_relobj(
     output_local_symbol_count_(0),
     symbols_(NULL),
     local_symbol_offset_(0),
-    local_values_(),
-    local_indexes_()
+    local_values_()
 {
 }
 
@@ -505,10 +504,9 @@ Sized_relobj<size, big_endian>::do_add_symbols(Symbol_table* symtab,
 
 // Finalize the local symbols.  Here we record the file offset at
 // which they should be output, we add their names to *POOL, and we
-// add their values to THIS->LOCAL_VALUES_ and their indexes in the
-// output symbol table to THIS->LOCAL_INDEXES_.  Return the symbol
-// index.  This function is always called from the main thread.  The
-// actual output of the local symbols will occur in a separate task.
+// add their values to THIS->LOCAL_VALUES_.  Return the symbol index.
+// This function is always called from the main thread.  The actual
+// output of the local symbols will occur in a separate task.
 
 template<int size, bool big_endian>
 unsigned int
@@ -542,7 +540,6 @@ Sized_relobj<size, big_endian>::do_finalize_local_symbols(unsigned int index,
                                              locsize);
 
   this->local_values_.resize(loccount);
-  this->local_indexes_.resize(loccount);
 
   // Read the symbol names.
   const unsigned int strtab_shndx = symtabshdr.get_sh_link();
@@ -562,12 +559,15 @@ Sized_relobj<size, big_endian>::do_finalize_local_symbols(unsigned int index,
     {
       elfcpp::Sym<size, big_endian> sym(psyms);
 
+      Symbol_value<size>& lv(this->local_values_[i]);
+
       unsigned int shndx = sym.get_st_shndx();
+      lv.set_input_shndx(shndx);
 
       if (shndx >= elfcpp::SHN_LORESERVE)
        {
          if (shndx == elfcpp::SHN_ABS)
-           this->local_values_[i] = sym.get_st_value();
+           lv.set_output_value(sym.get_st_value());
          else
            {
              // FIXME: Handle SHN_XINDEX.
@@ -589,23 +589,28 @@ Sized_relobj<size, big_endian>::do_finalize_local_symbols(unsigned int index,
              gold_exit(false);
            }
 
-         if (mo[shndx].output_section == NULL)
+         Output_section* os = mo[shndx].output_section;
+
+         if (os == NULL)
            {
-             this->local_values_[i] = 0;
-             this->local_indexes_[i] = -1U;
+             lv.set_output_value(0);
+             lv.set_no_output_symtab_entry();
              continue;
            }
 
-         this->local_values_[i] = (mo[shndx].output_section->address()
-                                   + mo[shndx].offset
-                                   + sym.get_st_value());
+         if (mo[shndx].offset == -1)
+           lv.set_input_value(sym.get_st_value());
+         else
+           lv.set_output_value(mo[shndx].output_section->address()
+                               + mo[shndx].offset
+                               + sym.get_st_value());
        }
 
       // Decide whether this symbol should go into the output file.
 
       if (sym.get_st_type() == elfcpp::STT_SECTION)
        {
-         this->local_indexes_[i] = -1U;
+         lv.set_no_output_symtab_entry();
          continue;
        }
 
@@ -622,9 +627,8 @@ Sized_relobj<size, big_endian>::do_finalize_local_symbols(unsigned int index,
 
       const char* name = pnames + sym.get_st_name();
       pool->add(name, NULL);
-      this->local_indexes_[i] = index;
+      lv.set_output_symtab_index(index);
       ++index;
-      off += sym_size;
       ++count;
     }
 
@@ -633,6 +637,23 @@ Sized_relobj<size, big_endian>::do_finalize_local_symbols(unsigned int index,
   return index;
 }
 
+// Return the value of a local symbol defined in input section SHNDX,
+// with value VALUE, adding addend ADDEND.  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,
+                                           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);
+  return os->output_address(this, shndx, value + addend);
+}
+
 // Write out the local symbols.
 
 template<int size, bool big_endian>
@@ -676,7 +697,6 @@ Sized_relobj<size, big_endian>::write_local_symbols(Output_file* of,
   const std::vector<Map_to_output>& mo(this->map_to_output());
 
   gold_assert(this->local_values_.size() == loccount);
-  gold_assert(this->local_indexes_.size() == loccount);
 
   unsigned char* ov = oview;
   psyms += sym_size;
@@ -684,9 +704,8 @@ Sized_relobj<size, big_endian>::write_local_symbols(Output_file* of,
     {
       elfcpp::Sym<size, big_endian> isym(psyms);
 
-      if (this->local_indexes_[i] == -1U)
+      if (!this->local_values_[i].needs_output_symtab_entry())
        continue;
-      gold_assert(this->local_indexes_[i] != 0);
 
       unsigned int st_shndx = isym.get_st_shndx();
       if (st_shndx < elfcpp::SHN_LORESERVE)
@@ -702,7 +721,7 @@ Sized_relobj<size, big_endian>::write_local_symbols(Output_file* of,
       gold_assert(isym.get_st_name() < strtab_size);
       const char* name = pnames + isym.get_st_name();
       osym.put_st_name(sympool->get_offset(name));
-      osym.put_st_value(this->local_values_[i]);
+      osym.put_st_value(this->local_values_[i].value(this, 0));
       osym.put_st_size(isym.get_st_size());
       osym.put_st_info(isym.get_st_info());
       osym.put_st_other(isym.get_st_other());
index 2df04ab..2027f8e 100644 (file)
@@ -15,12 +15,14 @@ namespace gold
 {
 
 class General_options;
-class Stringpool;
 class Layout;
 class Output_section;
 class Output_file;
 class Dynobj;
 
+template<typename Stringpool_char>
+class Stringpool_template;
+
 // Data to pass from read_symbols() to add_symbols().
 
 struct Read_symbols_data
@@ -338,7 +340,7 @@ Object::sized_target(ACCEPT_SIZE_ENDIAN_ONLY)
 }
 
 // A regular object (ET_REL).  This is an abstract base class itself.
-// The implementations is the template class Sized_relobj.
+// The implementation is the template class Sized_relobj.
 
 class Relobj : public Object
 {
@@ -362,7 +364,8 @@ class Relobj : public Object
   // symbol information will be stored; add local symbol names to
   // *POOL; return the new local symbol index.
   unsigned int
-  finalize_local_symbols(unsigned int index, off_t off, Stringpool* pool)
+  finalize_local_symbols(unsigned int index, off_t off,
+                        Stringpool_template<char>* pool)
   { return this->do_finalize_local_symbols(index, off, pool); }
 
   // Relocate the input sections and write out the local symbols.
@@ -383,7 +386,7 @@ class Relobj : public Object
   // (which will be NULL if the section is not included in the link)
   // and set *POFF to the offset within that section.
   inline Output_section*
-  output_section(unsigned int shndx, off_t* poff);
+  output_section(unsigned int shndx, off_t* poff) const;
 
   // Set the offset of an input section within its output section.
   void
@@ -402,7 +405,8 @@ class Relobj : public Object
     // The output section.  This is NULL if the input section is to be
     // discarded.
     Output_section* output_section;
-    // The offset within the output section.
+    // The offset within the output section.  This is -1 if the
+    // section requires special handling.
     off_t offset;
   };
 
@@ -417,7 +421,8 @@ class Relobj : public Object
 
   // Finalize local symbols--implemented by child class.
   virtual unsigned int
-  do_finalize_local_symbols(unsigned int, off_t, Stringpool*) = 0;
+  do_finalize_local_symbols(unsigned int, off_t,
+                           Stringpool_template<char>*) = 0;
 
   // Relocate the input sections and write out the local
   // symbols--implemented by child class.
@@ -430,6 +435,10 @@ class Relobj : public Object
   map_to_output()
   { return this->map_to_output_; }
 
+  const std::vector<Map_to_output>&
+  map_to_output() const
+  { return this->map_to_output_; }
+
  private:
   // Mapping from input sections to output section.
   std::vector<Map_to_output> map_to_output_;
@@ -437,7 +446,7 @@ class Relobj : public Object
 
 // Implement Object::output_section inline for efficiency.
 inline Output_section*
-Relobj::output_section(unsigned int shndx, off_t* poff)
+Relobj::output_section(unsigned int shndx, off_t* poff) const
 {
   gold_assert(shndx < this->map_to_output_.size());
   const Map_to_output& mo(this->map_to_output_[shndx]);
@@ -445,6 +454,108 @@ Relobj::output_section(unsigned int shndx, off_t* poff)
   return mo.output_section;
 }
 
+// This POD class is holds the value of a symbol.  This is used for
+// local symbols, and for all symbols during relocation processing.
+// In order to process relocs we need to be able to handle SHF_MERGE
+// sections correctly.
+
+template<int size>
+class Symbol_value
+{
+ public:
+  typedef typename elfcpp::Elf_types<size>::Elf_Addr Value;
+
+  Symbol_value()
+    : output_symtab_index_(0), input_shndx_(0), needs_output_address_(false),
+      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.
+  template<bool big_endian>
+  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_, addend);
+  }
+
+  // Set the value of this symbol in the output symbol table.
+  void
+  set_output_value(Value value)
+  {
+    this->value_ = value;
+    this->needs_output_address_ = false;
+  }
+
+  // If this symbol is mapped to an output section which requires
+  // special handling to determine the output value, we store the
+  // value of the symbol in the input file.  This is used for
+  // SHF_MERGE sections.
+  void
+  set_input_value(Value value)
+  {
+    this->value_ = value;
+    this->needs_output_address_ = true;
+  }
+
+  // Return whether this symbol should go into the output symbol
+  // table.
+  bool
+  needs_output_symtab_entry() const
+  {
+    gold_assert(this->output_symtab_index_ != 0);
+    return this->output_symtab_index_ != -1U;
+  }
+
+  // Return the index in the output symbol table.
+  unsigned int
+  output_symtab_index() const
+  {
+    gold_assert(this->output_symtab_index_ != 0);
+    return this->output_symtab_index_;
+  }
+
+  // Set the index in the output symbol table.
+  void
+  set_output_symtab_index(unsigned int i)
+  {
+    gold_assert(this->output_symtab_index_ == 0);
+    this->output_symtab_index_ = i;
+  }
+
+  // Record that this symbol should not go into the output symbol
+  // table.
+  void
+  set_no_output_symtab_entry()
+  {
+    gold_assert(this->output_symtab_index_ == 0);
+    this->output_symtab_index_ = -1U;
+  }
+
+  // Set the index of the input section in the input file.
+  void
+  set_input_shndx(unsigned int i)
+  { this->input_shndx_ = i; }
+
+ private:
+  // The index of this local symbol in the output symbol table.  This
+  // will be -1 if the symbol should not go into the symbol table.
+  unsigned int output_symtab_index_;
+  // The section index in the input file in which this symbol is
+  // defined.
+  unsigned int input_shndx_ : 31;
+  // Whether getting the value of this symbol requires calling an
+  // Output_section method.  For example, this will be true of a
+  // STT_SECTION 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_;
+};
+
 // A regular object file.  This is size and endian specific.
 
 template<int size, bool big_endian>
@@ -452,7 +563,7 @@ class Sized_relobj : public Relobj
 {
  public:
   typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
-  typedef std::vector<Address> Local_values;
+  typedef std::vector<Symbol_value<size> > Local_values;
 
   Sized_relobj(const std::string& name, Input_file* input_file, off_t offset,
               const typename elfcpp::Ehdr<size, big_endian>&);
@@ -468,9 +579,8 @@ class Sized_relobj : public Relobj
   unsigned int
   symtab_index(unsigned int sym) const
   {
-    gold_assert(sym < this->local_indexes_.size());
-    gold_assert(this->local_indexes_[sym] != 0);
-    return this->local_indexes_[sym];
+    gold_assert(sym < this->local_values_.size());
+    return this->local_values_[sym].output_symtab_index();
   }
 
   // Read the symbols.
@@ -497,7 +607,8 @@ class Sized_relobj : public Relobj
 
   // Finalize the local symbols.
   unsigned int
-  do_finalize_local_symbols(unsigned int, off_t, Stringpool*);
+  do_finalize_local_symbols(unsigned int, off_t,
+                           Stringpool_template<char>*);
 
   // Relocate the input sections and write out the local symbols.
   void
@@ -528,6 +639,12 @@ class Sized_relobj : public Relobj
           SELECT_SIZE_ENDIAN_ONLY(size, big_endian));
   }
 
+  // Return the value of a local symbol define in input section SHNDX,
+  // with value VALUE, adding addend ADDEND.  This handles SHF_MERGE
+  // sections.
+  Address
+  local_value(unsigned int shndx, Address value, Address addend) const;
+
  private:
   // For convenience.
   typedef Sized_relobj<size, big_endian> This;
@@ -574,7 +691,8 @@ class Sized_relobj : public Relobj
 
   // Write out the local symbols.
   void
-  write_local_symbols(Output_file*, const Stringpool*);
+  write_local_symbols(Output_file*,
+                     const Stringpool_template<char>*);
 
   // General access to the ELF file.
   elfcpp::Elf_file<size, big_endian, Object> elf_file_;
@@ -590,8 +708,6 @@ class Sized_relobj : public Relobj
   off_t local_symbol_offset_;
   // Values of local symbols.
   Local_values local_values_;
-  // Indexes of local symbols in the output file; -1U if not present.
-  std::vector<unsigned int> local_indexes_;
 };
 
 // A class to manage the list of all objects.
index f0d7985..2a7400d 100644 (file)
@@ -12,6 +12,7 @@
 #include "object.h"
 #include "symtab.h"
 #include "reloc.h"
+#include "merge.h"
 #include "output.h"
 
 namespace gold
@@ -790,9 +791,9 @@ off_t
 Output_section::Input_section::data_size() const
 {
   if (this->is_input_section())
-    return this->data_size_;
+    return this->u1_.data_size;
   else
-    return this->u_.posd->data_size();
+    return this->u2_.posd->data_size();
 }
 
 // Set the address and file offset.
@@ -802,9 +803,33 @@ Output_section::Input_section::set_address(uint64_t addr, off_t off,
                                           off_t secoff)
 {
   if (this->is_input_section())
-    this->u_.object->set_section_offset(this->shndx_, off - secoff);
+    this->u2_.object->set_section_offset(this->shndx_, off - secoff);
   else
-    this->u_.posd->set_address(addr, off);
+    this->u2_.posd->set_address(addr, off);
+}
+
+// Try to turn an input address into an output address.
+
+bool
+Output_section::Input_section::output_address(const Relobj* object,
+                                             unsigned int shndx,
+                                             off_t offset,
+                                             uint64_t output_section_address,
+                                             uint64_t *poutput) const
+{
+  if (!this->is_input_section())
+    return this->u2_.posd->output_address(object, shndx, offset,
+                                         output_section_address, poutput);
+  else
+    {
+      if (this->u2_.object != object)
+       return false;
+      off_t output_offset;
+      Output_section* os = object->output_section(shndx, &output_offset);
+      gold_assert(os != NULL);
+      *poutput = output_section_address + output_offset + offset;
+      return true;
+    }
 }
 
 // Write out the data.  We don't have to do anything for an input
@@ -815,7 +840,7 @@ void
 Output_section::Input_section::write(Output_file* of)
 {
   if (!this->is_input_section())
-    this->u_.posd->write(of);
+    this->u2_.posd->write(of);
 }
 
 // Output_section methods.
@@ -823,7 +848,7 @@ Output_section::Input_section::write(Output_file* of)
 // Construct an Output_section.  NAME will point into a Stringpool.
 
 Output_section::Output_section(const char* name, elfcpp::Elf_Word type,
-                              elfcpp::Elf_Xword flags, bool may_add_data)
+                              elfcpp::Elf_Xword flags)
   : name_(name),
     addralign_(0),
     entsize_(0),
@@ -838,7 +863,6 @@ Output_section::Output_section(const char* name, elfcpp::Elf_Word type,
     dynsym_index_(0),
     input_sections_(),
     first_input_offset_(0),
-    may_add_data_(may_add_data),
     needs_symtab_index_(false),
     needs_dynsym_index_(false),
     should_link_to_symtab_(false),
@@ -873,8 +897,6 @@ Output_section::add_input_section(Relobj* object, unsigned int shndx,
                                  const char* secname,
                                  const elfcpp::Shdr<size, big_endian>& shdr)
 {
-  gold_assert(this->may_add_data_);
-
   elfcpp::Elf_Xword addralign = shdr.get_sh_addralign();
   if ((addralign & (addralign - 1)) != 0)
     {
@@ -887,6 +909,20 @@ Output_section::add_input_section(Relobj* object, unsigned int shndx,
   if (addralign > this->addralign_)
     this->addralign_ = addralign;
 
+  // If this is a SHF_MERGE section, we pass all the input sections to
+  // a Output_data_merge.
+  if ((shdr.get_sh_flags() & elfcpp::SHF_MERGE) != 0)
+    {
+      if (this->add_merge_input_section(object, shndx, shdr.get_sh_flags(),
+                                       shdr.get_sh_entsize(),
+                                       addralign))
+       {
+         // Tell the relocation routines that they need to call the
+         // output_address method to determine the final address.
+         return -1;
+       }
+    }
+
   off_t ssize = this->data_size();
   ssize = align_address(ssize, addralign);
   this->set_data_size(ssize + shdr.get_sh_size());
@@ -907,18 +943,107 @@ Output_section::add_input_section(Relobj* object, unsigned int shndx,
 void
 Output_section::add_output_section_data(Output_section_data* posd)
 {
-  gold_assert(this->may_add_data_);
+  Input_section inp(posd);
+  this->add_output_section_data(&inp);
+}
+
+// Add arbitrary data to an output section by Input_section.
 
+void
+Output_section::add_output_section_data(Input_section* inp)
+{
   if (this->input_sections_.empty())
     this->first_input_offset_ = this->data_size();
 
-  this->input_sections_.push_back(Input_section(posd));
+  this->input_sections_.push_back(*inp);
 
-  uint64_t addralign = posd->addralign();
+  uint64_t addralign = inp->addralign();
   if (addralign > this->addralign_)
     this->addralign_ = addralign;
 
-  posd->set_output_section(this);
+  inp->set_output_section(this);
+}
+
+// Add a merge section to an output section.
+
+void
+Output_section::add_output_merge_section(Output_section_data* posd,
+                                        bool is_string, uint64_t entsize)
+{
+  Input_section inp(posd, is_string, entsize);
+  this->add_output_section_data(&inp);
+}
+
+// Add an input section to a SHF_MERGE section.
+
+bool
+Output_section::add_merge_input_section(Relobj* object, unsigned int shndx,
+                                       uint64_t flags, uint64_t entsize,
+                                       uint64_t addralign)
+{
+  // We only merge constants if the alignment is not more than the
+  // entry size.  This could be handled, but it's unusual.
+  if (addralign > entsize)
+    return false;
+
+  bool is_string = (flags & elfcpp::SHF_STRINGS) != 0;
+  Input_section_list::iterator p;
+  for (p = this->input_sections_.begin();
+       p != this->input_sections_.end();
+       ++p)
+    if (p->is_merge_section(is_string, entsize))
+      break;
+
+  // We handle the actual constant merging in Output_merge_data or
+  // Output_merge_string_data.
+  if (p != this->input_sections_.end())
+    p->add_input_section(object, shndx);
+  else
+    {
+      Output_section_data* posd;
+      if (!is_string)
+       posd = new Output_merge_data(entsize);
+      else if (entsize == 1)
+       posd = new Output_merge_string<char>();
+      else if (entsize == 2)
+       posd = new Output_merge_string<uint16_t>();
+      else if (entsize == 4)
+       posd = new Output_merge_string<uint32_t>();
+      else
+       return false;
+
+      this->add_output_merge_section(posd, is_string, entsize);
+      posd->add_input_section(object, shndx);
+    }
+
+  return true;
+}
+
+// Return the output virtual address of OFFSET relative to the start
+// of input section SHNDX in object OBJECT.
+
+uint64_t
+Output_section::output_address(const Relobj* object, unsigned int shndx,
+                              off_t offset) const
+{
+  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());
+      uint64_t output;
+      if (p->output_address(object, shndx, offset, addr, &output))
+       return output;
+      addr += p->data_size();
+    }
+
+  // If we get here, it means that we don't know the mapping for this
+  // input section.  This might happen in principle if
+  // add_input_section were called before add_output_section_data.
+  // But it should never actually happen.
+
+  gold_unreachable();
 }
 
 // Set the address of an Output_section.  This is where we handle
@@ -1189,7 +1314,8 @@ Output_segment::set_section_addresses(uint64_t addr, off_t* poff,
   return ret;
 }
 
-// Set the addresses in a list of Output_data structures.
+// Set the addresses and file offsets in a list of Output_data
+// structures.
 
 uint64_t
 Output_segment::set_section_list_addresses(Output_data_list* pdl,
@@ -1541,3 +1667,4 @@ template
 class Output_data_got<64, true>;
 
 } // End namespace gold.
+
index 013a19f..7ed53ff 100644 (file)
@@ -314,6 +314,26 @@ class Output_section_data : public Output_data
   void
   set_output_section(Output_section* os);
 
+  // Add an input section, for SHF_MERGE sections.  This returns true
+  // if the section was handled.
+  bool
+  add_input_section(Relobj* object, unsigned int shndx)
+  { return this->do_add_input_section(object, shndx); }
+
+  // Given an input OBJECT, an input section index SHNDX within that
+  // object, and an OFFSET relative to the start of that input
+  // section, return whether or not the output address is known.
+  // OUTPUT_SECTION_ADDRESS is the address of the output section which
+  // this is a part of.  If this function returns true, it sets
+  // *POUTPUT to the output address.
+  virtual bool
+  output_address(const Relobj* object, unsigned int shndx, off_t offset,
+                uint64_t output_section_address, uint64_t *poutput) const
+  {
+    return this->do_output_address(object, shndx, offset,
+                                  output_section_address, poutput);
+  }
+
  protected:
   // The child class must implement do_write.
 
@@ -323,6 +343,18 @@ class Output_section_data : public Output_data
   do_adjust_output_section(Output_section*)
   { }
 
+  // May be implemented by child class.  Return true if the section
+  // was handled.
+  virtual bool
+  do_add_input_section(Relobj*, unsigned int)
+  { gold_unreachable(); }
+
+  // The child class may implement output_address.
+  virtual bool
+  do_output_address(const Relobj*, unsigned int, off_t, uint64_t,
+                   uint64_t*) const
+  { return false; }
+
   // Return the required alignment.
   uint64_t
   do_addralign() const
@@ -1114,8 +1146,7 @@ class Output_section : public Output_data
 {
  public:
   // Create an output section, giving the name, type, and flags.
-  Output_section(const char* name, elfcpp::Elf_Word, elfcpp::Elf_Xword,
-                bool may_add_data);
+  Output_section(const char* name, elfcpp::Elf_Word, elfcpp::Elf_Xword);
   virtual ~Output_section();
 
   // Add a new input section SHNDX, named NAME, with header SHDR, from
@@ -1125,7 +1156,7 @@ class Output_section : public Output_data
   add_input_section(Relobj* object, unsigned int shndx, const char *name,
                    const elfcpp::Shdr<size, big_endian>& shdr);
 
-  // Add generated data ODATA to this output section.
+  // Add generated data POSD to this output section.
   void
   add_output_section_data(Output_section_data* posd);
 
@@ -1284,6 +1315,12 @@ class Output_section : public Output_data
     this->dynsym_index_ = index;
   }
 
+  // Return the output virtual address of OFFSET relative to the start
+  // of input section SHNDX in object OBJECT.
+  uint64_t
+  output_address(const Relobj* object, unsigned int shndx,
+                off_t offset) const;
+
   // Set the address of the Output_section.  For a typical
   // Output_section, there is nothing to do, but if there are any
   // Output_section_data objects we need to set the final addresses
@@ -1339,24 +1376,44 @@ class Output_section : public Output_data
   {
    public:
     Input_section()
-      : shndx_(0), p2align_(0), data_size_(0)
-    { this->u_.object = NULL; }
+      : shndx_(0), p2align_(0)
+    {
+      this->u1_.data_size = 0;
+      this->u2_.object = NULL;
+    }
 
+    // For an ordinary input section.
     Input_section(Relobj* object, unsigned int shndx, off_t data_size,
                  uint64_t addralign)
       : shndx_(shndx),
-       p2align_(ffsll(static_cast<long long>(addralign))),
-       data_size_(data_size)
+       p2align_(ffsll(static_cast<long long>(addralign)))
     {
-      gold_assert(shndx != -1U);
-      this->u_.object = object;
+      gold_assert(shndx != OUTPUT_SECTION_CODE
+                 && shndx != MERGE_DATA_SECTION_CODE
+                 && shndx != MERGE_STRING_SECTION_CODE);
+      this->u1_.data_size = data_size;
+      this->u2_.object = object;
     }
 
+    // For a non-merge output section.
     Input_section(Output_section_data* posd)
-      : shndx_(-1U),
-       p2align_(ffsll(static_cast<long long>(posd->addralign()))),
-       data_size_(0)
-    { this->u_.posd = posd; }
+      : shndx_(OUTPUT_SECTION_CODE),
+       p2align_(ffsll(static_cast<long long>(posd->addralign())))
+    {
+      this->u1_.data_size = 0;
+      this->u2_.posd = posd;
+    }
+
+    // For a merge section.
+    Input_section(Output_section_data* posd, bool is_string, uint64_t entsize)
+      : shndx_(is_string
+              ? MERGE_STRING_SECTION_CODE
+              : MERGE_DATA_SECTION_CODE),
+       p2align_(ffsll(static_cast<long long>(posd->addralign())))
+    {
+      this->u1_.entsize = entsize;
+      this->u2_.posd = posd;
+    }
 
     // The required alignment.
     uint64_t
@@ -1371,41 +1428,125 @@ class Output_section : public Output_data
     off_t
     data_size() const;
 
+    // Return whether this is a merge section which matches the
+    // parameters.
+    bool
+    is_merge_section(bool is_string, uint64_t entsize) const
+    {
+      return (this->shndx_ == (is_string
+                              ? MERGE_STRING_SECTION_CODE
+                              : MERGE_DATA_SECTION_CODE)
+             && this->u1_.entsize == entsize);
+    }
+
+    // Set the output section.
+    void
+    set_output_section(Output_section* os)
+    {
+      gold_assert(!this->is_input_section());
+      this->u2_.posd->set_output_section(os);
+    }
+
     // Set the address and file offset.  This is called during
     // Layout::finalize.  SECOFF is the file offset of the enclosing
     // section.
     void
     set_address(uint64_t addr, off_t off, off_t secoff);
 
+    // Add an input section, for SHF_MERGE sections.
+    bool
+    add_input_section(Relobj* object, unsigned int shndx)
+    {
+      gold_assert(this->shndx_ == MERGE_DATA_SECTION_CODE
+                 || this->shndx_ == MERGE_STRING_SECTION_CODE);
+      return this->u2_.posd->add_input_section(object, shndx);
+    }
+
+    // Given an input OBJECT, an input section index SHNDX within that
+    // object, and an OFFSET relative to the start of that input
+    // section, return whether or not the output address is known.
+    // OUTPUT_SECTION_ADDRESS is the address of the output section
+    // which this is a part of.  If this function returns true, it
+    // sets *POUTPUT to the output address.
+    bool
+    output_address(const Relobj* object, unsigned int shndx, off_t offset,
+                  uint64_t output_section_address, uint64_t *poutput) const;
+
     // Write out the data.  This does nothing for an input section.
     void
     write(Output_file*);
 
    private:
+    // Code values which appear in shndx_.  If the value is not one of
+    // these codes, it is the input section index in the object file.
+    enum
+    {
+      // An Output_section_data.
+      OUTPUT_SECTION_CODE = -1U,
+      // An Output_section_data for an SHF_MERGE section with
+      // SHF_STRINGS not set.
+      MERGE_DATA_SECTION_CODE = -2U,
+      // An Output_section_data for an SHF_MERGE section with
+      // SHF_STRINGS set.
+      MERGE_STRING_SECTION_CODE = -3U
+    };
+
     // Whether this is an input section.
     bool
     is_input_section() const
-    { return this->shndx_ != -1U; }
+    {
+      return (this->shndx_ != OUTPUT_SECTION_CODE
+             && this->shndx_ != MERGE_DATA_SECTION_CODE
+             && this->shndx_ != MERGE_STRING_SECTION_CODE);
+    }
 
-    // For an ordinary input section, this is the section index in
-    // the input file.  For an Output_section_data, this is -1U.
+    // For an ordinary input section, this is the section index in the
+    // input file.  For an Output_section_data, this is
+    // OUTPUT_SECTION_CODE or MERGE_DATA_SECTION_CODE or
+    // MERGE_STRING_SECTION_CODE.
     unsigned int shndx_;
     // The required alignment, stored as a power of 2.
     unsigned int p2align_;
-    // For an ordinary input section, the section size.
-    off_t data_size_;
     union
     {
-      // If shndx_ != -1U, this points to the object which holds the
+      // For an ordinary input section, the section size.
+      off_t data_size;
+      // For OUTPUT_SECTION_CODE, this is not used.  For
+      // MERGE_DATA_SECTION_CODE or MERGE_STRING_SECTION_CODE, the
+      // entity size.
+      uint64_t entsize;
+    } u1_;
+    union
+    {
+      // For an ordinary input section, the object which holds the
       // input section.
       Relobj* object;
-      // If shndx_ == -1U, this is the data to write out.
+      // For OUTPUT_SECTION_CODE or MERGE_DATA_SECTION_CODE or
+      // MERGE_STRING_SECTION_CODE, the data.
       Output_section_data* posd;
-    } u_;
+    } u2_;
   };
 
   typedef std::vector<Input_section> Input_section_list;
 
+  // Add a new output section by Input_section.
+  void
+  add_output_section_data(Input_section*);
+
+  // Add an SHF_MERGE input section.  Returns true if the section was
+  // handled.
+  bool
+  add_merge_input_section(Relobj* object, unsigned int shndx, uint64_t flags,
+                         uint64_t entsize, uint64_t addralign);
+
+  // Add an output SHF_MERGE section POSD to this output section.
+  // IS_STRING indicates whether it is a SHF_STRINGS section, and
+  // ENTSIZE is the entity size.  This returns the entry added to
+  // input_sections_.
+  void
+  add_output_merge_section(Output_section_data* posd, bool is_string,
+                          uint64_t entsize);
+
   // Most of these fields are only valid after layout.
 
   // The name of the section.  This will point into a Stringpool.
@@ -1445,8 +1586,6 @@ class Output_section : public Output_data
   Input_section_list input_sections_;
   // The offset of the first entry in input_sections_.
   off_t first_input_offset_;
-  // Whether we permit adding data.
-  bool may_add_data_ : 1;
   // Whether this output section needs a STT_SECTION symbol in the
   // normal symbol table.  This will be true if there is a relocation
   // which needs it.
index 834e31a..0c4145a 100644 (file)
@@ -17,6 +17,8 @@ gold-threads.h
 i386.cc
 layout.cc
 layout.h
+merge.cc
+merge.h
 object.cc
 object.h
 options.cc
index f89d7ce..79d0628 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2006-12-05 17:51-0800\n"
+"POT-Creation-Date: 2007-05-16 10:40-0700\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -101,7 +101,7 @@ msgstr ""
 msgid "%s: %s: dynamic symbol table name section has wrong type: %u\n"
 msgstr ""
 
-#: dynobj.cc:368 object.cc:421
+#: dynobj.cc:368 object.cc:420
 #, c-format
 msgid "%s: %s: bad section name offset for section %u: %lu\n"
 msgstr ""
@@ -255,7 +255,7 @@ msgstr ""
 msgid "%s: missing expected TLS relocation\n"
 msgstr ""
 
-#: i386.cc:729 i386.cc:870 i386.cc:1125
+#: i386.cc:729 i386.cc:870 i386.cc:1136
 #, c-format
 msgid "%s: %s: unexpected reloc %u in object file\n"
 msgstr ""
@@ -280,31 +280,42 @@ msgstr ""
 msgid "%s: %s: missing expected TLS relocation\n"
 msgstr ""
 
-#: i386.cc:1157 i386.cc:1232 i386.cc:1243
+#: i386.cc:1168 i386.cc:1245 i386.cc:1256
 #, c-format
 msgid "%s: %s: unsupported reloc %u\n"
 msgstr ""
 
-#: i386.cc:1184
+#: i386.cc:1195
 #, c-format
 msgid "%s: %s: TLS reloc but no TLS segment\n"
 msgstr ""
 
-#: i386.cc:1217
+#: i386.cc:1230
 #, c-format
 msgid "%s: %s: unsupported reloc type %u\n"
 msgstr ""
 
-#: i386.cc:1426
+#: i386.cc:1439
 #, c-format
 msgid "%s: %s: TLS relocation out of range\n"
 msgstr ""
 
-#: i386.cc:1444
+#: i386.cc:1457
 #, c-format
 msgid "%s: %s: TLS relocation against invalid instruction\n"
 msgstr ""
 
+#: merge.cc:252
+#, c-format
+msgid ""
+"%s: %s: mergeable string section length not multiple of character size\n"
+msgstr ""
+
+#: merge.cc:269
+#, c-format
+msgid "%s: %s: entry in mergeable string section not null terminated\n"
+msgstr ""
+
 #: object.cc:30
 #, c-format
 msgid "%s: %s: unsupported ELF machine number %d\n"
@@ -315,32 +326,32 @@ msgstr ""
 msgid "%s: %s: section name section has wrong type: %u\n"
 msgstr ""
 
-#: object.cc:229
+#: object.cc:228
 #, c-format
 msgid "%s: %s: invalid symbol table name index: %u\n"
 msgstr ""
 
-#: object.cc:237
+#: object.cc:236
 #, c-format
 msgid "%s: %s: symbol table name section has wrong type: %u\n"
 msgstr ""
 
-#: object.cc:293
+#: object.cc:292
 #, c-format
 msgid "%s: %s: section group %u info %u out of range\n"
 msgstr ""
 
-#: object.cc:310
+#: object.cc:309
 #, c-format
 msgid "%s: %s: symbol %u name offset %u out of range\n"
 msgstr ""
 
-#: object.cc:344
+#: object.cc:343
 #, c-format
 msgid "%s: %s: section %u in section group %u out of range"
 msgstr ""
 
-#: object.cc:488
+#: object.cc:487
 #, c-format
 msgid "%s: %s: size of symbols is not multiple of symbol size\n"
 msgstr ""
@@ -355,47 +366,47 @@ msgstr ""
 msgid "%s: %s: local symbol %u section index %u out of range\n"
 msgstr ""
 
-#: object.cc:615
+#: object.cc:620
 #, c-format
 msgid "%s: %s: local symbol %u section name out of range: %u >= %u\n"
 msgstr ""
 
-#: object.cc:815
+#: object.cc:834
 #, c-format
 msgid "%s: %s: unsupported ELF file type %d\n"
 msgstr ""
 
-#: object.cc:834 object.cc:887 object.cc:908
+#: object.cc:853 object.cc:906 object.cc:927
 #, c-format
 msgid "%s: %s: ELF file too short\n"
 msgstr ""
 
-#: object.cc:843
+#: object.cc:862
 #, c-format
 msgid "%s: %s: invalid ELF version 0\n"
 msgstr ""
 
-#: object.cc:846
+#: object.cc:865
 #, c-format
 msgid "%s: %s: unsupported ELF version %d\n"
 msgstr ""
 
-#: object.cc:854
+#: object.cc:873
 #, c-format
 msgid "%s: %s: invalid ELF class 0\n"
 msgstr ""
 
-#: object.cc:861
+#: object.cc:880
 #, c-format
 msgid "%s: %s: unsupported ELF class %d\n"
 msgstr ""
 
-#: object.cc:869
+#: object.cc:888
 #, c-format
 msgid "%s: %s: invalid ELF data encoding\n"
 msgstr ""
 
-#: object.cc:876
+#: object.cc:895
 #, c-format
 msgid "%s: %s: unsupported ELF data encoding %d\n"
 msgstr ""
@@ -511,37 +522,37 @@ msgstr ""
 msgid "%s: -%c: %s\n"
 msgstr ""
 
-#: output.cc:881
+#: output.cc:903
 #, c-format
 msgid "%s: %s: invalid alignment %lu for section \"%s\"\n"
 msgstr ""
 
-#: output.cc:1393
+#: output.cc:1519
 #, c-format
 msgid "%s: %s: open: %s\n"
 msgstr ""
 
-#: output.cc:1402
+#: output.cc:1528
 #, c-format
 msgid "%s: %s: lseek: %s\n"
 msgstr ""
 
-#: output.cc:1409
+#: output.cc:1535
 #, c-format
 msgid "%s: %s: write: %s\n"
 msgstr ""
 
-#: output.cc:1419
+#: output.cc:1545
 #, c-format
 msgid "%s: %s: mmap: %s\n"
 msgstr ""
 
-#: output.cc:1433
+#: output.cc:1559
 #, c-format
 msgid "%s: %s: munmap: %s\n"
 msgstr ""
 
-#: output.cc:1441
+#: output.cc:1567
 #, c-format
 msgid "%s: %s: close: %s\n"
 msgstr ""
@@ -562,22 +573,22 @@ msgstr ""
 msgid "%s: %s: not an object or archive\n"
 msgstr ""
 
-#: reloc.cc:169 reloc.cc:410
+#: reloc.cc:169 reloc.cc:413
 #, c-format
 msgid "%s: %s: relocation section %u has bad info %u\n"
 msgstr ""
 
-#: reloc.cc:188 reloc.cc:427
+#: reloc.cc:188 reloc.cc:430
 #, c-format
 msgid "%s: %s: relocation section %u uses unexpected symbol table %u\n"
 msgstr ""
 
-#: reloc.cc:204 reloc.cc:446
+#: reloc.cc:204 reloc.cc:449
 #, c-format
 msgid "%s: %s: unexpected entsize for reloc section %u: %lu != %u"
 msgstr ""
 
-#: reloc.cc:215 reloc.cc:457
+#: reloc.cc:215 reloc.cc:460
 #, c-format
 msgid "%s: %s: reloc section %u size %lu uneven"
 msgstr ""
@@ -632,12 +643,12 @@ msgstr ""
 msgid "%s: %s: warning: %s\n"
 msgstr ""
 
-#: target-reloc.h:164
+#: target-reloc.h:170
 #, c-format
 msgid "%s: %s: reloc has bad offset %zu\n"
 msgstr ""
 
-#: target-reloc.h:174
+#: target-reloc.h:180
 #, c-format
 msgid "%s: %s: undefined reference to '%s'\n"
 msgstr ""
index 001fb01..76ab196 100644 (file)
@@ -343,6 +343,9 @@ Sized_relobj<size, big_endian>::write_sections(const unsigned char* pshdrs,
 
       pvs->view = NULL;
 
+      if (map_sections[i].offset == -1)
+       continue;
+
       const Output_section* os = map_sections[i].output_section;
       if (os == NULL)
        continue;
index 1edaa57..1aa0d89 100644 (file)
@@ -13,13 +13,18 @@ namespace gold
 class General_options;
 class Relobj;
 class Read_relocs_data;
-class Stringpool;
 class Symbol;
 class Layout;
 
 template<int size>
 class Sized_symbol;
 
+template<int size, bool big_endian>
+class Sized_relobj;
+
+template<int size>
+class Symbol_value;
+
 template<int sh_type, bool dynamic, int size, bool big_endian>
 class Output_data_reloc;
 
@@ -151,6 +156,22 @@ private:
     elfcpp::Swap<valsize, big_endian>::writeval(wv, x + value);
   }
 
+  // Do a simple relocation using a Symbol_value with the addend in
+  // the section contents.  VALSIZE is the size of the value to
+  // relocate.
+  template<int valsize>
+  static inline void
+  rel(unsigned char* view,
+      const Sized_relobj<size, big_endian>* object,
+      const Symbol_value<size>* psymval)
+  {
+    typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
+    Valtype* wv = reinterpret_cast<Valtype*>(view);
+    Valtype x = elfcpp::Swap<valsize, big_endian>::readval(wv);
+    x = psymval->value(object, x);
+    elfcpp::Swap<valsize, big_endian>::writeval(wv, x);
+  }
+
   // Do a simple PC relative relocation with the addend in the section
   // contents.  VALSIZE is the size of the value.
   template<int valsize>
@@ -165,76 +186,129 @@ private:
     elfcpp::Swap<valsize, big_endian>::writeval(wv, x + value - address);
   }
 
+  // Do a simple PC relative relocation with a Symbol_value with the
+  // addend in the section contents.  VALSIZE is the size of the
+  // value.
+  template<int valsize>
+  static inline void
+  pcrel(unsigned char* view,
+       const Sized_relobj<size, big_endian>* object,
+       const Symbol_value<size>* psymval,
+       typename elfcpp::Elf_types<size>::Elf_Addr address)
+  {
+    typedef typename elfcpp::Swap<valsize, big_endian>::Valtype Valtype;
+    Valtype* wv = reinterpret_cast<Valtype*>(view);
+    Valtype x = elfcpp::Swap<valsize, big_endian>::readval(wv);
+    x = psymval->value(object, x);
+    elfcpp::Swap<valsize, big_endian>::writeval(wv, x - address);
+  }
+
   typedef Relocate_functions<size, big_endian> This;
 
 public:
-  // Do a simple 8-bit REL relocation with the addend in the object
-  // file data.
+  // Do a simple 8-bit REL relocation with the addend in the section
+  // contents.
   static inline void
   rel8(unsigned char* view, unsigned char value)
-  {
-    This::template rel<8>(view, value);
-  }
+  { This::template rel<8>(view, value); }
+
+  static inline void
+  rel8(unsigned char* view,
+       const Sized_relobj<size, big_endian>* object,
+       const Symbol_value<size>* psymval)
+  { This::template rel<8>(view, object, psymval); }
 
   // Do a simple 8-bit PC relative relocation with the addend in the
-  // object file data.
+  // section contents.
   static inline void
   pcrel8(unsigned char* view, unsigned char value,
         typename elfcpp::Elf_types<size>::Elf_Addr address)
-  {
-    This::template pcrel<8>(view, value, address);
-  }
+  { This::template pcrel<8>(view, value, address); }
+
+  static inline void
+  pcrel8(unsigned char* view,
+        const Sized_relobj<size, big_endian>* object,
+        const Symbol_value<size>* psymval,
+        typename elfcpp::Elf_types<size>::Elf_Addr address)
+  { This::template pcrel<8>(view, object, psymval, address); }
 
-  // Do a simple 16-bit REL relocation with the addend in the object
-  // file data.
+  // Do a simple 16-bit REL relocation with the addend in the section
+  // contents.
   static inline void
   rel16(unsigned char* view, elfcpp::Elf_Half value)
-  {
-    This::template rel<16>(view, value);
-  }
+  { This::template rel<16>(view, value); }
+
+  static inline void
+  rel16(unsigned char* view,
+       const Sized_relobj<size, big_endian>* object,
+       const Symbol_value<size>* psymval)
+  { This::template rel<16>(view, object, psymval); }
 
   // Do a simple 32-bit PC relative REL relocation with the addend in
-  // the object file data.
+  // the section contents.
   static inline void
   pcrel16(unsigned char* view, elfcpp::Elf_Word value,
          typename elfcpp::Elf_types<size>::Elf_Addr address)
-  {
-    This::template pcrel<16>(view, value, address);
-  }
+  { This::template pcrel<16>(view, value, address); }
+
+  static inline void
+  pcrel16(unsigned char* view,
+         const Sized_relobj<size, big_endian>* object,
+         const Symbol_value<size>* psymval,
+         typename elfcpp::Elf_types<size>::Elf_Addr address)
+  { This::template pcrel<16>(view, object, psymval, address); }
 
   // Do a simple 32-bit REL relocation with the addend in the section
   // contents.
   static inline void
   rel32(unsigned char* view, elfcpp::Elf_Word value)
-  {
-    This::template rel<32>(view, value);
-  }
+  { This::template rel<32>(view, value); }
+
+  static inline void
+  rel32(unsigned char* view,
+       const Sized_relobj<size, big_endian>* object,
+       const Symbol_value<size>* psymval)
+  { This::template rel<32>(view, object, psymval); }
 
   // Do a simple 32-bit PC relative REL relocation with the addend in
   // the section contents.
   static inline void
   pcrel32(unsigned char* view, elfcpp::Elf_Word value,
          typename elfcpp::Elf_types<size>::Elf_Addr address)
-  {
-    This::template pcrel<32>(view, value, address);
-  }
+  { This::template pcrel<32>(view, value, address); }
+
+  static inline void
+  pcrel32(unsigned char* view,
+         const Sized_relobj<size, big_endian>* object,
+         const Symbol_value<size>* psymval,
+         typename elfcpp::Elf_types<size>::Elf_Addr address)
+  { This::template pcrel<32>(view, object, psymval, address); }
 
   // Do a simple 64-bit REL relocation with the addend in the section
   // contents.
   static inline void
   rel64(unsigned char* view, elfcpp::Elf_Xword value)
-  {
-    This::template rel<64>(view, value);
-  }
+  { This::template rel<64>(view, value); }
+
+  static inline void
+  rel64(unsigned char* view,
+       const Sized_relobj<size, big_endian>* object,
+       const Symbol_value<size>* psymval)
+  { This::template rel<64>(view, object, psymval); }
 
   // Do a simple 64-bit PC relative REL relocation with the addend in
   // the section contents.
   static inline void
   pcrel64(unsigned char* view, elfcpp::Elf_Xword value,
          typename elfcpp::Elf_types<size>::Elf_Addr address)
-  {
-    This::template pcrel<64>(view, value, address);
-  }
+  { This::template pcrel<64>(view, value, address); }
+
+  static inline void
+  pcrel64(unsigned char* view,
+         const Sized_relobj<size, big_endian>* object,
+         const Symbol_value<size>* psymval,
+         typename elfcpp::Elf_types<size>::Elf_Addr address)
+  { This::template pcrel<64>(view, object, psymval, address); }
 };
 
 // We try to avoid COPY relocations when possible.  A COPY relocation
index d53cf77..34c11b4 100644 (file)
 namespace gold
 {
 
-Stringpool::Stringpool()
-  : string_set_(), strings_(), strtab_size_(0), next_index_(1)
+template<typename Stringpool_char>
+Stringpool_template<Stringpool_char>::Stringpool_template(bool zero_null)
+  : string_set_(), strings_(), strtab_size_(0), next_index_(1),
+    zero_null_(zero_null)
 {
 }
 
-Stringpool::~Stringpool()
+template<typename Stringpool_char>
+Stringpool_template<Stringpool_char>::~Stringpool_template()
 {
-  for (std::list<Stringdata*>::iterator p = this->strings_.begin();
+  for (typename std::list<Stringdata*>::iterator p = this->strings_.begin();
        p != this->strings_.end();
        ++p)
     delete[] reinterpret_cast<char*>(*p);
 }
 
+// Return the length of a string of arbitrary character type.
+
+template<typename Stringpool_char>
+size_t
+Stringpool_template<Stringpool_char>::string_length(const Stringpool_char* p)
+{
+  size_t len = 0;
+  for (; *p != 0; ++p)
+    ++len;
+  return len;
+}
+
+// Specialize string_length for char.  Maybe we could just use
+// std::char_traits<>::length?
+
+template<>
+inline size_t
+Stringpool_template<char>::string_length(const char* p)
+{
+  return strlen(p);
+}
+
+// Equality comparison function.
+
+template<typename Stringpool_char>
+bool
+Stringpool_template<Stringpool_char>::Stringpool_eq::operator()(
+    const Stringpool_char* s1,
+    const Stringpool_char* s2) const
+{
+  while (*s1 != 0)
+    if (*s1++ != *s2++)
+      return false;
+  return *s2 == 0;
+}
+
+// Specialize equality comparison for char.
+
+template<>
+bool
+Stringpool_template<char>::Stringpool_eq::operator()(const char* s1,
+                                                    const char* s2) const
+{
+  return strcmp(s1, s2) == 0;
+}
+
 // Hash function.
 
+template<typename Stringpool_char>
 size_t
-Stringpool::Stringpool_hash::operator()(const char* s) const
+Stringpool_template<Stringpool_char>::Stringpool_hash::operator()(
+    const Stringpool_char* s) const
 {
   // Fowler/Noll/Vo (FNV) hash (type FNV-1a).
   if (sizeof(size_t) == 8)
     {
       size_t result = static_cast<size_t>(14695981039346656037ULL);
-      while (*s != '\0')
+      while (*s != 0)
        {
-         result &= (size_t) *s++;
-         result *= 1099511628211ULL;
+         const char* p = reinterpret_cast<const char*>(s);
+         for (size_t i = 0; i < sizeof(Stringpool_char); ++i)
+           {
+             result &= (size_t) *p++;
+             result *= 1099511628211ULL;
+           }
+         ++s;
        }
       return result;
     }
   else
     {
       size_t result = 2166136261UL;
-      while (*s != '\0')
+      while (*s != 0)
        {
-         result ^= (size_t) *s++;
-         result *= 16777619UL;
+         const char* p = reinterpret_cast<const char*>(s);
+         for (size_t i = 0; i < sizeof(Stringpool_char); ++i)
+           {
+             result ^= (size_t) *p++;
+             result *= 16777619UL;
+           }
+         ++s;
        }
       return result;
     }
@@ -56,8 +117,10 @@ Stringpool::Stringpool_hash::operator()(const char* s) const
 // Add a string to the list of canonical strings.  Return a pointer to
 // the canonical string.  If PKEY is not NULL, set *PKEY to the key.
 
-const char*
-Stringpool::add_string(const char* s, Key* pkey)
+template<typename Stringpool_char>
+const Stringpool_char*
+Stringpool_template<Stringpool_char>::add_string(const Stringpool_char* s,
+                                                Key* pkey)
 {
   // We are in trouble if we've already computed the string offsets.
   gold_assert(this->strtab_size_ == 0);
@@ -69,11 +132,11 @@ Stringpool::add_string(const char* s, Key* pkey)
   const size_t key_mult = 1024;
   gold_assert(key_mult >= buffer_size);
 
-  size_t len = strlen(s);
+  size_t len = (string_length(s) + 1) * sizeof(Stringpool_char);
 
   size_t alc;
   bool front = true;
-  if (len >= buffer_size)
+  if (len > buffer_size)
     {
       alc = sizeof(Stringdata) + len;
       front = false;
@@ -83,26 +146,26 @@ Stringpool::add_string(const char* s, Key* pkey)
   else
     {
       Stringdata *psd = this->strings_.front();
-      if (len >= psd->alc - psd->len)
+      if (len > psd->alc - psd->len)
        alc = sizeof(Stringdata) + buffer_size;
       else
        {
          char* ret = psd->data + psd->len;
-         memcpy(ret, s, len + 1);
+         memcpy(ret, s, len);
 
          if (pkey != NULL)
            *pkey = psd->index * key_mult + psd->len;
 
-         psd->len += len + 1;
+         psd->len += len;
 
-         return ret;
+         return reinterpret_cast<const Stringpool_char*>(ret);
        }
     }
 
   Stringdata *psd = reinterpret_cast<Stringdata*>(new char[alc]);
   psd->alc = alc - sizeof(Stringdata);
-  memcpy(psd->data, s, len + 1);
-  psd->len = len + 1;
+  memcpy(psd->data, s, len);
+  psd->len = len;
   psd->index = this->next_index_;
   ++this->next_index_;
 
@@ -114,13 +177,14 @@ Stringpool::add_string(const char* s, Key* pkey)
   else
     this->strings_.push_back(psd);
 
-  return psd->data;
+  return reinterpret_cast<const Stringpool_char*>(psd->data);
 }
 
 // Add a string to a string pool.
 
-const char*
-Stringpool::add(const char* s, Key* pkey)
+template<typename Stringpool_char>
+const Stringpool_char*
+Stringpool_template<Stringpool_char>::add(const Stringpool_char* s, Key* pkey)
 {
   // FIXME: This will look up the entry twice in the hash table.  The
   // problem is that we can't insert S before we canonicalize it.  I
@@ -128,7 +192,7 @@ Stringpool::add(const char* s, Key* pkey)
   // unordered_map, so this should be replaced with custom code to do
   // what we need, which is to return the empty slot.
 
-  String_set_type::const_iterator p = this->string_set_.find(s);
+  typename String_set_type::const_iterator p = this->string_set_.find(s);
   if (p != this->string_set_.end())
     {
       if (pkey != NULL)
@@ -137,11 +201,12 @@ Stringpool::add(const char* s, Key* pkey)
     }
 
   Key k;
-  const char* ret = this->add_string(s, &k);
+  const Stringpool_char* ret = this->add_string(s, &k);
 
   const off_t ozero = 0;
-  std::pair<const char*, Val> element(ret, std::make_pair(k, ozero));
-  std::pair<String_set_type::iterator, bool> ins =
+  std::pair<const Stringpool_char*, Val> element(ret,
+                                                std::make_pair(k, ozero));
+  std::pair<typename String_set_type::iterator, bool> ins =
     this->string_set_.insert(element);
   gold_assert(ins.second);
 
@@ -153,19 +218,23 @@ Stringpool::add(const char* s, Key* pkey)
 
 // Add a prefix of a string to a string pool.
 
-const char*
-Stringpool::add(const char* s, size_t len, Key* pkey)
+template<typename Stringpool_char>
+const Stringpool_char*
+Stringpool_template<Stringpool_char>::add(const Stringpool_char* s, size_t len,
+                                Key* pkey)
 {
   // FIXME: This implementation should be rewritten when we rewrite
   // the hash table to avoid copying.
-  std::string st(s, len);
+  std::basic_string<Stringpool_char> st(s, len);
   return this->add(st, pkey);
 }
 
-const char*
-Stringpool::find(const char* s, Key* pkey) const
+template<typename Stringpool_char>
+const Stringpool_char*
+Stringpool_template<Stringpool_char>::find(const Stringpool_char* s,
+                                          Key* pkey) const
 {
-  String_set_type::const_iterator p = this->string_set_.find(s);
+  typename String_set_type::const_iterator p = this->string_set_.find(s);
   if (p == this->string_set_.end())
     return NULL;
 
@@ -186,19 +255,20 @@ Stringpool::find(const char* s, Key* pkey) const
 // in, but we need to ensure that suffixes wind up next to each other.
 // So we do a reversed lexicographic sort on the reversed string.
 
+template<typename Stringpool_char>
 bool
-Stringpool::Stringpool_sort_comparison::operator()(
-  String_set_type::iterator it1,
-  String_set_type::iterator it2) const
+Stringpool_template<Stringpool_char>::Stringpool_sort_comparison::operator()(
+  typename String_set_type::iterator it1,
+  typename String_set_type::iterator it2) const
 {
-  const char* s1 = it1->first;
-  const char* s2 = it2->first;
-  int len1 = strlen(s1);
-  int len2 = strlen(s2);
-  int minlen = len1 < len2 ? len1 : len2;
-  const char* p1 = s1 + len1 - 1;
-  const char* p2 = s2 + len2 - 1;
-  for (int i = minlen - 1; i >= 0; --i, --p1, --p2)
+  const Stringpool_char* s1 = it1->first;
+  const Stringpool_char* s2 = it2->first;
+  size_t len1 = string_length(s1);
+  size_t len2 = string_length(s2);
+  size_t minlen = len1 < len2 ? len1 : len2;
+  const Stringpool_char* p1 = s1 + len1 - 1;
+  const Stringpool_char* p2 = s2 + len2 - 1;
+  for (size_t i = minlen; i > 0; --i, --p1, --p2)
     {
       if (*p1 != *p2)
        return *p1 > *p2;
@@ -208,21 +278,24 @@ Stringpool::Stringpool_sort_comparison::operator()(
 
 // Return whether s1 is a suffix of s2.
 
+template<typename Stringpool_char>
 bool
-Stringpool::is_suffix(const char* s1, const char* s2)
+Stringpool_template<Stringpool_char>::is_suffix(const Stringpool_char* s1,
+                                               const Stringpool_char* s2)
 {
-  size_t len1 = strlen(s1);
-  size_t len2 = strlen(s2);
+  size_t len1 = string_length(s1);
+  size_t len2 = string_length(s2);
   if (len1 > len2)
     return false;
-  return strcmp(s1, s2 + len2 - len1) == 0;
+  return memcmp(s1, s2 + len2 - len1, len1 * sizeof(Stringpool_char)) == 0;
 }
 
 // Turn the stringpool into an ELF strtab: determine the offsets of
 // each string in the table.
 
+template<typename Stringpool_char>
 void
-Stringpool::set_string_offsets()
+Stringpool_template<Stringpool_char>::set_string_offsets()
 {
   if (this->strtab_size_ != 0)
     {
@@ -232,30 +305,33 @@ Stringpool::set_string_offsets()
 
   size_t count = this->string_set_.size();
 
-  std::vector<String_set_type::iterator> v;
+  std::vector<typename String_set_type::iterator> v;
   v.reserve(count);
 
-  for (String_set_type::iterator p = this->string_set_.begin();
+  for (typename String_set_type::iterator p = this->string_set_.begin();
        p != this->string_set_.end();
        ++p)
     v.push_back(p);
 
   std::sort(v.begin(), v.end(), Stringpool_sort_comparison());
 
-  // Offset 0 is reserved for the empty string.
-  off_t offset = 1;
+  const size_t charsize = sizeof(Stringpool_char);
+
+  // Offset 0 may be reserved for the empty string.
+  off_t offset = this->zero_null_ ? charsize : 0;
   for (size_t i = 0; i < count; ++i)
     {
-      if (v[i]->first[0] == '\0')
+      if (this->zero_null_ && v[i]->first[0] == 0)
        v[i]->second.second = 0;
-      else if (i > 0 && Stringpool::is_suffix(v[i]->first, v[i - 1]->first))
+      else if (i > 0 && is_suffix(v[i]->first, v[i - 1]->first))
        v[i]->second.second = (v[i - 1]->second.second
-                              + strlen(v[i - 1]->first)
-                              - strlen(v[i]->first));
+                              + ((string_length(v[i - 1]->first)
+                                  - string_length(v[i]->first))
+                                 * charsize));
       else
        {
          v[i]->second.second = offset;
-         offset += strlen(v[i]->first) + 1;
+         offset += (string_length(v[i]->first) + 1) * charsize;
        }
     }
 
@@ -265,11 +341,13 @@ Stringpool::set_string_offsets()
 // Get the offset of a string in the ELF strtab.  The string must
 // exist.
 
+template<typename Stringpool_char>
 off_t
-Stringpool::get_offset(const char* s) const
+Stringpool_template<Stringpool_char>::get_offset(const Stringpool_char* s)
+  const
 {
   gold_assert(this->strtab_size_ != 0);
-  String_set_type::const_iterator p = this->string_set_.find(s);
+  typename String_set_type::const_iterator p = this->string_set_.find(s);
   if (p != this->string_set_.end())
     return p->second.second;
   gold_unreachable();
@@ -277,18 +355,32 @@ Stringpool::get_offset(const char* s) const
 
 // Write the ELF strtab into the output file at the specified offset.
 
+template<typename Stringpool_char>
 void
-Stringpool::write(Output_file* of, off_t offset)
+Stringpool_template<Stringpool_char>::write(Output_file* of, off_t offset)
 {
   gold_assert(this->strtab_size_ != 0);
   unsigned char* viewu = of->get_output_view(offset, this->strtab_size_);
   char* view = reinterpret_cast<char*>(viewu);
-  view[0] = '\0';
-  for (String_set_type::const_iterator p = this->string_set_.begin();
+  if (this->zero_null_)
+    view[0] = '\0';
+  for (typename String_set_type::const_iterator p = this->string_set_.begin();
        p != this->string_set_.end();
        ++p)
-    strcpy(view + p->second.second, p->first);
+    memcpy(view + p->second.second, p->first,
+          (string_length(p->first) + 1) * sizeof(Stringpool_char));
   of->write_output_view(offset, this->strtab_size_, viewu);
 }
 
+// Instantiate the templates we need.
+
+template
+class Stringpool_template<char>;
+
+template
+class Stringpool_template<uint16_t>;
+
+template
+class Stringpool_template<uint32_t>;
+
 } // End namespace gold.
index 05c498e..b9a65f6 100644 (file)
@@ -14,7 +14,8 @@ namespace gold
 
 class Output_file;
 
-class Stringpool
+template<typename Stringpool_char>
+class Stringpool_template
 {
  public:
   // The type of a key into the stringpool.  A key value will always
@@ -24,43 +25,47 @@ class Stringpool
   // into an unordered hash table.  Zero is never a valid key.
   typedef size_t Key;
 
-  Stringpool();
+  // Create a Stringpool.  ZERO_NULL is true if we should reserve
+  // offset 0 to hold the empty string.
+  Stringpool_template(bool zero_null = true);
 
-  ~Stringpool();
+  ~Stringpool_template();
 
   // Add a string to the pool.  This returns a canonical permanent
   // pointer to the string.  If PKEY is not NULL, this sets *PKEY to
   // the key for the string.
-  const char*
-  add(const char*, Key* pkey);
+  const Stringpool_char*
+  add(const Stringpool_char*, Key* pkey);
 
-  const char*
-  add(const std::string& s, Key* pkey)
+  const Stringpool_char*
+  add(const std::basic_string<Stringpool_char>& s, Key* pkey)
   { return this->add(s.c_str(), pkey); }
 
   // Add the prefix of a string to the pool.
-  const char*
-  add(const char *, size_t, Key* pkey);
+  const Stringpool_char*
+  add(const Stringpool_char*, size_t, Key* pkey);
 
   // If a string is present, return the canonical string.  Otherwise,
   // return NULL.  If PKEY is not NULL, set *PKEY to the key.
-  const char*
-  find(const char*, Key* pkey) const;
+  const Stringpool_char*
+  find(const Stringpool_char*, Key* pkey) const;
 
   // Turn the stringpool into an ELF strtab: determine the offsets of
   // all the strings.
   void
   set_string_offsets();
 
-  // Get the offset of a string in an ELF strtab.
+  // Get the offset of a string in an ELF strtab.  This returns the
+  // offset in bytes, not characters.
   off_t
-  get_offset(const char*) const;
+  get_offset(const Stringpool_char*) const;
 
   off_t
-  get_offset(const std::string& s) const
+  get_offset(const std::basic_string<Stringpool_char>& s) const
   { return this->get_offset(s.c_str()); }
 
-  // Get the size of the ELF strtab.
+  // Get the size of the ELF strtab.  This returns the number of
+  // bytes, not characters.
   off_t
   get_strtab_size() const
   {
@@ -73,8 +78,12 @@ class Stringpool
   write(Output_file*, off_t offset);
 
  private:
-  Stringpool(const Stringpool&);
-  Stringpool& operator=(const Stringpool&);
+  Stringpool_template(const Stringpool_template&);
+  Stringpool_template& operator=(const Stringpool_template&);
+
+  // Return the length of a string.
+  static size_t
+  string_length(const Stringpool_char*);
 
   // We store the actual data in a list of these buffers.
   struct Stringdata
@@ -90,24 +99,24 @@ class Stringpool
   };
 
   // Copy a string into the buffers, returning a canonical string.
-  const char*
-  add_string(const char*, Key*);
+  const Stringpool_char*
+  add_string(const Stringpool_char*, Key*);
 
   struct Stringpool_hash
   {
     size_t
-    operator()(const char*) const;
+    operator()(const Stringpool_char*) const;
   };
 
   struct Stringpool_eq
   {
     bool
-    operator()(const char* p1, const char* p2) const
-    { return strcmp(p1, p2) == 0; }
+    operator()(const Stringpool_char* p1, const Stringpool_char* p2) const;
   };
 
   // Return whether s1 is a suffix of s2.
-  static bool is_suffix(const char* s1, const char* s2);
+  static bool
+  is_suffix(const Stringpool_char* s1, const Stringpool_char* s2);
 
   // The hash table is a map from string names to a pair of Key and
   // ELF strtab offsets.  We only use the offsets if we turn this into
@@ -116,12 +125,13 @@ class Stringpool
   typedef std::pair<Key, off_t> Val;
 
 #ifdef HAVE_TR1_UNORDERED_SET
-  typedef Unordered_map<const char*, Val, Stringpool_hash,
+  typedef Unordered_map<const Stringpool_char*, Val, Stringpool_hash,
                        Stringpool_eq,
-                       std::allocator<std::pair<const char* const, Val> >,
+                       std::allocator<std::pair<const Stringpool_char* const,
+                                                Val> >,
                        true> String_set_type;
 #else
-  typedef Unordered_map<const char*, Val, Stringpool_hash,
+  typedef Unordered_map<const Stringpool_char*, Val, Stringpool_hash,
                        Stringpool_eq> String_set_type;
 #endif
 
@@ -130,8 +140,8 @@ class Stringpool
   struct Stringpool_sort_comparison
   {
     bool
-    operator()(String_set_type::iterator,
-              String_set_type::iterator) const;
+    operator()(typename String_set_type::iterator,
+              typename String_set_type::iterator) const;
   };
 
   // List of Stringdata structures.
@@ -145,8 +155,13 @@ class Stringpool
   off_t strtab_size_;
   // Next Stringdata index.
   unsigned int next_index_;
+  // Whether to reserve offset 0 to hold the null string.
+  bool zero_null_;
 };
 
+// The most common type of Stringpool.
+typedef Stringpool_template<char> Stringpool;
+
 } // End namespace gold.
 
 #endif // !defined(GOLD_STRINGPOOL_H)
index 727de02..d282805 100644 (file)
@@ -137,12 +137,13 @@ relocate_section(
       unsigned int r_type = elfcpp::elf_r_type<size>(r_info);
 
       const Sized_symbol<size>* sym;
-      typename elfcpp::Elf_types<size>::Elf_Addr value;
 
+      Symbol_value<size> symval;
+      const Symbol_value<size> *psymval;
       if (r_sym < local_count)
        {
          sym = NULL;
-         value = (*local_values)[r_sym];
+         psymval = &(*local_values)[r_sym];
        }
       else
        {
@@ -152,10 +153,15 @@ relocate_section(
            gsym = relinfo->symtab->resolve_forwards(gsym);
 
          sym = static_cast<const Sized_symbol<size>*>(gsym);
-         value = sym->value();
+         if (sym->has_symtab_index())
+           symval.set_output_symtab_index(sym->symtab_index());
+         else
+           symval.set_no_output_symtab_entry();
+         symval.set_output_value(sym->value());
+         psymval = &symval;
        }
 
-      if (!relocate.relocate(relinfo, target, i, reloc, r_type, sym, value,
+      if (!relocate.relocate(relinfo, target, i, reloc, r_type, sym, psymval,
                             view + offset, view_address + offset, view_size))
        continue;