Fixes to powerpc64 gold ELFv2 support
authorAlan Modra <amodra@gmail.com>
Fri, 15 Nov 2013 00:06:34 +0000 (10:36 +1030)
committerAlan Modra <amodra@gmail.com>
Fri, 15 Nov 2013 00:06:34 +0000 (10:36 +1030)
* 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.

gold/ChangeLog
gold/powerpc.cc

index d953d70cbd0b10453a912d07f30831b302f6c06c..14e282479a4936dae96eca5c22ff5e3703b55ed6 100644 (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
index ada4d0c010c4f913270ea7664b9635a67eb5dce9..1aa4791841906126bfa9f670c929a12964c6e50a 100644 (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)
+      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,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)
-       {
-         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>