summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2016-08-30 11:58:05 +0930
committerAlan Modra <amodra@gmail.com>2017-02-22 09:40:18 +1030
commit2ec4b4a8bc3b056b710710fc24ecdd19d6c9670a (patch)
tree551a3428aebbaf73040e515b119641e49b929256
parentcd54b50e7b5ac662ca52d636c01dfaa506cdb07b (diff)
downloadbinutils-gdb-2ec4b4a8bc3b056b710710fc24ecdd19d6c9670a.tar.gz
[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.
-rw-r--r--gold/ChangeLog10
-rw-r--r--gold/powerpc.cc103
2 files changed, 80 insertions, 33 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog
index e551591c3c4..966aa2fc5af 100644
--- a/gold/ChangeLog
+++ b/gold/ChangeLog
@@ -1,6 +1,16 @@
2017-02-22 Alan Modra <amodra@gmail.com>
Apply from master
+ 2016-08-30 Alan Modra <amodra@gmail.com>
+ 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 <shenhan@google.com>
PR gold/20529 - relaxing loop never ends.
* powerpc.cc (Stub_table::min_size_threshold_): New member to
diff --git a/gold/powerpc.cc b/gold/powerpc.cc
index c269ae03514..d0dd6728e15 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<size, big_endian>::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);
}