diff options
author | Alan Modra <amodra@gmail.com> | 2016-12-07 14:12:26 +1030 |
---|---|---|
committer | Alan Modra <amodra@gmail.com> | 2016-12-07 14:15:44 +1030 |
commit | a5018ae555cdf491005907c03f997558ba15fc47 (patch) | |
tree | 5d31dd0711e31865b1c8549cbc2628ff43f88c1d | |
parent | dc60b26d983494a3cdec0d4f167ed98fde1a832a (diff) | |
download | fsf-binutils-gdb-a5018ae555cdf491005907c03f997558ba15fc47.zip fsf-binutils-gdb-a5018ae555cdf491005907c03f997558ba15fc47.tar.gz fsf-binutils-gdb-a5018ae555cdf491005907c03f997558ba15fc47.tar.bz2 |
[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/ChangeLog | 12 | ||||
-rw-r--r-- | gold/options.h | 3 | ||||
-rw-r--r-- | gold/powerpc.cc | 127 |
3 files changed, 64 insertions, 78 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog index 50af9a2..367cb02 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,5 +1,17 @@ 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 d21b935..90ccc80 100644 --- a/gold/options.h +++ b/gold/options.h @@ -1216,8 +1216,7 @@ 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"), + "stubs are always after the group. 1 means use default size"), N_("SIZE")); DEFINE_uint(split_stack_adjust_size, options::TWO_DASHES, '\0', 0x4000, diff --git a/gold/powerpc.cc b/gold/powerpc.cc index a850c36..f763618 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(§ion_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(); |