From 57f6d32deafcfec43bef5e963e12aacc0783a1e4 Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Tue, 30 Aug 2016 11:58:05 +0930 Subject: [PATCH] [GOLD] correct grouping of stubs This patch rewrites the rather obscure can_add_to_stub_group, fixing a problem with the handling of sections containing conditional external branches. When a section group contains any such section, the group size needs to be limited to a much smaller size than groups with only non-conditional external branches. PR 20523 * powerpc.cc (class Stub_control): Add has14_. Comment owner_. (Stub_control::can_add_to_stub_group): Correct grouping of sections containing 14-bit external branches. When returning false, set state_ to reflect the fact that we have one section for the next group. Rewrite most of function for clarity. Add and expand comments. (Target_powerpc::do_relax): Print stub group size retry in hex. --- gold/ChangeLog | 11 ++++++ gold/powerpc.cc | 103 ++++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 81 insertions(+), 33 deletions(-) diff --git a/gold/ChangeLog b/gold/ChangeLog index f01188e..0ea09f9 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,14 @@ +2016-08-30 Alan Modra + + PR 20523 + * powerpc.cc (class Stub_control): Add has14_. Comment owner_. + (Stub_control::can_add_to_stub_group): Correct grouping of + sections containing 14-bit external branches. When returning + false, set state_ to reflect the fact that we have one section + for the next group. Rewrite most of function for clarity. + Add and expand comments. + (Target_powerpc::do_relax): Print stub group size retry in hex. + 2016-08-26 Han Shen PR gold/20529 - relaxing loop never ends. diff --git a/gold/powerpc.cc b/gold/powerpc.cc index c269ae0..d0dd672 100644 --- a/gold/powerpc.cc +++ b/gold/powerpc.cc @@ -2441,7 +2441,7 @@ class Stub_control : state_(NO_GROUP), stub_group_size_(abs(size)), stub14_group_size_(abs(size) >> 10), stubs_always_before_branch_(size < 0), - suppress_size_errors_(no_size_errors), + suppress_size_errors_(no_size_errors), has14_(false), group_end_addr_(0), owner_(NULL), output_section_(NULL) { } @@ -2482,21 +2482,23 @@ class Stub_control uint32_t stub14_group_size_; bool stubs_always_before_branch_; bool suppress_size_errors_; + bool has14_; uint64_t group_end_addr_; + // owner_ and output_section_ specify the section to which stubs are + // attached. The stubs are placed at the end of this section. const Output_section::Input_section* owner_; Output_section* output_section_; }; // Return true iff input section can be handled by current stub -// group. +// group. Sections are presented to this function in reverse order, +// so the first section is the tail of the group. bool Stub_control::can_add_to_stub_group(Output_section* o, const Output_section::Input_section* i, bool has14) { - uint32_t group_size - = has14 ? this->stub14_group_size_ : this->stub_group_size_; bool whole_sec = o->order() == ORDER_INIT || o->order() == ORDER_FINI; uint64_t this_size; uint64_t start_addr = o->address(); @@ -2510,46 +2512,81 @@ Stub_control::can_add_to_stub_group(Output_section* o, start_addr += i->relobj()->output_section_offset(i->shndx()); this_size = i->data_size(); } + + uint32_t group_size + = has14 ? this->stub14_group_size_ : this->stub_group_size_; uint64_t end_addr = start_addr + this_size; - bool toobig = this_size > group_size; - if (toobig && !this->suppress_size_errors_) + if (this_size > group_size && !this->suppress_size_errors_) gold_warning(_("%s:%s exceeds group size"), i->relobj()->name().c_str(), i->relobj()->section_name(i->shndx()).c_str()); - if (this->state_ != HAS_STUB_SECTION - && (!whole_sec || this->output_section_ != o) - && (this->state_ == NO_GROUP - || this->group_end_addr_ - end_addr < group_size)) - { - this->owner_ = i; - this->output_section_ = o; - } + this->has14_ = this->has14_ || has14; + group_size = this->has14_ ? this->stub14_group_size_ : this->stub_group_size_; - if (this->state_ == NO_GROUP) + if (this->state_ == HAS_STUB_SECTION) { - this->state_ = FINDING_STUB_SECTION; - this->group_end_addr_ = end_addr; - } - else if (this->group_end_addr_ - start_addr < group_size) - ; - // Adding this section would make the group larger than GROUP_SIZE. - else if (this->state_ == FINDING_STUB_SECTION - && !this->stubs_always_before_branch_ - && !toobig) - { - // But wait, there's more! Input sections up to GROUP_SIZE - // bytes before the stub table can be handled by it too. - this->state_ = HAS_STUB_SECTION; - this->group_end_addr_ = end_addr; + // Can we add this section, which is before the stubs, to the + // group? + if (this->group_end_addr_ - start_addr <= group_size) + return true; } else { - this->state_ = NO_GROUP; - return false; + // Stubs are added at the end of "owner_". + // The current section can always be the stub owner, except when + // whole_sec is true and the current section isn't the last of + // the pasted sections. (This restriction for the whole_sec + // case is just to simplify the corner case mentioned in + // group_sections.) + // Note that "owner_" itself is not necessarily part of the + // group of sections served by these stubs! + if (!whole_sec || this->output_section_ != o) + { + this->owner_ = i; + this->output_section_ = o; + } + + if (this->state_ == FINDING_STUB_SECTION) + { + if (this->group_end_addr_ - start_addr <= group_size) + return true; + // The group after the stubs has reached maximum size. + // Now see about adding sections before the stubs to the + // group. If the current section has a 14-bit branch and + // the group after the stubs exceeds stub14_group_size_ + // (because they didn't have 14-bit branches), don't add + // sections before the stubs: The size of stubs for such a + // large group may exceed the reach of a 14-bit branch. + if (!this->stubs_always_before_branch_ + && this_size <= group_size + && this->group_end_addr_ - end_addr <= group_size) + { + this->state_ = HAS_STUB_SECTION; + this->group_end_addr_ = end_addr; + return true; + } + } + else if (this->state_ == NO_GROUP) + { + // Only here on very first use of Stub_control + this->state_ = FINDING_STUB_SECTION; + this->group_end_addr_ = end_addr; + return true; + } + else + gold_unreachable(); } - return true; + + // The section fails to fit in the current group. Set up a few + // things for the next group. owner_ and output_section_ will be + // set later after we've retrieved those values for the current + // group. + this->state_ = FINDING_STUB_SECTION; + this->has14_ = has14; + this->group_end_addr_ = end_addr; + return false; } // Look over all the input sections, deciding where to place stubs. @@ -2887,7 +2924,7 @@ Target_powerpc::do_relax(int pass, } 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"), + gold_info(_("%s: stub group size is too large; retrying with %#x"), program_name, this->stub_group_size_); this->group_sections(layout, task, true); } -- 2.7.4