* powerpc.cc (Target_powerpc::Scan::reloc_needs_plt_for_ifunc): Make

static and public.  Add report_err param.  Return false for data refs.
	(Target_powerpc::rela_dyn_section): New overloaded function.
	(Target_powerpc::plt_, iplt_): Elucidate.
	(Output_data_plt_powerpc::entry_count): Handle current_data_size()==0.
	(Output_data_plt_powerpc::do_write): Don't write .iplt.
	(Output_data_plt_powerpc::plt_entry_count): Don't add .iplt entries.
	(Target_powerpc::Scan::local, global): Adjust reloc_needs_plt_for_ifunc
	calls.  Put ifunc dynamic relocs in irela_dyn_section.  Only
	push_branch and make_plt_entry for ifunc syms when
	reloc_needs_plt_for_ifunc.
	(Target_powerpc::Relocate::relocate): Don't use plt entry value
	for ifunc unless reloc_needs_plt_for_ifunc.
This commit is contained in:
Alan Modra 2013-03-15 08:40:49 +00:00
parent ad3d8a2f04
commit b3ccdeb53f
2 changed files with 140 additions and 86 deletions

View File

@ -1,3 +1,19 @@
2013-03-15 Alan Modra <amodra@gmail.com>
* powerpc.cc (Target_powerpc::Scan::reloc_needs_plt_for_ifunc): Make
static and public. Add report_err param. Return false for data refs.
(Target_powerpc::rela_dyn_section): New overloaded function.
(Target_powerpc::plt_, iplt_): Elucidate.
(Output_data_plt_powerpc::entry_count): Handle current_data_size()==0.
(Output_data_plt_powerpc::do_write): Don't write .iplt.
(Output_data_plt_powerpc::plt_entry_count): Don't add .iplt entries.
(Target_powerpc::Scan::local, global): Adjust reloc_needs_plt_for_ifunc
calls. Put ifunc dynamic relocs in irela_dyn_section. Only
push_branch and make_plt_entry for ifunc syms when
reloc_needs_plt_for_ifunc.
(Target_powerpc::Relocate::relocate): Don't use plt entry value
for ifunc unless reloc_needs_plt_for_ifunc.
2013-03-15 Alan Modra <amodra@gmail.com>
* gc.h (gc_process_relocs): Don't look through function descriptors.

View File

@ -899,6 +899,10 @@ class Target_powerpc : public Sized_target<size, big_endian>
return !is_branch_reloc(r_type);
}
static bool
reloc_needs_plt_for_ifunc(Sized_relobj_file<size, big_endian>* object,
unsigned int r_type, bool report_err);
private:
static void
unsupported_reloc_local(Sized_relobj_file<size, big_endian>*,
@ -915,10 +919,6 @@ class Target_powerpc : public Sized_target<size, big_endian>
void
check_non_pic(Relobj*, unsigned int r_type);
bool
reloc_needs_plt_for_ifunc(Sized_relobj_file<size, big_endian>* object,
unsigned int r_type);
// Whether we have issued an error about a non-PIC compilation.
bool issued_non_pic_error_;
};
@ -1068,6 +1068,10 @@ class Target_powerpc : public Sized_target<size, big_endian>
Reloc_section*
rela_dyn_section(Layout*);
// Similarly, but for ifunc symbols get the one for ifunc.
Reloc_section*
rela_dyn_section(Symbol_table*, Layout*, bool for_ifunc);
// Copy a relocation against a global symbol.
void
copy_reloc(Symbol_table* symtab, Layout* layout,
@ -1144,9 +1148,30 @@ class Target_powerpc : public Sized_target<size, big_endian>
// The GOT section.
Output_data_got_powerpc<size, big_endian>* got_;
// The PLT section.
// The PLT section. This is a container for a table of addresses,
// and their relocations. Each address in the PLT has a dynamic
// relocation (R_*_JMP_SLOT) and each address will have a
// corresponding entry in .glink for lazy resolution of the PLT.
// ppc32 initialises the PLT to point at the .glink entry, while
// ppc64 leaves this to ld.so. To make a call via the PLT, the
// linker adds a stub that loads the PLT entry into ctr then
// branches to ctr. There may be more than one stub for each PLT
// entry. DT_JMPREL points at the first PLT dynamic relocation and
// DT_PLTRELSZ gives the total size of PLT dynamic relocations.
Output_data_plt_powerpc<size, big_endian>* plt_;
// The IPLT section.
// The IPLT section. Like plt_, this is a container for a table of
// addresses and their relocations, specifically for STT_GNU_IFUNC
// functions that resolve locally (STT_GNU_IFUNC functions that
// don't resolve locally go in PLT). Unlike plt_, these have no
// entry in .glink for lazy resolution, and the relocation section
// does not have a 1-1 correspondence with IPLT addresses. In fact,
// the relocation section may contain relocations against
// STT_GNU_IFUNC symbols at locations outside of IPLT. The
// relocation section will appear at the end of other dynamic
// relocations, so that ld.so applies these relocations after other
// dynamic relocations. In a static executable, the relocation
// section is emitted and marked with __rela_iplt_start and
// __rela_iplt_end symbols.
Output_data_plt_powerpc<size, big_endian>* iplt_;
// Section holding long branch destinations.
Output_data_brlt_powerpc<size, big_endian>* brlt_section_;
@ -2122,6 +2147,22 @@ Target_powerpc<size, big_endian>::rela_dyn_section(Layout* layout)
return this->rela_dyn_;
}
// Similarly, but for ifunc symbols get the one for ifunc.
template<int size, bool big_endian>
typename Target_powerpc<size, big_endian>::Reloc_section*
Target_powerpc<size, big_endian>::rela_dyn_section(Symbol_table* symtab,
Layout* layout,
bool for_ifunc)
{
if (!for_ifunc)
return this->rela_dyn_section(layout);
if (this->iplt_ == NULL)
this->make_iplt_section(symtab, layout);
return this->iplt_->rel_plt();
}
class Stub_control
{
public:
@ -2660,6 +2701,8 @@ class Output_data_plt_powerpc : public Output_section_data_build
unsigned int
entry_count() const
{
if (this->current_data_size() == 0)
return 0;
return ((this->current_data_size() - this->initial_plt_entry_size_)
/ plt_entry_size);
}
@ -2838,7 +2881,7 @@ template<int size, bool big_endian>
void
Output_data_plt_powerpc<size, big_endian>::do_write(Output_file* of)
{
if (size == 32)
if (size == 32 && this->name_[3] != 'I')
{
const section_size_type offset = this->offset();
const section_size_type oview_size
@ -2880,7 +2923,7 @@ Target_powerpc<size, big_endian>::make_plt_section(Symbol_table* symtab,
// Ensure that .rela.dyn always appears before .rela.plt This is
// necessary due to how, on PowerPC and some other targets, .rela.dyn
// needs to include .rela.plt in it's range.
// needs to include .rela.plt in its range.
this->rela_dyn_section(layout);
Reloc_section* plt_rel = new Reloc_section(false);
@ -4543,10 +4586,7 @@ Target_powerpc<size, big_endian>::plt_entry_count() const
{
if (this->plt_ == NULL)
return 0;
unsigned int count = this->plt_->entry_count();
if (this->iplt_ != NULL)
count += this->iplt_->entry_count();
return count;
return this->plt_->entry_count();
}
// Return the offset of the first non-reserved PLT entry.
@ -4790,7 +4830,8 @@ template<int size, bool big_endian>
bool
Target_powerpc<size, big_endian>::Scan::reloc_needs_plt_for_ifunc(
Sized_relobj_file<size, big_endian>* object,
unsigned int r_type)
unsigned int r_type,
bool report_err)
{
// In non-pic code any reference will resolve to the plt call stub
// for the ifunc symbol.
@ -4799,29 +4840,29 @@ Target_powerpc<size, big_endian>::Scan::reloc_needs_plt_for_ifunc(
switch (r_type)
{
// Word size refs from data sections are OK.
// Word size refs from data sections are OK, but don't need a PLT entry.
case elfcpp::R_POWERPC_ADDR32:
case elfcpp::R_POWERPC_UADDR32:
if (size == 32)
return true;
return false;
break;
case elfcpp::R_PPC64_ADDR64:
case elfcpp::R_PPC64_UADDR64:
if (size == 64)
return true;
return false;
break;
// GOT refs are good.
// GOT refs are good, but also don't need a PLT entry.
case elfcpp::R_POWERPC_GOT16:
case elfcpp::R_POWERPC_GOT16_LO:
case elfcpp::R_POWERPC_GOT16_HI:
case elfcpp::R_POWERPC_GOT16_HA:
case elfcpp::R_PPC64_GOT16_DS:
case elfcpp::R_PPC64_GOT16_LO_DS:
return true;
return false;
// So are function calls.
// Function calls are good, and these do need a PLT entry.
case elfcpp::R_POWERPC_ADDR24:
case elfcpp::R_POWERPC_ADDR14:
case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
@ -4846,7 +4887,8 @@ Target_powerpc<size, big_endian>::Scan::reloc_needs_plt_for_ifunc(
// writable and non-executable to apply text relocations. So we'll
// segfault when trying to run the indirection function to resolve
// the reloc.
gold_error(_("%s: unsupported reloc %u for IFUNC symbol"),
if (report_err)
gold_error(_("%s: unsupported reloc %u for IFUNC symbol"),
object->name().c_str(), r_type);
return false;
}
@ -4900,7 +4942,7 @@ Target_powerpc<size, big_endian>::Scan::local(
// A local STT_GNU_IFUNC symbol may require a PLT entry.
bool is_ifunc = lsym.get_st_type() == elfcpp::STT_GNU_IFUNC;
if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type))
if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type, true))
{
unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
@ -4966,18 +5008,14 @@ Target_powerpc<size, big_endian>::Scan::local(
if (parameters->options().output_is_position_independent()
|| (size == 64 && is_ifunc))
{
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
Reloc_section* rela_dyn = target->rela_dyn_section(symtab, layout,
is_ifunc);
if ((size == 32 && r_type == elfcpp::R_POWERPC_ADDR32)
|| (size == 64 && r_type == elfcpp::R_PPC64_ADDR64))
{
unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
unsigned int dynrel = elfcpp::R_POWERPC_RELATIVE;
if (is_ifunc)
{
rela_dyn = target->iplt_section()->rel_plt();
dynrel = elfcpp::R_POWERPC_IRELATIVE;
}
unsigned int dynrel = (is_ifunc ? elfcpp::R_POWERPC_IRELATIVE
: elfcpp::R_POWERPC_RELATIVE);
rela_dyn->add_local_relative(object, r_sym, dynrel,
output_section, data_shndx,
reloc.get_r_offset(),
@ -4997,17 +5035,13 @@ Target_powerpc<size, big_endian>::Scan::local(
case elfcpp::R_POWERPC_REL24:
case elfcpp::R_PPC_PLTREL24:
case elfcpp::R_PPC_LOCAL24PC:
target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
reloc.get_r_addend());
break;
case elfcpp::R_POWERPC_REL14:
case elfcpp::R_POWERPC_REL14_BRTAKEN:
case elfcpp::R_POWERPC_REL14_BRNTAKEN:
target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
reloc.get_r_addend());
if (!is_ifunc)
target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
reloc.get_r_addend());
break;
case elfcpp::R_PPC64_REL64:
@ -5073,13 +5107,10 @@ Target_powerpc<size, big_endian>::Scan::local(
off = got->add_constant(0);
object->set_local_got_offset(r_sym, GOT_TYPE_STANDARD, off);
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
unsigned int dynrel = elfcpp::R_POWERPC_RELATIVE;
if (is_ifunc)
{
rela_dyn = target->iplt_section()->rel_plt();
dynrel = elfcpp::R_POWERPC_IRELATIVE;
}
Reloc_section* rela_dyn = target->rela_dyn_section(symtab, layout,
is_ifunc);
unsigned int dynrel = (is_ifunc ? elfcpp::R_POWERPC_IRELATIVE
: elfcpp::R_POWERPC_RELATIVE);
rela_dyn->add_local_relative(object, r_sym, dynrel,
got, off, 0, false);
}
@ -5260,8 +5291,8 @@ Target_powerpc<size, big_endian>::Scan::global(
= static_cast<Powerpc_relobj<size, big_endian>*>(object);
// A STT_GNU_IFUNC symbol may require a PLT entry.
if (gsym->type() == elfcpp::STT_GNU_IFUNC
&& this->reloc_needs_plt_for_ifunc(object, r_type))
bool is_ifunc = gsym->type() == elfcpp::STT_GNU_IFUNC;
if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type, true))
{
target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
@ -5337,11 +5368,14 @@ Target_powerpc<size, big_endian>::Scan::global(
// Make a PLT entry if necessary.
if (gsym->needs_plt_entry())
{
target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
r_type,
elfcpp::elf_r_sym<size>(reloc.get_r_info()),
reloc.get_r_addend());
target->make_plt_entry(symtab, layout, gsym);
if (!is_ifunc)
{
target->push_branch(ppc_object, data_shndx,
reloc.get_r_offset(), r_type,
elfcpp::elf_r_sym<size>(reloc.get_r_info()),
reloc.get_r_addend());
target->make_plt_entry(symtab, layout, gsym);
}
// Since this is not a PC-relative relocation, we may be
// taking the address of a function. In that case we need to
// set the entry in the dynamic symbol table to the address of
@ -5353,7 +5387,7 @@ Target_powerpc<size, big_endian>::Scan::global(
}
// Make a dynamic relocation if necessary.
if (needs_dynamic_reloc<size>(gsym, Scan::get_reference_flags(r_type))
|| (size == 64 && gsym->type() == elfcpp::STT_GNU_IFUNC))
|| (size == 64 && is_ifunc))
{
if (gsym->may_need_copy_reloc())
{
@ -5370,20 +5404,18 @@ Target_powerpc<size, big_endian>::Scan::global(
&& (gsym->can_use_relative_reloc(false)
|| data_shndx == ppc_object->opd_shndx())))
{
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
unsigned int dynrel = elfcpp::R_POWERPC_RELATIVE;
if (gsym->type() == elfcpp::STT_GNU_IFUNC)
{
rela_dyn = target->iplt_section()->rel_plt();
dynrel = elfcpp::R_POWERPC_IRELATIVE;
}
Reloc_section* rela_dyn
= target->rela_dyn_section(symtab, layout, is_ifunc);
unsigned int dynrel = (is_ifunc ? elfcpp::R_POWERPC_IRELATIVE
: elfcpp::R_POWERPC_RELATIVE);
rela_dyn->add_symbolless_global_addend(
gsym, dynrel, output_section, object, data_shndx,
reloc.get_r_offset(), reloc.get_r_addend());
}
else
{
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
Reloc_section* rela_dyn
= target->rela_dyn_section(symtab, layout, is_ifunc);
check_non_pic(object, r_type);
rela_dyn->add_global(gsym, r_type, output_section,
object, data_shndx,
@ -5396,15 +5428,19 @@ Target_powerpc<size, big_endian>::Scan::global(
case elfcpp::R_PPC_PLTREL24:
case elfcpp::R_POWERPC_REL24:
target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
reloc.get_r_addend());
if (gsym->needs_plt_entry()
|| (!gsym->final_value_is_known()
&& (gsym->is_undefined()
|| gsym->is_from_dynobj()
|| gsym->is_preemptible())))
target->make_plt_entry(symtab, layout, gsym);
if (!is_ifunc)
{
target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
r_type,
elfcpp::elf_r_sym<size>(reloc.get_r_info()),
reloc.get_r_addend());
if (gsym->needs_plt_entry()
|| (!gsym->final_value_is_known()
&& (gsym->is_undefined()
|| gsym->is_from_dynobj()
|| gsym->is_preemptible())))
target->make_plt_entry(symtab, layout, gsym);
}
// Fall thru
case elfcpp::R_PPC64_REL64:
@ -5420,7 +5456,8 @@ Target_powerpc<size, big_endian>::Scan::global(
}
else
{
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
Reloc_section* rela_dyn
= target->rela_dyn_section(symtab, layout, is_ifunc);
check_non_pic(object, r_type);
rela_dyn->add_global(gsym, r_type, output_section, object,
data_shndx, reloc.get_r_offset(),
@ -5432,9 +5469,10 @@ Target_powerpc<size, big_endian>::Scan::global(
case elfcpp::R_POWERPC_REL14:
case elfcpp::R_POWERPC_REL14_BRTAKEN:
case elfcpp::R_POWERPC_REL14_BRNTAKEN:
target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
reloc.get_r_addend());
if (!is_ifunc)
target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
reloc.get_r_addend());
break;
case elfcpp::R_POWERPC_REL16:
@ -5484,7 +5522,7 @@ Target_powerpc<size, big_endian>::Scan::global(
got = target->got_section(symtab, layout);
if (gsym->final_value_is_known())
{
if (size == 32 && gsym->type() == elfcpp::STT_GNU_IFUNC)
if (size == 32 && is_ifunc)
got->add_global_plt(gsym, GOT_TYPE_STANDARD);
else
got->add_global(gsym, GOT_TYPE_STANDARD);
@ -5496,18 +5534,16 @@ Target_powerpc<size, big_endian>::Scan::global(
unsigned int off = got->add_constant(0);
gsym->set_got_offset(GOT_TYPE_STANDARD, off);
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
Reloc_section* rela_dyn
= target->rela_dyn_section(symtab, layout, is_ifunc);
if (gsym->can_use_relative_reloc(false)
&& !(size == 32
&& gsym->visibility() == elfcpp::STV_PROTECTED
&& parameters->options().shared()))
{
unsigned int dynrel = elfcpp::R_POWERPC_RELATIVE;
if (gsym->type() == elfcpp::STT_GNU_IFUNC)
{
rela_dyn = target->iplt_section()->rel_plt();
dynrel = elfcpp::R_POWERPC_IRELATIVE;
}
unsigned int dynrel = (is_ifunc ? elfcpp::R_POWERPC_IRELATIVE
: elfcpp::R_POWERPC_RELATIVE);
rela_dyn->add_global_relative(gsym, dynrel, got, off, 0, false);
}
else
@ -5540,8 +5576,8 @@ Target_powerpc<size, big_endian>::Scan::global(
{
Output_data_got_powerpc<size, big_endian>* got
= target->got_section(symtab, layout);
got->add_global_pair_with_rel(gsym, GOT_TYPE_TLSGD,
target->rela_dyn_section(layout),
Reloc_section* rela_dyn = target->rela_dyn_section(layout);
got->add_global_pair_with_rel(gsym, GOT_TYPE_TLSGD, rela_dyn,
elfcpp::R_POWERPC_DTPMOD,
elfcpp::R_POWERPC_DTPREL);
}
@ -6178,9 +6214,11 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
Address value = 0;
bool has_plt_value = false;
unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
if (gsym != NULL
? use_plt_offset<size>(gsym, Scan::get_reference_flags(r_type))
: object->local_has_plt_offset(r_sym))
if ((gsym != NULL
? use_plt_offset<size>(gsym, Scan::get_reference_flags(r_type))
: object->local_has_plt_offset(r_sym))
&& (!psymval->is_ifunc_symbol()
|| Scan::reloc_needs_plt_for_ifunc(object, r_type, false)))
{
Stub_table<size, big_endian>* stub_table
= object->stub_table(relinfo->data_shndx);