summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2016-12-07 14:12:26 +1030
committerAlan Modra <amodra@gmail.com>2017-02-22 09:40:21 +1030
commit983d1db4d7b5644b8c16a64bb6cb7267f602ebef (patch)
treef73f2f550fa67783da992f4f3933090316c56c88
parentf9aa8e5d979f7b60ca37b331a1dacebcf5e89a4f (diff)
downloadbinutils-gdb-983d1db4d7b5644b8c16a64bb6cb7267f602ebef.tar.gz
[GOLD] powerpc64le-linux fails to link large Linux kernel
Gold attaches stubs to an existing section in contrast to ld.bfd which inserts a new section for stubs. If we want stubs before branches, then the stubs must be added to the previous section. Adding to the previous section is a disaster if there is a large gap between the previous section and the group. PR gold/20878 * powerpc.cc (Stub_control): Replace stubs_always_before_branch_ with stubs_always_after_branch_, group_end_addr_ with group_start_addr_. (Stub_control::can_add_to_stub_group): Rewrite to suit scanning sections by increasing address. (Target_powerpc::group_sections): Scan that way. Delete corner case. * options.h (--stub-group-size): Update help string.
-rw-r--r--gold/ChangeLog11
-rw-r--r--gold/options.h5
-rw-r--r--gold/powerpc.cc127
3 files changed, 64 insertions, 79 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog
index 30d53d9250e..d45bb48b71c 100644
--- a/gold/ChangeLog
+++ b/gold/ChangeLog
@@ -2,6 +2,17 @@
Apply from master
2016-12-07 Alan Modra <amodra@gmail.com>
+ PR gold/20878
+ * powerpc.cc (Stub_control): Replace stubs_always_before_branch_
+ with stubs_always_after_branch_, group_end_addr_ with
+ group_start_addr_.
+ (Stub_control::can_add_to_stub_group): Rewrite to suit scanning
+ sections by increasing address.
+ (Target_powerpc::group_sections): Scan that way. Delete corner
+ case.
+ * options.h (--stub-group-size): Update help string.
+
+ 2016-12-07 Alan Modra <amodra@gmail.com>
* powerpc.cc (Stub_table_owner): Provide constructor.
(Powerpc_relobj::set_stub_table): Resize fill with -1.
(Target_powerpc::Branch_info::make_stub): Provide target debug
diff --git a/gold/options.h b/gold/options.h
index 4c5b2aea122..f63758ba976 100644
--- a/gold/options.h
+++ b/gold/options.h
@@ -1097,9 +1097,8 @@ class General_options
DEFINE_int(stub_group_size, options::TWO_DASHES , '\0', 1,
N_("(ARM, PowerPC only) The maximum distance from instructions "
- "in a group of sections to their stubs. Negative values mean "
- "stubs are always after (PowerPC before) the group. 1 means "
- "use default size.\n"),
+ "in a group of sections to their stubs. Negative values mean "
+ "stubs are always after the group. 1 means use default size"),
N_("SIZE"));
DEFINE_bool(no_keep_memory, options::TWO_DASHES, '\0', false,
diff --git a/gold/powerpc.cc b/gold/powerpc.cc
index d8d332a95e1..931f177901c 100644
--- a/gold/powerpc.cc
+++ b/gold/powerpc.cc
@@ -2439,13 +2439,13 @@ class Stub_control
public:
// Determine the stub group size. The group size is the absolute
// value of the parameter --stub-group-size. If --stub-group-size
- // is passed a negative value, we restrict stubs to be always before
+ // is passed a negative value, we restrict stubs to be always after
// the stubbed branches.
Stub_control(int32_t size, bool no_size_errors)
: state_(NO_GROUP), stub_group_size_(abs(size)),
- stubs_always_before_branch_(size < 0),
+ stubs_always_after_branch_(size < 0),
suppress_size_errors_(no_size_errors), group_size_(0),
- group_end_addr_(0), owner_(NULL), output_section_(NULL)
+ group_start_addr_(0), owner_(NULL), output_section_(NULL)
{
}
@@ -2482,13 +2482,13 @@ class Stub_control
State state_;
uint32_t stub_group_size_;
- bool stubs_always_before_branch_;
+ bool stubs_always_after_branch_;
bool suppress_size_errors_;
// Current max size of group. Starts at stub_group_size_ but is
// reduced to stub_group_size_/1024 on seeing a section with
// external conditional branches.
uint32_t group_size_;
- uint64_t group_end_addr_;
+ uint64_t group_start_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_;
@@ -2496,8 +2496,8 @@ class Stub_control
};
// Return true iff input section can be handled by current stub
-// group. Sections are presented to this function in reverse order,
-// so the first section is the tail of the group.
+// group. Sections are presented to this function in order,
+// so the first section is the head of the group.
bool
Stub_control::can_add_to_stub_group(Output_section* o,
@@ -2518,6 +2518,7 @@ Stub_control::can_add_to_stub_group(Output_section* o,
this_size = i->data_size();
}
+ uint64_t end_addr = start_addr + this_size;
uint32_t group_size = this->stub_group_size_;
if (has14)
this->group_size_ = group_size = group_size >> 10;
@@ -2532,64 +2533,56 @@ Stub_control::can_add_to_stub_group(Output_section* o,
i->relobj()->name().c_str(),
i->relobj()->section_name(i->shndx()).c_str(),
(long long) this_size,
- (long long) this->group_end_addr_ - start_addr);
+ (this->state_ == NO_GROUP
+ ? this_size
+ : (long long) end_addr - this->group_start_addr_));
- uint64_t end_addr = start_addr + this_size;
if (this->state_ == HAS_STUB_SECTION)
{
- // Can we add this section, which is before the stubs, to the
+ // Can we add this section, which is after the stubs, to the
// group?
- if (this->group_end_addr_ - start_addr <= this->group_size_)
+ if (end_addr - this->group_start_addr_ <= this->group_size_)
return true;
}
- else
+ else if (this->state_ == FINDING_STUB_SECTION)
{
- // 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)
+ if ((whole_sec && this->output_section_ == o)
+ || end_addr - this->group_start_addr_ <= this->group_size_)
{
+ // Stubs are added at the end of "owner_".
this->owner_ = i;
this->output_section_ = o;
+ return true;
}
-
- if (this->state_ == FINDING_STUB_SECTION)
- {
- if (this->group_end_addr_ - start_addr <= this->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 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 <= this->group_size_
- && this->group_end_addr_ - end_addr <= this->group_size_)
- {
- gold_debug(DEBUG_TARGET, "adding before stubs");
- this->state_ = HAS_STUB_SECTION;
- this->group_end_addr_ = end_addr;
- return true;
- }
- }
- else if (this->state_ == NO_GROUP)
+ // The group before the stubs has reached maximum size.
+ // Now see about adding sections after the stubs to the
+ // group. If the current section has a 14-bit branch and
+ // the group before the stubs exceeds group_size_ (because
+ // they didn't have 14-bit branches), don't add sections
+ // after the stubs: The size of stubs for such a large
+ // group may exceed the reach of a 14-bit branch.
+ if (!this->stubs_always_after_branch_
+ && this_size <= this->group_size_
+ && start_addr - this->group_start_addr_ <= this->group_size_)
{
- // Only here on very first use of Stub_control
- this->state_ = FINDING_STUB_SECTION;
- this->group_size_ = group_size;
- this->group_end_addr_ = end_addr;
+ gold_debug(DEBUG_TARGET, "adding after stubs");
+ this->state_ = HAS_STUB_SECTION;
+ this->group_start_addr_ = start_addr;
return true;
}
- else
- gold_unreachable();
}
+ else if (this->state_ == NO_GROUP)
+ {
+ // Only here on very first use of Stub_control
+ this->owner_ = i;
+ this->output_section_ = o;
+ this->state_ = FINDING_STUB_SECTION;
+ this->group_size_ = group_size;
+ this->group_start_addr_ = start_addr;
+ return true;
+ }
+ else
+ gold_unreachable();
gold_debug(DEBUG_TARGET, "nope, didn't fit\n");
@@ -2599,7 +2592,7 @@ Stub_control::can_add_to_stub_group(Output_section* o,
// group.
this->state_ = FINDING_STUB_SECTION;
this->group_size_ = group_size;
- this->group_end_addr_ = end_addr;
+ this->group_start_addr_ = start_addr;
return false;
}
@@ -2619,14 +2612,14 @@ Target_powerpc<size, big_endian>::group_sections(Layout* layout,
Layout::Section_list section_list;
layout->get_executable_sections(&section_list);
std::stable_sort(section_list.begin(), section_list.end(), Sort_sections());
- for (Layout::Section_list::reverse_iterator o = section_list.rbegin();
- o != section_list.rend();
+ for (Layout::Section_list::iterator o = section_list.begin();
+ o != section_list.end();
++o)
{
typedef Output_section::Input_section_list Input_section_list;
- for (Input_section_list::const_reverse_iterator i
- = (*o)->input_sections().rbegin();
- i != (*o)->input_sections().rend();
+ for (Input_section_list::const_iterator i
+ = (*o)->input_sections().begin();
+ i != (*o)->input_sections().end();
++i)
{
if (i->is_input_section()
@@ -2653,26 +2646,8 @@ Target_powerpc<size, big_endian>::group_sections(Layout* layout,
}
if (table_owner != NULL)
{
- const Output_section::Input_section* i = stub_control.owner();
-
- 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.
- 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(), tables.size() - 1);
- }
- else
- {
- table_owner->output_section = stub_control.output_section();
- table_owner->owner = i;
- }
+ table_owner->output_section = stub_control.output_section();
+ table_owner->owner = stub_control.owner();;
}
for (typename std::vector<Stub_table_owner*>::iterator t = tables.begin();
t != tables.end();