From 7ee7ff7015840f2d0156bf386a050bd728d22fc4 Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Fri, 23 Jun 2017 20:39:43 +0930 Subject: [PATCH] [GOLD] PowerPC64 localentry:0 plt call optimization elfcpp/ * elfcpp.h (DT_PPC64_OPT): Define. * powerpc.h (PPC64_OPT_TLS, PPC64_OPT_MULTI_TOC, PPC64_OPT_LOCALENTRY): Define. gold/ * options.h (General_options): Add plt_localentry. * powerpc.cc (Target_powerpc::st_other): New function. (Target_powerpc::plt_localentry0_, plt_localentry0_init_, has_localentry0_): New vars. (Target_powerpc::plt_localentry0, set_has_localentry0, is_elfv2_localentry0): New functions. (Target_powerpc::Branch_info::mark_pltcall): Don't set tocsave or return true for localentry:0 calls. (Stub_table::Plt_stub_ent::localentry0_): New var. (Stub_table::add_plt_call_entry): Set localentry0_ and has_localentry0_. Don't set r2save_ for localentry:0 calls. (Output_data_glink::do_write): Save r2 in __glink_PLTresolve for elfv2. (Target_powerpc::scan_relocs): Default plt_localentry0_. (Target_powerpc::do_finalize_sections): Set DT_PPC64_OPT. (Target_powerpc::Relocate::relocate): Don't require nop following calls for localentry:0 plt calls, and don't change nop. --- elfcpp/ChangeLog | 6 +++ elfcpp/elfcpp.h | 3 ++ elfcpp/powerpc.h | 8 ++++ gold/ChangeLog | 19 ++++++++ gold/options.h | 4 ++ gold/powerpc.cc | 120 ++++++++++++++++++++++++++++++++++++++++++----- 6 files changed, 149 insertions(+), 11 deletions(-) diff --git a/elfcpp/ChangeLog b/elfcpp/ChangeLog index 6a61d61fee1..0be1bcb9cdd 100644 --- a/elfcpp/ChangeLog +++ b/elfcpp/ChangeLog @@ -1,3 +1,9 @@ +2017-06-21 Alan Modra + + * elfcpp.h (DT_PPC64_OPT): Define. + * powerpc.h (PPC64_OPT_TLS, PPC64_OPT_MULTI_TOC, + PPC64_OPT_LOCALENTRY): Define. + 2017-01-02 Alan Modra Update year range in copyright notice of all files. diff --git a/elfcpp/elfcpp.h b/elfcpp/elfcpp.h index 82eb37feb3e..a57f5476f12 100644 --- a/elfcpp/elfcpp.h +++ b/elfcpp/elfcpp.h @@ -775,6 +775,9 @@ enum DT DT_PPC64_OPD = 0x70000001, DT_PPC64_OPDSZ = 0x70000002, + // Specify whether various optimisations are possible. + DT_PPC64_OPT = 0x70000003, + // The index of an STT_SPARC_REGISTER symbol within the DT_SYMTAB // symbol table. One dynamic entry exists for every STT_SPARC_REGISTER // symbol in the symbol table. diff --git a/elfcpp/powerpc.h b/elfcpp/powerpc.h index 99647bd9855..3dc08286777 100644 --- a/elfcpp/powerpc.h +++ b/elfcpp/powerpc.h @@ -228,6 +228,14 @@ enum EF_PPC64_ABI = 3 }; +// DT_PPC64_OPT bits +enum +{ + PPC64_OPT_TLS = 1, + PPC64_OPT_MULTI_TOC = 2, + PPC64_OPT_LOCALENTRY = 4 +}; + enum { // The ELFv2 ABI uses three bits in the symbol st_other field of a diff --git a/gold/ChangeLog b/gold/ChangeLog index 0428b038b91..7a0a1cfc845 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,22 @@ +2017-06-23 Alan Modra + + * options.h (General_options): Add plt_localentry. + * powerpc.cc (Target_powerpc::st_other): New function. + (Target_powerpc::plt_localentry0_, plt_localentry0_init_, + has_localentry0_): New vars. + (Target_powerpc::plt_localentry0, set_has_localentry0, + is_elfv2_localentry0): New functions. + (Target_powerpc::Branch_info::mark_pltcall): Don't set tocsave or + return true for localentry:0 calls. + (Stub_table::Plt_stub_ent::localentry0_): New var. + (Stub_table::add_plt_call_entry): Set localentry0_ and has_localentry0_. + Don't set r2save_ for localentry:0 calls. + (Output_data_glink::do_write): Save r2 in __glink_PLTresolve for elfv2. + (Target_powerpc::scan_relocs): Default plt_localentry0_. + (Target_powerpc::do_finalize_sections): Set DT_PPC64_OPT. + (Target_powerpc::Relocate::relocate): Don't require nop following + calls for localentry:0 plt calls, and don't change nop. + 2017-06-23 Alan Modra * powerpc.cc (Target_powerpc::tocsave_loc_): New var. diff --git a/gold/options.h b/gold/options.h index 202d4b0e8d2..c7c032bbd77 100644 --- a/gold/options.h +++ b/gold/options.h @@ -1104,6 +1104,10 @@ class General_options N_("(PowerPC64 only) Align PLT call stubs to fit cache lines"), N_("[=P2ALIGN]"), true, int, int, options::parse_uint, false); + DEFINE_bool(plt_localentry, options::TWO_DASHES, '\0', false, + N_("(PowerPC64 only) Optimize calls to ELFv2 localentry:0 functions"), + N_("(PowerPC64 only) Don't optimize ELFv2 calls")); + DEFINE_bool(plt_static_chain, options::TWO_DASHES, '\0', false, N_("(PowerPC64 only) PLT call stubs should load r11"), N_("(PowerPC64 only) PLT call stubs should not load r11")); diff --git a/gold/powerpc.cc b/gold/powerpc.cc index 0d1f0e6c208..a046bbee33f 100644 --- a/gold/powerpc.cc +++ b/gold/powerpc.cc @@ -372,6 +372,12 @@ public: void set_abiversion(int ver); + unsigned int + st_other (unsigned int symndx) const + { + return this->st_other_[symndx]; + } + unsigned int ppc64_local_entry_offset(const Symbol* sym) const { return elfcpp::ppc64_decode_local_entry(sym->nonvis() >> 3); } @@ -605,7 +611,9 @@ class Target_powerpc : public Sized_target glink_(NULL), rela_dyn_(NULL), copy_relocs_(), tlsld_got_offset_(-1U), stub_tables_(), branch_lookup_table_(), branch_info_(), tocsave_loc_(), - plt_thread_safe_(false), relax_failed_(false), relax_fail_count_(0), + plt_thread_safe_(false), plt_localentry0_(false), + plt_localentry0_init_(false), has_localentry0_(false), + relax_failed_(false), relax_fail_count_(0), stub_group_size_(0), savres_section_(0) { } @@ -1000,6 +1008,49 @@ class Target_powerpc : public Sized_target plt_thread_safe() const { return this->plt_thread_safe_; } + bool + plt_localentry0() const + { return this->plt_localentry0_; } + + void + set_has_localentry0() + { + this->has_localentry0_ = true; + } + + bool + is_elfv2_localentry0(const Symbol* gsym) const + { + return (size == 64 + && this->abiversion() >= 2 + && this->plt_localentry0() + && gsym->type() == elfcpp::STT_FUNC + && gsym->is_defined() + && gsym->nonvis() >> 3 == 0); + } + + bool + is_elfv2_localentry0(const Sized_relobj_file* object, + unsigned int r_sym) const + { + const Powerpc_relobj* ppc_object + = static_cast*>(object); + + if (size == 64 + && this->abiversion() >= 2 + && this->plt_localentry0() + && ppc_object->st_other(r_sym) >> 5 == 0) + { + const Symbol_value* psymval = object->local_symbol(r_sym); + bool is_ordinary; + if (!psymval->is_ifunc_symbol() + && psymval->input_shndx(&is_ordinary) != elfcpp::SHN_UNDEF + && is_ordinary) + return true; + } + return false; + } + int abiversion () const { return this->processor_specific_flags() & elfcpp::EF_PPC64_ABI; } @@ -1474,6 +1525,9 @@ class Target_powerpc : public Sized_target Tocsave_loc tocsave_loc_; bool plt_thread_safe_; + bool plt_localentry0_; + bool plt_localentry0_init_; + bool has_localentry0_; bool relax_failed_; int relax_fail_count_; @@ -2953,8 +3007,10 @@ Target_powerpc::Branch_info::mark_pltcall( sym = symtab->resolve_forwards(sym); const Sized_symbol* gsym = static_cast*>(sym); if (gsym != NULL - ? gsym->use_plt_offset(Scan::get_reference_flags(this->r_type_, target)) - : this->object_->local_has_plt_offset(this->r_sym_)) + ? (gsym->use_plt_offset(Scan::get_reference_flags(this->r_type_, target)) + && !target->is_elfv2_localentry0(gsym)) + : (this->object_->local_has_plt_offset(this->r_sym_) + && !target->is_elfv2_localentry0(this->object_, this->r_sym_))) { this->tocsave_ = 1; return true; @@ -3988,12 +4044,13 @@ class Stub_table : public Output_relaxed_input_section struct Plt_stub_ent { Plt_stub_ent(unsigned int off, unsigned int indx) - : off_(off), indx_(indx), r2save_(0) + : off_(off), indx_(indx), r2save_(0), localentry0_(0) { } unsigned int off_; - unsigned int indx_ : 31; + unsigned int indx_ : 30; unsigned int r2save_ : 1; + unsigned int localentry0_ : 1; }; typedef typename elfcpp::Elf_types::Elf_Addr Address; static const Address invalid_address = static_cast
(0) - 1; @@ -4438,8 +4495,18 @@ Stub_table::add_plt_call_entry( std::pair p = this->plt_call_stubs_.insert(std::make_pair(key, ent)); if (p.second) - this->plt_size_ = ent.off_ + this->plt_call_size(p.first); - if (size == 64 && !tocsave) + { + this->plt_size_ = ent.off_ + this->plt_call_size(p.first); + if (size == 64 + && this->targ_->is_elfv2_localentry0(gsym)) + { + p.first->second.localentry0_ = 1; + this->targ_->set_has_localentry0(); + } + } + if (size == 64 + && !tocsave + && !p.first->second.localentry0_) p.first->second.r2save_ = 1; return this->can_reach_stub(from, ent.off_, r_type); } @@ -4459,8 +4526,18 @@ Stub_table::add_plt_call_entry( std::pair p = this->plt_call_stubs_.insert(std::make_pair(key, ent)); if (p.second) - this->plt_size_ = ent.off_ + this->plt_call_size(p.first); - if (size == 64 && !tocsave) + { + this->plt_size_ = ent.off_ + this->plt_call_size(p.first); + if (size == 64 + && this->targ_->is_elfv2_localentry0(object, locsym_index)) + { + p.first->second.localentry0_ = 1; + this->targ_->set_has_localentry0(); + } + } + if (size == 64 + && !tocsave + && !p.first->second.localentry0_) p.first->second.r2save_ = 1; return this->can_reach_stub(from, ent.off_, r_type); } @@ -5183,6 +5260,7 @@ Output_data_glink::do_write(Output_file* of) write_insn(p, mflr_0), p += 4; write_insn(p, bcl_20_31), p += 4; write_insn(p, mflr_11), p += 4; + write_insn(p, std_2_1 + 24), p += 4; write_insn(p, ld_2_11 + l(-16)), p += 4; write_insn(p, mtlr_0), p += 4; write_insn(p, sub_12_12_11), p += 4; @@ -7574,6 +7652,21 @@ Target_powerpc::scan_relocs( typedef gold::Default_classify_reloc Classify_reloc; + if (!this->plt_localentry0_init_) + { + bool plt_localentry0 = false; + if (size == 64 + && this->abiversion() >= 2) + { + if (parameters->options().user_set_plt_localentry()) + plt_localentry0 = parameters->options().plt_localentry(); + else + plt_localentry0 = symtab->lookup("GLIBC_2.26", NULL) != NULL; + } + this->plt_localentry0_ = plt_localentry0; + this->plt_localentry0_init_ = true; + } + if (sh_type == elfcpp::SHT_REL) { gold_error(_("%s: unsupported REL reloc section"), @@ -7770,6 +7863,9 @@ Target_powerpc::do_finalize_sections( (this->glink_->pltresolve_size - 32)); } + if (this->has_localentry0_) + odyn->add_constant(elfcpp::DT_PPC64_OPT, + elfcpp::PPC64_OPT_LOCALENTRY); } } @@ -7914,6 +8010,7 @@ Target_powerpc::Relocate::relocate( = static_cast*>(relinfo->object); Address value = 0; bool has_stub_value = false; + bool localentry0 = false; unsigned int r_sym = elfcpp::elf_r_sym(rela.get_r_info()); if ((gsym != NULL ? gsym->use_plt_offset(Scan::get_reference_flags(r_type, target)) @@ -7969,6 +8066,7 @@ Target_powerpc::Relocate::relocate( && next_rela.get_r_offset() == rela.get_r_offset() + 4) value += 4; } + localentry0 = ent->localentry0_; has_stub_value = true; } } @@ -8012,8 +8110,8 @@ Target_powerpc::Relocate::relocate( { typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype; Valtype* wv = reinterpret_cast(view); - bool can_plt_call = false; - if (rela.get_r_offset() + 8 <= view_size) + bool can_plt_call = localentry0; + if (!localentry0 && rela.get_r_offset() + 8 <= view_size) { Valtype insn = elfcpp::Swap<32, big_endian>::readval(wv); Valtype insn2 = elfcpp::Swap<32, big_endian>::readval(wv + 1); -- 2.30.2