summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@google.com>2007-05-16 17:42:48 +0000
committerIan Lance Taylor <iant@google.com>2007-05-16 17:42:48 +0000
commitb8e6aad9606384125f588d531c567f49bad346e2 (patch)
treee1172d50384713b91c329cc88cabcd2728f929a9
parent60dfee729999b913a247956c4816e758afb86a4b (diff)
downloadbinutils-gdb-b8e6aad9606384125f588d531c567f49bad346e2.tar.gz
Add support for SHF_MERGE sections.
-rw-r--r--gold/Makefile.am2
-rw-r--r--gold/Makefile.in12
-rw-r--r--gold/dynobj.h1
-rw-r--r--gold/i386.cc55
-rw-r--r--gold/layout.cc11
-rw-r--r--gold/merge.cc333
-rw-r--r--gold/merge.h226
-rw-r--r--gold/object.cc61
-rw-r--r--gold/object.h146
-rw-r--r--gold/output.cc155
-rw-r--r--gold/output.h185
-rw-r--r--gold/po/POTFILES.in2
-rw-r--r--gold/po/gold.pot83
-rw-r--r--gold/reloc.cc3
-rw-r--r--gold/reloc.h136
-rw-r--r--gold/stringpool.cc222
-rw-r--r--gold/stringpool.h71
-rw-r--r--gold/target-reloc.h14
18 files changed, 1451 insertions, 267 deletions
diff --git a/gold/Makefile.am b/gold/Makefile.am
index 02a6da11f63..a411127df94 100644
--- a/gold/Makefile.am
+++ b/gold/Makefile.am
@@ -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 \
diff --git a/gold/Makefile.in b/gold/Makefile.in
index 36994c3dad9..884b1a52c88 100644
--- a/gold/Makefile.in
+++ b/gold/Makefile.in
@@ -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@
diff --git a/gold/dynobj.h b/gold/dynobj.h
index 476b602f30b..d63aa6af0b2 100644
--- a/gold/dynobj.h
+++ b/gold/dynobj.h
@@ -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.
diff --git a/gold/i386.cc b/gold/i386.cc
index dbbd2c09b73..448453aa349 100644
--- a/gold/i386.cc
+++ b/gold/i386.cc
@@ -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));
diff --git a/gold/layout.cc b/gold/layout.cc
index c150707819b..62ba5f30dec 100644
--- a/gold/layout.cc
+++ b/gold/layout.cc
@@ -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
index 00000000000..7af4faa03c9
--- /dev/null
+++ b/gold/merge.cc
@@ -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
index 00000000000..dd97bf2d39e
--- /dev/null
+++ b/gold/merge.h
@@ -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)
diff --git a/gold/object.cc b/gold/object.cc
index 01c4c1625ce..eb975c6c734 100644
--- a/gold/object.cc
+++ b/gold/object.cc
@@ -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());
diff --git a/gold/object.h b/gold/object.h
index 2df04abad85..2027f8e97df 100644
--- a/gold/object.h
+++ b/gold/object.h
@@ -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.
diff --git a/gold/output.cc b/gold/output.cc
index f0d7985732d..2a7400def77 100644
--- a/gold/output.cc
+++ b/gold/output.cc
@@ -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.
+
diff --git a/gold/output.h b/gold/output.h
index 013a19f1b91..7ed53ff06d7 100644
--- a/gold/output.h
+++ b/gold/output.h
@@ -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.
diff --git a/gold/po/POTFILES.in b/gold/po/POTFILES.in
index 834e31aacb0..0c4145af71a 100644
--- a/gold/po/POTFILES.in
+++ b/gold/po/POTFILES.in
@@ -17,6 +17,8 @@ gold-threads.h
i386.cc
layout.cc
layout.h
+merge.cc
+merge.h
object.cc
object.h
options.cc
diff --git a/gold/po/gold.pot b/gold/po/gold.pot
index f89d7cef0ba..79d0628dc44 100644
--- a/gold/po/gold.pot
+++ b/gold/po/gold.pot
@@ -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 ""
diff --git a/gold/reloc.cc b/gold/reloc.cc
index 001fb01cc1e..76ab1961395 100644
--- a/gold/reloc.cc
+++ b/gold/reloc.cc
@@ -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;
diff --git a/gold/reloc.h b/gold/reloc.h
index 1edaa579cee..1aa0d896717 100644
--- a/gold/reloc.h
+++ b/gold/reloc.h
@@ -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
diff --git a/gold/stringpool.cc b/gold/stringpool.cc
index d53cf7792ff..34c11b4fc42 100644
--- a/gold/stringpool.cc
+++ b/gold/stringpool.cc
@@ -12,42 +12,103 @@
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.
diff --git a/gold/stringpool.h b/gold/stringpool.h
index 05c498e49fa..b9a65f6db98 100644
--- a/gold/stringpool.h
+++ b/gold/stringpool.h
@@ -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)
diff --git a/gold/target-reloc.h b/gold/target-reloc.h
index 727de02e4e8..d282805dbeb 100644
--- a/gold/target-reloc.h
+++ b/gold/target-reloc.h
@@ -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;