diff options
author | Alan Modra <amodra@gmail.com> | 2014-11-26 11:10:29 +1030 |
---|---|---|
committer | Alan Modra <amodra@gmail.com> | 2014-11-26 13:29:29 +1030 |
commit | a3e60ddb0b43a3aed32f2982d472ab305c7e8f67 (patch) | |
tree | 85f47346b1bb39addca84555cdc14064f092258c /gold/powerpc.cc | |
parent | aa4893954a75660d2aa66245cb9d020049cb9546 (diff) | |
download | gdb-a3e60ddb0b43a3aed32f2982d472ab305c7e8f67.zip gdb-a3e60ddb0b43a3aed32f2982d472ab305c7e8f67.tar.gz gdb-a3e60ddb0b43a3aed32f2982d472ab305c7e8f67.tar.bz2 |
Retry powerpc gold stub grouping when groups prove too large
An unusually large number of stubs can result in the default section
group size being too large; sections plus stubs exceed the range of a
branch. Restarting the relaxation pass with a smaller group size can
sometimes help.
* powerpc.cc (struct Stub_table_owner): New.
(Powerpc_relobj): Rename stub_table_ to stub_table_index_, an
unsigned int vector. Update all references.
(powerpc_relobj::set_stub_table): Take an unsigned int param
rather than a Stub_table. Update callers.
(Powerpc_relobj::clear_stub_table): New function.
(Target_powerpc): Add relax_failed_, relax_fail_count_ and
stub_group_size_ vars.
(Target_powerpc::new_stub_table): Delete.
(max_branch_delta): New function, extracted from..
(Target_powerpc::Relocate::relocate): ..here..
(Target_powerpc::Branch_info::make_stub): ..and here. Return
status on whether stub created successfully.
(Stub_control::Stub_control): Add "no_size_errors" param. Move
default sizing to..
(Target_powerpc::do_relax): ..here. Init stub_group_size_ and
reduce on relax failure.
(Target_powerpc::group_sections): Add "no_size_errors" param.
Use stub_group_size_. Set up group info in a temp vector,
before building Stub_table vector. Account for input sections
possibly already converted to relaxed sections.
(Stub_table::init): Delete. Merge into..
(Stub_table::Stub_table): ..here.
(Stub_table::can_reach_stub): New function.
(Stub_table::add_plt_call_entry): Add "from" parameter and
return true iff stub could be reached.
(Stub_table::add_long_branch_entry): Similarly. Add "r_type"
param too.
(Stub_table::clear_stubs): Add "all" param.
Diffstat (limited to 'gold/powerpc.cc')
-rw-r--r-- | gold/powerpc.cc | 343 |
1 files changed, 211 insertions, 132 deletions
diff --git a/gold/powerpc.cc b/gold/powerpc.cc index 2319789..554bf38 100644 --- a/gold/powerpc.cc +++ b/gold/powerpc.cc @@ -62,6 +62,15 @@ class Output_data_glink; template<int size, bool big_endian> class Stub_table; +template<int size, bool big_endian> +class Target_powerpc; + +struct Stub_table_owner +{ + Output_section* output_section; + const Output_section::Input_section* owner; +}; + inline bool is_branch_reloc(unsigned int r_type); @@ -77,7 +86,7 @@ public: const typename elfcpp::Ehdr<size, big_endian>& ehdr) : Sized_relobj_file<size, big_endian>(name, input_file, offset, ehdr), special_(0), has_small_toc_reloc_(false), opd_valid_(false), - opd_ent_(), access_from_map_(), has14_(), stub_table_(), + opd_ent_(), access_from_map_(), has14_(), stub_table_index_(), e_flags_(ehdr.get_e_flags()), st_other_() { this->set_abiversion(0); @@ -260,21 +269,34 @@ public: { return shndx < this->has14_.size() && this->has14_[shndx]; } void - set_stub_table(unsigned int shndx, Stub_table<size, big_endian>* stub_table) + set_stub_table(unsigned int shndx, unsigned int stub_index) { - if (shndx >= this->stub_table_.size()) - this->stub_table_.resize(shndx + 1); - this->stub_table_[shndx] = stub_table; + if (shndx >= this->stub_table_index_.size()) + this->stub_table_index_.resize(shndx + 1); + this->stub_table_index_[shndx] = stub_index; } Stub_table<size, big_endian>* stub_table(unsigned int shndx) { - if (shndx < this->stub_table_.size()) - return this->stub_table_[shndx]; + if (shndx < this->stub_table_index_.size()) + { + Target_powerpc<size, big_endian>* target + = static_cast<Target_powerpc<size, big_endian>*>( + parameters->sized_target<size, big_endian>()); + unsigned int indx = this->stub_table_index_[shndx]; + gold_assert(indx < target->stub_tables().size()); + return target->stub_tables()[indx]; + } return NULL; } + void + clear_stub_table() + { + this->stub_table_index_.clear(); + } + int abiversion() const { return this->e_flags_ & elfcpp::EF_PPC64_ABI; } @@ -343,7 +365,7 @@ private: std::vector<bool> has14_; // The stub table to use for a given input section. - std::vector<Stub_table<size, big_endian>*> stub_table_; + std::vector<unsigned int> stub_table_index_; // Header e_flags elfcpp::Elf_Word e_flags_; @@ -487,7 +509,8 @@ class Target_powerpc : public Sized_target<size, big_endian> glink_(NULL), rela_dyn_(NULL), copy_relocs_(elfcpp::R_POWERPC_COPY), tlsld_got_offset_(-1U), stub_tables_(), branch_lookup_table_(), branch_info_(), - plt_thread_safe_(false) + plt_thread_safe_(false), relax_failed_(false), relax_fail_count_(0), + stub_group_size_(0) { } @@ -562,9 +585,6 @@ class Target_powerpc : public Sized_target<size, big_endian> ppc_object->set_has_14bit_branch(data_shndx); } - Stub_table<size, big_endian>* - new_stub_table(); - void do_define_standard_symbols(Symbol_table*, Layout*); @@ -1179,7 +1199,7 @@ class Target_powerpc : public Sized_target<size, big_endian> // Look over all the input sections, deciding where to place stubs. void - group_sections(Layout*, const Task*); + group_sections(Layout*, const Task*, bool); // Sort output sections by address. struct Sort_sections @@ -1206,7 +1226,7 @@ class Target_powerpc : public Sized_target<size, big_endian> { } // If this branch needs a plt call stub, or a long branch stub, make one. - void + bool make_stub(Stub_table<size, big_endian>*, Stub_table<size, big_endian>*, Symbol_table*) const; @@ -1284,6 +1304,10 @@ class Target_powerpc : public Sized_target<size, big_endian> Branches branch_info_; bool plt_thread_safe_; + + bool relax_failed_; + int relax_fail_count_; + int32_t stub_group_size_; }; template<> @@ -2361,27 +2385,13 @@ class Stub_control // value of the parameter --stub-group-size. If --stub-group-size // is passed a negative value, we restrict stubs to be always before // the stubbed branches. - Stub_control(int32_t size) + Stub_control(int32_t size, bool no_size_errors) : state_(NO_GROUP), stub_group_size_(abs(size)), stub14_group_size_(abs(size) >> 10), - stubs_always_before_branch_(size < 0), suppress_size_errors_(false), + stubs_always_before_branch_(size < 0), + suppress_size_errors_(no_size_errors), group_end_addr_(0), owner_(NULL), output_section_(NULL) { - if (stub_group_size_ == 1) - { - // Default values. - if (stubs_always_before_branch_) - { - stub_group_size_ = 0x1e00000; - stub14_group_size_ = 0x7800; - } - else - { - stub_group_size_ = 0x1c00000; - stub14_group_size_ = 0x7000; - } - suppress_size_errors_ = true; - } } // Return true iff input section can be handled by current stub @@ -2495,12 +2505,14 @@ Stub_control::can_add_to_stub_group(Output_section* o, template<int size, bool big_endian> void Target_powerpc<size, big_endian>::group_sections(Layout* layout, - const Task*) + const Task*, + bool no_size_errors) { - Stub_control stub_control(parameters->options().stub_group_size()); + Stub_control stub_control(this->stub_group_size_, no_size_errors); // Group input sections and insert stub table - Stub_table<size, big_endian>* stub_table = NULL; + Stub_table_owner* table_owner = NULL; + std::vector<Stub_table_owner*> tables; Layout::Section_list section_list; layout->get_executable_sections(§ion_list); std::stable_sort(section_list.begin(), section_list.end(), Sort_sections()); @@ -2514,50 +2526,89 @@ Target_powerpc<size, big_endian>::group_sections(Layout* layout, i != (*o)->input_sections().rend(); ++i) { - if (i->is_input_section()) + if (i->is_input_section() + || i->is_relaxed_input_section()) { Powerpc_relobj<size, big_endian>* ppcobj = static_cast <Powerpc_relobj<size, big_endian>*>(i->relobj()); bool has14 = ppcobj->has_14bit_branch(i->shndx()); if (!stub_control.can_add_to_stub_group(*o, &*i, has14)) { - stub_table->init(stub_control.owner(), - stub_control.output_section()); + table_owner->output_section = stub_control.output_section(); + table_owner->owner = stub_control.owner(); stub_control.set_output_and_owner(*o, &*i); - stub_table = NULL; + table_owner = NULL; + } + if (table_owner == NULL) + { + table_owner = new Stub_table_owner; + tables.push_back(table_owner); } - if (stub_table == NULL) - stub_table = this->new_stub_table(); - ppcobj->set_stub_table(i->shndx(), stub_table); + ppcobj->set_stub_table(i->shndx(), tables.size() - 1); } } } - if (stub_table != NULL) + if (table_owner != NULL) { const Output_section::Input_section* i = stub_control.owner(); - if (!i->is_input_section()) + + if (tables.size() >= 2 && tables[tables.size() - 2]->owner == i) { // Corner case. A new stub group was made for the first // section (last one looked at here) for some reason, but // the first section is already being used as the owner for // a stub table for following sections. Force it into that // stub group. - gold_assert(this->stub_tables_.size() >= 2); - this->stub_tables_.pop_back(); - delete stub_table; + tables.pop_back(); + delete table_owner; Powerpc_relobj<size, big_endian>* ppcobj = static_cast <Powerpc_relobj<size, big_endian>*>(i->relobj()); - ppcobj->set_stub_table(i->shndx(), this->stub_tables_.back()); + ppcobj->set_stub_table(i->shndx(), tables.size() - 1); + } + else + { + table_owner->output_section = stub_control.output_section(); + table_owner->owner = i; } + } + for (typename std::vector<Stub_table_owner*>::iterator t = tables.begin(); + t != tables.end(); + ++t) + { + Stub_table<size, big_endian>* stub_table; + + if ((*t)->owner->is_input_section()) + stub_table = new Stub_table<size, big_endian>(this, + (*t)->output_section, + (*t)->owner); + else if ((*t)->owner->is_relaxed_input_section()) + stub_table = static_cast<Stub_table<size, big_endian>*>( + (*t)->owner->relaxed_input_section()); else - stub_table->init(i, stub_control.output_section()); + gold_unreachable(); + this->stub_tables_.push_back(stub_table); + delete *t; } } +static unsigned long +max_branch_delta (unsigned int r_type) +{ + if (r_type == elfcpp::R_POWERPC_REL14 + || r_type == elfcpp::R_POWERPC_REL14_BRTAKEN + || r_type == elfcpp::R_POWERPC_REL14_BRNTAKEN) + return 1L << 15; + if (r_type == elfcpp::R_POWERPC_REL24 + || r_type == elfcpp::R_PPC_PLTREL24 + || r_type == elfcpp::R_PPC_LOCAL24PC) + return 1L << 25; + return 0; +} + // If this branch needs a plt call stub, or a long branch stub, make one. template<int size, bool big_endian> -void +bool Target_powerpc<size, big_endian>::Branch_info::make_stub( Stub_table<size, big_endian>* stub_table, Stub_table<size, big_endian>* ifunc_stub_table, @@ -2590,27 +2641,25 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub( stub_table = ifunc_stub_table; } gold_assert(stub_table != NULL); + Address from = this->object_->get_output_section_offset(this->shndx_); + if (from != invalid_address) + from += (this->object_->output_section(this->shndx_)->address() + + this->offset_); if (gsym != NULL) - stub_table->add_plt_call_entry(this->object_, gsym, - this->r_type_, this->addend_); + return stub_table->add_plt_call_entry(from, + this->object_, gsym, + this->r_type_, this->addend_); else - stub_table->add_plt_call_entry(this->object_, this->r_sym_, - this->r_type_, this->addend_); + return stub_table->add_plt_call_entry(from, + this->object_, this->r_sym_, + this->r_type_, this->addend_); } } else { - unsigned long max_branch_offset; - if (this->r_type_ == elfcpp::R_POWERPC_REL14 - || this->r_type_ == elfcpp::R_POWERPC_REL14_BRTAKEN - || this->r_type_ == elfcpp::R_POWERPC_REL14_BRNTAKEN) - max_branch_offset = 1 << 15; - else if (this->r_type_ == elfcpp::R_POWERPC_REL24 - || this->r_type_ == elfcpp::R_PPC_PLTREL24 - || this->r_type_ == elfcpp::R_PPC_LOCAL24PC) - max_branch_offset = 1 << 25; - else - return; + unsigned long max_branch_offset = max_branch_delta(this->r_type_); + if (max_branch_offset == 0) + return true; Address from = this->object_->get_output_section_offset(this->shndx_); gold_assert(from != invalid_address); from += (this->object_->output_section(this->shndx_)->address() @@ -2625,16 +2674,16 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub( Object* symobj = gsym->object(); if (symobj->is_dynamic() || symobj->pluginobj() != NULL) - return; + return true; bool is_ordinary; unsigned int shndx = gsym->shndx(&is_ordinary); if (shndx == elfcpp::SHN_UNDEF) - return; + return true; } break; case Symbol::IS_UNDEFINED: - return; + return true; default: break; @@ -2642,7 +2691,7 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub( Symbol_table::Compute_final_value_status status; to = symtab->compute_final_value<size>(gsym, &status); if (status != Symbol_table::CFVS_OK) - return; + return true; if (size == 64) to += this->object_->ppc64_local_entry_offset(gsym); } @@ -2657,7 +2706,7 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub( &symval, symtab); if (status != ObjType::CFLV_OK || !symval.has_output_value()) - return; + return true; to = symval.value(this->object_, 0); if (size == 64) to += this->object_->ppc64_local_entry_offset(this->r_sym_); @@ -2680,11 +2729,13 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub( " no long branch stub for you"), this->object_->name().c_str(), this->object_->section_name(this->shndx_).c_str()); - return; + return true; } - stub_table->add_long_branch_entry(this->object_, to); + return stub_table->add_long_branch_entry(this->object_, + this->r_type_, from, to); } } + return true; } // Relaxation hook. This is where we do stub generation. @@ -2752,7 +2803,34 @@ Target_powerpc<size, big_endian>::do_relax(int pass, } } this->plt_thread_safe_ = thread_safe; - this->group_sections(layout, task); + } + + if (pass == 1) + { + this->stub_group_size_ = parameters->options().stub_group_size(); + bool no_size_errors = true; + if (this->stub_group_size_ == 1) + this->stub_group_size_ = 0x1c00000; + else if (this->stub_group_size_ == -1) + this->stub_group_size_ = -0x1e00000; + else + no_size_errors = false; + this->group_sections(layout, task, no_size_errors); + } + else if (this->relax_failed_ && this->relax_fail_count_ < 3) + { + this->branch_lookup_table_.clear(); + for (typename Stub_tables::iterator p = this->stub_tables_.begin(); + p != this->stub_tables_.end(); + ++p) + { + (*p)->clear_stubs(true); + } + this->stub_tables_.clear(); + this->stub_group_size_ = this->stub_group_size_ / 4 * 3; + gold_info(_("%s: stub group size is too large; retrying with %d"), + program_name, this->stub_group_size_); + this->group_sections(layout, task, true); } // We need address of stub tables valid for make_stub. @@ -2777,11 +2855,12 @@ Target_powerpc<size, big_endian>::do_relax(int pass, p != this->stub_tables_.end(); ++p) { - (*p)->clear_stubs(); + (*p)->clear_stubs(false); } } // Build all the stubs. + this->relax_failed_ = false; Stub_table<size, big_endian>* ifunc_stub_table = this->stub_tables_.size() == 0 ? NULL : this->stub_tables_[0]; Stub_table<size, big_endian>* one_stub_table @@ -2790,7 +2869,14 @@ Target_powerpc<size, big_endian>::do_relax(int pass, b != this->branch_info_.end(); b++) { - b->make_stub(one_stub_table, ifunc_stub_table, symtab); + if (!b->make_stub(one_stub_table, ifunc_stub_table, symtab) + && !this->relax_failed_) + { + this->relax_failed_ = true; + this->relax_fail_count_++; + if (this->relax_fail_count_ < 3) + return true; + } } // Did anything change size? @@ -3475,26 +3561,35 @@ class Stub_table : public Output_relaxed_input_section typedef typename elfcpp::Elf_types<size>::Elf_Addr Address; static const Address invalid_address = static_cast<Address>(0) - 1; - Stub_table(Target_powerpc<size, big_endian>* targ) - : Output_relaxed_input_section(NULL, 0, 0), + Stub_table(Target_powerpc<size, big_endian>* targ, + Output_section* output_section, + const Output_section::Input_section* owner) + : Output_relaxed_input_section(owner->relobj(), owner->shndx(), + owner->relobj() + ->section_addralign(owner->shndx())), targ_(targ), plt_call_stubs_(), long_branch_stubs_(), - orig_data_size_(0), plt_size_(0), last_plt_size_(0), + orig_data_size_(owner->current_data_size()), + plt_size_(0), last_plt_size_(0), branch_size_(0), last_branch_size_(0), eh_frame_added_(false) - { } + { + this->set_output_section(output_section); - // Delayed Output_relaxed_input_section init. - void - init(const Output_section::Input_section*, Output_section*); + std::vector<Output_relaxed_input_section*> new_relaxed; + new_relaxed.push_back(this); + output_section->convert_input_sections_to_relaxed_sections(new_relaxed); + } // Add a plt call stub. - void - add_plt_call_entry(const Sized_relobj_file<size, big_endian>*, + bool + add_plt_call_entry(Address, + const Sized_relobj_file<size, big_endian>*, const Symbol*, unsigned int, Address); - void - add_plt_call_entry(const Sized_relobj_file<size, big_endian>*, + bool + add_plt_call_entry(Address, + const Sized_relobj_file<size, big_endian>*, unsigned int, unsigned int, Address); @@ -3520,20 +3615,37 @@ class Stub_table : public Output_relaxed_input_section Address) const; // Add a long branch stub. - void - add_long_branch_entry(const Powerpc_relobj<size, big_endian>*, Address); + bool + add_long_branch_entry(const Powerpc_relobj<size, big_endian>*, + unsigned int, Address, Address); Address find_long_branch_entry(const Powerpc_relobj<size, big_endian>*, Address) const; + bool + can_reach_stub(Address from, unsigned int off, unsigned int r_type) + { + unsigned long max_branch_offset = max_branch_delta(r_type); + if (max_branch_offset == 0) + return true; + gold_assert(from != invalid_address); + Address loc = off + this->stub_address(); + return loc - from + max_branch_offset < 2 * max_branch_offset; + } + void - clear_stubs() + clear_stubs(bool all) { this->plt_call_stubs_.clear(); this->plt_size_ = 0; this->long_branch_stubs_.clear(); this->branch_size_ = 0; + if (all) + { + this->last_plt_size_ = 0; + this->last_branch_size_ = 0; + } } Address @@ -3837,44 +3949,13 @@ class Stub_table : public Output_relaxed_input_section bool eh_frame_added_; }; -// Make a new stub table, and record. - -template<int size, bool big_endian> -Stub_table<size, big_endian>* -Target_powerpc<size, big_endian>::new_stub_table() -{ - Stub_table<size, big_endian>* stub_table - = new Stub_table<size, big_endian>(this); - this->stub_tables_.push_back(stub_table); - return stub_table; -} - -// Delayed stub table initialisation, because we create the stub table -// before we know to which section it will be attached. - -template<int size, bool big_endian> -void -Stub_table<size, big_endian>::init( - const Output_section::Input_section* owner, - Output_section* output_section) -{ - this->set_relobj(owner->relobj()); - this->set_shndx(owner->shndx()); - this->set_addralign(this->relobj()->section_addralign(this->shndx())); - this->set_output_section(output_section); - this->orig_data_size_ = owner->current_data_size(); - - std::vector<Output_relaxed_input_section*> new_relaxed; - new_relaxed.push_back(this); - output_section->convert_input_sections_to_relaxed_sections(new_relaxed); -} - // Add a plt call stub, if we do not already have one for this // sym/object/addend combo. template<int size, bool big_endian> -void +bool Stub_table<size, big_endian>::add_plt_call_entry( + Address from, const Sized_relobj_file<size, big_endian>* object, const Symbol* gsym, unsigned int r_type, @@ -3886,11 +3967,13 @@ Stub_table<size, big_endian>::add_plt_call_entry( = this->plt_call_stubs_.insert(std::make_pair(ent, off)); if (p.second) this->plt_size_ = off + this->plt_call_size(p.first); + return this->can_reach_stub(from, off, r_type); } template<int size, bool big_endian> -void +bool Stub_table<size, big_endian>::add_plt_call_entry( + Address from, const Sized_relobj_file<size, big_endian>* object, unsigned int locsym_index, unsigned int r_type, @@ -3902,6 +3985,7 @@ Stub_table<size, big_endian>::add_plt_call_entry( = this->plt_call_stubs_.insert(std::make_pair(ent, off)); if (p.second) this->plt_size_ = off + this->plt_call_size(p.first); + return this->can_reach_stub(from, off, r_type); } // Find a plt call stub. @@ -3956,9 +4040,11 @@ Stub_table<size, big_endian>::find_plt_call_entry( // destination. template<int size, bool big_endian> -void +bool Stub_table<size, big_endian>::add_long_branch_entry( const Powerpc_relobj<size, big_endian>* object, + unsigned int r_type, + Address from, Address to) { Branch_stub_ent ent(object, to); @@ -3970,6 +4056,7 @@ Stub_table<size, big_endian>::add_long_branch_entry( if (size == 64 && stub_size != 4) this->targ_->add_branch_lookup_table(to); } + return this->can_reach_stub(from, off, r_type); } // Find long branch stub. @@ -7107,15 +7194,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate( value = target->symval_for_branch(relinfo->symtab, value, gsym, object, &dest_shndx); } - unsigned int max_branch_offset = 0; - if (r_type == elfcpp::R_POWERPC_REL24 - || r_type == elfcpp::R_PPC_PLTREL24 - || r_type == elfcpp::R_PPC_LOCAL24PC) - max_branch_offset = 1 << 25; - else if (r_type == elfcpp::R_POWERPC_REL14 - || r_type == elfcpp::R_POWERPC_REL14_BRTAKEN - || r_type == elfcpp::R_POWERPC_REL14_BRNTAKEN) - max_branch_offset = 1 << 15; + unsigned long max_branch_offset = max_branch_delta(r_type); if (max_branch_offset != 0 && value - address + max_branch_offset >= 2 * max_branch_offset) { |