Fixes to powerpc64 gold ELFv2 support

* powerpc.cc (Target_powerpc::glink_section): Provide non-const
	accessor.
	(Target_powerpc::Branch_info::make_stub): Make global entry stubs.
	Only call ppc64_local_entry_offset for 64-bit.  Restrict
	symval_for_branch lookup to ELFv1.
	(Stub_table::add_plt_call_entry): Use unsigned int off.
	(Output_data_glink::Address, invalid_address): New.
	(Output_data_glink::add_eh_frame): Move out of line.  Add
	support for ELFv2.
	(Output_data_glink::add_global_entry, find_global_entry,
	global_entry_address): New functions.
	(Output_data_glink::global_entry_stubs_, end_branch_table_,
	ge_size): New variables.
	(Output_data_glink::set_final_data_size): Add global entry
	stub sizing.
	(Output_data_glink::do_write): Write global entry stubs.
	(Target_powerpc::Scan::reloc_needs_plt_for_ifunc): Add target
	parameter.  Return true for ELFv2.  Adjust callers.
	(Target_powerpc::Scan::local, global): Restrict opd lookup to
	ELFv1.  Similarly for ifunc and dynamic relocation processing
	specific to ELFv1.  Recognize that symbols are defined on
	their plt entries for ELFv2.
	(Target_powerpc::symval_for_branch): Assert if called for
	ELFv2 or ppc32.
	(Target_powerpc::Relocate::relocate): Use global entry plt
	stub for symbol value if such exists on ELFv2.
	(Target_powerpc::Relocate::relocate): Don't call
	symval_for_branch when ELFv2.  Do adjust for local entry
	offset when ELFv2.
	(Target_powerpc::do_dynsym_value): Set symbols to global entry
	plt stub for ELFv2.
	(Target_powerpc::do_plt_address_for_global): Similarly.
This commit is contained in:
Alan Modra 2013-11-15 10:36:34 +10:30
parent ef1bc9e72f
commit 9055360d4a
2 changed files with 353 additions and 157 deletions

View File

@ -1,3 +1,38 @@
2013-11-15 Alan Modra <amodra@gmail.com>
* powerpc.cc (Target_powerpc::glink_section): Provide non-const
accessor.
(Target_powerpc::Branch_info::make_stub): Make global entry stubs.
Only call ppc64_local_entry_offset for 64-bit. Restrict
symval_for_branch lookup to ELFv1.
(Stub_table::add_plt_call_entry): Use unsigned int off.
(Output_data_glink::Address, invalid_address): New.
(Output_data_glink::add_eh_frame): Move out of line. Add
support for ELFv2.
(Output_data_glink::add_global_entry, find_global_entry,
global_entry_address): New functions.
(Output_data_glink::global_entry_stubs_, end_branch_table_,
ge_size): New variables.
(Output_data_glink::set_final_data_size): Add global entry
stub sizing.
(Output_data_glink::do_write): Write global entry stubs.
(Target_powerpc::Scan::reloc_needs_plt_for_ifunc): Add target
parameter. Return true for ELFv2. Adjust callers.
(Target_powerpc::Scan::local, global): Restrict opd lookup to
ELFv1. Similarly for ifunc and dynamic relocation processing
specific to ELFv1. Recognize that symbols are defined on
their plt entries for ELFv2.
(Target_powerpc::symval_for_branch): Assert if called for
ELFv2 or ppc32.
(Target_powerpc::Relocate::relocate): Use global entry plt
stub for symbol value if such exists on ELFv2.
(Target_powerpc::Relocate::relocate): Don't call
symval_for_branch when ELFv2. Do adjust for local entry
offset when ELFv2.
(Target_powerpc::do_dynsym_value): Set symbols to global entry
plt stub for ELFv2.
(Target_powerpc::do_plt_address_for_global): Similarly.
2013-11-14 Cary Coutant <ccoutant@google.com>
Revert patch -- this did not fix the problem, and there is

View File

@ -687,6 +687,13 @@ class Target_powerpc : public Sized_target<size, big_endian>
return this->glink_;
}
Output_data_glink<size, big_endian>*
glink_section()
{
gold_assert(this->glink_ != NULL);
return this->glink_;
}
bool has_glink() const
{ return this->glink_ != NULL; }
@ -974,7 +981,8 @@ class Target_powerpc : public Sized_target<size, big_endian>
}
static bool
reloc_needs_plt_for_ifunc(Sized_relobj_file<size, big_endian>* object,
reloc_needs_plt_for_ifunc(Target_powerpc<size, big_endian>* target,
Sized_relobj_file<size, big_endian>* object,
unsigned int r_type, bool report_err);
private:
@ -2530,20 +2538,29 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
? gsym->use_plt_offset(Scan::get_reference_flags(this->r_type_, target))
: this->object_->local_has_plt_offset(this->r_sym_))
{
if (stub_table == NULL)
stub_table = this->object_->stub_table(this->shndx_);
if (stub_table == NULL)
{
// This is a ref from a data section to an ifunc symbol.
stub_table = ifunc_stub_table;
}
gold_assert(stub_table != NULL);
if (gsym != NULL)
stub_table->add_plt_call_entry(this->object_, gsym,
this->r_type_, this->addend_);
if (size == 64
&& gsym != NULL
&& target->abiversion() >= 2
&& !parameters->options().output_is_position_independent()
&& !is_branch_reloc(this->r_type_))
target->glink_section()->add_global_entry(gsym);
else
stub_table->add_plt_call_entry(this->object_, this->r_sym_,
this->r_type_, this->addend_);
{
if (stub_table == NULL)
stub_table = this->object_->stub_table(this->shndx_);
if (stub_table == NULL)
{
// This is a ref from a data section to an ifunc symbol.
stub_table = ifunc_stub_table;
}
gold_assert(stub_table != NULL);
if (gsym != NULL)
stub_table->add_plt_call_entry(this->object_, gsym,
this->r_type_, this->addend_);
else
stub_table->add_plt_call_entry(this->object_, this->r_sym_,
this->r_type_, this->addend_);
}
}
else
{
@ -2590,7 +2607,8 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
to = symtab->compute_final_value<size>(gsym, &status);
if (status != Symbol_table::CFVS_OK)
return;
to += this->object_->ppc64_local_entry_offset(gsym);
if (size == 64)
to += this->object_->ppc64_local_entry_offset(gsym);
}
else
{
@ -2605,12 +2623,13 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
|| !symval.has_output_value())
return;
to = symval.value(this->object_, 0);
to += this->object_->ppc64_local_entry_offset(this->r_sym_);
if (size == 64)
to += this->object_->ppc64_local_entry_offset(this->r_sym_);
}
to += this->addend_;
if (stub_table == NULL)
stub_table = this->object_->stub_table(this->shndx_);
if (size == 64 && is_branch_reloc(this->r_type_))
if (size == 64 && target->abiversion() < 2)
{
unsigned int dest_shndx;
to = target->symval_for_branch(symtab, to, gsym,
@ -3036,6 +3055,7 @@ static const uint32_t ld_11_2 = 0xe9620000;
static const uint32_t ld_11_11 = 0xe96b0000;
static const uint32_t ld_12_2 = 0xe9820000;
static const uint32_t ld_12_11 = 0xe98b0000;
static const uint32_t ld_12_12 = 0xe98c0000;
static const uint32_t lfd_0_1 = 0xc8010000;
static const uint32_t li_0_0 = 0x38000000;
static const uint32_t li_12_0 = 0x39800000;
@ -3802,7 +3822,7 @@ Stub_table<size, big_endian>::add_plt_call_entry(
Address addend)
{
Plt_stub_ent ent(object, gsym, r_type, addend);
Address off = this->plt_size_;
unsigned int off = this->plt_size_;
std::pair<typename Plt_stub_entries::iterator, bool> p
= this->plt_call_stubs_.insert(std::make_pair(ent, off));
if (p.second)
@ -3818,7 +3838,7 @@ Stub_table<size, big_endian>::add_plt_call_entry(
Address addend)
{
Plt_stub_ent ent(object, locsym_index, r_type, addend);
Address off = this->plt_size_;
unsigned int off = this->plt_size_;
std::pair<typename Plt_stub_entries::iterator, bool> p
= this->plt_call_stubs_.insert(std::make_pair(ent, off));
if (p.second)
@ -3913,50 +3933,30 @@ template<int size, bool big_endian>
class Output_data_glink : public Output_section_data
{
public:
typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
static const Address invalid_address = static_cast<Address>(0) - 1;
static const int pltresolve_size = 16*4;
Output_data_glink(Target_powerpc<size, big_endian>* targ)
: Output_section_data(16), targ_(targ)
: Output_section_data(16), targ_(targ), global_entry_stubs_(),
end_branch_table_(), ge_size_(0)
{ }
void
add_eh_frame(Layout* layout)
{
if (!parameters->options().ld_generated_unwind_info())
return;
add_eh_frame(Layout* layout);
if (size == 64)
{
if (this->targ_->abiversion() < 2)
layout->add_eh_frame_for_plt(this,
Eh_cie<64>::eh_frame_cie,
sizeof (Eh_cie<64>::eh_frame_cie),
glink_eh_frame_fde_64v1,
sizeof (glink_eh_frame_fde_64v1));
else
layout->add_eh_frame_for_plt(this,
Eh_cie<64>::eh_frame_cie,
sizeof (Eh_cie<64>::eh_frame_cie),
glink_eh_frame_fde_64v2,
sizeof (glink_eh_frame_fde_64v2));
}
else
{
// 32-bit .glink can use the default since the CIE return
// address reg, LR, is valid.
layout->add_eh_frame_for_plt(this,
Eh_cie<32>::eh_frame_cie,
sizeof (Eh_cie<32>::eh_frame_cie),
default_fde,
sizeof (default_fde));
// Except where LR is used in a PIC __glink_PLTresolve.
if (parameters->options().output_is_position_independent())
layout->add_eh_frame_for_plt(this,
Eh_cie<32>::eh_frame_cie,
sizeof (Eh_cie<32>::eh_frame_cie),
glink_eh_frame_fde_32,
sizeof (glink_eh_frame_fde_32));
}
void
add_global_entry(const Symbol*);
Address
find_global_entry(const Symbol*) const;
Address
global_entry_address() const
{
gold_assert(this->is_data_size_valid());
unsigned int global_entry_off = (this->end_branch_table_ + 15) & -16;
return this->address() + global_entry_off;
}
protected:
@ -3975,8 +3975,74 @@ class Output_data_glink : public Output_section_data
// Allows access to .got and .plt for do_write.
Target_powerpc<size, big_endian>* targ_;
// Map sym to stub offset.
typedef Unordered_map<const Symbol*, unsigned int> Global_entry_stub_entries;
Global_entry_stub_entries global_entry_stubs_;
unsigned int end_branch_table_, ge_size_;
};
template<int size, bool big_endian>
void
Output_data_glink<size, big_endian>::add_eh_frame(Layout* layout)
{
if (!parameters->options().ld_generated_unwind_info())
return;
if (size == 64)
{
if (this->targ_->abiversion() < 2)
layout->add_eh_frame_for_plt(this,
Eh_cie<64>::eh_frame_cie,
sizeof (Eh_cie<64>::eh_frame_cie),
glink_eh_frame_fde_64v1,
sizeof (glink_eh_frame_fde_64v1));
else
layout->add_eh_frame_for_plt(this,
Eh_cie<64>::eh_frame_cie,
sizeof (Eh_cie<64>::eh_frame_cie),
glink_eh_frame_fde_64v2,
sizeof (glink_eh_frame_fde_64v2));
}
else
{
// 32-bit .glink can use the default since the CIE return
// address reg, LR, is valid.
layout->add_eh_frame_for_plt(this,
Eh_cie<32>::eh_frame_cie,
sizeof (Eh_cie<32>::eh_frame_cie),
default_fde,
sizeof (default_fde));
// Except where LR is used in a PIC __glink_PLTresolve.
if (parameters->options().output_is_position_independent())
layout->add_eh_frame_for_plt(this,
Eh_cie<32>::eh_frame_cie,
sizeof (Eh_cie<32>::eh_frame_cie),
glink_eh_frame_fde_32,
sizeof (glink_eh_frame_fde_32));
}
}
template<int size, bool big_endian>
void
Output_data_glink<size, big_endian>::add_global_entry(const Symbol* gsym)
{
std::pair<typename Global_entry_stub_entries::iterator, bool> p
= this->global_entry_stubs_.insert(std::make_pair(gsym, this->ge_size_));
if (p.second)
this->ge_size_ += 16;
}
template<int size, bool big_endian>
typename Output_data_glink<size, big_endian>::Address
Output_data_glink<size, big_endian>::find_global_entry(const Symbol* gsym) const
{
typename Global_entry_stub_entries::const_iterator p
= this->global_entry_stubs_.find(gsym);
return p == this->global_entry_stubs_.end() ? invalid_address : p->second;
}
template<int size, bool big_endian>
void
Output_data_glink<size, big_endian>::set_final_data_size()
@ -4008,6 +4074,9 @@ Output_data_glink<size, big_endian>::set_final_data_size()
}
}
}
this->end_branch_table_ = total;
total = (total + 15) & -16;
total += this->ge_size_;
this->set_data_size(total);
}
@ -4341,64 +4410,101 @@ Output_data_glink<size, big_endian>::do_write(Output_file* of)
if (size == 64)
{
// Write pltresolve stub.
p = oview;
Address after_bcl = this->address() + 16;
Address pltoff = plt_base - after_bcl;
elfcpp::Swap<64, big_endian>::writeval(p, pltoff), p += 8;
if (this->targ_->abiversion() < 2)
if (this->end_branch_table_ != 0)
{
write_insn<big_endian>(p, mflr_12), p += 4;
write_insn<big_endian>(p, bcl_20_31), p += 4;
write_insn<big_endian>(p, mflr_11), p += 4;
write_insn<big_endian>(p, ld_2_11 + l(-16)), p += 4;
write_insn<big_endian>(p, mtlr_12), p += 4;
write_insn<big_endian>(p, add_11_2_11), p += 4;
write_insn<big_endian>(p, ld_12_11 + 0), p += 4;
write_insn<big_endian>(p, ld_2_11 + 8), p += 4;
write_insn<big_endian>(p, mtctr_12), p += 4;
write_insn<big_endian>(p, ld_11_11 + 16), p += 4;
}
else
{
write_insn<big_endian>(p, mflr_0), p += 4;
write_insn<big_endian>(p, bcl_20_31), p += 4;
write_insn<big_endian>(p, mflr_11), p += 4;
write_insn<big_endian>(p, ld_2_11 + l(-16)), p += 4;
write_insn<big_endian>(p, mtlr_0), p += 4;
write_insn<big_endian>(p, sub_12_12_11), p += 4;
write_insn<big_endian>(p, add_11_2_11), p += 4;
write_insn<big_endian>(p, addi_0_12 + l(-48)), p += 4;
write_insn<big_endian>(p, ld_12_11 + 0), p += 4;
write_insn<big_endian>(p, srdi_0_0_2), p += 4;
write_insn<big_endian>(p, mtctr_12), p += 4;
write_insn<big_endian>(p, ld_11_11 + 8), p += 4;
}
write_insn<big_endian>(p, bctr), p += 4;
while (p < oview + this->pltresolve_size)
write_insn<big_endian>(p, nop), p += 4;
// Write pltresolve stub.
p = oview;
Address after_bcl = this->address() + 16;
Address pltoff = plt_base - after_bcl;
elfcpp::Swap<64, big_endian>::writeval(p, pltoff), p += 8;
// Write lazy link call stubs.
uint32_t indx = 0;
while (p < oview + oview_size)
{
if (this->targ_->abiversion() < 2)
{
if (indx < 0x8000)
{
write_insn<big_endian>(p, li_0_0 + indx), p += 4;
}
else
{
write_insn<big_endian>(p, lis_0_0 + hi(indx)), p += 4;
write_insn<big_endian>(p, ori_0_0_0 + l(indx)), p += 4;
}
write_insn<big_endian>(p, mflr_12), p += 4;
write_insn<big_endian>(p, bcl_20_31), p += 4;
write_insn<big_endian>(p, mflr_11), p += 4;
write_insn<big_endian>(p, ld_2_11 + l(-16)), p += 4;
write_insn<big_endian>(p, mtlr_12), p += 4;
write_insn<big_endian>(p, add_11_2_11), p += 4;
write_insn<big_endian>(p, ld_12_11 + 0), p += 4;
write_insn<big_endian>(p, ld_2_11 + 8), p += 4;
write_insn<big_endian>(p, mtctr_12), p += 4;
write_insn<big_endian>(p, ld_11_11 + 16), p += 4;
}
uint32_t branch_off = 8 - (p - oview);
write_insn<big_endian>(p, b + (branch_off & 0x3fffffc)), p += 4;
indx++;
else
{
write_insn<big_endian>(p, mflr_0), p += 4;
write_insn<big_endian>(p, bcl_20_31), p += 4;
write_insn<big_endian>(p, mflr_11), p += 4;
write_insn<big_endian>(p, ld_2_11 + l(-16)), p += 4;
write_insn<big_endian>(p, mtlr_0), p += 4;
write_insn<big_endian>(p, sub_12_12_11), p += 4;
write_insn<big_endian>(p, add_11_2_11), p += 4;
write_insn<big_endian>(p, addi_0_12 + l(-48)), p += 4;
write_insn<big_endian>(p, ld_12_11 + 0), p += 4;
write_insn<big_endian>(p, srdi_0_0_2), p += 4;
write_insn<big_endian>(p, mtctr_12), p += 4;
write_insn<big_endian>(p, ld_11_11 + 8), p += 4;
}
write_insn<big_endian>(p, bctr), p += 4;
while (p < oview + this->pltresolve_size)
write_insn<big_endian>(p, nop), p += 4;
// Write lazy link call stubs.
uint32_t indx = 0;
while (p < oview + this->end_branch_table_)
{
if (this->targ_->abiversion() < 2)
{
if (indx < 0x8000)
{
write_insn<big_endian>(p, li_0_0 + indx), p += 4;
}
else
{
write_insn<big_endian>(p, lis_0_0 + hi(indx)), p += 4;
write_insn<big_endian>(p, ori_0_0_0 + l(indx)), p += 4;
}
}
uint32_t branch_off = 8 - (p - oview);
write_insn<big_endian>(p, b + (branch_off & 0x3fffffc)), p += 4;
indx++;
}
}
Address plt_base = this->targ_->plt_section()->address();
Address iplt_base = invalid_address;
unsigned int global_entry_off = (this->end_branch_table_ + 15) & -16;
Address global_entry_base = this->address() + global_entry_off;
typename Global_entry_stub_entries::const_iterator ge;
for (ge = this->global_entry_stubs_.begin();
ge != this->global_entry_stubs_.end();
++ge)
{
p = oview + global_entry_off + ge->second;
Address plt_addr = ge->first->plt_offset();
if (ge->first->type() == elfcpp::STT_GNU_IFUNC
&& ge->first->can_use_relative_reloc(false))
{
if (iplt_base == invalid_address)
iplt_base = this->targ_->iplt_section()->address();
plt_addr += iplt_base;
}
else
plt_addr += plt_base;
Address my_addr = global_entry_base + ge->second;
Address off = plt_addr - my_addr;
if (off + 0x80008000 > 0xffffffff || (off & 3) != 0)
gold_error(_("%s: linkage table error against `%s'"),
ge->first->object()->name().c_str(),
ge->first->demangled_name().c_str());
write_insn<big_endian>(p, addis_12_12 + ha(off)), p += 4;
write_insn<big_endian>(p, ld_12_12 + l(off)), p += 4;
write_insn<big_endian>(p, mtctr_12), p += 4;
write_insn<big_endian>(p, bctr);
}
}
else
@ -5119,13 +5225,15 @@ Target_powerpc<size, big_endian>::Scan::check_non_pic(Relobj* object,
template<int size, bool big_endian>
bool
Target_powerpc<size, big_endian>::Scan::reloc_needs_plt_for_ifunc(
Target_powerpc<size, big_endian>* target,
Sized_relobj_file<size, big_endian>* object,
unsigned int r_type,
bool report_err)
{
// In non-pic code any reference will resolve to the plt call stub
// for the ifunc symbol.
if (size == 32 && !parameters->options().output_is_position_independent())
if ((size == 32 || target->abiversion() >= 2)
&& !parameters->options().output_is_position_independent())
return true;
switch (r_type)
@ -5232,7 +5340,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, true))
if (is_ifunc && this->reloc_needs_plt_for_ifunc(target, 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(),
@ -5257,6 +5365,7 @@ Target_powerpc<size, big_endian>::Scan::local(
{
Address off = reloc.get_r_offset();
if (size == 64
&& target->abiversion() < 2
&& data_shndx == ppc_object->opd_shndx()
&& ppc_object->get_opd_discard(off - 8))
break;
@ -5297,7 +5406,7 @@ Target_powerpc<size, big_endian>::Scan::local(
// executable), we need to create a dynamic relocation for
// this location.
if (parameters->options().output_is_position_independent()
|| (size == 64 && is_ifunc))
|| (size == 64 && is_ifunc && target->abiversion() < 2))
{
Reloc_section* rela_dyn = target->rela_dyn_section(symtab, layout,
is_ifunc);
@ -5389,7 +5498,8 @@ Target_powerpc<size, big_endian>::Scan::local(
if (!parameters->options().output_is_position_independent())
{
if (size == 32 && is_ifunc)
if ((size == 32 && is_ifunc)
|| (size == 64 && target->abiversion() >= 2))
got->add_local_plt(object, r_sym, GOT_TYPE_STANDARD);
else
got->add_local(object, r_sym, GOT_TYPE_STANDARD);
@ -5587,12 +5697,14 @@ Target_powerpc<size, big_endian>::Scan::global(
// A STT_GNU_IFUNC symbol may require a PLT entry.
bool is_ifunc = gsym->type() == elfcpp::STT_GNU_IFUNC;
if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type, true))
bool pushed_ifunc = false;
if (is_ifunc && this->reloc_needs_plt_for_ifunc(target, 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()),
reloc.get_r_addend());
target->make_plt_entry(symtab, layout, gsym);
pushed_ifunc = true;
}
switch (r_type)
@ -5632,6 +5744,7 @@ Target_powerpc<size, big_endian>::Scan::global(
case elfcpp::R_PPC64_ADDR64:
if (size == 64
&& target->abiversion() < 2
&& data_shndx == ppc_object->opd_shndx()
&& (gsym->is_defined_in_discarded_section()
|| gsym->object() != object))
@ -5664,7 +5777,19 @@ Target_powerpc<size, big_endian>::Scan::global(
// Make a PLT entry if necessary.
if (gsym->needs_plt_entry())
{
if (!is_ifunc)
// 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
// the PLT call stub.
bool need_ifunc_plt = false;
if ((size == 32 || target->abiversion() >= 2)
&& gsym->is_from_dynobj()
&& !parameters->options().output_is_position_independent())
{
gsym->set_needs_dynsym_value();
need_ifunc_plt = true;
}
if (!is_ifunc || (!pushed_ifunc && need_ifunc_plt))
{
target->push_branch(ppc_object, data_shndx,
reloc.get_r_offset(), r_type,
@ -5672,31 +5797,27 @@ Target_powerpc<size, big_endian>::Scan::global(
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
// the PLT call stub.
if (size == 32
&& gsym->is_from_dynobj()
&& !parameters->options().output_is_position_independent())
gsym->set_needs_dynsym_value();
}
// Make a dynamic relocation if necessary.
if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type, target))
|| (size == 64 && is_ifunc))
|| (size == 64 && is_ifunc && target->abiversion() < 2))
{
if (gsym->may_need_copy_reloc())
{
target->copy_reloc(symtab, layout, object,
data_shndx, output_section, gsym, reloc);
}
else if ((size == 32
&& r_type == elfcpp::R_POWERPC_ADDR32
else if ((((size == 32
&& r_type == elfcpp::R_POWERPC_ADDR32)
|| (size == 64
&& r_type == elfcpp::R_PPC64_ADDR64
&& target->abiversion() >= 2))
&& gsym->can_use_relative_reloc(false)
&& !(gsym->visibility() == elfcpp::STV_PROTECTED
&& parameters->options().shared()))
|| (size == 64
&& r_type == elfcpp::R_PPC64_ADDR64
&& target->abiversion() < 2
&& (gsym->can_use_relative_reloc(false)
|| data_shndx == ppc_object->opd_shndx())))
{
@ -5822,7 +5943,8 @@ Target_powerpc<size, big_endian>::Scan::global(
got = target->got_section(symtab, layout);
if (gsym->final_value_is_known())
{
if (size == 32 && is_ifunc)
if ((size == 32 && is_ifunc)
|| (size == 64 && target->abiversion() >= 2))
got->add_global_plt(gsym, GOT_TYPE_STANDARD);
else
got->add_global(gsym, GOT_TYPE_STANDARD);
@ -5838,7 +5960,8 @@ Target_powerpc<size, big_endian>::Scan::global(
= target->rela_dyn_section(symtab, layout, is_ifunc);
if (gsym->can_use_relative_reloc(false)
&& !(size == 32
&& !((size == 32
|| target->abiversion() >= 2)
&& gsym->visibility() == elfcpp::STV_PROTECTED
&& parameters->options().shared()))
{
@ -6438,9 +6561,9 @@ Target_powerpc<size, big_endian>::symval_for_branch(
Powerpc_relobj<size, big_endian>* object,
unsigned int *dest_shndx)
{
if (size == 32 || this->abiversion() >= 2)
gold_unreachable();
*dest_shndx = 0;
if (size == 32)
return value;
// If the symbol is defined in an opd section, ie. is a function
// descriptor, use the function descriptor code entry address
@ -6522,26 +6645,39 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
? gsym->use_plt_offset(Scan::get_reference_flags(r_type, target))
: object->local_has_plt_offset(r_sym))
&& (!psymval->is_ifunc_symbol()
|| Scan::reloc_needs_plt_for_ifunc(object, r_type, false)))
|| Scan::reloc_needs_plt_for_ifunc(target, object, r_type, false)))
{
Stub_table<size, big_endian>* stub_table
= object->stub_table(relinfo->data_shndx);
if (stub_table == NULL)
if (size == 64
&& gsym != NULL
&& target->abiversion() >= 2
&& !parameters->options().output_is_position_independent()
&& !is_branch_reloc(r_type))
{
// This is a ref from a data section to an ifunc symbol.
if (target->stub_tables().size() != 0)
stub_table = target->stub_tables()[0];
unsigned int off = target->glink_section()->find_global_entry(gsym);
gold_assert(off != (unsigned int)-1);
value = target->glink_section()->global_entry_address() + off;
}
gold_assert(stub_table != NULL);
Address off;
if (gsym != NULL)
off = stub_table->find_plt_call_entry(object, gsym, r_type,
rela.get_r_addend());
else
off = stub_table->find_plt_call_entry(object, r_sym, r_type,
rela.get_r_addend());
gold_assert(off != invalid_address);
value = stub_table->stub_address() + off;
{
Stub_table<size, big_endian>* stub_table
= object->stub_table(relinfo->data_shndx);
if (stub_table == NULL)
{
// This is a ref from a data section to an ifunc symbol.
if (target->stub_tables().size() != 0)
stub_table = target->stub_tables()[0];
}
gold_assert(stub_table != NULL);
Address off;
if (gsym != NULL)
off = stub_table->find_plt_call_entry(object, gsym, r_type,
rela.get_r_addend());
else
off = stub_table->find_plt_call_entry(object, r_sym, r_type,
rela.get_r_addend());
gold_assert(off != invalid_address);
value = stub_table->stub_address() + off;
}
has_plt_value = true;
}
@ -6616,11 +6752,15 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
if (gsym->source() == Symbol::FROM_OBJECT
&& gsym->object() == object)
{
Address addend = rela.get_r_addend();
unsigned int dest_shndx;
Address opdent = psymval->value(object, addend);
code = target->symval_for_branch(relinfo->symtab, opdent,
gsym, object, &dest_shndx);
unsigned int dest_shndx = 0;
if (target->abiversion() < 2)
{
Address addend = rela.get_r_addend();
Address opdent = psymval->value(object, addend);
code = target->symval_for_branch(relinfo->symtab,
opdent, gsym, object,
&dest_shndx);
}
bool is_ordinary;
if (dest_shndx == 0)
dest_shndx = gsym->shndx(&is_ordinary);
@ -6881,13 +7021,19 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
if (r_type != elfcpp::R_PPC_PLTREL24)
addend = rela.get_r_addend();
value = psymval->value(object, addend);
if (gsym != NULL)
value += object->ppc64_local_entry_offset(gsym);
else
value += object->ppc64_local_entry_offset(r_sym);
if (size == 64 && is_branch_reloc(r_type))
value = target->symval_for_branch(relinfo->symtab, value,
gsym, object, &dest_shndx);
{
if (target->abiversion() >= 2)
{
if (gsym != NULL)
value += object->ppc64_local_entry_offset(gsym);
else
value += object->ppc64_local_entry_offset(r_sym);
}
else
value = target->symval_for_branch(relinfo->symtab, value,
gsym, object, &dest_shndx);
}
unsigned int max_branch_offset = 0;
if (r_type == elfcpp::R_POWERPC_REL24
|| r_type == elfcpp::R_PPC_PLTREL24
@ -7846,6 +7992,12 @@ Target_powerpc<size, big_endian>::do_dynsym_value(const Symbol* gsym) const
return (*p)->stub_address() + off;
}
}
else if (this->abiversion() >= 2)
{
unsigned int off = this->glink_section()->find_global_entry(gsym);
if (off != (unsigned int)-1)
return this->glink_section()->global_entry_address() + off;
}
gold_unreachable();
}
@ -7890,6 +8042,12 @@ Target_powerpc<size, big_endian>::do_plt_address_for_global(
return (*p)->stub_address() + off;
}
}
else if (this->abiversion() >= 2)
{
unsigned int off = this->glink_section()->find_global_entry(gsym);
if (off != (unsigned int)-1)
return this->glink_section()->global_entry_address() + off;
}
gold_unreachable();
}
@ -7987,6 +8145,9 @@ Target_selector_powerpc<64, false> target_selector_ppc64le;
template<int size, bool big_endian>
const int Output_data_glink<size, big_endian>::pltresolve_size;
template<int size, bool big_endian>
const typename Output_data_glink<size, big_endian>::Address
Output_data_glink<size, big_endian>::invalid_address;
template<int size, bool big_endian>
const typename Stub_table<size, big_endian>::Address
Stub_table<size, big_endian>::invalid_address;
template<int size, bool big_endian>