aboutsummaryrefslogtreecommitdiff
path: root/gold
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2021-11-25 00:00:50 +1030
committerAlan Modra <amodra@gmail.com>2021-12-06 22:16:46 +1030
commitf21dbd7c8038acee8ece4d57f2454083c37e98f6 (patch)
treee7d3d9725fd59d0385bbdcbe1cdfcc8527c4e2e7 /gold
parent74044dc84074abca775c63462ecccc10a7900987 (diff)
downloadfsf-binutils-gdb-f21dbd7c8038acee8ece4d57f2454083c37e98f6.zip
fsf-binutils-gdb-f21dbd7c8038acee8ece4d57f2454083c37e98f6.tar.gz
fsf-binutils-gdb-f21dbd7c8038acee8ece4d57f2454083c37e98f6.tar.bz2
[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.
Diffstat (limited to 'gold')
-rw-r--r--gold/powerpc.cc226
1 files changed, 162 insertions, 64 deletions
diff --git a/gold/powerpc.cc b/gold/powerpc.cc
index 3a6d3c6..103eef8 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