From a9a60db6891083f0afc4df6353a0c7ef80508874 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 21 Dec 2007 21:19:45 +0000 Subject: [PATCH] Speed up relocations against local symbols in merged sections. --- gold/ehframe.cc | 9 +++ gold/ehframe.h | 4 + gold/layout.cc | 14 +++- gold/merge.cc | 200 ++++++++++++++++++++---------------------------- gold/merge.h | 150 +++++++++++++++++++++++++++++++++++- gold/object.cc | 56 +++++--------- gold/object.h | 180 ++++++++++++++++++++++++++++++++++--------- gold/output.cc | 40 ++++++++++ gold/output.h | 54 +++++++++---- gold/reloc.cc | 101 +++++++++++++++++++++++- 10 files changed, 598 insertions(+), 210 deletions(-) diff --git a/gold/ehframe.cc b/gold/ehframe.cc index b77148b4398..b2406b4f7e1 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 ce4f6df5879..8e09657e948 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 231e2c9f414..3897ec7c24c 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 beb0906c0e4..75a3eeea924 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 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 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 +void +Object_merge_map::initialize_input_to_output_map( + unsigned int shndx, + typename elfcpp::Elf_types::Elf_Addr starting_address, + Unordered_map::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; template class Output_merge_string; +#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::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::Elf_Addr>*); +#endif + } // End namespace gold. diff --git a/gold/merge.h b/gold/merge.h index c232ab0787c..c385a44e667 100644 --- a/gold/merge.h +++ b/gold/merge.h @@ -24,6 +24,8 @@ #define GOLD_MERGE_H #include +#include +#include #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 + void + initialize_input_to_output_map( + unsigned int shndx, + typename elfcpp::Elf_types::Elf_Addr starting_address, + Unordered_map::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 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 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 5216049e3d1..e92a66d2e49 100644 --- a/gold/object.cc +++ b/gold/object.cc @@ -889,14 +889,30 @@ Sized_relobj::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* msv = + new Merged_symbol_value(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::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 -typename elfcpp::Elf_types::Elf_Addr -Sized_relobj::local_value(unsigned int shndx, - Address value, - bool is_section_symbol, - Address addend) const -{ - const std::vector& 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 diff --git a/gold/object.h b/gold/object.h index 1ad9796a425..f5e4ab62482 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 +class Merged_symbol_value +{ + public: + typedef typename elfcpp::Elf_types::Elf_Addr Value; + + // We use a hash table to map offsets in the input section to output + // addresses. + typedef Unordered_map 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* 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* 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* 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* 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 793d6a3a4a4..3d1d27e644a 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 895d70378cc..3d1a44d9d54 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 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 37a9a85591a..e9c13266549 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::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::relocate_sections( } } +// Create merge hash tables for the local symbols. These are used to +// speed up relocations. + +template +void +Sized_relobj::initialize_input_to_output_maps() +{ + const unsigned int loccount = this->local_symbol_count_; + for (unsigned int i = 1; i < loccount; ++i) + { + Symbol_value& lv(this->local_values_[i]); + lv.initialize_input_to_output_map(this); + } +} + +// Free merge hash tables for the local symbols. + +template +void +Sized_relobj::free_input_to_output_maps() +{ + const unsigned int loccount = this->local_symbol_count_; + for (unsigned int i = 1; i < loccount; ++i) + { + Symbol_value& lv(this->local_values_[i]); + lv.free_input_to_output_map(); + } +} + +// Class Merged_symbol_value. + +template +void +Merged_symbol_value::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(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 +typename elfcpp::Elf_types::Elf_Addr +Merged_symbol_value::value_from_output_section( + const Relobj* object, + unsigned int input_shndx, + typename elfcpp::Elf_types::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>; -- 2.30.2