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