From f21dbd7c8038acee8ece4d57f2454083c37e98f6 Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Thu, 25 Nov 2021 00:00:50 +1030 Subject: [PATCH] [GOLD] PowerPC64 inline plt sequences The fixes gold failures to handle inline PLT sequences properly. PowerPC gold was always turning these back into direct calls due to gsym->use_plt_offset() returning false. This is fixed for dynamic linking by correcting get_reference_flags, and for static linking by overriding use_plt_offset() in relocate(). The rest of the patch revolves around needing to create PLT entries for inline PLT calls when statically linking (for gcc -mlongcall). The lplt section handled that for local symbols, now it does globals too. * powerpc.cc (Target_powerpc::plt_off): Return proper section for static link. (Target_powerpc::symval_for_branch): Make public. (Target_powerpc::make_lplt_section): Add Symbol_table* param. Adjust all calls. (Target_powerpc::make_local_plt_entry): Likewise. (Target_powerpc::make_local_plt_entry): New variant for global syms. (Powerpc_relobj::do_relocate_sections): Don't write lplt contents. (Output_data_plt_powerpc::do_write): Write lplt contents here. (Output_data_plt_powerpc::Output_data_plt_powerpc): Save symbol table pointer. Adjust all uses. (Output_data_plt_powerpc::add_entry): Add stash parameter. Don't do dynamic reloc handling when no reloc section. Save symbol for local plt entries. (Output_data_plt_powerpc::add_local_entry): Save symbol. (Output_data_plt_powerpc::Local_plt_ent): New class. (Output_data_plt_powerpc::sym_ents_): New vector. (Target_powerpc::Scan::get_reference_flags): Return FUNCTION_CALL|RELATIVE_REF for inline plt relocs. (Target_powerpc::Scan::global): Make entries in lplt for inline plt call relocation symbols. (Target_powerpc::Relocate::relocate): Rename has_plt_offset to use_plt_offset. Set use_plt_offset for inline plt relocs. --- gold/powerpc.cc | 226 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 162 insertions(+), 64 deletions(-) diff --git a/gold/powerpc.cc b/gold/powerpc.cc index 3a6d3c6f612..103eef85943 100644 --- a/gold/powerpc.cc +++ b/gold/powerpc.cc @@ -927,8 +927,10 @@ class Target_powerpc : public Sized_target if (gsym->type() == elfcpp::STT_GNU_IFUNC && gsym->can_use_relative_reloc(false)) *sec = this->iplt_section(); - else + else if (!parameters->doing_static_link()) *sec = this->plt_section(); + else + *sec = this->lplt_section(); return gsym->plt_offset(); } @@ -1262,6 +1264,12 @@ class Target_powerpc : public Sized_target void merge_object_attributes(const Object*, const Attributes_section_data*); + bool + symval_for_branch(const Symbol_table* symtab, + const Sized_symbol* gsym, + Powerpc_relobj* object, + Address *value, unsigned int *dest_shndx); + private: class Track_tls @@ -1464,12 +1472,6 @@ class Target_powerpc : public Sized_target bool issued_non_pic_error_; }; - bool - symval_for_branch(const Symbol_table* symtab, - const Sized_symbol* gsym, - Powerpc_relobj* object, - Address *value, unsigned int *dest_shndx); - // The class which implements relocation. class Relocate : protected Track_tls { @@ -1569,7 +1571,7 @@ class Target_powerpc : public Sized_target make_iplt_section(Symbol_table*, Layout*); void - make_lplt_section(Layout*); + make_lplt_section(Symbol_table*, Layout*); void make_brlt_section(Layout*); @@ -1586,10 +1588,12 @@ class Target_powerpc : public Sized_target // Create a PLT entry for a local non-IFUNC symbol. void - make_local_plt_entry(Layout*, + make_local_plt_entry(Symbol_table*, Layout*, Sized_relobj_file*, unsigned int); + void + make_local_plt_entry(Symbol_table*, Layout*, Symbol*); // Create a GOT entry for local dynamic __tls_get_addr. unsigned int @@ -2771,33 +2775,6 @@ Powerpc_relobj::do_relocate_sections( } this->relocate_section_range(symtab, layout, pshdrs, of, pviews, start, this->shnum() - 1); - - if (!parameters->options().output_is_position_independent()) - { - Target_powerpc* target - = static_cast*>( - parameters->sized_target()); - if (target->lplt_section() && target->lplt_section()->data_size() != 0) - { - const section_size_type offset = target->lplt_section()->offset(); - const section_size_type oview_size - = convert_to_section_size_type(target->lplt_section()->data_size()); - unsigned char* const oview = of->get_output_view(offset, oview_size); - - bool modified = false; - unsigned int nsyms = this->local_symbol_count(); - for (unsigned int i = 0; i < nsyms; i++) - if (this->local_has_plt_offset(i)) - { - Address value = this->local_symbol_value(i, 0); - size_t off = this->local_plt_offset(i); - elfcpp::Swap::writeval(oview + off, value); - modified = true; - } - if (modified) - of->write_output_view(offset, oview_size, oview); - } - } } // Set up some symbols. @@ -4047,17 +4024,16 @@ class Output_data_plt_powerpc : public Output_section_data_build size, big_endian> Reloc_section; Output_data_plt_powerpc(Target_powerpc* targ, + Symbol_table* symtab, Reloc_section* plt_rel, const char* name) : Output_section_data_build(size == 32 ? 4 : 8), - rel_(plt_rel), - targ_(targ), - name_(name) + rel_(plt_rel), targ_(targ), symtab_(symtab), name_(name), sym_ents_() { } // Add an entry to the PLT. void - add_entry(Symbol*); + add_entry(Symbol*, bool = false); void add_ifunc_entry(Symbol*); @@ -4098,6 +4074,23 @@ class Output_data_plt_powerpc : public Output_section_data_build { mapfile->print_output_data(this, this->name_); } private: + struct Local_plt_ent + { + Local_plt_ent(Sized_relobj_file* obj, unsigned int rsym) + { rsym_ = rsym; u.obj_ = obj; } + Local_plt_ent(Symbol* sym) + { rsym_ = -1u; u.gsym_ = sym; } + ~Local_plt_ent() + { } + + unsigned int rsym_; + union + { + Sized_relobj_file* obj_; + Symbol* gsym_; + } u; + }; + // Return the offset of the first non-reserved PLT entry. unsigned int first_plt_entry_offset() const @@ -4123,15 +4116,19 @@ class Output_data_plt_powerpc : public Output_section_data_build Reloc_section* rel_; // Allows access to .glink for do_write. Target_powerpc* targ_; + Symbol_table* symtab_; // What to report in map file. const char *name_; + + std::vector sym_ents_; }; // Add an entry to the PLT. template void -Output_data_plt_powerpc::add_entry(Symbol* gsym) +Output_data_plt_powerpc::add_entry(Symbol* gsym, + bool stash) { if (!gsym->has_plt_offset()) { @@ -4139,11 +4136,19 @@ Output_data_plt_powerpc::add_entry(Symbol* gsym) if (off == 0) off += this->first_plt_entry_offset(); gsym->set_plt_offset(off); - gsym->set_needs_dynsym_entry(); - unsigned int dynrel = elfcpp::R_POWERPC_JMP_SLOT; - this->rel_->add_global(gsym, dynrel, this, off, 0); + if (this->rel_) + { + gsym->set_needs_dynsym_entry(); + unsigned int dynrel = elfcpp::R_POWERPC_JMP_SLOT; + this->rel_->add_global(gsym, dynrel, this, off, 0); + } off += this->plt_entry_size(); this->set_current_data_size(off); + if (stash) + { + Local_plt_ent sym(gsym); + this->sym_ents_.push_back(sym); + } } } @@ -4188,6 +4193,8 @@ Output_data_plt_powerpc::add_local_entry( } off += this->plt_entry_size(); this->set_current_data_size(off); + Local_plt_ent sym(relobj, local_sym_index); + this->sym_ents_.push_back(sym); } } @@ -4326,6 +4333,58 @@ template void Output_data_plt_powerpc::do_write(Output_file* of) { + if (!this->sym_ents_.empty()) + { + const section_size_type offset = this->offset(); + const section_size_type oview_size + = convert_to_section_size_type(this->data_size()); + unsigned char* const oview = of->get_output_view(offset, oview_size); + unsigned char* pov = oview; + unsigned char* endpov = oview + oview_size; + + for (typename std::vector::iterator e + = this->sym_ents_.begin(); + e != this->sym_ents_.end(); + e++) + { + typename elfcpp::Elf_types::Elf_Addr val; + Sized_symbol* gsym = NULL; + Powerpc_relobj* obj = NULL; + if (e->rsym_ == -1u) + { + gsym = static_cast*>(e->u.gsym_); + val = gsym->value(); + } + else + { + obj = static_cast*>(e->u.obj_); + val = obj->local_symbol(e->rsym_)->value(obj, 0); + } + if (this->targ_->abiversion() >= 2) + { + elfcpp::Swap::writeval(pov, val); + pov += size / 8; + } + else + { + unsigned int shndx; + this->targ_->symval_for_branch(this->symtab_, gsym, obj, + &val, &shndx); + elfcpp::Swap::writeval(pov, val); + pov += size / 8; + val = this->targ_->toc_pointer(); + elfcpp::Swap::writeval(pov, val); + pov += size / 8; + if (this->plt_entry_size() > 16) + { + elfcpp::Swap::writeval(pov, 0); + pov += size / 8; + } + } + } + gold_assert(pov == endpov); + } + if (size == 32 && (this->name_[3] != 'I' && this->name_[3] != 'L')) { const section_size_type offset = this->offset(); @@ -4376,7 +4435,7 @@ Target_powerpc::make_plt_section(Symbol_table* symtab, elfcpp::SHF_ALLOC, plt_rel, ORDER_DYNAMIC_PLT_RELOCS, false); this->plt_ - = new Output_data_plt_powerpc(this, plt_rel, + = new Output_data_plt_powerpc(this, symtab, plt_rel, "** PLT"); layout->add_output_section_data(".plt", (size == 32 @@ -4404,13 +4463,13 @@ Target_powerpc::make_iplt_section(Symbol_table* symtab, if (this->iplt_ == NULL) { this->make_plt_section(symtab, layout); - this->make_lplt_section(layout); + this->make_lplt_section(symtab, layout); Reloc_section* iplt_rel = new Reloc_section(false); if (this->rela_dyn_->output_section()) this->rela_dyn_->output_section()->add_output_section_data(iplt_rel); this->iplt_ - = new Output_data_plt_powerpc(this, iplt_rel, + = new Output_data_plt_powerpc(this, symtab, iplt_rel, "** IPLT"); if (this->plt_->output_section()) this->plt_->output_section()->add_output_section_data(this->iplt_); @@ -4421,7 +4480,8 @@ Target_powerpc::make_iplt_section(Symbol_table* symtab, template void -Target_powerpc::make_lplt_section(Layout* layout) +Target_powerpc::make_lplt_section(Symbol_table* symtab, + Layout* layout) { if (this->lplt_ == NULL) { @@ -4435,7 +4495,7 @@ Target_powerpc::make_lplt_section(Layout* layout) ->add_output_section_data(lplt_rel); } this->lplt_ - = new Output_data_plt_powerpc(this, lplt_rel, + = new Output_data_plt_powerpc(this, symtab, lplt_rel, "** LPLT"); this->make_brlt_section(layout); if (this->brlt_section_ && this->brlt_section_->output_section()) @@ -7416,15 +7476,27 @@ Target_powerpc::make_plt_entry(Symbol_table* symtab, template void Target_powerpc::make_local_plt_entry( + Symbol_table* symtab, Layout* layout, Sized_relobj_file* relobj, unsigned int r_sym) { if (this->lplt_ == NULL) - this->make_lplt_section(layout); + this->make_lplt_section(symtab, layout); this->lplt_->add_local_entry(relobj, r_sym); } +template +void +Target_powerpc::make_local_plt_entry(Symbol_table* symtab, + Layout* layout, + Symbol* gsym) +{ + if (this->lplt_ == NULL) + this->make_lplt_section(symtab, layout); + this->lplt_->add_entry(gsym, true); +} + // Make a PLT entry for a local STT_GNU_IFUNC symbol. template @@ -7552,6 +7624,16 @@ Target_powerpc::Scan::get_reference_flags( case elfcpp::R_POWERPC_REL14: case elfcpp::R_POWERPC_REL14_BRTAKEN: case elfcpp::R_POWERPC_REL14_BRNTAKEN: + case elfcpp::R_POWERPC_PLT16_LO: + case elfcpp::R_POWERPC_PLT16_HI: + case elfcpp::R_POWERPC_PLT16_HA: + case elfcpp::R_PPC64_PLT16_LO_DS: + case elfcpp::R_POWERPC_PLTSEQ: + case elfcpp::R_PPC64_PLTSEQ_NOTOC: + case elfcpp::R_POWERPC_PLTCALL: + case elfcpp::R_PPC64_PLTCALL_NOTOC: + case elfcpp::R_PPC64_PLT_PCREL34: + case elfcpp::R_PPC64_PLT_PCREL34_NOTOC: ref = Symbol::FUNCTION_CALL | Symbol::RELATIVE_REF; break; @@ -7568,12 +7650,6 @@ Target_powerpc::Scan::get_reference_flags( case elfcpp::R_PPC64_TOC16_HA: case elfcpp::R_PPC64_TOC16_DS: case elfcpp::R_PPC64_TOC16_LO_DS: - case elfcpp::R_POWERPC_PLT16_LO: - case elfcpp::R_POWERPC_PLT16_HI: - case elfcpp::R_POWERPC_PLT16_HA: - case elfcpp::R_PPC64_PLT16_LO_DS: - case elfcpp::R_PPC64_PLT_PCREL34: - case elfcpp::R_PPC64_PLT_PCREL34_NOTOC: ref = Symbol::RELATIVE_REF; break; @@ -8040,7 +8116,7 @@ Target_powerpc::Scan::local( if (!is_ifunc) { unsigned int r_sym = elfcpp::elf_r_sym(reloc.get_r_info()); - target->make_local_plt_entry(layout, object, r_sym); + target->make_local_plt_entry(symtab, layout, object, r_sym); } break; @@ -8785,7 +8861,12 @@ Target_powerpc::Scan::global( case elfcpp::R_POWERPC_PLT16_HA: case elfcpp::R_PPC64_PLT16_LO_DS: if (!pushed_ifunc) - target->make_plt_entry(symtab, layout, gsym); + { + if (!parameters->doing_static_link()) + target->make_plt_entry(symtab, layout, gsym); + else + target->make_local_plt_entry(symtab, layout, gsym); + } break; case elfcpp::R_PPC64_REL24_NOTOC: @@ -10469,11 +10550,28 @@ Target_powerpc::Relocate::relocate( bool has_stub_value = false; bool localentry0 = false; unsigned int r_sym = elfcpp::elf_r_sym(rela.get_r_info()); - bool has_plt_offset + bool use_plt_offset = (gsym != NULL ? gsym->use_plt_offset(Scan::get_reference_flags(r_type, target)) : object->local_has_plt_offset(r_sym)); - if (has_plt_offset + if (is_plt16_reloc(r_type) + || r_type == elfcpp::R_PPC64_PLT_PCREL34 + || r_type == elfcpp::R_PPC64_PLT_PCREL34_NOTOC + || r_type == elfcpp::R_POWERPC_PLTSEQ + || r_type == elfcpp::R_PPC64_PLTSEQ_NOTOC + || r_type == elfcpp::R_POWERPC_PLTCALL + || r_type == elfcpp::R_PPC64_PLTCALL_NOTOC) + { + // It would be possible to replace inline plt calls with direct + // calls if the PLTCALL is in range. The only difficulty is + // that the decision depends on the PLTCALL reloc, and we don't + // know the address of that instruction when processing others + // in the sequence. So the decision needs to be made in + // do_relax(). For now, don't optimise inline plt calls. + if (gsym) + use_plt_offset = gsym->has_plt_offset(); + } + if (use_plt_offset && !is_got_reloc(r_type) && !is_plt16_reloc(r_type) && r_type != elfcpp::R_PPC64_PLT_PCREL34 @@ -10567,7 +10665,7 @@ Target_powerpc::Relocate::relocate( gold_assert(has_stub_value || !(os->flags() & elfcpp::SHF_ALLOC)); } - if (has_plt_offset && (is_plt16_reloc(r_type) + if (use_plt_offset && (is_plt16_reloc(r_type) || r_type == elfcpp::R_PPC64_PLT_PCREL34 || r_type == elfcpp::R_PPC64_PLT_PCREL34_NOTOC)) { @@ -10597,7 +10695,7 @@ Target_powerpc::Relocate::relocate( value -= target->toc_pointer(); } } - else if (!has_plt_offset + else if (!use_plt_offset && (is_plt16_reloc(r_type) || r_type == elfcpp::R_POWERPC_PLTSEQ || r_type == elfcpp::R_PPC64_PLTSEQ_NOTOC)) @@ -10606,7 +10704,7 @@ Target_powerpc::Relocate::relocate( elfcpp::Swap<32, big_endian>::writeval(iview, nop); r_type = elfcpp::R_POWERPC_NONE; } - else if (!has_plt_offset + else if (!use_plt_offset && (r_type == elfcpp::R_PPC64_PLT_PCREL34 || r_type == elfcpp::R_PPC64_PLT_PCREL34_NOTOC)) { @@ -11085,7 +11183,7 @@ Target_powerpc::Relocate::relocate( } else if (!has_stub_value) { - if (!has_plt_offset && (r_type == elfcpp::R_POWERPC_PLTCALL + if (!use_plt_offset && (r_type == elfcpp::R_POWERPC_PLTCALL || r_type == elfcpp::R_PPC64_PLTCALL_NOTOC)) { // PLTCALL without plt entry => convert to direct call -- 2.30.2