[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.
This commit is contained in:
Alan Modra
2021-11-25 00:00:50 +10:30
parent 74044dc840
commit f21dbd7c80

View File

@@ -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