mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2025-02-17 13:10:12 +08:00
Speed up relocations against local symbols in merged sections.
This commit is contained in:
parent
df1764b8ab
commit
a9a60db689
@ -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
|
||||
|
@ -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*);
|
||||
|
@ -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())
|
||||
{
|
||||
|
200
gold/merge.cc
200
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.
|
||||
|
150
gold/merge.h
150
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
|
||||
|
@ -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>
|
||||
|
182
gold/object.h
182
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,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*,
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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*);
|
||||
|
101
gold/reloc.cc
101
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>;
|
||||
|
Loading…
Reference in New Issue
Block a user