Support PLT16 relocs against local symbols

Necessary if gcc is to use PLT16 relocs to implement -mlongcall, and
there isn't a good technical reason why local symbols should be
excluded from PLT16 support.  Non-ifunc local symbol PLT entries go in
a separate section to other PLT entries.  In a fixed position
executable they won't need to be relocated, and in a PIE or shared
library I chose to not implement lazy relocation.

bfd/
	* elf64-ppc.c (LOCAL_PLT_ENTRY_SIZE): Define.
	(struct ppc_stub_hash_entry): Add symtype field.
	(PLT_KEEP): Define.
	(struct ppc_link_hash_table): Add pltlocal and relpltlocal.
	(create_linkage_sections): Create pltlocal and relpltlocal.
	(ppc64_elf_check_relocs): Allow PLT relocs on local symbols.
	Set PLT_KEEP.
	(ppc64_elf_adjust_dynamic_symbol): Keep PLT entries for inline calls.
	(allocate_dynrelocs): Allocate pltlocal and relpltlocal.
	(ppc64_elf_size_dynamic_sections): Size pltlocal and relpltlocal.
	Keep PLT entries for inline calls against locals.
	(ppc_build_one_stub): Use pltlocal as appropriate.
	(ppc_size_one_stub): Likewise.
	(ppc64_elf_size_stubs): Set symtype.
	(build_global_entry_stubs_and_plt): Init pltlocal and write
	relpltlocal for globals.
	(write_plt_relocs_for_local_syms): Likewise for local syms.
	(ppc64_elf_relocate_section): Support PLT for local syms.
	* elf32-ppc.c (PLT_KEEP): Define.
	(struct ppc_elf_link_hash_table): Add pltlocal and relpltlocal.
	(ppc_elf_create_glink): Create pltlocal and relpltlocal.
	(ppc_elf_check_relocs): Allow PLT relocs on local symbols.
	Set PLT_KEEP.  Adjust update_local_sym_info call.
	(ppc_elf_adjust_dynamic_symbol): Keep PLT entries for inline calls.
	(allocate_dynrelocs): Allocate pltlocal and relpltlocal.
	(ppc_elf_size_dynamic_sections): Size pltlocal and relpltlocal.
	(ppc_elf_relocate_section): Support PLT16 relocs for local syms.
	(write_global_sym_plt): Init pltlocal and write relpltlocal.
	(ppc_finish_symbols): Likewise for locals.
ld/
	* emulparams/elf32ppc.sh (OTHER_RELRO_SECTIONS_2): Add .branch_lt.
	(OTHER_GOT_RELOC_SECTIONS): Add .rela.branch_lt.
	* testsuite/ld-powerpc/elfv2so.d: Update for symbol/stub reordering.
	* testsuite/ld-powerpc/relbrlt.d: Likewise.
	* testsuite/ld-powerpc/relbrlt.s: Likewise.
	* testsuite/ld-powerpc/tlsso.r: Likewise.
	* testsuite/ld-powerpc/tlstocso.r: Likewise.
gold/
	* powerpc.cc (Target_powerpc::lplt_): New variable.
	(Target_powerpc::lplt_section): Associated accessor.
	(Target_powerpc::plt_off): Handle local non-ifunc symbols.
	(Target_powerpc::make_lplt_section): New function.
	(Target_powerpc::make_local_plt_entry): New function.
	(Powerpc_relobj::do_relocate_sections): Write out lplt.
	(Output_data_plt_powerpc::first_plt_entry_offset): Zero for lplt.
	(Output_data_plt_powerpc::add_local_entry): New function.
	(Output_data_plt_powerpc::do_write): Ignore lplt.
	(Target_powerpc::make_iplt_section): Make lplt first.
	(Target_powerpc::make_brlt_section): Make .branch_lt relro.
	(Target_powerpc::Scan::local): Handle PLT16 relocs.
This commit is contained in:
Alan Modra
2018-04-09 09:22:53 +09:30
parent 49c09209d0
commit 2d7ad24e87
12 changed files with 701 additions and 231 deletions

View File

@@ -1,3 +1,18 @@
2018-04-09 Alan Modra <amodra@gmail.com>
* powerpc.cc (Target_powerpc::lplt_): New variable.
(Target_powerpc::lplt_section): Associated accessor.
(Target_powerpc::plt_off): Handle local non-ifunc symbols.
(Target_powerpc::make_lplt_section): New function.
(Target_powerpc::make_local_plt_entry): New function.
(Powerpc_relobj::do_relocate_sections): Write out lplt.
(Output_data_plt_powerpc::first_plt_entry_offset): Zero for lplt.
(Output_data_plt_powerpc::add_local_entry): New function.
(Output_data_plt_powerpc::do_write): Ignore lplt.
(Target_powerpc::make_iplt_section): Make lplt first.
(Target_powerpc::make_brlt_section): Make .branch_lt relro.
(Target_powerpc::Scan::local): Handle PLT16 relocs.
2018-04-09 Alan Modra <amodra@gmail.com>
* powerpc.cc (Target_powerpc::plt_off): New functions.

View File

@@ -607,7 +607,7 @@ class Target_powerpc : public Sized_target<size, big_endian>
Target_powerpc()
: Sized_target<size, big_endian>(&powerpc_info),
got_(NULL), plt_(NULL), iplt_(NULL), brlt_section_(NULL),
got_(NULL), plt_(NULL), iplt_(NULL), lplt_(NULL), brlt_section_(NULL),
glink_(NULL), rela_dyn_(NULL), copy_relocs_(),
tlsld_got_offset_(-1U),
stub_tables_(), branch_lookup_table_(), branch_info_(), tocsave_loc_(),
@@ -860,6 +860,13 @@ class Target_powerpc : public Sized_target<size, big_endian>
return this->iplt_;
}
// Get the LPLT section.
const Output_data_plt_powerpc<size, big_endian>*
lplt_section() const
{
return this->lplt_;
}
// Return the plt offset and section for the given global sym.
Address
plt_off(const Symbol* gsym,
@@ -879,7 +886,11 @@ class Target_powerpc : public Sized_target<size, big_endian>
unsigned int local_sym_index,
const Output_data_plt_powerpc<size, big_endian>** sec) const
{
*sec = this->iplt_section();
const Symbol_value<size>* lsym = relobj->local_symbol(local_sym_index);
if (lsym->is_ifunc_symbol())
*sec = this->iplt_section();
else
*sec = this->lplt_section();
return relobj->local_plt_offset(local_sym_index);
}
@@ -1440,6 +1451,9 @@ class Target_powerpc : public Sized_target<size, big_endian>
void
make_iplt_section(Symbol_table*, Layout*);
void
make_lplt_section(Layout*);
void
make_brlt_section(Layout*);
@@ -1453,6 +1467,12 @@ class Target_powerpc : public Sized_target<size, big_endian>
Sized_relobj_file<size, big_endian>*,
unsigned int);
// Create a PLT entry for a local non-IFUNC symbol.
void
make_local_plt_entry(Layout*,
Sized_relobj_file<size, big_endian>*,
unsigned int);
// Create a GOT entry for local dynamic __tls_get_addr.
unsigned int
@@ -1585,6 +1605,8 @@ class Target_powerpc : public Sized_target<size, big_endian>
// section is emitted and marked with __rela_iplt_start and
// __rela_iplt_end symbols.
Output_data_plt_powerpc<size, big_endian>* iplt_;
// A PLT style section for local, non-ifunc symbols
Output_data_plt_powerpc<size, big_endian>* lplt_;
// Section holding long branch destinations.
Output_data_brlt_powerpc<size, big_endian>* brlt_section_;
// The .glink section.
@@ -2469,6 +2491,35 @@ 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);
if (size == 64)
value += ppc64_local_entry_offset(i);
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.
@@ -3667,6 +3718,9 @@ class Output_data_plt_powerpc : public Output_section_data_build
void
add_ifunc_entry(Symbol*);
void
add_local_entry(Sized_relobj_file<size, big_endian>*, unsigned int);
void
add_local_ifunc_entry(Sized_relobj_file<size, big_endian>*, unsigned int);
@@ -3704,8 +3758,8 @@ class Output_data_plt_powerpc : public Output_section_data_build
unsigned int
first_plt_entry_offset() const
{
// IPLT has no reserved entry.
if (this->name_[3] == 'I')
// IPLT and LPLT have no reserved entry.
if (this->name_[3] == 'I' || this->name_[3] == 'L')
return 0;
return this->targ_->first_plt_entry_offset();
}
@@ -3768,6 +3822,31 @@ Output_data_plt_powerpc<size, big_endian>::add_ifunc_entry(Symbol* gsym)
}
}
// Add an entry for a local symbol to the PLT.
template<int size, bool big_endian>
void
Output_data_plt_powerpc<size, big_endian>::add_local_entry(
Sized_relobj_file<size, big_endian>* relobj,
unsigned int local_sym_index)
{
if (!relobj->local_has_plt_offset(local_sym_index))
{
section_size_type off = this->current_data_size();
relobj->set_local_plt_offset(local_sym_index, off);
if (this->rel_)
{
unsigned int dynrel = elfcpp::R_POWERPC_RELATIVE;
if (size == 64 && this->targ_->abiversion() < 2)
dynrel = elfcpp::R_POWERPC_JMP_SLOT;
this->rel_->add_symbolless_local_addend(relobj, local_sym_index,
dynrel, this, off, 0);
}
off += this->plt_entry_size();
this->set_current_data_size(off);
}
}
// Add an entry for a local ifunc symbol to the IPLT.
template<int size, bool big_endian>
@@ -3888,7 +3967,7 @@ template<int size, bool big_endian>
void
Output_data_plt_powerpc<size, big_endian>::do_write(Output_file* of)
{
if (size == 32 && this->name_[3] != 'I')
if (size == 32 && (this->name_[3] != 'I' && this->name_[3] != 'L'))
{
const section_size_type offset = this->offset();
const section_size_type oview_size
@@ -3966,6 +4045,7 @@ 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);
Reloc_section* iplt_rel = new Reloc_section(false);
if (this->rela_dyn_->output_section())
@@ -3978,6 +4058,40 @@ Target_powerpc<size, big_endian>::make_iplt_section(Symbol_table* symtab,
}
}
// Create the LPLT section.
template<int size, bool big_endian>
void
Target_powerpc<size, big_endian>::make_lplt_section(Layout* layout)
{
if (this->lplt_ == NULL)
{
Reloc_section* lplt_rel = NULL;
if (parameters->options().output_is_position_independent())
{
lplt_rel = new Reloc_section(false);
this->rela_dyn_section(layout);
if (this->rela_dyn_->output_section())
this->rela_dyn_->output_section()
->add_output_section_data(lplt_rel);
}
this->lplt_
= new Output_data_plt_powerpc<size, big_endian>(this, lplt_rel,
"** LPLT");
this->make_brlt_section(layout);
if (this->brlt_section_ && this->brlt_section_->output_section())
this->brlt_section_->output_section()
->add_output_section_data(this->lplt_);
else
layout->add_output_section_data(".branch_lt",
elfcpp::SHT_PROGBITS,
elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE,
this->lplt_,
ORDER_RELRO,
true);
}
}
// A section for huge long branch addresses, similar to plt section.
template<int size, bool big_endian>
@@ -6132,6 +6246,20 @@ Target_powerpc<size, big_endian>::make_plt_entry(Symbol_table* symtab,
}
}
// Make a PLT entry for a local symbol.
template<int size, bool big_endian>
void
Target_powerpc<size, big_endian>::make_local_plt_entry(
Layout* layout,
Sized_relobj_file<size, big_endian>* relobj,
unsigned int r_sym)
{
if (this->lplt_ == NULL)
this->make_lplt_section(layout);
this->lplt_->add_local_entry(relobj, r_sym);
}
// Make a PLT entry for a local STT_GNU_IFUNC symbol.
template<int size, bool big_endian>
@@ -6669,6 +6797,17 @@ Target_powerpc<size, big_endian>::Scan::local(
}
break;
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:
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);
}
break;
case elfcpp::R_POWERPC_REL24:
case elfcpp::R_PPC_PLTREL24:
case elfcpp::R_PPC_LOCAL24PC: