Speed up relocations against local symbols in merged sections.

This commit is contained in:
Ian Lance Taylor 2007-12-21 21:19:45 +00:00
parent df1764b8ab
commit a9a60db689
10 changed files with 599 additions and 211 deletions

View File

@ -1041,6 +1041,15 @@ Eh_frame::do_output_offset(const Relobj* object, unsigned int shndx,
return this->merge_map_.get_output_offset(object, shndx, offset, poutput);
}
// Return whether this is the merge section for an input section.
bool
Eh_frame::do_is_merge_section_for(const Relobj* object,
unsigned int shndx) const
{
return this->merge_map_.is_merge_section_for(object, shndx);
}
// Write the data to the output file.
void

View File

@ -326,6 +326,10 @@ class Eh_frame : public Output_section_data
section_offset_type offset,
section_offset_type* poutput) const;
// Return whether this is the merge section for an input section.
bool
do_is_merge_section_for(const Relobj*, unsigned int shndx) const;
// Write the data to the file.
void
do_write(Output_file*);

View File

@ -732,6 +732,12 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab,
// they contain.
off_t off = this->set_segment_offsets(target, load_seg, &shndx);
// Set the file offsets of all the non-data sections we've seen so
// far which don't have to wait for the input sections. We need
// this in order to finalize local symbols in non-allocated
// sections.
off = this->set_section_offsets(off, BEFORE_INPUT_SECTIONS_PASS);
// Create the symbol table sections.
this->create_symtab_sections(input_objects, symtab, task, &off);
if (!parameters->doing_static_link())
@ -740,8 +746,8 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab,
// Create the .shstrtab section.
Output_section* shstrtab_section = this->create_shstrtab();
// Set the file offsets of all the non-data sections which don't
// have to wait for the input sections.
// Set the file offsets of the rest of the non-data sections which
// don't have to wait for the input sections.
off = this->set_section_offsets(off, BEFORE_INPUT_SECTIONS_PASS);
// Now that all sections have been created, set the section indexes.
@ -1113,6 +1119,10 @@ Layout::set_section_offsets(off_t off, Layout::Section_offset_pass pass)
if (*p == this->symtab_section_)
continue;
// If we've already set the data size, don't set it again.
if ((*p)->is_offset_valid() && (*p)->is_data_size_valid())
continue;
if (pass == BEFORE_INPUT_SECTIONS_PASS
&& (*p)->requires_postprocessing())
{

View File

@ -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.

View File

@ -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

View File

@ -889,14 +889,30 @@ Sized_relobj<size, big_endian>::do_finalize_local_symbols(unsigned int index,
}
else if (mo[shndx].offset == -1)
{
// Leave the input value in place for SHF_MERGE sections.
// This is a SHF_MERGE section or one which otherwise
// requires special handling. We get the output address
// of the start of the merged section. If this is not a
// section symbol, we can then determine the final
// value. If it is a section symbol, we can not, as in
// that case we have to consider the addend to determine
// the value to use in a relocation.
section_offset_type start =
os->starting_output_address(this, shndx);
if (!lv.is_section_symbol())
lv.set_output_value(lv.input_value() + start);
else
{
Merged_symbol_value<size>* msv =
new Merged_symbol_value<size>(lv.input_value(), start);
lv.set_merged_symbol_value(msv);
}
}
else if (lv.is_tls_symbol())
lv.set_output_value(mo[shndx].output_section->tls_offset()
lv.set_output_value(os->tls_offset()
+ mo[shndx].offset
+ lv.input_value());
else
lv.set_output_value(mo[shndx].output_section->address()
lv.set_output_value(os->address()
+ mo[shndx].offset
+ lv.input_value());
}
@ -953,40 +969,6 @@ Sized_relobj<size, big_endian>::local_symbol_value(unsigned int symndx) const
return lv.value(this, 0);
}
// Return the value of a local symbol defined in input section SHNDX,
// with value VALUE, adding addend ADDEND. IS_SECTION_SYMBOL
// indicates whether the symbol is a section symbol. This handles
// SHF_MERGE sections.
template<int size, bool big_endian>
typename elfcpp::Elf_types<size>::Elf_Addr
Sized_relobj<size, big_endian>::local_value(unsigned int shndx,
Address value,
bool is_section_symbol,
Address addend) const
{
const std::vector<Map_to_output>& mo(this->map_to_output());
Output_section* os = mo[shndx].output_section;
if (os == NULL)
return addend;
gold_assert(mo[shndx].offset == -1);
// Do the mapping required by the output section. If this is not a
// section symbol, then we want to map the symbol value, and then
// include the addend. If this is a section symbol, then we need to
// include the addend to figure out where in the section we are,
// before we do the mapping. This will do the right thing provided
// the assembler is careful to only convert a relocation in a merged
// section to a section symbol if there is a zero addend. If the
// assembler does not do this, then in general we can't know what to
// do, because we can't distinguish the addend for the instruction
// format from the addend for the section offset.
if (is_section_symbol)
return os->output_address(this, shndx, value + addend);
else
return addend + os->output_address(this, shndx, value);
}
// Write out the local symbols.
template<int size, bool big_endian>

View File

@ -622,6 +622,71 @@ Relobj::output_section(unsigned int shndx, section_offset_type* poff) const
return mo.output_section;
}
// This class is used to handle relocations against a section symbol
// in an SHF_MERGE section. For such a symbol, we need to know the
// addend of the relocation before we can determine the final value.
// The addend gives us the location in the input section, and we can
// determine how it is mapped to the output section. For a
// non-section symbol, we apply the addend to the final value of the
// symbol; that is done in finalize_local_symbols, and does not use
// this class.
template<int size>
class Merged_symbol_value
{
public:
typedef typename elfcpp::Elf_types<size>::Elf_Addr Value;
// We use a hash table to map offsets in the input section to output
// addresses.
typedef Unordered_map<section_offset_type, Value> Output_addresses;
Merged_symbol_value(Value input_value, Value output_start_address)
: input_value_(input_value), output_start_address_(output_start_address),
output_addresses_()
{ }
// Initialize the hash table.
void
initialize_input_to_output_map(const Relobj*, unsigned int input_shndx);
// Release the hash table to save space.
void
free_input_to_output_map()
{ this->output_addresses_.clear(); }
// Get the output value corresponding to an addend. The object and
// input section index are passed in because the caller will have
// them; otherwise we could store them here.
Value
value(const Relobj* object, unsigned int input_shndx, Value addend) const
{
Value input_offset = this->input_value_ + addend;
typename Output_addresses::const_iterator p =
this->output_addresses_.find(input_offset);
if (p != this->output_addresses_.end())
return p->second;
return this->value_from_output_section(object, input_shndx, input_offset);
}
private:
// Get the output value for an input offset if we couldn't find it
// in the hash table.
Value
value_from_output_section(const Relobj*, unsigned int input_shndx,
Value input_offset) const;
// The value of the section symbol in the input file. This is
// normally zero, but could in principle be something else.
Value input_value_;
// The start address of this merged section in the output file.
Value output_start_address_;
// A hash table which maps offsets in the input section to output
// addresses. This only maps specific offsets, not all offsets.
Output_addresses output_addresses_;
};
// This POD class is holds the value of a symbol. This is used for
// local symbols, and for all symbols during relocation processing.
// For special sections, such as SHF_MERGE sections, this calls a
@ -636,8 +701,8 @@ class Symbol_value
Symbol_value()
: output_symtab_index_(0), output_dynsym_index_(-1U), input_shndx_(0),
is_section_symbol_(false), is_tls_symbol_(false),
needs_output_address_(false), value_(0)
{ }
has_output_value_(true)
{ this->u_.value = 0; }
// Get the value of this symbol. OBJECT is the object in which this
// symbol is defined, and ADDEND is an addend to add to the value.
@ -645,40 +710,63 @@ 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
initialize_input_to_output_map(const Relobj* object)
{
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_);
}
}
// Free the input to output map for a section symbol in a merged
// section.
void
free_input_to_output_map()
{
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->value_ = value;
this->needs_output_address_ = true;
}
{ this->u_.value = value; }
// Return the input value.
// Return the input value. This is only called by
// finalize_local_symbols.
Value
input_value() const
{
gold_assert(this->needs_output_address_);
return this->value_;
}
{ return this->u_.value; }
// Return whether this symbol should go into the output symbol
// table.
@ -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*,

View File

@ -1369,6 +1369,18 @@ Output_section::Input_section::output_offset(
}
}
// Return whether this is the merge section for the input section
// SHNDX in OBJECT.
inline bool
Output_section::Input_section::is_merge_section_for(const Relobj* object,
unsigned int shndx) const
{
if (this->is_input_section())
return false;
return this->u2_.posd->is_merge_section_for(object, shndx);
}
// Write out the data. We don't have to do anything for an input
// section--they are handled via Object::relocate--but this is where
// we write out the data for an Output_section_data.
@ -1710,6 +1722,34 @@ Output_section::output_address(const Relobj* object, unsigned int shndx,
gold_unreachable();
}
// Return the output address of the start of the merged section for
// input section SHNDX in object OBJECT.
uint64_t
Output_section::starting_output_address(const Relobj* object,
unsigned int shndx) const
{
gold_assert(object->is_section_specially_mapped(shndx));
uint64_t addr = this->address() + this->first_input_offset_;
for (Input_section_list::const_iterator p = this->input_sections_.begin();
p != this->input_sections_.end();
++p)
{
addr = align_address(addr, p->addralign());
// It would be nice if we could use the existing output_offset
// method to get the output offset of input offset 0.
// Unfortunately we don't know for sure that input offset 0 is
// mapped at all.
if (p->is_merge_section_for(object, shndx))
return addr;
addr += p->data_size();
}
gold_unreachable();
}
// Set the data size of an Output_section. This is where we handle
// setting the addresses of any Output_section_data objects.

View File

@ -198,6 +198,21 @@ class Output_data
dynamic_reloc_count() const
{ return this->dynamic_reloc_count_; }
// Whether the address is valid.
bool
is_address_valid() const
{ return this->is_address_valid_; }
// Whether the file offset is valid.
bool
is_offset_valid() const
{ return this->is_offset_valid_; }
// Whether the data size is valid.
bool
is_data_size_valid() const
{ return this->is_data_size_valid_; }
protected:
// Functions that child classes may or in some cases must implement.
@ -256,21 +271,6 @@ class Output_data
// Functions that child classes may call.
// Whether the address is valid.
bool
is_address_valid() const
{ return this->is_address_valid_; }
// Whether the file offset is valid.
bool
is_offset_valid() const
{ return this->is_offset_valid_; }
// Whether the data size is valid.
bool
is_data_size_valid() const
{ return this->is_data_size_valid_; }
// Set the size of the data.
void
set_data_size(off_t data_size)
@ -466,6 +466,13 @@ class Output_section_data : public Output_data
section_offset_type *poutput) const
{ return this->do_output_offset(object, shndx, offset, poutput); }
// Return whether this is the merge section for the input section
// SHNDX in OBJECT. This should return true when output_offset
// would return true for some values of OFFSET.
bool
is_merge_section_for(const Relobj* object, unsigned int shndx) const
{ return this->do_is_merge_section_for(object, shndx); }
// Write the contents to a buffer. This is used for sections which
// require postprocessing, such as compression.
void
@ -499,6 +506,11 @@ class Output_section_data : public Output_data
section_offset_type*) const
{ return false; }
// The child class may implement is_merge_section_for.
virtual bool
do_is_merge_section_for(const Relobj*, unsigned int) const
{ return false; }
// The child class may implement write_to_buffer. Most child
// classes can not appear in a compressed section, and they do not
// implement this.
@ -1683,6 +1695,13 @@ class Output_section : public Output_data
output_address(const Relobj* object, unsigned int shndx,
off_t offset) const;
// Return the output address of the start of the merged section for
// input section SHNDX in object OBJECT. This is not necessarily
// the offset corresponding to input offset 0 in the section, since
// the section may be mapped arbitrarily.
uint64_t
starting_output_address(const Relobj* object, unsigned int shndx) const;
// Write the section header into *OPHDR.
template<int size, bool big_endian>
void
@ -1904,6 +1923,11 @@ class Output_section : public Output_data
section_offset_type offset,
section_offset_type *poutput) const;
// Return whether this is the merge section for the input section
// SHNDX in OBJECT.
bool
is_merge_section_for(const Relobj* object, unsigned int shndx) const;
// Write out the data. This does nothing for an input section.
void
write(Output_file*);

View File

@ -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>;