From 7e57d19e48f94ff9a19a9413cf25d1887e3a7f52 Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Fri, 23 Jun 2017 20:37:34 +0930 Subject: [PATCH] [GOLD] PowerPC64 tocsave This adds support to gold for the tocsave relocs already supported by ld.bfd. R_PPC64_TOCSAVE relocs are part of a scheme to move r2 saves to the prologue of a function rather than in each plt call stub. We don't want a compiler to always emit the r2 save, as this would be wasted if the calls turned out to be local. See the tocsave*.s in ld/testsuite/ld-powerpc/. * powerpc.cc (Target_powerpc::tocsave_loc_): New var. (Target_powerpc::mark_pltcall, add_tocsave, tocsave_loc): New functions. (Target_powerpc::Branch_info::tocsave_): New var. (Target_powerpc::Branch_info::mark_pltcall): New function. (Target_powerpc::Branch_info::make_stub): Pass tocsave_ to add_plt_call_entry. (Stub_table::Plt_stub_ent): Make public. Add r2save_. (Stub_table::add_plt_call_entry): Add bool tocsave_ param. Set r2save_. (Stub_table::find_plt_call_entry): Return Plt_stub_ent*. Adjust use throughout. (Stub_table::do_write): Conditionally output r2 save in plt stubs. (Target_powerpc::Scan::local): Handle R_PPC64_TOCSAVE. (Target_powerpc::Scan::global): Likewise. (Target_powerpc::Relocate::relocate): Skip r2 save in plt call stub with tocsave reloc. Replace header tocsave nop with r2 save. * symtab.h (struct Symbol_location_hash): Make public. --- gold/ChangeLog | 20 ++++ gold/powerpc.cc | 279 ++++++++++++++++++++++++++++++++++++++---------- gold/symtab.h | 20 ++-- 3 files changed, 254 insertions(+), 65 deletions(-) diff --git a/gold/ChangeLog b/gold/ChangeLog index c6ebaa60e9f..0428b038b91 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,23 @@ +2017-06-23 Alan Modra + + * powerpc.cc (Target_powerpc::tocsave_loc_): New var. + (Target_powerpc::mark_pltcall, add_tocsave, tocsave_loc): New functions. + (Target_powerpc::Branch_info::tocsave_): New var. + (Target_powerpc::Branch_info::mark_pltcall): New function. + (Target_powerpc::Branch_info::make_stub): Pass tocsave_ to + add_plt_call_entry. + (Stub_table::Plt_stub_ent): Make public. Add r2save_. + (Stub_table::add_plt_call_entry): Add bool tocsave_ param. Set + r2save_. + (Stub_table::find_plt_call_entry): Return Plt_stub_ent*. Adjust + use throughout. + (Stub_table::do_write): Conditionally output r2 save in plt stubs. + (Target_powerpc::Scan::local): Handle R_PPC64_TOCSAVE. + (Target_powerpc::Scan::global): Likewise. + (Target_powerpc::Relocate::relocate): Skip r2 save in plt call stub + with tocsave reloc. Replace header tocsave nop with r2 save. + * symtab.h (struct Symbol_location_hash): Make public. + 2017-06-21 Alan Modra * powerpc.cc (Plt_stub_key): Rename from Plt_stub_ent. Remove indx_. diff --git a/gold/powerpc.cc b/gold/powerpc.cc index 3bd646748f5..0d1f0e6c208 100644 --- a/gold/powerpc.cc +++ b/gold/powerpc.cc @@ -593,6 +593,7 @@ class Target_powerpc : public Sized_target Output_data_reloc Reloc_section; typedef typename elfcpp::Elf_types::Elf_Addr Address; typedef typename elfcpp::Elf_types::Elf_Swxword Signed_address; + typedef Unordered_set Tocsave_loc; static const Address invalid_address = static_cast
(0) - 1; // Offset of tp and dtp pointers from start of TLS block. static const Address tp_offset = 0x7000; @@ -603,7 +604,7 @@ class Target_powerpc : public Sized_target got_(NULL), plt_(NULL), iplt_(NULL), brlt_section_(NULL), glink_(NULL), rela_dyn_(NULL), copy_relocs_(), tlsld_got_offset_(-1U), - stub_tables_(), branch_lookup_table_(), branch_info_(), + stub_tables_(), branch_lookup_table_(), branch_info_(), tocsave_loc_(), plt_thread_safe_(false), relax_failed_(false), relax_fail_count_(0), stub_group_size_(0), savres_section_(0) { @@ -680,6 +681,39 @@ class Target_powerpc : public Sized_target ppc_object->set_has_14bit_branch(data_shndx); } + // Return whether the last branch is a plt call, and if so, mark the + // branch as having an R_PPC64_TOCSAVE. + bool + mark_pltcall(Powerpc_relobj* ppc_object, + unsigned int data_shndx, Address r_offset, Symbol_table* symtab) + { + return (size == 64 + && !this->branch_info_.empty() + && this->branch_info_.back().mark_pltcall(ppc_object, data_shndx, + r_offset, this, symtab)); + } + + // Say the given location, that of a nop in a function prologue with + // an R_PPC64_TOCSAVE reloc, will be used to save r2. + // R_PPC64_TOCSAVE relocs on nops following calls point at this nop. + void + add_tocsave(Powerpc_relobj* ppc_object, + unsigned int shndx, Address offset) + { + Symbol_location loc; + loc.object = ppc_object; + loc.shndx = shndx; + loc.offset = offset; + this->tocsave_loc_.insert(loc); + } + + // Accessor + const Tocsave_loc + tocsave_loc() const + { + return this->tocsave_loc_; + } + void do_define_standard_symbols(Symbol_table*, Layout*); @@ -1346,12 +1380,19 @@ class Target_powerpc : public Sized_target unsigned int r_sym, Address addend) : object_(ppc_object), shndx_(data_shndx), offset_(r_offset), - r_type_(r_type), r_sym_(r_sym), addend_(addend) + r_type_(r_type), tocsave_ (0), r_sym_(r_sym), addend_(addend) { } ~Branch_info() { } + // Return whether this branch is going via a plt call stub, and if + // so, mark it as having an R_PPC64_TOCSAVE. + bool + mark_pltcall(Powerpc_relobj* ppc_object, + unsigned int shndx, Address offset, + Target_powerpc* target, Symbol_table* symtab); + // If this branch needs a plt call stub, or a long branch stub, make one. bool make_stub(Stub_table*, @@ -1364,7 +1405,8 @@ class Target_powerpc : public Sized_target unsigned int shndx_; Address offset_; // ..and the branch type and destination. - unsigned int r_type_; + unsigned int r_type_ : 31; + unsigned int tocsave_ : 1; unsigned int r_sym_; Address addend_; }; @@ -1429,6 +1471,7 @@ class Target_powerpc : public Sized_target typedef std::vector Branches; Branches branch_info_; + Tocsave_loc tocsave_loc_; bool plt_thread_safe_; @@ -2889,6 +2932,36 @@ max_branch_delta (unsigned int r_type) return 0; } +// Return whether this branch is going via a plt call stub. + +template +bool +Target_powerpc::Branch_info::mark_pltcall( + Powerpc_relobj* ppc_object, + unsigned int shndx, + Address offset, + Target_powerpc* target, + Symbol_table* symtab) +{ + if (this->object_ != ppc_object + || this->shndx_ != shndx + || this->offset_ != offset) + return false; + + Symbol* sym = this->object_->global_symbol(this->r_sym_); + if (sym != NULL && sym->is_forwarder()) + 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_)) + { + this->tocsave_ = 1; + return true; + } + return false; +} + // If this branch needs a plt call stub, or a long branch stub, make one. template @@ -2934,11 +3007,13 @@ Target_powerpc::Branch_info::make_stub( if (gsym != NULL) ok = stub_table->add_plt_call_entry(from, this->object_, gsym, - this->r_type_, this->addend_); + this->r_type_, this->addend_, + this->tocsave_); else ok = stub_table->add_plt_call_entry(from, this->object_, this->r_sym_, - this->r_type_, this->addend_); + this->r_type_, this->addend_, + this->tocsave_); } } else @@ -3910,6 +3985,16 @@ template class Stub_table : public Output_relaxed_input_section { public: + struct Plt_stub_ent + { + Plt_stub_ent(unsigned int off, unsigned int indx) + : off_(off), indx_(indx), r2save_(0) + { } + + unsigned int off_; + unsigned int indx_ : 31; + unsigned int r2save_ : 1; + }; typedef typename elfcpp::Elf_types::Elf_Addr Address; static const Address invalid_address = static_cast
(0) - 1; @@ -3939,30 +4024,32 @@ class Stub_table : public Output_relaxed_input_section const Sized_relobj_file*, const Symbol*, unsigned int, - Address); + Address, + bool); bool add_plt_call_entry(Address, const Sized_relobj_file*, unsigned int, unsigned int, - Address); + Address, + bool); // Find a given plt call stub. - Address + const Plt_stub_ent* find_plt_call_entry(const Symbol*) const; - Address + const Plt_stub_ent* find_plt_call_entry(const Sized_relobj_file*, unsigned int) const; - Address + const Plt_stub_ent* find_plt_call_entry(const Sized_relobj_file*, const Symbol*, unsigned int, Address) const; - Address + const Plt_stub_ent* find_plt_call_entry(const Sized_relobj_file*, unsigned int, unsigned int, @@ -4119,15 +4206,6 @@ class Stub_table : public Output_relaxed_input_section private: class Plt_stub_key; class Plt_stub_key_hash; - struct Plt_stub_ent - { - Plt_stub_ent(unsigned int off, unsigned int indx) - : off_(off), indx_(indx) - { } - - unsigned int off_; - unsigned int indx_; - }; typedef Unordered_map Plt_stub_entries; class Branch_stub_ent; @@ -4352,7 +4430,8 @@ Stub_table::add_plt_call_entry( const Sized_relobj_file* object, const Symbol* gsym, unsigned int r_type, - Address addend) + Address addend, + bool tocsave) { Plt_stub_key key(object, gsym, r_type, addend); Plt_stub_ent ent(this->plt_size_, this->plt_call_stubs_.size()); @@ -4360,6 +4439,8 @@ Stub_table::add_plt_call_entry( = 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) + p.first->second.r2save_ = 1; return this->can_reach_stub(from, ent.off_, r_type); } @@ -4370,7 +4451,8 @@ Stub_table::add_plt_call_entry( const Sized_relobj_file* object, unsigned int locsym_index, unsigned int r_type, - Address addend) + Address addend, + bool tocsave) { Plt_stub_key key(object, locsym_index, r_type, addend); Plt_stub_ent ent(this->plt_size_, this->plt_call_stubs_.size()); @@ -4378,13 +4460,15 @@ Stub_table::add_plt_call_entry( = 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) + p.first->second.r2save_ = 1; return this->can_reach_stub(from, ent.off_, r_type); } // Find a plt call stub. template -typename Stub_table::Address +const typename Stub_table::Plt_stub_ent* Stub_table::find_plt_call_entry( const Sized_relobj_file* object, const Symbol* gsym, @@ -4394,21 +4478,23 @@ Stub_table::find_plt_call_entry( Plt_stub_key key(object, gsym, r_type, addend); typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(key); if (p == this->plt_call_stubs_.end()) - return invalid_address; - return p->second.off_; + return NULL; + return &p->second; } template -typename Stub_table::Address +const typename Stub_table::Plt_stub_ent* Stub_table::find_plt_call_entry(const Symbol* gsym) const { Plt_stub_key key(gsym); typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(key); - return p == this->plt_call_stubs_.end() ? invalid_address : p->second.off_; + if (p == this->plt_call_stubs_.end()) + return NULL; + return &p->second; } template -typename Stub_table::Address +const typename Stub_table::Plt_stub_ent* Stub_table::find_plt_call_entry( const Sized_relobj_file* object, unsigned int locsym_index, @@ -4418,19 +4504,21 @@ Stub_table::find_plt_call_entry( Plt_stub_key key(object, locsym_index, r_type, addend); typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(key); if (p == this->plt_call_stubs_.end()) - return invalid_address; - return p->second.off_; + return NULL; + return &p->second; } template -typename Stub_table::Address +const typename Stub_table::Plt_stub_ent* Stub_table::find_plt_call_entry( const Sized_relobj_file* object, unsigned int locsym_index) const { Plt_stub_key key(object, locsym_index); typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(key); - return p == this->plt_call_stubs_.end() ? invalid_address : p->second.off_; + if (p == this->plt_call_stubs_.end()) + return NULL; + return &p->second; } // Add a long branch stub if we don't already have one to given @@ -4786,7 +4874,8 @@ Stub_table::do_write(Output_file* of) Address to = this->targ_->glink_section()->address() + glinkoff; Address from - = (this->stub_address() + cs->second.off_ + 24 + = (this->stub_address() + cs->second.off_ + 20 + + 4 * cs->second.r2save_ + 4 * (ha(off) != 0) + 4 * (ha(off + 8 + 8 * static_chain) != ha(off)) + 4 * static_chain); @@ -4797,8 +4886,12 @@ Stub_table::do_write(Output_file* of) p = oview + cs->second.off_; if (ha(off) != 0) { - write_insn(p, std_2_1 + this->targ_->stk_toc()); - p += 4; + if (cs->second.r2save_) + { + write_insn(p, + std_2_1 + this->targ_->stk_toc()); + p += 4; + } if (plt_load_toc) { write_insn(p, addis_11_2 + ha(off)); @@ -4842,8 +4935,12 @@ Stub_table::do_write(Output_file* of) } else { - write_insn(p, std_2_1 + this->targ_->stk_toc()); - p += 4; + if (cs->second.r2save_) + { + write_insn(p, + std_2_1 + this->targ_->stk_toc()); + p += 4; + } write_insn(p, ld_12_2 + l(off)); p += 4; if (plt_load_toc @@ -6046,7 +6143,6 @@ Target_powerpc::Scan::local( case elfcpp::R_POWERPC_NONE: case elfcpp::R_POWERPC_GNU_VTINHERIT: case elfcpp::R_POWERPC_GNU_VTENTRY: - case elfcpp::R_PPC64_TOCSAVE: case elfcpp::R_POWERPC_TLS: case elfcpp::R_PPC64_ENTRY: break; @@ -6154,6 +6250,27 @@ Target_powerpc::Scan::local( } break; + case elfcpp::R_PPC64_TOCSAVE: + // R_PPC64_TOCSAVE follows a call instruction to indicate the + // caller has already saved r2 and thus a plt call stub need not + // save r2. + if (size == 64 + && target->mark_pltcall(ppc_object, data_shndx, + reloc.get_r_offset() - 4, symtab)) + { + unsigned int r_sym = elfcpp::elf_r_sym(reloc.get_r_info()); + unsigned int shndx = lsym.get_st_shndx(); + bool is_ordinary; + shndx = object->adjust_sym_shndx(r_sym, shndx, &is_ordinary); + if (!is_ordinary) + object->error(_("tocsave symbol %u has bad shndx %u"), + r_sym, shndx); + else + target->add_tocsave(ppc_object, shndx, + lsym.get_st_value() + reloc.get_r_addend()); + } + break; + case elfcpp::R_PPC64_REL64: case elfcpp::R_POWERPC_REL32: case elfcpp::R_POWERPC_REL16: @@ -6757,6 +6874,29 @@ Target_powerpc::Scan::global( } break; + case elfcpp::R_PPC64_TOCSAVE: + // R_PPC64_TOCSAVE follows a call instruction to indicate the + // caller has already saved r2 and thus a plt call stub need not + // save r2. + if (size == 64 + && target->mark_pltcall(ppc_object, data_shndx, + reloc.get_r_offset() - 4, symtab)) + { + unsigned int r_sym = elfcpp::elf_r_sym(reloc.get_r_info()); + bool is_ordinary; + unsigned int shndx = gsym->shndx(&is_ordinary); + if (!is_ordinary) + object->error(_("tocsave symbol %u has bad shndx %u"), + r_sym, shndx); + else + { + Sized_symbol* sym = symtab->get_sized_symbol(gsym); + target->add_tocsave(ppc_object, shndx, + sym->value() + reloc.get_r_addend()); + } + } + break; + case elfcpp::R_POWERPC_REL16: case elfcpp::R_POWERPC_REL16_LO: case elfcpp::R_POWERPC_REL16_HI: @@ -7806,16 +7946,29 @@ Target_powerpc::Relocate::relocate( } if (stub_table != NULL) { - Address off; + const typename Stub_table::Plt_stub_ent* ent; if (gsym != NULL) - off = stub_table->find_plt_call_entry(object, gsym, r_type, + ent = stub_table->find_plt_call_entry(object, gsym, r_type, rela.get_r_addend()); else - off = stub_table->find_plt_call_entry(object, r_sym, r_type, + ent = stub_table->find_plt_call_entry(object, r_sym, r_type, rela.get_r_addend()); - if (off != invalid_address) + if (ent != NULL) { - value = stub_table->stub_address() + off; + value = stub_table->stub_address() + ent->off_; + const int reloc_size = elfcpp::Elf_sizes::rela_size; + elfcpp::Shdr shdr(relinfo->reloc_shdr); + size_t reloc_count = shdr.get_sh_size() / reloc_size; + if (size == 64 + && ent->r2save_ + && relnum + 1 < reloc_count) + { + Reltype next_rela(preloc + reloc_size); + if (elfcpp::elf_r_type(next_rela.get_r_info()) + == elfcpp::R_PPC64_TOCSAVE + && next_rela.get_r_offset() == rela.get_r_offset() + 4) + value += 4; + } has_stub_value = true; } } @@ -8844,12 +8997,26 @@ Target_powerpc::Relocate::relocate( r_type); break; - case elfcpp::R_PPC_EMB_SDA21: + case elfcpp::R_PPC64_TOCSAVE: if (size == 32) + // R_PPC_EMB_SDA21 goto unsupp; else { - // R_PPC64_TOCSAVE. For the time being this can be ignored. + Symbol_location loc; + loc.object = relinfo->object; + loc.shndx = relinfo->data_shndx; + loc.offset = rela.get_r_offset(); + Tocsave_loc::const_iterator p = target->tocsave_loc().find(loc); + if (p != target->tocsave_loc().end()) + { + // If we've generated plt calls using this tocsave, then + // the nop needs to be changed to save r2. + Insn* iview = reinterpret_cast(view); + if (elfcpp::Swap<32, big_endian>::readval(iview) == nop) + elfcpp::Swap<32, big_endian>:: + writeval(iview, std_2_1 + target->stk_toc()); + } } break; @@ -9407,9 +9574,10 @@ Target_powerpc::do_dynsym_value(const Symbol* gsym) const p != this->stub_tables_.end(); ++p) { - Address off = (*p)->find_plt_call_entry(gsym); - if (off != invalid_address) - return (*p)->stub_address() + off; + const typename Stub_table::Plt_stub_ent* ent + = (*p)->find_plt_call_entry(gsym); + if (ent != NULL) + return (*p)->stub_address() + ent->off_; } } else if (this->abiversion() >= 2) @@ -9436,10 +9604,10 @@ Target_powerpc::do_plt_address_for_local( p != this->stub_tables_.end(); ++p) { - Address off = (*p)->find_plt_call_entry(relobj->sized_relobj(), - symndx); - if (off != invalid_address) - return (*p)->stub_address() + off; + const typename Stub_table::Plt_stub_ent* ent + = (*p)->find_plt_call_entry(relobj->sized_relobj(), symndx); + if (ent != NULL) + return (*p)->stub_address() + ent->off_; } } gold_unreachable(); @@ -9457,9 +9625,10 @@ Target_powerpc::do_plt_address_for_global( p != this->stub_tables_.end(); ++p) { - Address off = (*p)->find_plt_call_entry(gsym); - if (off != invalid_address) - return (*p)->stub_address() + off; + const typename Stub_table::Plt_stub_ent* ent + = (*p)->find_plt_call_entry(gsym); + if (ent != NULL) + return (*p)->stub_address() + ent->off_; } } else if (this->abiversion() >= 2) diff --git a/gold/symtab.h b/gold/symtab.h index 46c7fce81b6..77552ae7ca8 100644 --- a/gold/symtab.h +++ b/gold/symtab.h @@ -1273,6 +1273,16 @@ struct Symbol_location } }; +// A map from symbol name (as a pointer into the namepool) to all +// the locations the symbols is (weakly) defined (and certain other +// conditions are met). This map will be used later to detect +// possible One Definition Rule (ODR) violations. +struct Symbol_location_hash +{ + size_t operator()(const Symbol_location& loc) const + { return reinterpret_cast(loc.object) ^ loc.offset ^ loc.shndx; } +}; + // This class manages warnings. Warnings are a GNU extension. When // we see a section named .gnu.warning.SYM in an object file, and if // we wind using the definition of SYM from that object file, then we @@ -1695,16 +1705,6 @@ class Symbol_table typedef Unordered_map Symbol_table_type; - // A map from symbol name (as a pointer into the namepool) to all - // the locations the symbols is (weakly) defined (and certain other - // conditions are met). This map will be used later to detect - // possible One Definition Rule (ODR) violations. - struct Symbol_location_hash - { - size_t operator()(const Symbol_location& loc) const - { return reinterpret_cast(loc.object) ^ loc.offset ^ loc.shndx; } - }; - typedef Unordered_map > Odr_map; -- 2.30.2