PowerPC64 ELFv2 support for gold.
authorAlan Modra <amodra@gmail.com>
Tue, 29 Oct 2013 07:15:48 +0000 (17:45 +1030)
committerAlan Modra <amodra@gmail.com>
Wed, 30 Oct 2013 03:15:05 +0000 (13:45 +1030)
elfcpp/
* powerpc.h (EF_PPC64_ABI): New enum constant.
(STO_PPC64_LOCAL_BIT, STO_PPC64_LOCAL_MASK): Likewise.
(ppc64_decode_local_entry): New function.
(ppc64_encode_local_entry): Likewise.
gold/
* powerpc.cc (Powerpc_relobj::abiversion, set_abiversion,
ppc64_local_entry_offset, ppc64_local_entry_offset,
do_read_symbols): New functions.
(Powerpc_relobj::e_flags_, st_other_): New vars.
(Powerpc_relobj::Powerpc_relobj): Call set_abiversion.
(Powerpc_dynobj::abiversion, set_abiversion): New functions.
(Powerpc_relobj::e_flags_): New var.
(Target_powerpc::first_plt_entry_offset, plt_entry_size): Inline
and adjust for ELFv2.
(Target_powerpc::abiversion, set_abiversion, stk_toc): New functions.
(Powerpc_relobj::do_find_special_sections): Check no .opd in ELFv2.
(Powerpc_dynobj::do_find_special_sections): Likewise.
(Target_powerpc::do_define_standard_symbols): Define ".TOC.".
(Target_powerpc::Branch_info::make_stub): Adjust stub destination
to ELFv2 local entry.
(Target_powerpc::do_relax): No thread safe barriers needed for
ELFv2.
(Output_data_plt_powerpc::initial_plt_entry_size_,
plt_entry_size): Delete.  Replace all uses with
first_plt_entry_offset() and plt_entry_size().
(Output_data_plt_powerpc::Output_data_plt_powerpc): Remove
reserved_size parm.  Update callers.
(Output_data_plt_powerpc::entry_count): Update.
(Output_data_plt_powerpc::first_plt_entry_offset): Make private
and use Target_powerpc::first_plt_entry_offset().
(Output_data_plt_powerpc::get_plt_entry_size): Similarly and
rename to plt_entry_size.
(Output_data_plt_powerpc::add_ifunc_entry,
add_local_ifunc_entry): Adjust reloc for ELFv2.
(glink_eh_frame_fde_64): Rename to glink_eh_frame_fde_64v1.
(glink_eh_frame_fde_64v2): New.
(Stub_table::plt_call_size): Support ELFv2 sizing.
(Output_data_glink::add_eh_frame): Use the new FDE.
(Output_data_glink::set_final_data_size): Adjust for ELFv2 glink.
(Stub_table::do_write): Write ELFv2 stubs and glink.
(Target_powerpc::Relocate::relocate): Replaces nop after call
with ld 2,24(1) and adjust local offset destination for ELFv2.

elfcpp/ChangeLog
elfcpp/powerpc.h
gold/ChangeLog
gold/powerpc.cc

index 0c9c00139312aa004b8531c0e641dbe309af27bb..4d0a5037f762dfac46a5c9440b421c81c54fb4ed 100644 (file)
@@ -1,3 +1,10 @@
+2013-10-30  Alan Modra  <amodra@gmail.com>
+
+       * powerpc.h (EF_PPC64_ABI): New enum constant.
+       (STO_PPC64_LOCAL_BIT, STO_PPC64_LOCAL_MASK): Likewise.
+       (ppc64_decode_local_entry): New function.
+       (ppc64_encode_local_entry): Likewise.
+
 2013-10-30  Alan Modra  <amodra@gmail.com>
 
        * powerpc.h (R_PPC64_ADDR16_HIGH, R_PPC64_ADDR16_HIGHA,
index fb4256bdafe8d77e1c9d2b0a096c9e3685086816..98354a2c7d65b826d435e6ea4009c4c869347e4f 100644 (file)
@@ -1,6 +1,6 @@
 // powerpc.h -- ELF definitions specific to EM_PPC and EM_PPC64  -*- C++ -*-
 
-// Copyright 2008, 2010, 2012 Free Software Foundation, Inc.
+// Copyright 2008, 2010, 2012, 2013 Free Software Foundation, Inc.
 // Written by David S. Miller <davem@davemloft.net>.
 
 // This file is part of elfcpp.
@@ -214,6 +214,59 @@ enum
   EF_PPC_RELOCATABLE_LIB = 0x00008000, // PowerPC -mrelocatable-lib flag.  */
 };
 
+// e_flags values defined for powerpc64
+enum
+{
+  // ABI version
+  // 1 for original function descriptor using ABI,
+  // 2 for revised ABI without function descriptors,
+  // 0 for unspecified or not using any features affected by the differences.
+  EF_PPC64_ABI = 3
+};
+
+enum
+{
+  // The ELFv2 ABI uses three bits in the symbol st_other field of a
+  // function definition to specify the number of instructions between a
+  // function's global entry point and local entry point.
+  // The global entry point is used when it is necessary to set up the
+  // toc pointer (r2) for the function.  Callers must enter the global
+  // entry point with r12 set to the global entry point address.  On
+  // return from the function, r2 may have a different value to that
+  // which it had on entry.
+  // The local entry point is used when r2 is known to already be valid
+  // for the function.  There is no requirement on r12 when using the
+  // local entry point, and on return r2 will contain the same value as
+  // at entry.
+  // A value of zero in these bits means that the function has a single
+  // entry point with no requirement on r12 or r2, and that on return r2
+  // will contain the same value as at entry.
+  // Values of one and seven are reserved.
+
+  STO_PPC64_LOCAL_BIT = 5,
+  STO_PPC64_LOCAL_MASK = 0xE0
+};
+
+// 3 bit other field to bytes.
+static inline unsigned int
+ppc64_decode_local_entry(unsigned int other)
+{
+  return ((1 << other) >> 2) << 2;
+}
+
+// bytes to field value.
+static inline unsigned int
+ppc64_encode_local_entry(unsigned int val)
+{
+  return (val >= 4 * 4
+         ? (val >= 8 * 4
+            ? (val >= 16 * 4 ? 6 : 5)
+            : 4)
+         : (val >= 2 * 4
+            ? 3
+            : (val >= 1 * 4 ? 2 : 0)));
+}
+
 } // End namespace elfcpp.
 
 #endif // !defined(ELFCPP_POWERPC_H)
index 5607cb3eb9c4cedd896731871fab417d28e07ea0..6acb64e94b56119fe919da1652e16838922b1674 100644 (file)
@@ -1,3 +1,43 @@
+2013-10-30  Alan Modra  <amodra@gmail.com>
+
+       * powerpc.cc (Powerpc_relobj::abiversion, set_abiversion,
+       ppc64_local_entry_offset, ppc64_local_entry_offset,
+       do_read_symbols): New functions.
+       (Powerpc_relobj::e_flags_, st_other_): New vars.
+       (Powerpc_relobj::Powerpc_relobj): Call set_abiversion.
+       (Powerpc_dynobj::abiversion, set_abiversion): New functions.
+       (Powerpc_relobj::e_flags_): New var.
+       (Target_powerpc::first_plt_entry_offset, plt_entry_size): Inline
+       and adjust for ELFv2.
+       (Target_powerpc::abiversion, set_abiversion, stk_toc): New functions.
+       (Powerpc_relobj::do_find_special_sections): Check no .opd in ELFv2.
+       (Powerpc_dynobj::do_find_special_sections): Likewise.
+       (Target_powerpc::do_define_standard_symbols): Define ".TOC.".
+       (Target_powerpc::Branch_info::make_stub): Adjust stub destination
+       to ELFv2 local entry.
+       (Target_powerpc::do_relax): No thread safe barriers needed for
+       ELFv2.
+       (Output_data_plt_powerpc::initial_plt_entry_size_,
+       plt_entry_size): Delete.  Replace all uses with
+       first_plt_entry_offset() and plt_entry_size().
+       (Output_data_plt_powerpc::Output_data_plt_powerpc): Remove
+       reserved_size parm.  Update callers.
+       (Output_data_plt_powerpc::entry_count): Update.
+       (Output_data_plt_powerpc::first_plt_entry_offset): Make private
+       and use Target_powerpc::first_plt_entry_offset().
+       (Output_data_plt_powerpc::get_plt_entry_size): Similarly and
+       rename to plt_entry_size.
+       (Output_data_plt_powerpc::add_ifunc_entry,
+       add_local_ifunc_entry): Adjust reloc for ELFv2.
+       (glink_eh_frame_fde_64): Rename to glink_eh_frame_fde_64v1.
+       (glink_eh_frame_fde_64v2): New.
+       (Stub_table::plt_call_size): Support ELFv2 sizing.
+       (Output_data_glink::add_eh_frame): Use the new FDE.
+       (Output_data_glink::set_final_data_size): Adjust for ELFv2 glink.
+       (Stub_table::do_write): Write ELFv2 stubs and glink.
+       (Target_powerpc::Relocate::relocate): Replaces nop after call
+       with ld 2,24(1) and adjust local offset destination for ELFv2.
+
 2013-10-30  Alan Modra  <amodra@gmail.com>
 
        * powerpc.cc (Target_powerpc::Scan::check_non_pic): Handle new relocs.
index 75fc604cf2736b679c1c6d84bd9688376b8d309f..0d6822d03bdd42ade74d38021d6cf48cc69ef158 100644 (file)
@@ -77,12 +77,19 @@ public:
                 const typename elfcpp::Ehdr<size, big_endian>& ehdr)
     : Sized_relobj_file<size, big_endian>(name, input_file, offset, ehdr),
       special_(0), has_small_toc_reloc_(false), opd_valid_(false),
-      opd_ent_(), access_from_map_(), has14_(), stub_table_()
-  { }
+      opd_ent_(), access_from_map_(), has14_(), stub_table_(),
+      e_flags_(ehdr.get_e_flags()), st_other_()
+  {
+    this->set_abiversion(0);
+  }
 
   ~Powerpc_relobj()
   { }
 
+  // Read the symbols then set up st_other vector.
+  void
+  do_read_symbols(Read_symbols_data*);
+
   // The .got2 section shndx.
   unsigned int
   got2_shndx() const
@@ -268,6 +275,22 @@ public:
     return NULL;
   }
 
+  int
+  abiversion() const
+  { return this->e_flags_ & elfcpp::EF_PPC64_ABI; }
+
+  // Set ABI version for input and output
+  void
+  set_abiversion(int ver);
+
+  unsigned int
+  ppc64_local_entry_offset(const Symbol* sym) const
+  { return elfcpp::ppc64_decode_local_entry(sym->nonvis() >> 3); }
+
+  unsigned int
+  ppc64_local_entry_offset(unsigned int symndx) const
+  { return elfcpp::ppc64_decode_local_entry(this->st_other_[symndx] >> 5); }
+
 private:
   struct Opd_ent
   {
@@ -321,6 +344,12 @@ private:
 
   // The stub table to use for a given input section.
   std::vector<Stub_table<size, big_endian>*> stub_table_;
+
+  // Header e_flags
+  elfcpp::Elf_Word e_flags_;
+
+  // ELF st_other field for local symbols.
+  std::vector<unsigned char> st_other_;
 };
 
 template<int size, bool big_endian>
@@ -332,8 +361,10 @@ public:
   Powerpc_dynobj(const std::string& name, Input_file* input_file, off_t offset,
                 const typename elfcpp::Ehdr<size, big_endian>& ehdr)
     : Sized_dynobj<size, big_endian>(name, input_file, offset, ehdr),
-      opd_shndx_(0), opd_ent_()
-  { }
+      opd_shndx_(0), opd_ent_(), e_flags_(ehdr.get_e_flags())
+  {
+    this->set_abiversion(0);
+  }
 
   ~Powerpc_dynobj()
   { }
@@ -387,6 +418,14 @@ public:
     this->opd_ent_[ndx].off = value;
   }
 
+  int
+  abiversion() const
+  { return this->e_flags_ & elfcpp::EF_PPC64_ABI; }
+
+  // Set ABI version for input and output.
+  void
+  set_abiversion(int ver);
+
 private:
   // Used to specify extent of executable sections.
   struct Sec_info
@@ -424,6 +463,9 @@ private:
   // corresponding to the address.  Note that in dynamic objects,
   // offset is *not* relative to the section.
   std::vector<Opd_ent> opd_ent_;
+
+  // Header e_flags
+  elfcpp::Elf_Word e_flags_;
 };
 
 template<int size, bool big_endian>
@@ -679,11 +721,25 @@ class Target_powerpc : public Sized_target<size, big_endian>
 
   // Return the offset of the first non-reserved PLT entry.
   unsigned int
-  first_plt_entry_offset() const;
+  first_plt_entry_offset() const
+  {
+    if (size == 32)
+      return 0;
+    if (this->abiversion() >= 2)
+      return 16;
+    return 24;
+  }
 
   // Return the size of each PLT entry.
   unsigned int
-  plt_entry_size() const;
+  plt_entry_size() const
+  {
+    if (size == 32)
+      return 4;
+    if (this->abiversion() >= 2)
+      return 8;
+    return 24;
+  }
 
   // Add any special sections for this symbol to the gc work list.
   // For powerpc64, this adds the code section of a function
@@ -743,6 +799,24 @@ class Target_powerpc : public Sized_target<size, big_endian>
   plt_thread_safe() const
   { return this->plt_thread_safe_; }
 
+  int
+  abiversion () const
+  { return this->processor_specific_flags() & elfcpp::EF_PPC64_ABI; }
+
+  void
+  set_abiversion (int ver)
+  {
+    elfcpp::Elf_Word flags = this->processor_specific_flags();
+    flags &= ~elfcpp::EF_PPC64_ABI;
+    flags |= ver & elfcpp::EF_PPC64_ABI;
+    this->set_processor_specific_flags(flags);
+  }
+
+  // Offset to to save stack slot
+  int
+  stk_toc () const
+  { return this->abiversion() < 2 ? 40 : 24; }
+
  private:
 
   class Track_tls
@@ -1659,6 +1733,29 @@ public:
   }
 };
 
+// Set ABI version for input and output.
+
+template<int size, bool big_endian>
+void
+Powerpc_relobj<size, big_endian>::set_abiversion(int ver)
+{
+  this->e_flags_ |= ver;
+  if (this->abiversion() != 0)
+    {
+      Target_powerpc<size, big_endian>* target =
+       static_cast<Target_powerpc<size, big_endian>*>(
+          parameters->sized_target<size, big_endian>());
+      if (target->abiversion() == 0)
+       target->set_abiversion(this->abiversion());
+      else if (target->abiversion() != this->abiversion())
+       gold_error(_("%s: ABI version %d is not compatible "
+                    "with ABI version %d output"),
+                  this->name().c_str(),
+                  this->abiversion(), target->abiversion());
+
+    }
+}
+
 // Stash away the index of .got2 or .opd in a relocatable object, if
 // such a section exists.
 
@@ -1680,6 +1777,14 @@ Powerpc_relobj<size, big_endian>::do_find_special_sections(
     {
       unsigned int ndx = (s - pshdrs) / elfcpp::Elf_sizes<size>::shdr_size;
       this->special_ = ndx;
+      if (size == 64)
+       {
+         if (this->abiversion() == 0)
+           this->set_abiversion(1);
+         else if (this->abiversion() > 1)
+           gold_error(_("%s: .opd invalid in abiv%d"),
+                      this->name().c_str(), this->abiversion());
+       }
     }
   return Sized_relobj_file<size, big_endian>::do_find_special_sections(sd);
 }
@@ -1790,6 +1895,69 @@ Powerpc_relobj<size, big_endian>::do_read_relocs(Read_relocs_data* rd)
     }
 }
 
+// Read the symbols then set up st_other vector.
+
+template<int size, bool big_endian>
+void
+Powerpc_relobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
+{
+  Sized_relobj_file<size, big_endian>::do_read_symbols(sd);
+  if (size == 64)
+    {
+      const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
+      const unsigned char* const pshdrs = sd->section_headers->data();
+      const unsigned int loccount = this->do_local_symbol_count();
+      if (loccount != 0)
+       {
+         this->st_other_.resize(loccount);
+         const int sym_size = elfcpp::Elf_sizes<size>::sym_size;
+         off_t locsize = loccount * sym_size;
+         const unsigned int symtab_shndx = this->symtab_shndx();
+         const unsigned char *psymtab = pshdrs + symtab_shndx * shdr_size;
+         typename elfcpp::Shdr<size, big_endian> shdr(psymtab);
+         const unsigned char* psyms = this->get_view(shdr.get_sh_offset(),
+                                                     locsize, true, false);
+         psyms += sym_size;
+         for (unsigned int i = 1; i < loccount; ++i, psyms += sym_size)
+           {
+             elfcpp::Sym<size, big_endian> sym(psyms);
+             unsigned char st_other = sym.get_st_other();
+             this->st_other_[i] = st_other;
+             if ((st_other & elfcpp::STO_PPC64_LOCAL_MASK) != 0)
+               {
+                 if (this->abiversion() == 0)
+                   this->set_abiversion(2);
+                 else if (this->abiversion() < 2)
+                   gold_error(_("%s: local symbol %d has invalid st_other"
+                                " for ABI version 1"),
+                              this->name().c_str(), i);
+               }
+           }
+       }
+    }
+}
+
+template<int size, bool big_endian>
+void
+Powerpc_dynobj<size, big_endian>::set_abiversion(int ver)
+{
+  this->e_flags_ |= ver;
+  if (this->abiversion() != 0)
+    {
+      Target_powerpc<size, big_endian>* target =
+       static_cast<Target_powerpc<size, big_endian>*>(
+         parameters->sized_target<size, big_endian>());
+      if (target->abiversion() == 0)
+       target->set_abiversion(this->abiversion());
+      else if (target->abiversion() != this->abiversion())
+       gold_error(_("%s: ABI version %d is not compatible "
+                    "with ABI version %d output"),
+                  this->name().c_str(),
+                  this->abiversion(), target->abiversion());
+
+    }
+}
+
 // Call Sized_dynobj::do_read_symbols to read the symbols then
 // read .opd from a dynamic object, filling in opd_ent_ vector,
 
@@ -1821,6 +1989,12 @@ Powerpc_dynobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
          if (shdr.get_sh_type() == elfcpp::SHT_PROGBITS
              && (shdr.get_sh_flags() & elfcpp::SHF_ALLOC) != 0)
            {
+             if (this->abiversion() == 0)
+               this->set_abiversion(1);
+             else if (this->abiversion() > 1)
+               gold_error(_("%s: .opd invalid in abiv%d"),
+                          this->name().c_str(), this->abiversion());
+
              this->opd_shndx_ = (s - pshdrs) / shdr_size;
              this->opd_address_ = shdr.get_sh_addr();
              opd_size = convert_to_section_size_type(shdr.get_sh_size());
@@ -1937,6 +2111,26 @@ Target_powerpc<size, big_endian>::do_define_standard_symbols(
                                        0, false, false);
        }
     }
+  else
+    {
+      // Define .TOC. as for 32-bit _GLOBAL_OFFSET_TABLE_
+      Symbol *gotsym = symtab->lookup(".TOC.", NULL);
+      if (gotsym != NULL && gotsym->is_undefined())
+       {
+         Target_powerpc<size, big_endian>* target =
+           static_cast<Target_powerpc<size, big_endian>*>(
+               parameters->sized_target<size, big_endian>());
+         Output_data_got_powerpc<size, big_endian>* got
+           = target->got_section(symtab, layout);
+         symtab->define_in_output_data(".TOC.", NULL,
+                                       Symbol_table::PREDEFINED,
+                                       got, 0x8000, 0,
+                                       elfcpp::STT_OBJECT,
+                                       elfcpp::STB_LOCAL,
+                                       elfcpp::STV_HIDDEN, 0,
+                                       false, false);
+       }
+    }
 }
 
 // Set up PowerPC target specific relobj.
@@ -2446,7 +2640,7 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
     }
   else
     {
-      unsigned int max_branch_offset;
+      unsigned long max_branch_offset;
       if (this->r_type_ == elfcpp::R_POWERPC_REL14
          || this->r_type_ == elfcpp::R_POWERPC_REL14_BRTAKEN
          || this->r_type_ == elfcpp::R_POWERPC_REL14_BRNTAKEN)
@@ -2489,6 +2683,7 @@ 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);
        }
       else
        {
@@ -2503,6 +2698,7 @@ 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_);
        }
       to += this->addend_;
       if (stub_table == NULL)
@@ -2545,8 +2741,12 @@ Target_powerpc<size, big_endian>::do_relax(int pass,
   unsigned int prev_brlt_size = 0;
   if (pass == 1)
     {
-      bool thread_safe = parameters->options().plt_thread_safe();
-      if (size == 64 && !parameters->options().user_set_plt_thread_safe())
+      bool thread_safe
+       = this->abiversion() < 2 && parameters->options().plt_thread_safe();
+      if (size == 64
+         && this->abiversion() < 2
+         && !thread_safe
+         && !parameters->options().user_set_plt_thread_safe())
        {
          static const char* const thread_starter[] =
            {
@@ -2758,12 +2958,10 @@ class Output_data_plt_powerpc : public Output_section_data_build
 
   Output_data_plt_powerpc(Target_powerpc<size, big_endian>* targ,
                          Reloc_section* plt_rel,
-                         unsigned int reserved_size,
                          const char* name)
     : Output_section_data_build(size == 32 ? 4 : 8),
       rel_(plt_rel),
       targ_(targ),
-      initial_plt_entry_size_(reserved_size),
       name_(name)
   { }
 
@@ -2790,20 +2988,10 @@ class Output_data_plt_powerpc : public Output_section_data_build
   {
     if (this->current_data_size() == 0)
       return 0;
-    return ((this->current_data_size() - this->initial_plt_entry_size_)
-           / plt_entry_size);
+    return ((this->current_data_size() - this->first_plt_entry_offset())
+           / this->plt_entry_size());
   }
 
-  // Return the offset of the first non-reserved PLT entry.
-  unsigned int
-  first_plt_entry_offset()
-  { return this->initial_plt_entry_size_; }
-
-  // Return the size of a PLT entry.
-  static unsigned int
-  get_plt_entry_size()
-  { return plt_entry_size; }
-
  protected:
   void
   do_adjust_output_section(Output_section* os)
@@ -2817,8 +3005,22 @@ class Output_data_plt_powerpc : public Output_section_data_build
   { mapfile->print_output_data(this, this->name_); }
 
  private:
-  // The size of an entry in the PLT.
-  static const int plt_entry_size = size == 32 ? 4 : 24;
+  // Return the offset of the first non-reserved PLT entry.
+  unsigned int
+  first_plt_entry_offset() const
+  {
+    // IPLT has no reserved entry.
+    if (this->name_[3] == 'I')
+      return 0;
+    return this->targ_->first_plt_entry_offset();
+  }
+
+  // Return the size of each PLT entry.
+  unsigned int
+  plt_entry_size() const
+  {
+    return this->targ_->plt_entry_size();
+  }
 
   // Write out the PLT data.
   void
@@ -2828,8 +3030,6 @@ class Output_data_plt_powerpc : public Output_section_data_build
   Reloc_section* rel_;
   // Allows access to .glink for do_write.
   Target_powerpc<size, big_endian>* targ_;
-  // The size of the first reserved entry.
-  int initial_plt_entry_size_;
   // What to report in map file.
   const char *name_;
 };
@@ -2849,7 +3049,7 @@ Output_data_plt_powerpc<size, big_endian>::add_entry(Symbol* gsym)
       gsym->set_needs_dynsym_entry();
       unsigned int dynrel = elfcpp::R_POWERPC_JMP_SLOT;
       this->rel_->add_global(gsym, dynrel, this, off, 0);
-      off += plt_entry_size;
+      off += this->plt_entry_size();
       this->set_current_data_size(off);
     }
 }
@@ -2865,10 +3065,10 @@ Output_data_plt_powerpc<size, big_endian>::add_ifunc_entry(Symbol* gsym)
       section_size_type off = this->current_data_size();
       gsym->set_plt_offset(off);
       unsigned int dynrel = elfcpp::R_POWERPC_IRELATIVE;
-      if (size == 64)
+      if (size == 64 && this->targ_->abiversion() < 2)
        dynrel = elfcpp::R_PPC64_JMP_IREL;
       this->rel_->add_symbolless_global_addend(gsym, dynrel, this, off, 0);
-      off += plt_entry_size;
+      off += this->plt_entry_size();
       this->set_current_data_size(off);
     }
 }
@@ -2886,11 +3086,11 @@ Output_data_plt_powerpc<size, big_endian>::add_local_ifunc_entry(
       section_size_type off = this->current_data_size();
       relobj->set_local_plt_offset(local_sym_index, off);
       unsigned int dynrel = elfcpp::R_POWERPC_IRELATIVE;
-      if (size == 64)
+      if (size == 64 && this->targ_->abiversion() < 2)
        dynrel = elfcpp::R_PPC64_JMP_IREL;
       this->rel_->add_symbolless_local_addend(relobj, local_sym_index, dynrel,
                                              this, off, 0);
-      off += plt_entry_size;
+      off += this->plt_entry_size();
       this->set_current_data_size(off);
     }
 }
@@ -2900,50 +3100,50 @@ static const uint32_t add_2_2_11        = 0x7c425a14;
 static const uint32_t add_3_3_2                = 0x7c631214;
 static const uint32_t add_3_3_13       = 0x7c636a14;
 static const uint32_t add_11_0_11      = 0x7d605a14;
-static const uint32_t add_12_2_11      = 0x7d825a14;
-static const uint32_t add_12_12_11     = 0x7d8c5a14;
-static const uint32_t addi_11_11       = 0x396b0000;
-static const uint32_t addi_12_12       = 0x398c0000;
+static const uint32_t add_11_2_11      = 0x7d625a14;
+static const uint32_t add_11_11_2      = 0x7d6b1214;
+static const uint32_t addi_0_12                = 0x380c0000;
 static const uint32_t addi_2_2         = 0x38420000;
-static const uint32_t addi_3_2         = 0x38620000;
 static const uint32_t addi_3_3         = 0x38630000;
+static const uint32_t addi_11_11       = 0x396b0000;
+static const uint32_t addi_12_12       = 0x398c0000;
 static const uint32_t addis_0_2                = 0x3c020000;
 static const uint32_t addis_0_13       = 0x3c0d0000;
+static const uint32_t addis_3_2                = 0x3c620000;
+static const uint32_t addis_3_13       = 0x3c6d0000;
+static const uint32_t addis_11_2       = 0x3d620000;
 static const uint32_t addis_11_11      = 0x3d6b0000;
 static const uint32_t addis_11_30      = 0x3d7e0000;
 static const uint32_t addis_12_12      = 0x3d8c0000;
-static const uint32_t addis_12_2       = 0x3d820000;
-static const uint32_t addis_3_2                = 0x3c620000;
-static const uint32_t addis_3_13       = 0x3c6d0000;
 static const uint32_t b                        = 0x48000000;
 static const uint32_t bcl_20_31                = 0x429f0005;
 static const uint32_t bctr             = 0x4e800420;
 static const uint32_t blr              = 0x4e800020;
-static const uint32_t blrl             = 0x4e800021;
 static const uint32_t bnectr_p4                = 0x4ce20420;
 static const uint32_t cmpldi_2_0       = 0x28220000;
 static const uint32_t cror_15_15_15    = 0x4def7b82;
 static const uint32_t cror_31_31_31    = 0x4ffffb82;
 static const uint32_t ld_0_1           = 0xe8010000;
 static const uint32_t ld_0_12          = 0xe80c0000;
-static const uint32_t ld_11_12         = 0xe96c0000;
-static const uint32_t ld_11_2          = 0xe9620000;
 static const uint32_t ld_2_1           = 0xe8410000;
-static const uint32_t ld_2_11          = 0xe84b0000;
-static const uint32_t ld_2_12          = 0xe84c0000;
 static const uint32_t ld_2_2           = 0xe8420000;
+static const uint32_t ld_2_11          = 0xe84b0000;
+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 lfd_0_1          = 0xc8010000;
 static const uint32_t li_0_0           = 0x38000000;
 static const uint32_t li_12_0          = 0x39800000;
 static const uint32_t lis_0_0          = 0x3c000000;
 static const uint32_t lis_11           = 0x3d600000;
 static const uint32_t lis_12           = 0x3d800000;
+static const uint32_t lvx_0_12_0       = 0x7c0c00ce;
 static const uint32_t lwz_0_12         = 0x800c0000;
 static const uint32_t lwz_11_11                = 0x816b0000;
 static const uint32_t lwz_11_30                = 0x817e0000;
 static const uint32_t lwz_12_12                = 0x818c0000;
 static const uint32_t lwzu_0_12                = 0x840c0000;
-static const uint32_t lvx_0_12_0       = 0x7c0c00ce;
 static const uint32_t mflr_0           = 0x7c0802a6;
 static const uint32_t mflr_11          = 0x7d6802a6;
 static const uint32_t mflr_12          = 0x7d8802a6;
@@ -2954,13 +3154,16 @@ static const uint32_t mtlr_0            = 0x7c0803a6;
 static const uint32_t mtlr_12          = 0x7d8803a6;
 static const uint32_t nop              = 0x60000000;
 static const uint32_t ori_0_0_0                = 0x60000000;
+static const uint32_t srdi_0_0_2       = 0x7800f082;
 static const uint32_t std_0_1          = 0xf8010000;
 static const uint32_t std_0_12         = 0xf80c0000;
 static const uint32_t std_2_1          = 0xf8410000;
 static const uint32_t stfd_0_1         = 0xd8010000;
 static const uint32_t stvx_0_12_0      = 0x7c0c01ce;
 static const uint32_t sub_11_11_12     = 0x7d6c5850;
-static const uint32_t xor_11_11_11     = 0x7d6b5a78;
+static const uint32_t sub_12_12_11     = 0x7d8b6050;
+static const uint32_t xor_2_12_12      = 0x7d826278;
+static const uint32_t xor_11_12_12     = 0x7d8b6278;
 
 // Write out the PLT.
 
@@ -3019,7 +3222,6 @@ Target_powerpc<size, big_endian>::make_plt_section(Symbol_table* symtab,
                                      ORDER_DYNAMIC_PLT_RELOCS, false);
       this->plt_
        = new Output_data_plt_powerpc<size, big_endian>(this, plt_rel,
-                                                       size == 32 ? 0 : 24,
                                                        "** PLT");
       layout->add_output_section_data(".plt",
                                      (size == 32
@@ -3049,7 +3251,7 @@ Target_powerpc<size, big_endian>::make_iplt_section(Symbol_table* symtab,
       this->rela_dyn_->output_section()->add_output_section_data(iplt_rel);
       this->iplt_
        = new Output_data_plt_powerpc<size, big_endian>(this, iplt_rel,
-                                                       0, "** IPLT");
+                                                       "** IPLT");
       this->plt_->output_section()->add_output_section_data(this->iplt_);
     }
 }
@@ -3225,8 +3427,8 @@ const unsigned char Eh_cie<size>::eh_frame_cie[] =
   elfcpp::DW_CFA_def_cfa, 1, 0         // def_cfa: r1 offset 0.
 };
 
-// Describe __glink_PLTresolve use of LR, 64-bit version.
-static const unsigned char glink_eh_frame_fde_64[] =
+// Describe __glink_PLTresolve use of LR, 64-bit version ABIv1.
+static const unsigned char glink_eh_frame_fde_64v1[] =
 {
   0, 0, 0, 0,                          // Replaced with offset to .glink.
   0, 0, 0, 0,                          // Replaced with size of .glink.
@@ -3237,6 +3439,18 @@ static const unsigned char glink_eh_frame_fde_64[] =
   elfcpp::DW_CFA_restore_extended, 65
 };
 
+// Describe __glink_PLTresolve use of LR, 64-bit version ABIv2.
+static const unsigned char glink_eh_frame_fde_64v2[] =
+{
+  0, 0, 0, 0,                          // Replaced with offset to .glink.
+  0, 0, 0, 0,                          // Replaced with size of .glink.
+  0,                                   // Augmentation size.
+  elfcpp::DW_CFA_advance_loc + 1,
+  elfcpp::DW_CFA_register, 65, 0,
+  elfcpp::DW_CFA_advance_loc + 4,
+  elfcpp::DW_CFA_restore_extended, 65
+};
+
 // Describe __glink_PLTresolve use of LR, 32-bit version.
 static const unsigned char glink_eh_frame_fde_32[] =
 {
@@ -3493,13 +3707,16 @@ class Stub_table : public Output_relaxed_input_section
       <const Powerpc_relobj<size, big_endian>*>(p->first.object_);
     got_addr += ppcobj->toc_base_offset();
     Address off = plt_addr - got_addr;
-    bool static_chain = parameters->options().plt_static_chain();
-    bool thread_safe = this->targ_->plt_thread_safe();
-    unsigned int bytes = (4 * 5
-                         + 4 * static_chain
-                         + 8 * thread_safe
-                         + 4 * (ha(off) != 0)
-                         + 4 * (ha(off + 8 + 8 * static_chain) != ha(off)));
+    unsigned int bytes = 4 * 4 + 4 * (ha(off) != 0);
+    if (this->targ_->abiversion() < 2)
+      {
+       bool static_chain = parameters->options().plt_static_chain();
+       bool thread_safe = this->targ_->plt_thread_safe();
+       bytes += (4
+                 + 4 * static_chain
+                 + 8 * thread_safe
+                 + 4 * (ha(off + 8 + 8 * static_chain) != ha(off)));
+      }
     unsigned int align = 1 << parameters->options().plt_align();
     if (align > 1)
       bytes = (bytes + align - 1) & -align;
@@ -3805,11 +4022,20 @@ class Output_data_glink : public Output_section_data
       return;
 
     if (size == 64)
-      layout->add_eh_frame_for_plt(this,
-                                  Eh_cie<64>::eh_frame_cie,
-                                  sizeof (Eh_cie<64>::eh_frame_cie),
-                                  glink_eh_frame_fde_64,
-                                  sizeof (glink_eh_frame_fde_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
@@ -3869,9 +4095,13 @@ Output_data_glink<size, big_endian>::set_final_data_size()
          total += this->pltresolve_size;
 
          // space for branch table
-         total += 8 * count;
-         if (count > 0x8000)
-           total += 4 * (count - 0x8000);
+         total += 4 * count;
+         if (this->targ_->abiversion() < 2)
+           {
+             total += 4 * count;
+             if (count > 0x8000)
+               total += 4 * (count - 0x8000);
+           }
        }
     }
 
@@ -3934,8 +4164,11 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
                           cs->first.object_->name().c_str(),
                           cs->first.sym_->demangled_name().c_str());
 
-             bool static_chain = parameters->options().plt_static_chain();
-             bool thread_safe = this->targ_->plt_thread_safe();
+             bool plt_load_toc = this->targ_->abiversion() < 2;
+             bool static_chain
+               = plt_load_toc && parameters->options().plt_static_chain();
+             bool thread_safe
+               = plt_load_toc && this->targ_->plt_thread_safe();
              bool use_fake_dep = false;
              Address cmp_branch_off = 0;
              if (thread_safe)
@@ -3962,47 +4195,78 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
              p = oview + cs->second;
              if (ha(off) != 0)
                {
-                 write_insn<big_endian>(p, std_2_1 + 40),              p += 4;
-                 write_insn<big_endian>(p, addis_12_2 + ha(off)),      p += 4;
-                 write_insn<big_endian>(p, ld_11_12 + l(off)),         p += 4;
-                 if (ha(off + 8 + 8 * static_chain) != ha(off))
+                 write_insn<big_endian>(p, std_2_1 + this->targ_->stk_toc());
+                 p += 4;
+                 write_insn<big_endian>(p, addis_11_2 + ha(off));
+                 p += 4;
+                 write_insn<big_endian>(p, ld_12_11 + l(off));
+                 p += 4;
+                 if (plt_load_toc
+                     && ha(off + 8 + 8 * static_chain) != ha(off))
                    {
-                     write_insn<big_endian>(p, addi_12_12 + l(off)),   p += 4;
+                     write_insn<big_endian>(p, addi_11_11 + l(off));
+                     p += 4;
                      off = 0;
                    }
-                 write_insn<big_endian>(p, mtctr_11),                  p += 4;
-                 if (use_fake_dep)
+                 write_insn<big_endian>(p, mtctr_12);
+                 p += 4;
+                 if (plt_load_toc)
                    {
-                     write_insn<big_endian>(p, xor_11_11_11),          p += 4;
-                     write_insn<big_endian>(p, add_12_12_11),          p += 4;
+                     if (use_fake_dep)
+                       {
+                         write_insn<big_endian>(p, xor_2_12_12);
+                         p += 4;
+                         write_insn<big_endian>(p, add_11_11_2);
+                         p += 4;
+                       }
+                     write_insn<big_endian>(p, ld_2_11 + l(off + 8));
+                     p += 4;
+                     if (static_chain)
+                       {
+                         write_insn<big_endian>(p, ld_11_11 + l(off + 16));
+                         p += 4;
+                       }
                    }
-                 write_insn<big_endian>(p, ld_2_12 + l(off + 8)),      p += 4;
-                 if (static_chain)
-                   write_insn<big_endian>(p, ld_11_12 + l(off + 16)),  p += 4;
                }
              else
                {
-                 write_insn<big_endian>(p, std_2_1 + 40),              p += 4;
-                 write_insn<big_endian>(p, ld_11_2 + l(off)),          p += 4;
-                 if (ha(off + 8 + 8 * static_chain) != ha(off))
+                 write_insn<big_endian>(p, std_2_1 + this->targ_->stk_toc());
+                 p += 4;
+                 write_insn<big_endian>(p, ld_12_2 + l(off));
+                 p += 4;
+                 if (plt_load_toc
+                     && ha(off + 8 + 8 * static_chain) != ha(off))
                    {
-                     write_insn<big_endian>(p, addi_2_2 + l(off)),     p += 4;
+                     write_insn<big_endian>(p, addi_2_2 + l(off));
+                     p += 4;
                      off = 0;
                    }
-                 write_insn<big_endian>(p, mtctr_11),                  p += 4;
-                 if (use_fake_dep)
+                 write_insn<big_endian>(p, mtctr_12);
+                 p += 4;
+                 if (plt_load_toc)
                    {
-                     write_insn<big_endian>(p, xor_11_11_11),          p += 4;
-                     write_insn<big_endian>(p, add_2_2_11),            p += 4;
+                     if (use_fake_dep)
+                       {
+                         write_insn<big_endian>(p, xor_11_12_12);
+                         p += 4;
+                         write_insn<big_endian>(p, add_2_2_11);
+                         p += 4;
+                       }
+                     if (static_chain)
+                       {
+                         write_insn<big_endian>(p, ld_11_2 + l(off + 16));
+                         p += 4;
+                       }
+                     write_insn<big_endian>(p, ld_2_2 + l(off + 8));
+                     p += 4;
                    }
-                 if (static_chain)
-                   write_insn<big_endian>(p, ld_11_2 + l(off + 16)),   p += 4;
-                 write_insn<big_endian>(p, ld_2_2 + l(off + 8)),       p += 4;
                }
              if (thread_safe && !use_fake_dep)
                {
-                 write_insn<big_endian>(p, cmpldi_2_0),                p += 4;
-                 write_insn<big_endian>(p, bnectr_p4),                 p += 4;
+                 write_insn<big_endian>(p, cmpldi_2_0);
+                 p += 4;
+                 write_insn<big_endian>(p, bnectr_p4);
+                 p += 4;
                  write_insn<big_endian>(p, b | (cmp_branch_off & 0x3fffffc));
                }
              else
@@ -4031,14 +4295,14 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
              Address brltoff = brlt_addr - got_addr;
              if (ha(brltoff) == 0)
                {
-                 write_insn<big_endian>(p, ld_11_2 + l(brltoff)),      p += 4;
+                 write_insn<big_endian>(p, ld_12_2 + l(brltoff)),      p += 4;
                }
              else
                {
-                 write_insn<big_endian>(p, addis_12_2 + ha(brltoff)),  p += 4;
-                 write_insn<big_endian>(p, ld_11_12 + l(brltoff)),     p += 4;
+                 write_insn<big_endian>(p, addis_11_2 + ha(brltoff)),  p += 4;
+                 write_insn<big_endian>(p, ld_12_11 + l(brltoff)),     p += 4;
                }
-             write_insn<big_endian>(p, mtctr_11),                      p += 4;
+             write_insn<big_endian>(p, mtctr_12),                      p += 4;
              write_insn<big_endian>(p, bctr);
            }
        }
@@ -4180,16 +4444,34 @@ Output_data_glink<size, big_endian>::do_write(Output_file* of)
 
       elfcpp::Swap<64, big_endian>::writeval(p, pltoff),       p += 8;
 
-      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_12_2_11),                  p += 4;
-      write_insn<big_endian>(p, ld_11_12 + 0),                 p += 4;
-      write_insn<big_endian>(p, ld_2_12 + 8),                  p += 4;
-      write_insn<big_endian>(p, mtctr_11),                     p += 4;
-      write_insn<big_endian>(p, ld_11_12 + 16),                        p += 4;
+      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
+       {
+         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;
@@ -4198,14 +4480,17 @@ Output_data_glink<size, big_endian>::do_write(Output_file* of)
       uint32_t indx = 0;
       while (p < oview + oview_size)
        {
-         if (indx < 0x8000)
+         if (this->targ_->abiversion() < 2)
            {
-             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;
+             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;
@@ -4690,24 +4975,6 @@ Target_powerpc<size, big_endian>::plt_entry_count() const
   return this->plt_->entry_count();
 }
 
-// Return the offset of the first non-reserved PLT entry.
-
-template<int size, bool big_endian>
-unsigned int
-Target_powerpc<size, big_endian>::first_plt_entry_offset() const
-{
-  return this->plt_->first_plt_entry_offset();
-}
-
-// Return the size of each PLT entry.
-
-template<int size, bool big_endian>
-unsigned int
-Target_powerpc<size, big_endian>::plt_entry_size() const
-{
-  return Output_data_plt_powerpc<size, big_endian>::get_plt_entry_size();
-}
-
 // Create a GOT entry for local dynamic __tls_get_addr calls.
 
 template<int size, bool big_endian>
@@ -6403,7 +6670,8 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
                  && (insn2 == nop
                      || insn2 == cror_15_15_15 || insn2 == cror_31_31_31))
                {
-                 elfcpp::Swap<32, big_endian>::writeval(wv + 1, ld_2_1 + 40);
+                 elfcpp::Swap<32, big_endian>::
+                   writeval(wv + 1, ld_2_1 + target->stk_toc());
                  can_plt_call = true;
                }
            }
@@ -6695,6 +6963,10 @@ 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);