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:
parent
ef1bc9e72f
commit
9055360d4a
|
@ -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
|
||||
|
|
475
gold/powerpc.cc
475
gold/powerpc.cc
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue