summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2013-11-15 10:36:34 +1030
committerAlan Modra <amodra@gmail.com>2013-11-15 16:31:38 +1030
commit484649796420f367ff1b9d192125a78ced97ec40 (patch)
tree127b503cd05aa31a9a24c709b308658f37cfe3ca
parent50f9c79e1ecbae992f9de2fcb427cbb55828264e (diff)
downloadppe42-binutils-484649796420f367ff1b9d192125a78ced97ec40.tar.gz
ppe42-binutils-484649796420f367ff1b9d192125a78ced97ec40.zip
Fixes to powerpc64 gold ELFv2 support
(cherry picked from commit 9055360d4a69313949c3535ec065080cb814367d)
-rw-r--r--gold/ChangeLog34
-rw-r--r--gold/powerpc.cc469
2 files changed, 349 insertions, 154 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog
index 267d3eb6da..acbddb6ddc 100644
--- a/gold/ChangeLog
+++ b/gold/ChangeLog
@@ -1,6 +1,40 @@
2013-11-15 Alan Modra <amodra@gmail.com>
Apply changes from mainline to 2.24
+ 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-04 Alan Modra <amodra@gmail.com>
* symtab.h (Symbol::Reference_flags): Add FUNC_DESC_ABI.
(Symbol::needs_dynamic_reloc): Test new flag.
diff --git a/gold/powerpc.cc b/gold/powerpc.cc
index ada4d0c010..1aa4791841 100644
--- a/gold/powerpc.cc
+++ b/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)
+ 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
{
- // This is a ref from a data section to an ifunc symbol.
- stub_table = ifunc_stub_table;
+ 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_);
}
- 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,10 +3975,76 @@ 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()
{
unsigned int count = this->targ_->plt_entry_count();
@@ -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)
- {
- 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
+ if (this->end_branch_table_ != 0)
{
- 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, 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 lazy link call stubs.
+ uint32_t indx = 0;
+ while (p < oview + this->end_branch_table_)
+ {
+ if (this->targ_->abiversion() < 2)
{
- 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;
+ 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++;
}
- 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>
OpenPOWER on IntegriCloud