diff options
Diffstat (limited to 'gold')
-rw-r--r-- | gold/ChangeLog | 131 | ||||
-rw-r--r-- | gold/aarch64.cc | 8 | ||||
-rw-r--r-- | gold/arm.cc | 8 | ||||
-rw-r--r-- | gold/debug.h | 8 | ||||
-rw-r--r-- | gold/object.h | 7 | ||||
-rw-r--r-- | gold/options.h | 12 | ||||
-rw-r--r-- | gold/powerpc.cc | 922 | ||||
-rw-r--r-- | gold/reloc.cc | 73 |
8 files changed, 986 insertions, 183 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog index f5005ef96a3..59c60e7929c 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,134 @@ +2017-02-22 Alan Modra <amodra@gmail.com> + + Apply from master + * powerpc.cc (Target_powerpc::make_iplt_section): Check that + output_section exists before attempting add_output_section_data. + (Target_powerpc::make_brlt_section): Likewise. + + 2017-02-03 Alan Modra <amodra@gmail.com> + * powerpc.cc (Powerpc_relobj::make_toc_relative): Don't crash + when no .toc section exists. + + 2017-01-13 H.J. Lu <hongjiu.lu@intel.com> + PR gold/21040 + * powerpc.cc (Powerpc_relobj<size, big_endian>::make_toc_relative): + Cast 0x80008000 to uint64_t. + + 2017-01-11 Alan Modra <amodra@gmail.com> + * powerpc.cc (class Powerpc_copy_relocs): New. + (Powerpc_copy_relocs::emit): New function. + (Powerpc_relobj::relatoc_, toc_, no_toc_opt_): New variables. + (Powerpc_relobj::toc_shndx, set_no_toc_opt, no_toc_opt): New inlines. + (Powerpc_relobj::do_relocate_sections): New function. + (Powerpc_relobj::make_toc_relative): Likewise. + (Powerpc_relobj::do_find_special_sections): Stash away .rela.toc + and .toc too. + (ok_lo_toc_insn): Move earlier, and handle more insns. + (Target_powerpc::Scan::local): If optimizing toc accesses, set + no_toc_opt for entries we can't edit. Check insn validity. + Emit "toc optimization is not supported" warning, downgraded + from error. + (Target_powerpc::Scan::global): Likewise. + (Target_powerpc::Relocate::relocate): Edit TOC indirect code + to TOC relative. Don't emit "toc optimization is not supported" + error here. + + 2017-01-10 Cary Coutant <ccoutant@gmail.com> + * aarch64.cc (AArch64_relobj::do_relocate_sections): Call + Sized_relobj_file::relocate_section_range(). + * arm.cc (Arm_relobj::do_relocate_sections): Likewise. + * object.h (Sized_relobj_file::relocate_section_range): New method. + * reloc.cc (Sized_relobj_file::do_relocate_sections): Move + implementation... + (Sized_relobj_file::relocate_section_range): ...to new method. + + 2017-01-10 Alan Modra <amodra@gmail.com> + * options.h: Add --secure-plt option. + * powerpc.cc (Target_powerpc::Scan::local): Detect and error + on -fPIC -mbss-plt code. + (Target_powerpc::Scan::global): Likewise. + + 2017-01-09 Alan Modra <amodra@gmail.com> + * powerpc.cc (Target_powerpc::make_plt_section): Point sh_info of + ".rela.plt" at ".plt". + + 2017-01-07 Alan Modra <amodra@gmail.com> + * powerpc.cc: Use shorter equivalent elfcpp typedef for + Reltype and reloc_size throughout. + (Target_powerpc::symval_for_branch): Exclude dynamic symbols. + (Target_powerpc::Scan::local): Use local var r_sym. + (Target_powerpc::Scan::global: Likewise. + (Target_powerpc::Relocate::relocate): Delete shadowing r_sym. + + 2016-12-08 Alan Modra <amodra@gmail.com> + * powerpc.cc (Powerpc_relobj::stub_table): Return NULL rather + then asserting. + + 2016-12-08 Alan Modra <amodra@gmail.com> + * options.h (--stub-group-multi): Fix typo. + + 2016-12-07 Alan Modra <amodra@gmail.com> + * options.h (--stub-group-multi): New PowerPC option. + * powerpc.cc (Stub_control): Add multi_os_ var and param + to constructor. Sort start_ var later. Comment State. + (Stub_control::can_add_to_stub_group): Heed multi_os_. + (Target_powerpc::group_sections): Update. + + 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 + output on returning false. + + 2016-12-01 Cary Coutant <ccoutant@gmail.com> + PR gold/20807 + * aarch64.cc (Target_aarch64::scan_reloc_section_for_stubs): Handle + section symbols correctly. + * arm.cc (Target_arm): Likewise. + * powerpc.cc (Target_powerpc): Likewise. + + 2016-08-31 Alan Modra <amodra@gmail.com> + * powerpc.cc (class Stub_control): Delete stub14_group_size_ + and has14_. Add group_size_. + (Stub_control::can_add_to_stub_group): Adjust to suit. Print + debug info when switching to adding sections before stubs. + + 2016-08-31 Alan Modra <amodra@gmail.com> + * debug.h (DEBUG_TARGET): New. + (DEBUG_ALL): Add DEBUG_TARGET. + (gold_debug): Delete FORMAT param. + * powerpc.cc (Stub_control::can_add_to_stub_group): Print debug ourput. + + 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 + limit size. + (Stub_table::set_min_size_threshold): New member function. + (Stub_table::set_address_and_size): Add code to only allow size + increase. + (Target_powerpc::do_relax): Add code to record last size. + 2016-09-26 Cary Coutant <ccoutant@gmail.com> PR gold/20238 diff --git a/gold/aarch64.cc b/gold/aarch64.cc index db9f06c1c67..96ed66e3ef7 100644 --- a/gold/aarch64.cc +++ b/gold/aarch64.cc @@ -2044,9 +2044,9 @@ AArch64_relobj<size, big_endian>::do_relocate_sections( const unsigned char* pshdrs, Output_file* of, typename Sized_relobj_file<size, big_endian>::Views* pviews) { - // Call parent to relocate sections. - Sized_relobj_file<size, big_endian>::do_relocate_sections(symtab, layout, - pshdrs, of, pviews); + // Relocate the section data. + this->relocate_section_range(symtab, layout, pshdrs, of, pviews, + 1, this->shnum() - 1); // We do not generate stubs if doing a relocatable link. if (parameters->options().relocatable()) @@ -3865,6 +3865,8 @@ Target_aarch64<size, big_endian>::scan_reloc_section_for_stubs( if (!is_defined_in_discarded_section) { typedef Sized_relobj_file<size, big_endian> ObjType; + if (psymval->is_section_symbol()) + symval.set_is_section_symbol(); typename ObjType::Compute_final_local_value_status status = object->compute_final_local_value(r_sym, psymval, &symval, relinfo->symtab); diff --git a/gold/arm.cc b/gold/arm.cc index c47b00224cc..d9c0a2b20de 100644 --- a/gold/arm.cc +++ b/gold/arm.cc @@ -6555,9 +6555,9 @@ Arm_relobj<big_endian>::do_relocate_sections( Output_file* of, typename Sized_relobj_file<32, big_endian>::Views* pviews) { - // Call parent to relocate sections. - Sized_relobj_file<32, big_endian>::do_relocate_sections(symtab, layout, - pshdrs, of, pviews); + // Relocate the section data. + this->relocate_section_range(symtab, layout, pshdrs, of, pviews, + 1, this->shnum() - 1); // We do not generate stubs if doing a relocatable link. if (parameters->options().relocatable()) @@ -11998,6 +11998,8 @@ Target_arm<big_endian>::scan_reloc_section_for_stubs( if (!is_defined_in_discarded_section) { typedef Sized_relobj_file<32, big_endian> ObjType; + if (psymval->is_section_symbol()) + symval.set_is_section_symbol(); typename ObjType::Compute_final_local_value_status status = arm_object->compute_final_local_value(r_sym, psymval, &symval, relinfo->symtab); diff --git a/gold/debug.h b/gold/debug.h index e95408ffd9e..6fd72c2c1c8 100644 --- a/gold/debug.h +++ b/gold/debug.h @@ -39,10 +39,11 @@ const int DEBUG_FILES = 0x4; const int DEBUG_RELAXATION = 0x8; const int DEBUG_INCREMENTAL = 0x10; const int DEBUG_LOCATION = 0x20; +const int DEBUG_TARGET = 0x40; const int DEBUG_ALL = (DEBUG_TASK | DEBUG_SCRIPT | DEBUG_FILES | DEBUG_RELAXATION | DEBUG_INCREMENTAL - | DEBUG_LOCATION); + | DEBUG_LOCATION | DEBUG_TARGET); // Convert a debug string to the appropriate enum. inline int @@ -57,6 +58,7 @@ debug_string_to_enum(const char* arg) { "relaxation", DEBUG_RELAXATION }, { "incremental", DEBUG_INCREMENTAL }, { "location", DEBUG_LOCATION }, + { "target", DEBUG_TARGET }, { "all", DEBUG_ALL } }; @@ -70,11 +72,11 @@ debug_string_to_enum(const char* arg) // Print a debug message if TYPE is enabled. This is a macro so that // we only evaluate the arguments if necessary. -#define gold_debug(TYPE, FORMAT, ...) \ +#define gold_debug(TYPE, ...) \ do \ { \ if (is_debugging_enabled(TYPE)) \ - parameters->errors()->debug(FORMAT, __VA_ARGS__); \ + parameters->errors()->debug(__VA_ARGS__); \ } \ while (0) diff --git a/gold/object.h b/gold/object.h index 95f6d56e086..aa1a815f031 100644 --- a/gold/object.h +++ b/gold/object.h @@ -2562,6 +2562,13 @@ class Sized_relobj_file : public Sized_relobj<size, big_endian> const unsigned char* pshdrs, Output_file* of, Views* pviews); + // Relocate section data for a range of sections. + void + relocate_section_range(const Symbol_table* symtab, const Layout* layout, + const unsigned char* pshdrs, Output_file* of, + Views* pviews, unsigned int start_shndx, + unsigned int end_shndx); + // Adjust this local symbol value. Return false if the symbol // should be discarded from the output file. virtual bool diff --git a/gold/options.h b/gold/options.h index 4c5b2aea122..6786e9378e9 100644 --- a/gold/options.h +++ b/gold/options.h @@ -1069,6 +1069,9 @@ class General_options DEFINE_special(section_start, options::TWO_DASHES, '\0', N_("Set address of section"), N_("SECTION=ADDRESS")); + DEFINE_bool(secure_plt, options::TWO_DASHES , '\0', true, + N_("(PowerPC only) Use new-style PLT"), NULL); + DEFINE_optional_string(sort_common, options::TWO_DASHES, '\0', NULL, N_("Sort common symbols by alignment"), N_("[={ascending,descending}]")); @@ -1097,11 +1100,14 @@ 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(stub_group_multi, options::TWO_DASHES, '\0', false, + N_("(PowerPC only) Allow a group of stubs to serve multiple " + "output sections"), NULL); + DEFINE_bool(no_keep_memory, options::TWO_DASHES, '\0', false, N_("Use less memory and more disk I/O " "(included only for compatibility with GNU ld)"), NULL); diff --git a/gold/powerpc.cc b/gold/powerpc.cc index 60530bab984..2c70f9f8e4c 100644 --- a/gold/powerpc.cc +++ b/gold/powerpc.cc @@ -70,6 +70,10 @@ class Target_powerpc; struct Stub_table_owner { + Stub_table_owner() + : output_section(NULL), owner(NULL) + { } + Output_section* output_section; const Output_section::Input_section* owner; }; @@ -88,8 +92,9 @@ public: Powerpc_relobj(const std::string& name, Input_file* input_file, off_t offset, const typename elfcpp::Ehdr<size, big_endian>& ehdr) : Sized_relobj_file<size, big_endian>(name, input_file, offset, ehdr), - special_(0), has_small_toc_reloc_(false), opd_valid_(false), - opd_ent_(), access_from_map_(), has14_(), stub_table_index_(), + special_(0), relatoc_(0), toc_(0), no_toc_opt_(), + has_small_toc_reloc_(false), opd_valid_(false), opd_ent_(), + access_from_map_(), has14_(), stub_table_index_(), e_flags_(ehdr.get_e_flags()), st_other_() { this->set_abiversion(0); @@ -102,6 +107,52 @@ public: void do_read_symbols(Read_symbols_data*); + // Arrange to always relocate .toc first. + virtual void + do_relocate_sections( + const Symbol_table* symtab, const Layout* layout, + const unsigned char* pshdrs, Output_file* of, + typename Sized_relobj_file<size, big_endian>::Views* pviews); + + // The .toc section index. + unsigned int + toc_shndx() const + { + return this->toc_; + } + + // Mark .toc entry at OFF as not optimizable. + void + set_no_toc_opt(Address off) + { + if (this->no_toc_opt_.empty()) + this->no_toc_opt_.resize(this->section_size(this->toc_shndx()) + / (size / 8)); + off /= size / 8; + if (off < this->no_toc_opt_.size()) + this->no_toc_opt_[off] = true; + } + + // Mark the entire .toc as not optimizable. + void + set_no_toc_opt() + { + this->no_toc_opt_.resize(1); + this->no_toc_opt_[0] = true; + } + + // Return true if code using the .toc entry at OFF should not be edited. + bool + no_toc_opt(Address off) const + { + if (this->no_toc_opt_.empty()) + return false; + off /= size / 8; + if (off >= this->no_toc_opt_.size()) + return true; + return this->no_toc_opt_[off]; + } + // The .got2 section shndx. unsigned int got2_shndx() const @@ -184,6 +235,12 @@ public: const unsigned char* prelocs, const unsigned char* plocal_syms); + // Returns true if a code sequence loading a TOC entry can be + // converted into code calculating a TOC pointer relative offset. + bool + make_toc_relative(Target_powerpc<size, big_endian>* target, + Address* value); + // Perform the Sized_relobj_file method, then set up opd info from // .opd relocs. void @@ -275,7 +332,7 @@ public: set_stub_table(unsigned int shndx, unsigned int stub_index) { if (shndx >= this->stub_table_index_.size()) - this->stub_table_index_.resize(shndx + 1); + this->stub_table_index_.resize(shndx + 1, -1); this->stub_table_index_[shndx] = stub_index; } @@ -288,8 +345,8 @@ public: = static_cast<Target_powerpc<size, big_endian>*>( parameters->sized_target<size, big_endian>()); unsigned int indx = this->stub_table_index_[shndx]; - gold_assert(indx < target->stub_tables().size()); - return target->stub_tables()[indx]; + if (indx < target->stub_tables().size()) + return target->stub_tables()[indx]; } return NULL; } @@ -342,6 +399,14 @@ private: // For 32-bit the .got2 section shdnx, for 64-bit the .opd section shndx. unsigned int special_; + // For 64-bit the .rela.toc and .toc section shdnx. + unsigned int relatoc_; + unsigned int toc_; + + // For 64-bit, an array with one entry per 64-bit word in the .toc + // section, set if accesses using that word cannot be optimised. + std::vector<bool> no_toc_opt_; + // For 64-bit, whether this object uses small model relocs to access // the toc. bool has_small_toc_reloc_; @@ -493,6 +558,23 @@ private: elfcpp::Elf_Word e_flags_; }; +// Powerpc_copy_relocs class. Needed to peek at dynamic relocs the +// base class will emit. + +template<int sh_type, int size, bool big_endian> +class Powerpc_copy_relocs : public Copy_relocs<sh_type, size, big_endian> +{ + public: + Powerpc_copy_relocs() + : Copy_relocs<sh_type, size, big_endian>(elfcpp::R_POWERPC_COPY) + { } + + // Emit any saved relocations which turn out to be needed. This is + // called after all the relocs have been scanned. + void + emit(Output_data_reloc<sh_type, true, size, big_endian>*); +}; + template<int size, bool big_endian> class Target_powerpc : public Sized_target<size, big_endian> { @@ -509,7 +591,7 @@ class Target_powerpc : public Sized_target<size, big_endian> Target_powerpc() : Sized_target<size, big_endian>(&powerpc_info), got_(NULL), plt_(NULL), iplt_(NULL), brlt_section_(NULL), - glink_(NULL), rela_dyn_(NULL), copy_relocs_(elfcpp::R_POWERPC_COPY), + glink_(NULL), rela_dyn_(NULL), copy_relocs_(), tlsld_got_offset_(-1U), stub_tables_(), branch_lookup_table_(), branch_info_(), plt_thread_safe_(false), relax_failed_(false), relax_fail_count_(0), @@ -1310,7 +1392,7 @@ class Target_powerpc : public Sized_target<size, big_endian> // The dynamic reloc section. Reloc_section* rela_dyn_; // Relocs saved to avoid a COPY reloc. - Copy_relocs<elfcpp::SHT_RELA, size, big_endian> copy_relocs_; + Powerpc_copy_relocs<elfcpp::SHT_RELA, size, big_endian> copy_relocs_; // Offset of the GOT entry for local dynamic __tls_get_addr calls. unsigned int tlsld_got_offset_; @@ -1768,8 +1850,8 @@ Powerpc_relobj<size, big_endian>::set_abiversion(int ver) } } -// Stash away the index of .got2 or .opd in a relocatable object, if -// such a section exists. +// Stash away the index of .got2, .opd, .rela.toc, and .toc in a +// relocatable object, if such sections exists. template<int size, bool big_endian> bool @@ -1798,6 +1880,18 @@ Powerpc_relobj<size, big_endian>::do_find_special_sections( this->name().c_str(), this->abiversion()); } } + if (size == 64) + { + s = this->template find_shdr<size, big_endian>(pshdrs, ".rela.toc", + names, names_size, NULL); + if (s != NULL) + { + unsigned int ndx = (s - pshdrs) / elfcpp::Elf_sizes<size>::shdr_size; + this->relatoc_ = ndx; + typename elfcpp::Shdr<size, big_endian> shdr(s); + this->toc_ = this->adjust_shndx(shdr.get_sh_info()); + } + } return Sized_relobj_file<size, big_endian>::do_find_special_sections(sd); } @@ -1812,10 +1906,8 @@ Powerpc_relobj<size, big_endian>::scan_opd_relocs( { if (size == 64) { - typedef typename Reloc_types<elfcpp::SHT_RELA, size, big_endian>::Reloc - Reltype; - const int reloc_size - = Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size; + typedef typename elfcpp::Rela<size, big_endian> Reltype; + const int reloc_size = elfcpp::Elf_sizes<size>::rela_size; const int sym_size = elfcpp::Elf_sizes<size>::sym_size; Address expected_off = 0; bool regular = true; @@ -1880,6 +1972,60 @@ Powerpc_relobj<size, big_endian>::scan_opd_relocs( } } +// Returns true if a code sequence loading the TOC entry at VALUE +// relative to the TOC pointer can be converted into code calculating +// a TOC pointer relative offset. +// If so, the TOC pointer relative offset is stored to VALUE. + +template<int size, bool big_endian> +bool +Powerpc_relobj<size, big_endian>::make_toc_relative( + Target_powerpc<size, big_endian>* target, + Address* value) +{ + if (size != 64) + return false; + + // With -mcmodel=medium code it is quite possible to have + // toc-relative relocs referring to objects outside the TOC. + // Don't try to look at a non-existent TOC. + if (this->toc_shndx() == 0) + return false; + + // Convert VALUE back to an address by adding got_base (see below), + // then to an offset in the TOC by subtracting the TOC output + // section address and the TOC output offset. Since this TOC output + // section and the got output section are one and the same, we can + // omit adding and subtracting the output section address. + Address off = (*value + this->toc_base_offset() + - this->output_section_offset(this->toc_shndx())); + // Is this offset in the TOC? -mcmodel=medium code may be using + // TOC relative access to variables outside the TOC. Those of + // course can't be optimized. We also don't try to optimize code + // that is using a different object's TOC. + if (off >= this->section_size(this->toc_shndx())) + return false; + + if (this->no_toc_opt(off)) + return false; + + section_size_type vlen; + unsigned char* view = this->get_output_view(this->toc_shndx(), &vlen); + Address addr = elfcpp::Swap<size, big_endian>::readval(view + off); + // The TOC pointer + Address got_base = (target->got_section()->output_section()->address() + + this->toc_base_offset()); + addr -= got_base; + if (addr + (uint64_t) 0x80008000 >= (uint64_t) 1 << 32) + return false; + + *value = addr; + return true; +} + +// Perform the Sized_relobj_file method, then set up opd info from +// .opd relocs. + template<int size, bool big_endian> void Powerpc_relobj<size, big_endian>::do_read_relocs(Read_relocs_data* rd) @@ -2075,6 +2221,31 @@ Powerpc_dynobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd) } } +// Relocate sections. + +template<int size, bool big_endian> +void +Powerpc_relobj<size, big_endian>::do_relocate_sections( + const Symbol_table* symtab, const Layout* layout, + const unsigned char* pshdrs, Output_file* of, + typename Sized_relobj_file<size, big_endian>::Views* pviews) +{ + unsigned int start = 1; + if (size == 64 + && this->relatoc_ != 0 + && !parameters->options().relocatable()) + { + // Relocate .toc first. + this->relocate_section_range(symtab, layout, pshdrs, of, pviews, + this->relatoc_, this->relatoc_); + this->relocate_section_range(symtab, layout, pshdrs, of, pviews, + 1, this->relatoc_ - 1); + start = this->relatoc_ + 1; + } + this->relocate_section_range(symtab, layout, pshdrs, of, pviews, + start, this->shnum() - 1); +} + // Set up some symbols. template<int size, bool big_endian> @@ -2435,14 +2606,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)), - stub14_group_size_(abs(size) >> 10), - stubs_always_before_branch_(size < 0), - suppress_size_errors_(no_size_errors), - group_end_addr_(0), owner_(NULL), output_section_(NULL) + Stub_control(int32_t size, bool no_size_errors, bool multi_os) + : stub_group_size_(abs(size)), stubs_always_after_branch_(size < 0), + suppress_size_errors_(no_size_errors), multi_os_(multi_os), + state_(NO_GROUP), group_size_(0), group_start_addr_(0), + owner_(NULL), output_section_(NULL) { } @@ -2472,31 +2642,40 @@ class Stub_control private: typedef enum { + // Initial state. NO_GROUP, + // Adding group sections before the stubs. FINDING_STUB_SECTION, + // Adding group sections after the stubs. HAS_STUB_SECTION } State; - State state_; uint32_t stub_group_size_; - uint32_t stub14_group_size_; - bool stubs_always_before_branch_; + bool stubs_always_after_branch_; bool suppress_size_errors_; - uint64_t group_end_addr_; + // True if a stub group can serve multiple output sections. + bool multi_os_; + State state_; + // 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_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_; Output_section* output_section_; }; // Return true iff input section can be handled by current stub -// 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, 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 +2689,88 @@ Stub_control::can_add_to_stub_group(Output_section* o, start_addr += i->relobj()->output_section_offset(i->shndx()); this_size = i->data_size(); } + uint64_t end_addr = start_addr + this_size; - bool toobig = this_size > group_size; + uint32_t group_size = this->stub_group_size_; + if (has14) + this->group_size_ = group_size = group_size >> 10; - 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; - } + gold_debug(DEBUG_TARGET, "maybe add%s %s:%s size=%#llx total=%#llx", + has14 ? " 14bit" : "", + i->relobj()->name().c_str(), + i->relobj()->section_name(i->shndx()).c_str(), + (long long) this_size, + (this->state_ == NO_GROUP + ? this_size + : (long long) end_addr - this->group_start_addr_)); 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_end_addr_ = end_addr; + this->group_size_ = group_size; + this->group_start_addr_ = start_addr; + return true; } - else if (this->group_end_addr_ - start_addr < group_size) + else if (!this->multi_os_ && this->output_section_ != o) ; - // Adding this section would make the group larger than GROUP_SIZE. - else if (this->state_ == FINDING_STUB_SECTION - && !this->stubs_always_before_branch_ - && !toobig) + else if (this->state_ == HAS_STUB_SECTION) { - // 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 after the stubs, to the + // group? + if (end_addr - this->group_start_addr_ <= this->group_size_) + return true; } - else + else if (this->state_ == FINDING_STUB_SECTION) { - this->state_ = NO_GROUP; - return false; + 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; + } + // 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_) + { + gold_debug(DEBUG_TARGET, "adding after stubs"); + this->state_ = HAS_STUB_SECTION; + this->group_start_addr_ = start_addr; + return true; + } } - return true; + else + gold_unreachable(); + + gold_debug(DEBUG_TARGET, + !this->multi_os_ && this->output_section_ != o + ? "nope, new output section\n" + : "nope, didn't fit\n"); + + // 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->group_size_ = group_size; + this->group_start_addr_ = start_addr; + return false; } // Look over all the input sections, deciding where to place stubs. @@ -2560,7 +2781,8 @@ Target_powerpc<size, big_endian>::group_sections(Layout* layout, const Task*, bool no_size_errors) { - Stub_control stub_control(this->stub_group_size_, no_size_errors); + Stub_control stub_control(this->stub_group_size_, no_size_errors, + parameters->options().stub_group_multi()); // Group input sections and insert stub table Stub_table_owner* table_owner = NULL; @@ -2568,14 +2790,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() @@ -2602,26 +2824,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(); @@ -2673,6 +2877,8 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub( Target_powerpc<size, big_endian>* target = static_cast<Target_powerpc<size, big_endian>*>( parameters->sized_target<size, big_endian>()); + bool ok = true; + if (gsym != NULL ? gsym->use_plt_offset(Scan::get_reference_flags(this->r_type_, target)) : this->object_->local_has_plt_offset(this->r_sym_)) @@ -2698,13 +2904,13 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub( from += (this->object_->output_section(this->shndx_)->address() + this->offset_); if (gsym != NULL) - return stub_table->add_plt_call_entry(from, - this->object_, gsym, - this->r_type_, this->addend_); + ok = stub_table->add_plt_call_entry(from, + this->object_, gsym, + this->r_type_, this->addend_); else - return stub_table->add_plt_call_entry(from, - this->object_, this->r_sym_, - this->r_type_, this->addend_); + ok = stub_table->add_plt_call_entry(from, + this->object_, this->r_sym_, + this->r_type_, this->addend_); } } else @@ -2752,6 +2958,8 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub( const Symbol_value<size>* psymval = this->object_->local_symbol(this->r_sym_); Symbol_value<size> symval; + if (psymval->is_section_symbol()) + symval.set_is_section_symbol(); typedef Sized_relobj_file<size, big_endian> ObjType; typename ObjType::Compute_final_local_value_status status = this->object_->compute_final_local_value(this->r_sym_, psymval, @@ -2789,12 +2997,22 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub( && gsym != NULL && gsym->source() == Symbol::IN_OUTPUT_DATA && gsym->output_data() == target->savres_section()); - return stub_table->add_long_branch_entry(this->object_, - this->r_type_, - from, to, save_res); + ok = stub_table->add_long_branch_entry(this->object_, + this->r_type_, + from, to, save_res); } } - return true; + if (!ok) + gold_debug(DEBUG_TARGET, + "branch at %s:%s+%#lx\n" + "can't reach stub attached to %s:%s", + this->object_->name().c_str(), + this->object_->section_name(this->shndx_).c_str(), + (unsigned long) this->offset_, + stub_table->relobj()->name().c_str(), + stub_table->relobj()->section_name(stub_table->shndx()).c_str()); + + return ok; } // Relaxation hook. This is where we do stub generation. @@ -2887,7 +3105,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); } @@ -2982,7 +3200,13 @@ Target_powerpc<size, big_endian>::do_relax(int pass, Stub_table<size, big_endian>* stub_table = static_cast<Stub_table<size, big_endian>*>( i->relaxed_input_section()); - off += stub_table->set_address_and_size(os, off); + Address stub_table_size = stub_table->set_address_and_size(os, off); + off += stub_table_size; + // After a few iterations, set current stub table size + // as min size threshold, so later stub tables can only + // grow in size. + if (pass >= 4) + stub_table->set_min_size_threshold(stub_table_size); } else off += i->data_size(); @@ -3366,6 +3590,9 @@ Target_powerpc<size, big_endian>::make_plt_section(Symbol_table* symtab, ? ORDER_SMALL_DATA : ORDER_SMALL_BSS), false); + + Output_section* rela_plt_os = plt_rel->output_section(); + rela_plt_os->set_info_section(this->plt_->output_section()); } } @@ -3381,11 +3608,13 @@ Target_powerpc<size, big_endian>::make_iplt_section(Symbol_table* symtab, this->make_plt_section(symtab, layout); Reloc_section* iplt_rel = new Reloc_section(false); - this->rela_dyn_->output_section()->add_output_section_data(iplt_rel); + if (this->rela_dyn_->output_section()) + this->rela_dyn_->output_section()->add_output_section_data(iplt_rel); this->iplt_ = new Output_data_plt_powerpc<size, big_endian>(this, iplt_rel, "** IPLT"); - this->plt_->output_section()->add_output_section_data(this->iplt_); + if (this->plt_->output_section()) + this->plt_->output_section()->add_output_section_data(this->iplt_); } } @@ -3436,8 +3665,7 @@ class Output_data_brlt_powerpc : public Output_section_data_build os->set_section_offsets_need_adjustment(); if (this->rel_ != NULL) { - unsigned int reloc_size - = Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size; + const unsigned int reloc_size = elfcpp::Elf_sizes<size>::rela_size; this->rel_->reset_address_and_file_offset(); this->rel_->set_current_data_size(num_branches * reloc_size); this->rel_->finalize_data_size(); @@ -3482,14 +3710,16 @@ Target_powerpc<size, big_endian>::make_brlt_section(Layout* layout) { // When PIC we can't fill in .branch_lt (like .plt it can be // a bss style section) but must initialise at runtime via - // dynamic relocats. + // dynamic relocations. this->rela_dyn_section(layout); brlt_rel = new Reloc_section(false); - this->rela_dyn_->output_section()->add_output_section_data(brlt_rel); + if (this->rela_dyn_->output_section()) + this->rela_dyn_->output_section() + ->add_output_section_data(brlt_rel); } this->brlt_section_ = new Output_data_brlt_powerpc<size, big_endian>(this, brlt_rel); - if (this->plt_ && is_pic) + if (this->plt_ && is_pic && this->plt_->output_section()) this->plt_->output_section() ->add_output_section_data(this->brlt_section_); else @@ -3634,8 +3864,8 @@ class Stub_table : public Output_relaxed_input_section targ_(targ), plt_call_stubs_(), long_branch_stubs_(), 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), - need_save_res_(false) + branch_size_(0), last_branch_size_(0), min_size_threshold_(0), + eh_frame_added_(false), need_save_res_(false) { this->set_output_section(output_section); @@ -3726,6 +3956,11 @@ class Stub_table : public Output_relaxed_input_section off = align_address(off, this->stub_align()); // Include original section size and alignment padding in size my_size += off - start_off; + // Ensure new size is always larger than min size + // threshold. Alignment requirement is included in "my_size", so + // increase "my_size" does not invalidate alignment. + if (my_size < this->min_size_threshold_) + my_size = this->min_size_threshold_; this->reset_address_and_file_offset(); this->set_current_data_size(my_size); this->set_address_and_file_offset(os->address() + start_off, @@ -3751,6 +3986,9 @@ class Stub_table : public Output_relaxed_input_section plt_size() const { return this->plt_size_; } + void set_min_size_threshold(Address min_size) + { this->min_size_threshold_ = min_size; } + bool size_update() { @@ -4015,6 +4253,13 @@ class Stub_table : public Output_relaxed_input_section section_size_type orig_data_size_; // size of stubs section_size_type plt_size_, last_plt_size_, branch_size_, last_branch_size_; + // Some rare cases cause (PR/20529) fluctuation in stub table + // size, which leads to an endless relax loop. This is to be fixed + // by, after the first few iterations, allowing only increase of + // stub table size. This variable sets the minimal possible size of + // a stub table, it is zero for the first few iterations, then + // increases monotonically. + Address min_size_threshold_; // Whether .eh_frame info has been created for this stub section. bool eh_frame_added_; // Set if this stub group needs a copy of out-of-line register @@ -5545,6 +5790,45 @@ Target_powerpc<size, big_endian>::Scan::reloc_needs_plt_for_ifunc( return false; } +// Return TRUE iff INSN is one we expect on a _LO variety toc/got +// reloc. + +static bool +ok_lo_toc_insn(uint32_t insn, unsigned int r_type) +{ + return ((insn & (0x3f << 26)) == 12u << 26 /* addic */ + || (insn & (0x3f << 26)) == 14u << 26 /* addi */ + || (insn & (0x3f << 26)) == 32u << 26 /* lwz */ + || (insn & (0x3f << 26)) == 34u << 26 /* lbz */ + || (insn & (0x3f << 26)) == 36u << 26 /* stw */ + || (insn & (0x3f << 26)) == 38u << 26 /* stb */ + || (insn & (0x3f << 26)) == 40u << 26 /* lhz */ + || (insn & (0x3f << 26)) == 42u << 26 /* lha */ + || (insn & (0x3f << 26)) == 44u << 26 /* sth */ + || (insn & (0x3f << 26)) == 46u << 26 /* lmw */ + || (insn & (0x3f << 26)) == 47u << 26 /* stmw */ + || (insn & (0x3f << 26)) == 48u << 26 /* lfs */ + || (insn & (0x3f << 26)) == 50u << 26 /* lfd */ + || (insn & (0x3f << 26)) == 52u << 26 /* stfs */ + || (insn & (0x3f << 26)) == 54u << 26 /* stfd */ + || (insn & (0x3f << 26)) == 56u << 26 /* lq,lfq */ + || ((insn & (0x3f << 26)) == 57u << 26 /* lxsd,lxssp,lfdp */ + /* Exclude lfqu by testing reloc. If relocs are ever + defined for the reduced D field in psq_lu then those + will need testing too. */ + && r_type != elfcpp::R_PPC64_TOC16_LO + && r_type != elfcpp::R_POWERPC_GOT16_LO) + || ((insn & (0x3f << 26)) == 58u << 26 /* ld,lwa */ + && (insn & 1) == 0) + || (insn & (0x3f << 26)) == 60u << 26 /* stfq */ + || ((insn & (0x3f << 26)) == 61u << 26 /* lxv,stx{v,sd,ssp},stfdp */ + /* Exclude stfqu. psq_stu as above for psq_lu. */ + && r_type != elfcpp::R_PPC64_TOC16_LO + && r_type != elfcpp::R_POWERPC_GOT16_LO) + || ((insn & (0x3f << 26)) == 62u << 26 /* std,stq */ + && (insn & 1) == 0)); +} + // Scan a relocation for a local symbol. template<int size, bool big_endian> @@ -5708,9 +5992,11 @@ Target_powerpc<size, big_endian>::Scan::local( case elfcpp::R_POWERPC_REL14_BRTAKEN: case elfcpp::R_POWERPC_REL14_BRNTAKEN: if (!is_ifunc) - target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(), - r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()), - reloc.get_r_addend()); + { + unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info()); + target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(), + r_type, r_sym, reloc.get_r_addend()); + } break; case elfcpp::R_PPC64_REL64: @@ -5898,6 +6184,150 @@ Target_powerpc<size, big_endian>::Scan::local( break; } + if (size == 64 + && parameters->options().toc_optimize()) + { + if (data_shndx == ppc_object->toc_shndx()) + { + bool ok = true; + if (r_type != elfcpp::R_PPC64_ADDR64 + || (is_ifunc && target->abiversion() < 2)) + ok = false; + else if (parameters->options().output_is_position_independent()) + { + if (is_ifunc) + ok = false; + else + { + unsigned int shndx = lsym.get_st_shndx(); + if (shndx >= elfcpp::SHN_LORESERVE + && shndx != elfcpp::SHN_XINDEX) + ok = false; + } + } + if (!ok) + ppc_object->set_no_toc_opt(reloc.get_r_offset()); + } + + enum {no_check, check_lo, check_ha} insn_check; + switch (r_type) + { + default: + insn_check = no_check; + break; + + case elfcpp::R_POWERPC_GOT_TLSLD16_HA: + case elfcpp::R_POWERPC_GOT_TLSGD16_HA: + case elfcpp::R_POWERPC_GOT_TPREL16_HA: + case elfcpp::R_POWERPC_GOT_DTPREL16_HA: + case elfcpp::R_POWERPC_GOT16_HA: + case elfcpp::R_PPC64_TOC16_HA: + insn_check = check_ha; + break; + + case elfcpp::R_POWERPC_GOT_TLSLD16_LO: + case elfcpp::R_POWERPC_GOT_TLSGD16_LO: + case elfcpp::R_POWERPC_GOT_TPREL16_LO: + case elfcpp::R_POWERPC_GOT_DTPREL16_LO: + case elfcpp::R_POWERPC_GOT16_LO: + case elfcpp::R_PPC64_GOT16_LO_DS: + case elfcpp::R_PPC64_TOC16_LO: + case elfcpp::R_PPC64_TOC16_LO_DS: + insn_check = check_lo; + break; + } + + section_size_type slen; + const unsigned char* view = NULL; + if (insn_check != no_check) + { + view = ppc_object->section_contents(data_shndx, &slen, false); + section_size_type off = + convert_to_section_size_type(reloc.get_r_offset()) & -4; + if (off < slen) + { + uint32_t insn = elfcpp::Swap<32, big_endian>::readval(view + off); + if (insn_check == check_lo + ? !ok_lo_toc_insn(insn, r_type) + : ((insn & ((0x3f << 26) | 0x1f << 16)) + != ((15u << 26) | (2 << 16)) /* addis rt,2,imm */)) + { + ppc_object->set_no_toc_opt(); + gold_warning(_("%s: toc optimization is not supported " + "for %#08x instruction"), + ppc_object->name().c_str(), insn); + } + } + } + + switch (r_type) + { + default: + break; + case elfcpp::R_PPC64_TOC16: + case elfcpp::R_PPC64_TOC16_LO: + case elfcpp::R_PPC64_TOC16_HI: + case elfcpp::R_PPC64_TOC16_HA: + case elfcpp::R_PPC64_TOC16_DS: + case elfcpp::R_PPC64_TOC16_LO_DS: + unsigned int shndx = lsym.get_st_shndx(); + unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info()); + bool is_ordinary; + shndx = ppc_object->adjust_sym_shndx(r_sym, shndx, &is_ordinary); + if (is_ordinary && shndx == ppc_object->toc_shndx()) + { + Address dst_off = lsym.get_st_value() + reloc.get_r_offset(); + if (dst_off < ppc_object->section_size(shndx)) + { + bool ok = false; + if (r_type == elfcpp::R_PPC64_TOC16_HA) + ok = true; + else if (r_type == elfcpp::R_PPC64_TOC16_LO_DS) + { + // Need to check that the insn is a ld + if (!view) + view = ppc_object->section_contents(data_shndx, + &slen, + false); + section_size_type off = + (convert_to_section_size_type(reloc.get_r_offset()) + + (big_endian ? -2 : 3)); + if (off < slen + && (view[off] & (0x3f << 2)) == 58u << 2) + ok = true; + } + if (!ok) + ppc_object->set_no_toc_opt(dst_off); + } + } + break; + } + } + + if (size == 32) + { + switch (r_type) + { + case elfcpp::R_POWERPC_REL32: + if (ppc_object->got2_shndx() != 0 + && parameters->options().output_is_position_independent()) + { + unsigned int shndx = lsym.get_st_shndx(); + unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info()); + bool is_ordinary; + shndx = ppc_object->adjust_sym_shndx(r_sym, shndx, &is_ordinary); + if (is_ordinary && shndx == ppc_object->got2_shndx() + && (ppc_object->section_flags(data_shndx) + & elfcpp::SHF_EXECINSTR) != 0) + gold_error(_("%s: unsupported -mbss-plt code"), + ppc_object->name().c_str()); + } + break; + default: + break; + } + } + switch (r_type) { case elfcpp::R_POWERPC_GOT_TLSLD16: @@ -5971,9 +6401,9 @@ Target_powerpc<size, big_endian>::Scan::global( bool pushed_ifunc = false; if (is_ifunc && this->reloc_needs_plt_for_ifunc(target, object, r_type, true)) { + unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info()); target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(), - r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()), - reloc.get_r_addend()); + r_type, r_sym, reloc.get_r_addend()); target->make_plt_entry(symtab, layout, gsym); pushed_ifunc = true; } @@ -6063,9 +6493,9 @@ Target_powerpc<size, big_endian>::Scan::global( } if (!is_ifunc || (!pushed_ifunc && need_ifunc_plt)) { + unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info()); target->push_branch(ppc_object, data_shndx, - reloc.get_r_offset(), r_type, - elfcpp::elf_r_sym<size>(reloc.get_r_info()), + reloc.get_r_offset(), r_type, r_sym, reloc.get_r_addend()); target->make_plt_entry(symtab, layout, gsym); } @@ -6111,6 +6541,11 @@ Target_powerpc<size, big_endian>::Scan::global( object, data_shndx, reloc.get_r_offset(), reloc.get_r_addend()); + + if (size == 64 + && parameters->options().toc_optimize() + && data_shndx == ppc_object->toc_shndx()) + ppc_object->set_no_toc_opt(reloc.get_r_offset()); } } } @@ -6120,10 +6555,9 @@ Target_powerpc<size, big_endian>::Scan::global( case elfcpp::R_POWERPC_REL24: if (!is_ifunc) { + unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info()); target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(), - r_type, - elfcpp::elf_r_sym<size>(reloc.get_r_info()), - reloc.get_r_addend()); + r_type, r_sym, reloc.get_r_addend()); if (gsym->needs_plt_entry() || (!gsym->final_value_is_known() && (gsym->is_undefined() @@ -6161,9 +6595,11 @@ Target_powerpc<size, big_endian>::Scan::global( case elfcpp::R_POWERPC_REL14_BRTAKEN: case elfcpp::R_POWERPC_REL14_BRNTAKEN: if (!is_ifunc) - target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(), - r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()), - reloc.get_r_addend()); + { + unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info()); + target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(), + r_type, r_sym, reloc.get_r_addend()); + } break; case elfcpp::R_POWERPC_REL16: @@ -6398,6 +6834,136 @@ Target_powerpc<size, big_endian>::Scan::global( break; } + if (size == 64 + && parameters->options().toc_optimize()) + { + if (data_shndx == ppc_object->toc_shndx()) + { + bool ok = true; + if (r_type != elfcpp::R_PPC64_ADDR64 + || (is_ifunc && target->abiversion() < 2)) + ok = false; + else if (parameters->options().output_is_position_independent() + && (is_ifunc || gsym->is_absolute() || gsym->is_undefined())) + ok = false; + if (!ok) + ppc_object->set_no_toc_opt(reloc.get_r_offset()); + } + + enum {no_check, check_lo, check_ha} insn_check; + switch (r_type) + { + default: + insn_check = no_check; + break; + + case elfcpp::R_POWERPC_GOT_TLSLD16_HA: + case elfcpp::R_POWERPC_GOT_TLSGD16_HA: + case elfcpp::R_POWERPC_GOT_TPREL16_HA: + case elfcpp::R_POWERPC_GOT_DTPREL16_HA: + case elfcpp::R_POWERPC_GOT16_HA: + case elfcpp::R_PPC64_TOC16_HA: + insn_check = check_ha; + break; + + case elfcpp::R_POWERPC_GOT_TLSLD16_LO: + case elfcpp::R_POWERPC_GOT_TLSGD16_LO: + case elfcpp::R_POWERPC_GOT_TPREL16_LO: + case elfcpp::R_POWERPC_GOT_DTPREL16_LO: + case elfcpp::R_POWERPC_GOT16_LO: + case elfcpp::R_PPC64_GOT16_LO_DS: + case elfcpp::R_PPC64_TOC16_LO: + case elfcpp::R_PPC64_TOC16_LO_DS: + insn_check = check_lo; + break; + } + + section_size_type slen; + const unsigned char* view = NULL; + if (insn_check != no_check) + { + view = ppc_object->section_contents(data_shndx, &slen, false); + section_size_type off = + convert_to_section_size_type(reloc.get_r_offset()) & -4; + if (off < slen) + { + uint32_t insn = elfcpp::Swap<32, big_endian>::readval(view + off); + if (insn_check == check_lo + ? !ok_lo_toc_insn(insn, r_type) + : ((insn & ((0x3f << 26) | 0x1f << 16)) + != ((15u << 26) | (2 << 16)) /* addis rt,2,imm */)) + { + ppc_object->set_no_toc_opt(); + gold_warning(_("%s: toc optimization is not supported " + "for %#08x instruction"), + ppc_object->name().c_str(), insn); + } + } + } + + switch (r_type) + { + default: + break; + case elfcpp::R_PPC64_TOC16: + case elfcpp::R_PPC64_TOC16_LO: + case elfcpp::R_PPC64_TOC16_HI: + case elfcpp::R_PPC64_TOC16_HA: + case elfcpp::R_PPC64_TOC16_DS: + case elfcpp::R_PPC64_TOC16_LO_DS: + if (gsym->source() == Symbol::FROM_OBJECT + && !gsym->object()->is_dynamic()) + { + Powerpc_relobj<size, big_endian>* sym_object + = static_cast<Powerpc_relobj<size, big_endian>*>(gsym->object()); + bool is_ordinary; + unsigned int shndx = gsym->shndx(&is_ordinary); + if (shndx == sym_object->toc_shndx()) + { + Sized_symbol<size>* sym = symtab->get_sized_symbol<size>(gsym); + Address dst_off = sym->value() + reloc.get_r_offset(); + if (dst_off < sym_object->section_size(shndx)) + { + bool ok = false; + if (r_type == elfcpp::R_PPC64_TOC16_HA) + ok = true; + else if (r_type == elfcpp::R_PPC64_TOC16_LO_DS) + { + // Need to check that the insn is a ld + if (!view) + view = ppc_object->section_contents(data_shndx, + &slen, + false); + section_size_type off = + (convert_to_section_size_type(reloc.get_r_offset()) + + (big_endian ? -2 : 3)); + if (off < slen + && (view[off] & (0x3f << 2)) == (58u << 2)) + ok = true; + } + if (!ok) + sym_object->set_no_toc_opt(dst_off); + } + } + } + break; + } + } + + if (size == 32) + { + switch (r_type) + { + case elfcpp::R_PPC_LOCAL24PC: + if (strcmp(gsym->name(), "_GLOBAL_OFFSET_TABLE_") == 0) + gold_error(_("%s: unsupported -mbss-plt code"), + ppc_object->name().c_str()); + break; + default: + break; + } + } + switch (r_type) { case elfcpp::R_POWERPC_GOT_TLSLD16: @@ -6918,31 +7484,40 @@ Target_powerpc<size, big_endian>::do_finalize_sections( this->copy_relocs_.emit(this->rela_dyn_section(layout)); } -// Return TRUE iff INSN is one we expect on a _LO variety toc/got -// reloc. +// Emit any saved relocs, and mark toc entries using any of these +// relocs as not optimizable. -static bool -ok_lo_toc_insn(uint32_t insn) +template<int sh_type, int size, bool big_endian> +void +Powerpc_copy_relocs<sh_type, size, big_endian>::emit( + Output_data_reloc<sh_type, true, size, big_endian>* reloc_section) { - return ((insn & (0x3f << 26)) == 14u << 26 /* addi */ - || (insn & (0x3f << 26)) == 32u << 26 /* lwz */ - || (insn & (0x3f << 26)) == 34u << 26 /* lbz */ - || (insn & (0x3f << 26)) == 36u << 26 /* stw */ - || (insn & (0x3f << 26)) == 38u << 26 /* stb */ - || (insn & (0x3f << 26)) == 40u << 26 /* lhz */ - || (insn & (0x3f << 26)) == 42u << 26 /* lha */ - || (insn & (0x3f << 26)) == 44u << 26 /* sth */ - || (insn & (0x3f << 26)) == 46u << 26 /* lmw */ - || (insn & (0x3f << 26)) == 47u << 26 /* stmw */ - || (insn & (0x3f << 26)) == 48u << 26 /* lfs */ - || (insn & (0x3f << 26)) == 50u << 26 /* lfd */ - || (insn & (0x3f << 26)) == 52u << 26 /* stfs */ - || (insn & (0x3f << 26)) == 54u << 26 /* stfd */ - || ((insn & (0x3f << 26)) == 58u << 26 /* lwa,ld,lmd */ - && (insn & 3) != 1) - || ((insn & (0x3f << 26)) == 62u << 26 /* std, stmd */ - && ((insn & 3) == 0 || (insn & 3) == 3)) - || (insn & (0x3f << 26)) == 12u << 26 /* addic */); + if (size == 64 + && parameters->options().toc_optimize()) + { + for (typename Copy_relocs<sh_type, size, big_endian>:: + Copy_reloc_entries::iterator p = this->entries_.begin(); + p != this->entries_.end(); + ++p) + { + typename Copy_relocs<sh_type, size, big_endian>::Copy_reloc_entry& + entry = *p; + + // If the symbol is no longer defined in a dynamic object, + // then we emitted a COPY relocation. If it is still + // dynamic then we'll need dynamic relocations and thus + // can't optimize toc entries. + if (entry.sym_->is_from_dynobj()) + { + Powerpc_relobj<size, big_endian>* ppc_object + = static_cast<Powerpc_relobj<size, big_endian>*>(entry.relobj_); + if (entry.shndx_ == ppc_object->toc_shndx()) + ppc_object->set_no_toc_opt(entry.address_); + } + } + } + + Copy_relocs<sh_type, size, big_endian>::emit(reloc_section); } // Return the value to use for a branch relocation. @@ -6964,7 +7539,8 @@ Target_powerpc<size, big_endian>::symval_for_branch( // descriptor, use the function descriptor code entry address Powerpc_relobj<size, big_endian>* symobj = object; if (gsym != NULL - && gsym->source() != Symbol::FROM_OBJECT) + && (gsym->source() != Symbol::FROM_OBJECT + || gsym->object()->is_dynamic())) return true; if (gsym != NULL) symobj = static_cast<Powerpc_relobj<size, big_endian>*>(gsym->object()); @@ -7035,8 +7611,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate( typedef Powerpc_relocate_functions<size, big_endian> Reloc; typedef typename elfcpp::Swap<32, big_endian>::Valtype Insn; - typedef typename Reloc_types<elfcpp::SHT_RELA, - size, big_endian>::Reloc Reltype; + typedef typename elfcpp::Rela<size, big_endian> Reltype; // Offset from start of insn to d-field reloc. const int d_offset = big_endian ? 2 : 0; @@ -7110,7 +7685,6 @@ Target_powerpc<size, big_endian>::Relocate::relocate( } else { - unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info()); gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_STANDARD)); value = object->local_got_offset(r_sym, GOT_TYPE_STANDARD); } @@ -7212,7 +7786,6 @@ Target_powerpc<size, big_endian>::Relocate::relocate( } else { - unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info()); gold_assert(object->local_has_got_offset(r_sym, got_type)); value = object->local_got_offset(r_sym, got_type); } @@ -7312,7 +7885,6 @@ Target_powerpc<size, big_endian>::Relocate::relocate( } else { - unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info()); gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_DTPREL)); value = object->local_got_offset(r_sym, GOT_TYPE_DTPREL); } @@ -7335,7 +7907,6 @@ Target_powerpc<size, big_endian>::Relocate::relocate( } else { - unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info()); gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_TPREL)); value = object->local_got_offset(r_sym, GOT_TYPE_TPREL); } @@ -7607,14 +8178,24 @@ Target_powerpc<size, big_endian>::Relocate::relocate( if (size == 64) { - // Multi-instruction sequences that access the TOC can be - // optimized, eg. addis ra,r2,0; addi rb,ra,x; - // to nop; addi rb,r2,x; switch (r_type) { default: break; + // Multi-instruction sequences that access the GOT/TOC can + // be optimized, eg. + // addis ra,r2,x@got@ha; ld rb,x@got@l(ra); + // to addis ra,r2,x@toc@ha; addi rb,ra,x@toc@l; + // and + // addis ra,r2,0; addi rb,ra,x@toc@l; + // to nop; addi rb,r2,x@toc; + // FIXME: the @got sequence shown above is not yet + // optimized. Note that gcc as of 2017-01-07 doesn't use + // the ELF @got relocs except for TLS, instead using the + // PowerOpen variant of a compiler managed GOT (called TOC). + // The PowerOpen TOC sequence equivalent to the first + // example is optimized. case elfcpp::R_POWERPC_GOT_TLSLD16_HA: case elfcpp::R_POWERPC_GOT_TLSGD16_HA: case elfcpp::R_POWERPC_GOT_TPREL16_HA: @@ -7625,12 +8206,15 @@ Target_powerpc<size, big_endian>::Relocate::relocate( { Insn* iview = reinterpret_cast<Insn*>(view - d_offset); Insn insn = elfcpp::Swap<32, big_endian>::readval(iview); - if ((insn & ((0x3f << 26) | 0x1f << 16)) - != ((15u << 26) | (2 << 16)) /* addis rt,2,imm */) - gold_error_at_location(relinfo, relnum, rela.get_r_offset(), - _("toc optimization is not supported " - "for %#08x instruction"), insn); - else if (value + 0x8000 < 0x10000) + if (r_type == elfcpp::R_PPC64_TOC16_HA + && object->make_toc_relative(target, &value)) + { + gold_assert((insn & ((0x3f << 26) | 0x1f << 16)) + == ((15u << 26) | (2 << 16))); + } + if (((insn & ((0x3f << 26) | 0x1f << 16)) + == ((15u << 26) | (2 << 16)) /* addis rt,2,imm */) + && value + 0x8000 < 0x10000) { elfcpp::Swap<32, big_endian>::writeval(iview, nop); return true; @@ -7650,11 +8234,17 @@ Target_powerpc<size, big_endian>::Relocate::relocate( { Insn* iview = reinterpret_cast<Insn*>(view - d_offset); Insn insn = elfcpp::Swap<32, big_endian>::readval(iview); - if (!ok_lo_toc_insn(insn)) - gold_error_at_location(relinfo, relnum, rela.get_r_offset(), - _("toc optimization is not supported " - "for %#08x instruction"), insn); - else if (value + 0x8000 < 0x10000) + bool changed = false; + if (r_type == elfcpp::R_PPC64_TOC16_LO_DS + && object->make_toc_relative(target, &value)) + { + gold_assert ((insn & (0x3f << 26)) == 58u << 26 /* ld */); + insn ^= (14u << 26) ^ (58u << 26); + r_type = elfcpp::R_PPC64_TOC16_LO; + changed = true; + } + if (ok_lo_toc_insn(insn, r_type) + && value + 0x8000 < 0x10000) { if ((insn & (0x3f << 26)) == 12u << 26 /* addic */) { @@ -7667,8 +8257,10 @@ Target_powerpc<size, big_endian>::Relocate::relocate( insn &= ~(0x1f << 16); insn |= 2 << 16; } - elfcpp::Swap<32, big_endian>::writeval(iview, insn); + changed = true; } + if (changed) + elfcpp::Swap<32, big_endian>::writeval(iview, insn); } break; @@ -7731,8 +8323,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate( && gsym != NULL && strcmp(gsym->name(), ".TOC.") == 0) { - const int reloc_size - = Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size; + const int reloc_size = elfcpp::Elf_sizes<size>::rela_size; Reltype prev_rela(preloc - reloc_size); if ((prev_rela.get_r_info() == elfcpp::elf_r_info<size>(r_sym, @@ -8205,10 +8796,8 @@ template<int size, bool big_endian> class Powerpc_scan_relocatable_reloc { public: - typedef typename Reloc_types<elfcpp::SHT_RELA, size, big_endian>::Reloc - Reltype; - static const int reloc_size = - Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size; + typedef typename elfcpp::Rela<size, big_endian> Reltype; + static const int reloc_size = elfcpp::Elf_sizes<size>::rela_size; static const int sh_type = elfcpp::SHT_RELA; // Return the symbol referred to by the relocation. @@ -8348,12 +8937,9 @@ Target_powerpc<size, big_endian>::relocate_relocs( { gold_assert(sh_type == elfcpp::SHT_RELA); - typedef typename Reloc_types<elfcpp::SHT_RELA, size, big_endian>::Reloc - Reltype; - typedef typename Reloc_types<elfcpp::SHT_RELA, size, big_endian>::Reloc_write - Reltype_write; - const int reloc_size - = Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size; + typedef typename elfcpp::Rela<size, big_endian> Reltype; + typedef typename elfcpp::Rela_write<size, big_endian> Reltype_write; + const int reloc_size = elfcpp::Elf_sizes<size>::rela_size; // Offset from start of insn to d-field reloc. const int d_offset = big_endian ? 2 : 0; diff --git a/gold/reloc.cc b/gold/reloc.cc index ca54f153a3f..8bbb07d3960 100644 --- a/gold/reloc.cc +++ b/gold/reloc.cc @@ -871,7 +871,30 @@ Sized_relobj_file<size, big_endian>::do_relocate_sections( Output_file* of, Views* pviews) { - unsigned int shnum = this->shnum(); + this->relocate_section_range(symtab, layout, pshdrs, of, pviews, + 1, this->shnum() - 1); +} + +// Relocate section data for the range of sections START_SHNDX through +// END_SHNDX. + +template<int size, bool big_endian> +void +Sized_relobj_file<size, big_endian>::relocate_section_range( + const Symbol_table* symtab, + const Layout* layout, + const unsigned char* pshdrs, + Output_file* of, + Views* pviews, + unsigned int start_shndx, + unsigned int end_shndx) +{ + gold_assert(start_shndx >= 1); + gold_assert(end_shndx < this->shnum()); + + if (end_shndx < start_shndx) + return; + Sized_target<size, big_endian>* target = parameters->sized_target<size, big_endian>(); @@ -883,8 +906,8 @@ Sized_relobj_file<size, big_endian>::do_relocate_sections( relinfo.layout = layout; relinfo.object = this; - const unsigned char* p = pshdrs + This::shdr_size; - for (unsigned int i = 1; i < shnum; ++i, p += This::shdr_size) + const unsigned char* p = pshdrs + start_shndx * This::shdr_size; + for (unsigned int i = start_shndx; i <= end_shndx; ++i, p += This::shdr_size) { typename This::Shdr shdr(p); @@ -1718,6 +1741,17 @@ Sized_relobj_file<32, false>::do_relocate_sections( Views* pviews); template +void +Sized_relobj_file<32, false>::relocate_section_range( + const Symbol_table* symtab, + const Layout* layout, + const unsigned char* pshdrs, + Output_file* of, + Views* pviews, + unsigned int start_shndx, + unsigned int end_shndx); + +template unsigned char* Sized_relobj_file<32, false>::do_get_output_view( unsigned int shndx, @@ -1735,6 +1769,17 @@ Sized_relobj_file<32, true>::do_relocate_sections( Views* pviews); template +void +Sized_relobj_file<32, true>::relocate_section_range( + const Symbol_table* symtab, + const Layout* layout, + const unsigned char* pshdrs, + Output_file* of, + Views* pviews, + unsigned int start_shndx, + unsigned int end_shndx); + +template unsigned char* Sized_relobj_file<32, true>::do_get_output_view( unsigned int shndx, @@ -1752,6 +1797,17 @@ Sized_relobj_file<64, false>::do_relocate_sections( Views* pviews); template +void +Sized_relobj_file<64, false>::relocate_section_range( + const Symbol_table* symtab, + const Layout* layout, + const unsigned char* pshdrs, + Output_file* of, + Views* pviews, + unsigned int start_shndx, + unsigned int end_shndx); + +template unsigned char* Sized_relobj_file<64, false>::do_get_output_view( unsigned int shndx, @@ -1769,6 +1825,17 @@ Sized_relobj_file<64, true>::do_relocate_sections( Views* pviews); template +void +Sized_relobj_file<64, true>::relocate_section_range( + const Symbol_table* symtab, + const Layout* layout, + const unsigned char* pshdrs, + Output_file* of, + Views* pviews, + unsigned int start_shndx, + unsigned int end_shndx); + +template unsigned char* Sized_relobj_file<64, true>::do_get_output_view( unsigned int shndx, |