diff options
-rw-r--r-- | gold/ChangeLog | 8 | ||||
-rw-r--r-- | gold/arm.cc | 259 |
2 files changed, 267 insertions, 0 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog index 60ef9d0..7799d6c 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,11 @@ +2009-10-24 Doug Kwan <dougkwan@google.com> + + * arm.cc (Arm_output_section, Arm_relobj): Forward class declarations. + (Arm_input_section::as_arm_input_section): New method. + (Arm_output_section): New class definition. + (Arm_output_section::create_stub_group, + Arm_output_section::group_sections): New method definitions. + 2009-10-22 Doug Kwan <dougkwan@google.com> * arm.cc (Arm_input_section): New class definition. diff --git a/gold/arm.cc b/gold/arm.cc index b8bce33..4de6aab 100644 --- a/gold/arm.cc +++ b/gold/arm.cc @@ -62,6 +62,12 @@ template<bool big_endian> class Arm_input_section; template<bool big_endian> +class Arm_output_section; + +template<bool big_endian> +class Arm_relobj; + +template<bool big_endian> class Target_arm; // For convenience. @@ -778,6 +784,12 @@ class Arm_input_section : public Output_relaxed_input_section set_stub_table(Stub_table<big_endian>* stub_table) { this->stub_table_ = stub_table; } + // Downcast a base pointer to an Arm_input_section pointer. This is + // not type-safe but we only use Arm_input_section not the base class. + static Arm_input_section<big_endian>* + as_arm_input_section(Output_relaxed_input_section* poris) + { return static_cast<Arm_input_section<big_endian>*>(poris); } + protected: // Write data to output file. void @@ -834,6 +846,44 @@ class Arm_input_section : public Output_relaxed_input_section Stub_table<big_endian>* stub_table_; }; +// Arm output section class. This is defined mainly to add a number of +// stub generation methods. + +template<bool big_endian> +class Arm_output_section : public Output_section +{ + public: + Arm_output_section(const char* name, elfcpp::Elf_Word type, + elfcpp::Elf_Xword flags) + : Output_section(name, type, flags) + { } + + ~Arm_output_section() + { } + + // Group input sections for stub generation. + void + group_sections(section_size_type, bool, Target_arm<big_endian>*); + + // Downcast a base pointer to an Arm_output_section pointer. This is + // not type-safe but we only use Arm_output_section not the base class. + static Arm_output_section<big_endian>* + as_arm_output_section(Output_section* os) + { return static_cast<Arm_output_section<big_endian>*>(os); } + + private: + // For convenience. + typedef Output_section::Input_section Input_section; + typedef Output_section::Input_section_list Input_section_list; + + // Create a stub group. + void create_stub_group(Input_section_list::const_iterator, + Input_section_list::const_iterator, + Input_section_list::const_iterator, + Target_arm<big_endian>*, + std::vector<Output_relaxed_input_section*>*); +}; + // Utilities for manipulating integers of up to 32-bits namespace utils @@ -2649,6 +2699,215 @@ Arm_input_section<big_endian>::do_reset_address_and_file_offset() this->set_current_data_size(off); } +// Arm_output_section methods. + +// Create a stub group for input sections from BEGIN to END. OWNER +// points to the input section to be the owner a new stub table. + +template<bool big_endian> +void +Arm_output_section<big_endian>::create_stub_group( + Input_section_list::const_iterator begin, + Input_section_list::const_iterator end, + Input_section_list::const_iterator owner, + Target_arm<big_endian>* target, + std::vector<Output_relaxed_input_section*>* new_relaxed_sections) +{ + // Currently we convert ordinary input sections into relaxed sections only + // at this point but we may want to support creating relaxed input section + // very early. So we check here to see if owner is already a relaxed + // section. + + Arm_input_section<big_endian>* arm_input_section; + if (owner->is_relaxed_input_section()) + { + arm_input_section = + Arm_input_section<big_endian>::as_arm_input_section( + owner->relaxed_input_section()); + } + else + { + gold_assert(owner->is_input_section()); + // Create a new relaxed input section. + arm_input_section = + target->new_arm_input_section(owner->relobj(), owner->shndx()); + new_relaxed_sections->push_back(arm_input_section); + } + + // Create a stub table. + Stub_table<big_endian>* stub_table = + target->new_stub_table(arm_input_section); + + arm_input_section->set_stub_table(stub_table); + + Input_section_list::const_iterator p = begin; + Input_section_list::const_iterator prev_p; + + // Look for input sections or relaxed input sections in [begin ... end]. + do + { + if (p->is_input_section() || p->is_relaxed_input_section()) + { + // The stub table information for input sections live + // in their objects. + Arm_relobj<big_endian>* arm_relobj = + Arm_relobj<big_endian>::as_arm_relobj(p->relobj()); + arm_relobj->set_stub_table(p->shndx(), stub_table); + } + prev_p = p++; + } + while (prev_p != end); +} + +// Group input sections for stub generation. GROUP_SIZE is roughly the limit +// of stub groups. We grow a stub group by adding input section until the +// size is just below GROUP_SIZE. The last input section will be converted +// into a stub table. If STUB_ALWAYS_AFTER_BRANCH is false, we also add +// input section after the stub table, effectively double the group size. +// +// This is similar to the group_sections() function in elf32-arm.c but is +// implemented differently. + +template<bool big_endian> +void +Arm_output_section<big_endian>::group_sections( + section_size_type group_size, + bool stubs_always_after_branch, + Target_arm<big_endian>* target) +{ + // We only care about sections containing code. + if ((this->flags() & elfcpp::SHF_EXECINSTR) == 0) + return; + + // States for grouping. + typedef enum + { + // No group is being built. + NO_GROUP, + // A group is being built but the stub table is not found yet. + // We keep group a stub group until the size is just under GROUP_SIZE. + // The last input section in the group will be used as the stub table. + FINDING_STUB_SECTION, + // A group is being built and we have already found a stub table. + // We enter this state to grow a stub group by adding input section + // after the stub table. This effectively doubles the group size. + HAS_STUB_SECTION + } State; + + // Any newly created relaxed sections are stored here. + std::vector<Output_relaxed_input_section*> new_relaxed_sections; + + State state = NO_GROUP; + section_size_type off = 0; + section_size_type group_begin_offset = 0; + section_size_type group_end_offset = 0; + section_size_type stub_table_end_offset = 0; + Input_section_list::const_iterator group_begin = + this->input_sections().end(); + Input_section_list::const_iterator stub_table = + this->input_sections().end(); + Input_section_list::const_iterator group_end = this->input_sections().end(); + for (Input_section_list::const_iterator p = this->input_sections().begin(); + p != this->input_sections().end(); + ++p) + { + section_size_type section_begin_offset = + align_address(off, p->addralign()); + section_size_type section_end_offset = + section_begin_offset + p->data_size(); + + // Check to see if we should group the previously seens sections. + switch(state) + { + case NO_GROUP: + break; + + case FINDING_STUB_SECTION: + // Adding this section makes the group larger than GROUP_SIZE. + if (section_end_offset - group_begin_offset >= group_size) + { + if (stubs_always_after_branch) + { + gold_assert(group_end != this->input_sections().end()); + this->create_stub_group(group_begin, group_end, group_end, + target, &new_relaxed_sections); + state = NO_GROUP; + } + else + { + // But wait, there's more! Input sections up to + // stub_group_size bytes after the stub table can be + // handled by it too. + state = HAS_STUB_SECTION; + stub_table = group_end; + stub_table_end_offset = group_end_offset; + } + } + break; + + case HAS_STUB_SECTION: + // Adding this section makes the post stub-section group larger + // than GROUP_SIZE. + if (section_end_offset - stub_table_end_offset >= group_size) + { + gold_assert(group_end != this->input_sections().end()); + this->create_stub_group(group_begin, group_end, stub_table, + target, &new_relaxed_sections); + state = NO_GROUP; + } + break; + + default: + gold_unreachable(); + } + + // If we see an input section and currently there is no group, start + // a new one. Skip any empty sections. + if ((p->is_input_section() || p->is_relaxed_input_section()) + && (p->relobj()->section_size(p->shndx()) != 0)) + { + if (state == NO_GROUP) + { + state = FINDING_STUB_SECTION; + group_begin = p; + group_begin_offset = section_begin_offset; + } + + // Keep track of the last input section seen. + group_end = p; + group_end_offset = section_end_offset; + } + + off = section_end_offset; + } + + // Create a stub group for any ungrouped sections. + if (state == FINDING_STUB_SECTION || state == HAS_STUB_SECTION) + { + gold_assert(group_end != this->input_sections().end()); + this->create_stub_group(group_begin, group_end, + (state == FINDING_STUB_SECTION + ? group_end + : stub_table), + target, &new_relaxed_sections); + } + + // Convert input section into relaxed input section in a batch. + if (!new_relaxed_sections.empty()) + this->convert_input_sections_to_relaxed_sections(new_relaxed_sections); + + // Update the section offsets + for (size_t i = 0; i < new_relaxed_sections.size(); ++i) + { + Arm_relobj<big_endian>* arm_relobj = + Arm_relobj<big_endian>::as_arm_relobj( + new_relaxed_sections[i]->relobj()); + unsigned int shndx = new_relaxed_sections[i]->shndx(); + // Tell Arm_relobj that this input section is converted. + arm_relobj->convert_input_section_to_relaxed_section(shndx); + } +} + // A class to handle the PLT data. template<bool big_endian> |