[GOLD] PowerPC64 inline plt sequences
Fangrui Song
i@maskray.me
Mon Dec 6 18:08:20 GMT 2021
On 2021-12-06, Alan Modra via Binutils wrote:
>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.
>
>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<size, big_endian>
> 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<size, big_endian>
> void
> merge_object_attributes(const Object*, const Attributes_section_data*);
>
>+ bool
>+ symval_for_branch(const Symbol_table* symtab,
>+ const Sized_symbol<size>* gsym,
>+ Powerpc_relobj<size, big_endian>* object,
>+ Address *value, unsigned int *dest_shndx);
>+
> private:
>
> class Track_tls
>@@ -1464,12 +1472,6 @@ class Target_powerpc : public Sized_target<size, big_endian>
> bool issued_non_pic_error_;
> };
>
>- bool
>- symval_for_branch(const Symbol_table* symtab,
>- const Sized_symbol<size>* gsym,
>- Powerpc_relobj<size, big_endian>* 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<size, big_endian>
> 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<size, big_endian>
>
> // 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<size, big_endian>*,
> 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<size, big_endian>::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<size, big_endian>* target
>- = static_cast<Target_powerpc<size, big_endian>*>(
>- parameters->sized_target<size, big_endian>());
>- 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<size, big_endian>::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<size, big_endian>* 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<size, big_endian>* 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<size, big_endian>* 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<size, big_endian>* targ_;
>+ Symbol_table* symtab_;
> // What to report in map file.
> const char *name_;
>+
>+ std::vector<Local_plt_ent> sym_ents_;
> };
>
> // Add an entry to the PLT.
>
> template<int size, bool big_endian>
> void
>-Output_data_plt_powerpc<size, big_endian>::add_entry(Symbol* gsym)
>+Output_data_plt_powerpc<size, big_endian>::add_entry(Symbol* gsym,
>+ bool stash)
> {
> if (!gsym->has_plt_offset())
> {
>@@ -4139,11 +4136,19 @@ Output_data_plt_powerpc<size, big_endian>::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<size, big_endian>::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<int size, bool big_endian>
> void
> Output_data_plt_powerpc<size, big_endian>::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<Local_plt_ent>::iterator e
>+ = this->sym_ents_.begin();
>+ e != this->sym_ents_.end();
>+ e++)
>+ {
>+ typename elfcpp::Elf_types<size>::Elf_Addr val;
>+ Sized_symbol<size>* gsym = NULL;
>+ Powerpc_relobj<size, big_endian>* obj = NULL;
>+ if (e->rsym_ == -1u)
>+ {
>+ gsym = static_cast<Sized_symbol<size>*>(e->u.gsym_);
>+ val = gsym->value();
>+ }
>+ else
>+ {
>+ obj = static_cast<Powerpc_relobj<size, big_endian>*>(e->u.obj_);
>+ val = obj->local_symbol(e->rsym_)->value(obj, 0);
>+ }
>+ if (this->targ_->abiversion() >= 2)
>+ {
>+ elfcpp::Swap<size, big_endian>::writeval(pov, val);
>+ pov += size / 8;
>+ }
>+ else
>+ {
>+ unsigned int shndx;
>+ this->targ_->symval_for_branch(this->symtab_, gsym, obj,
>+ &val, &shndx);
>+ elfcpp::Swap<size, big_endian>::writeval(pov, val);
>+ pov += size / 8;
>+ val = this->targ_->toc_pointer();
>+ elfcpp::Swap<size, big_endian>::writeval(pov, val);
>+ pov += size / 8;
>+ if (this->plt_entry_size() > 16)
>+ {
>+ elfcpp::Swap<size, big_endian>::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<size, big_endian>::make_plt_section(Symbol_table* symtab,
> elfcpp::SHF_ALLOC, plt_rel,
> ORDER_DYNAMIC_PLT_RELOCS, false);
> this->plt_
>- = new Output_data_plt_powerpc<size, big_endian>(this, plt_rel,
>+ = new Output_data_plt_powerpc<size, big_endian>(this, symtab, plt_rel,
> "** PLT");
> layout->add_output_section_data(".plt",
> (size == 32
>@@ -4404,13 +4463,13 @@ Target_powerpc<size, big_endian>::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<size, big_endian>(this, iplt_rel,
>+ = new Output_data_plt_powerpc<size, big_endian>(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<size, big_endian>::make_iplt_section(Symbol_table* symtab,
>
> template<int size, bool big_endian>
> void
>-Target_powerpc<size, big_endian>::make_lplt_section(Layout* layout)
>+Target_powerpc<size, big_endian>::make_lplt_section(Symbol_table* symtab,
>+ Layout* layout)
> {
> if (this->lplt_ == NULL)
> {
>@@ -4435,7 +4495,7 @@ Target_powerpc<size, big_endian>::make_lplt_section(Layout* layout)
> ->add_output_section_data(lplt_rel);
> }
> this->lplt_
>- = new Output_data_plt_powerpc<size, big_endian>(this, lplt_rel,
>+ = new Output_data_plt_powerpc<size, big_endian>(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<size, big_endian>::make_plt_entry(Symbol_table* symtab,
> template<int size, bool big_endian>
> void
> Target_powerpc<size, big_endian>::make_local_plt_entry(
>+ Symbol_table* symtab,
> Layout* layout,
> Sized_relobj_file<size, big_endian>* 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<int size, bool big_endian>
>+void
>+Target_powerpc<size, big_endian>::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<int size, bool big_endian>
>@@ -7552,6 +7624,16 @@ Target_powerpc<size, big_endian>::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<size, big_endian>::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<size, big_endian>::Scan::local(
> if (!is_ifunc)
> {
> unsigned int r_sym = elfcpp::elf_r_sym<size>(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<size, big_endian>::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<size, big_endian>::Relocate::relocate(
> bool has_stub_value = false;
> bool localentry0 = false;
> unsigned int r_sym = elfcpp::elf_r_sym<size>(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<size>(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<size>(r_type)
> && r_type != elfcpp::R_PPC64_PLT_PCREL34
>@@ -10567,7 +10665,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
> gold_assert(has_stub_value || !(os->flags() & elfcpp::SHF_ALLOC));
> }
>
>- if (has_plt_offset && (is_plt16_reloc<size>(r_type)
>+ if (use_plt_offset && (is_plt16_reloc<size>(r_type)
> || r_type == elfcpp::R_PPC64_PLT_PCREL34
> || r_type == elfcpp::R_PPC64_PLT_PCREL34_NOTOC))
> {
>@@ -10597,7 +10695,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
> value -= target->toc_pointer();
> }
> }
>- else if (!has_plt_offset
>+ else if (!use_plt_offset
> && (is_plt16_reloc<size>(r_type)
> || r_type == elfcpp::R_POWERPC_PLTSEQ
> || r_type == elfcpp::R_PPC64_PLTSEQ_NOTOC))
>@@ -10606,7 +10704,7 @@ Target_powerpc<size, big_endian>::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<size, big_endian>::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
>
>--
>Alan Modra
>Australia Development Lab, IBM
(drive-by: feel free to ignore)
If writing a test (auto-numbered targets in automake file) is painful,
perhaps gold needs run_dump_test style separate tests... , perhaps using
a better form (https://sourceware.org/bugzilla/show_bug.cgi?id=28602)
More information about the Binutils
mailing list