From a3e60ddb0b43a3aed32f2982d472ab305c7e8f67 Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Wed, 26 Nov 2014 11:10:29 +1030 Subject: [PATCH] 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. --- gold/ChangeLog | 32 ++++++ gold/powerpc.cc | 343 ++++++++++++++++++++++++++++++++++---------------------- 2 files changed, 243 insertions(+), 132 deletions(-) diff --git a/gold/ChangeLog b/gold/ChangeLog index 9b78b35..1a75a1e 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,5 +1,37 @@ 2014-11-26 Alan Modra + * 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. + +2014-11-26 Alan Modra + * powerpc.cc (Stub_control::set_output_and_owner): New function. (Target_powerpc::group_sections): Use it. 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 class Stub_table; +template +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& ehdr) : Sized_relobj_file(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* 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* 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* target + = static_cast*>( + parameters->sized_target()); + 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 has14_; // The stub table to use for a given input section. - std::vector*> stub_table_; + std::vector stub_table_index_; // Header e_flags elfcpp::Elf_Word e_flags_; @@ -487,7 +509,8 @@ class Target_powerpc : public Sized_target 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 ppc_object->set_has_14bit_branch(data_shndx); } - Stub_table* - new_stub_table(); - void do_define_standard_symbols(Symbol_table*, Layout*); @@ -1179,7 +1199,7 @@ class Target_powerpc : public Sized_target // 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 { } // If this branch needs a plt call stub, or a long branch stub, make one. - void + bool make_stub(Stub_table*, Stub_table*, Symbol_table*) const; @@ -1284,6 +1304,10 @@ class Target_powerpc : public Sized_target 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 void Target_powerpc::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* stub_table = NULL; + Stub_table_owner* table_owner = NULL; + std::vector 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::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* ppcobj = static_cast *>(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* ppcobj = static_cast *>(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::iterator t = tables.begin(); + t != tables.end(); + ++t) + { + Stub_table* stub_table; + + if ((*t)->owner->is_input_section()) + stub_table = new Stub_table(this, + (*t)->output_section, + (*t)->owner); + else if ((*t)->owner->is_relaxed_input_section()) + stub_table = static_cast*>( + (*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 -void +bool Target_powerpc::Branch_info::make_stub( Stub_table* stub_table, Stub_table* ifunc_stub_table, @@ -2590,27 +2641,25 @@ Target_powerpc::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::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::Branch_info::make_stub( Symbol_table::Compute_final_value_status status; to = symtab->compute_final_value(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::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::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::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::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* ifunc_stub_table = this->stub_tables_.size() == 0 ? NULL : this->stub_tables_[0]; Stub_table* one_stub_table @@ -2790,7 +2869,14 @@ Target_powerpc::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::Elf_Addr Address; static const Address invalid_address = static_cast
(0) - 1; - Stub_table(Target_powerpc* targ) - : Output_relaxed_input_section(NULL, 0, 0), + Stub_table(Target_powerpc* 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 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*, + bool + add_plt_call_entry(Address, + const Sized_relobj_file*, const Symbol*, unsigned int, Address); - void - add_plt_call_entry(const Sized_relobj_file*, + bool + add_plt_call_entry(Address, + const Sized_relobj_file*, 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*, Address); + bool + add_long_branch_entry(const Powerpc_relobj*, + unsigned int, Address, Address); Address find_long_branch_entry(const Powerpc_relobj*, 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 -Stub_table* -Target_powerpc::new_stub_table() -{ - Stub_table* stub_table - = new Stub_table(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 -void -Stub_table::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 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 -void +bool Stub_table::add_plt_call_entry( + Address from, const Sized_relobj_file* object, const Symbol* gsym, unsigned int r_type, @@ -3886,11 +3967,13 @@ Stub_table::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 -void +bool Stub_table::add_plt_call_entry( + Address from, const Sized_relobj_file* object, unsigned int locsym_index, unsigned int r_type, @@ -3902,6 +3985,7 @@ Stub_table::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::find_plt_call_entry( // destination. template -void +bool Stub_table::add_long_branch_entry( const Powerpc_relobj* object, + unsigned int r_type, + Address from, Address to) { Branch_stub_ent ent(object, to); @@ -3970,6 +4056,7 @@ Stub_table::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::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) { -- 2.7.4