* target.h (Target::plt_fde_location, do_plt_fde_location): Declare.
[deliverable/binutils-gdb.git] / gold / powerpc.cc
index e346ac5258dbf7980edb9b923204443654eeb805..4521fc7e1736918f5aad11e6f7ac3f554b86376b 100644 (file)
@@ -23,7 +23,9 @@
 
 #include "gold.h"
 
+#include <algorithm>
 #include "elfcpp.h"
+#include "dwarf.h"
 #include "parameters.h"
 #include "reloc.h"
 #include "powerpc.h"
@@ -47,25 +49,31 @@ using namespace gold;
 template<int size, bool big_endian>
 class Output_data_plt_powerpc;
 
+template<int size, bool big_endian>
+class Output_data_brlt_powerpc;
+
 template<int size, bool big_endian>
 class Output_data_got_powerpc;
 
 template<int size, bool big_endian>
 class Output_data_glink;
 
+template<int size, bool big_endian>
+class Stub_table;
+
 template<int size, bool big_endian>
 class Powerpc_relobj : public Sized_relobj_file<size, big_endian>
 {
 public:
   typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
-  typedef typename elfcpp::Elf_types<size>::Elf_Off Offset;
   typedef Unordered_set<Section_id, Section_id_hash> Section_refs;
   typedef Unordered_map<Address, Section_refs> Access_from;
 
   Powerpc_relobj(const std::string& name, Input_file* input_file, off_t offset,
                 const typename elfcpp::Ehdr<size, big_endian>& ehdr)
     : Sized_relobj_file<size, big_endian>(name, input_file, offset, ehdr),
-      special_(0), opd_valid_(false), opd_ent_(), access_from_map_()
+      special_(0), has_small_toc_reloc_(false), opd_valid_(false),
+      opd_ent_(), access_from_map_(), has14_(), stub_table_()
   { }
 
   ~Powerpc_relobj()
@@ -190,6 +198,8 @@ public:
                  const unsigned char* prelocs,
                  const unsigned char* plocal_syms);
 
+  // Perform the Sized_relobj_file method, then set up opd info from
+  // .opd relocs.
   void
   do_read_relocs(Read_relocs_data*);
 
@@ -218,13 +228,49 @@ public:
   toc_base_offset() const
   { return 0x8000; }
 
+  void
+  set_has_small_toc_reloc()
+  { has_small_toc_reloc_ = true; }
+
+  bool
+  has_small_toc_reloc() const
+  { return has_small_toc_reloc_; }
+
+  void
+  set_has_14bit_branch(unsigned int shndx)
+  {
+    if (shndx >= this->has14_.size())
+      this->has14_.resize(shndx + 1);
+    this->has14_[shndx] = true;
+  }
+
+  bool
+  has_14bit_branch(unsigned int shndx) const
+  { return shndx < this->has14_.size() && this->has14_[shndx];  }
+
+  void
+  set_stub_table(unsigned int shndx, Stub_table<size, big_endian>* stub_table)
+  {
+    if (shndx >= this->stub_table_.size())
+      this->stub_table_.resize(shndx + 1);
+    this->stub_table_[shndx] = stub_table;
+  }
+
+  Stub_table<size, big_endian>*
+  stub_table(unsigned int shndx)
+  {
+    if (shndx < this->stub_table_.size())
+      return this->stub_table_[shndx];
+    return NULL;
+  }
+
 private:
   struct Opd_ent
   {
     unsigned int shndx;
     bool discard : 1;
     bool gc_mark : 1;
-    Offset off;
+    Address off;
   };
 
   // Return index into opd_ent_ array for .opd entry at OFF.
@@ -244,6 +290,10 @@ private:
   // For 32-bit the .got2 section shdnx, for 64-bit the .opd section shndx.
   unsigned int special_;
 
+  // For 64-bit, whether this object uses small model relocs to access
+  // the toc.
+  bool has_small_toc_reloc_;
+
   // Set at the start of gc_process_relocs, when we know opd_ent_
   // vector is valid.  The flag could be made atomic and set in
   // do_read_relocs with memory_order_release and then tested with
@@ -261,6 +311,12 @@ private:
   // gc_process_relocs for another object, before the opd_ent_ vector
   // is valid for this object.
   Access_from access_from_map_;
+
+  // Whether input section has a 14-bit branch reloc.
+  std::vector<bool> has14_;
+
+  // The stub table to use for a given input section.
+  std::vector<Stub_table<size, big_endian>*> stub_table_;
 };
 
 template<int size, bool big_endian>
@@ -278,9 +334,11 @@ class Target_powerpc : public Sized_target<size, big_endian>
 
   Target_powerpc()
     : Sized_target<size, big_endian>(&powerpc_info),
-      got_(NULL), plt_(NULL), iplt_(NULL), glink_(NULL), rela_dyn_(NULL),
-      copy_relocs_(elfcpp::R_POWERPC_COPY),
-      dynbss_(NULL), tlsld_got_offset_(-1U)
+      got_(NULL), plt_(NULL), iplt_(NULL), brlt_section_(NULL),
+      glink_(NULL), rela_dyn_(NULL), copy_relocs_(elfcpp::R_POWERPC_COPY),
+      dynbss_(NULL), tlsld_got_offset_(-1U),
+      stub_tables_(), branch_lookup_table_(), branch_info_(),
+      plt_thread_safe_(false)
   {
   }
 
@@ -325,6 +383,42 @@ class Target_powerpc : public Sized_target<size, big_endian>
     return NULL;
   }
 
+  // Provide linker defined save/restore functions.
+  void
+  define_save_restore_funcs(Layout*, Symbol_table*);
+
+  // No stubs unless a final link.
+  bool
+  do_may_relax() const
+  { return !parameters->options().relocatable(); }
+
+  bool
+  do_relax(int, const Input_objects*, Symbol_table*, Layout*, const Task*);
+
+  void
+  do_plt_fde_location(const Output_data*, unsigned char*,
+                     uint64_t*, off_t*) const;
+
+  // Stash info about branches, for stub generation.
+  void
+  push_branch(Powerpc_relobj<size, big_endian>* ppc_object,
+             unsigned int data_shndx, Address r_offset,
+             unsigned int r_type, unsigned int r_sym, Address addend)
+  {
+    Branch_info info(ppc_object, data_shndx, r_offset, r_type, r_sym, addend);
+    this->branch_info_.push_back(info);
+    if (r_type == elfcpp::R_POWERPC_REL14
+       || r_type == elfcpp::R_POWERPC_REL14_BRTAKEN
+       || r_type == elfcpp::R_POWERPC_REL14_BRNTAKEN)
+      ppc_object->set_has_14bit_branch(data_shndx);
+  }
+
+  Stub_table<size, big_endian>*
+  new_stub_table();
+
+  void
+  do_define_standard_symbols(Symbol_table*, Layout*);
+
   // Finalize the sections.
   void
   do_finalize_sections(Layout*, const Input_objects*, Symbol_table*);
@@ -389,7 +483,8 @@ class Target_powerpc : public Sized_target<size, big_endian>
                  const unsigned char* prelocs,
                  size_t reloc_count,
                  Output_section* output_section,
-                 off_t offset_in_output_section,
+                 typename elfcpp::Elf_types<size>::Elf_Off
+                    offset_in_output_section,
                  const Relocatable_relocs*,
                  unsigned char*,
                  Address view_address,
@@ -436,6 +531,9 @@ class Target_powerpc : public Sized_target<size, big_endian>
     return this->glink_;
   }
 
+  bool has_glink() const
+  { return this->glink_ != NULL; }
+
   // Get the GOT section.
   const Output_data_got_powerpc<size, big_endian>*
   got_section() const
@@ -444,6 +542,10 @@ class Target_powerpc : public Sized_target<size, big_endian>
     return this->got_;
   }
 
+  // Get the GOT section, creating it if necessary.
+  Output_data_got_powerpc<size, big_endian>*
+  got_section(Symbol_table*, Layout*);
+
   Object*
   do_make_elf_object(const std::string&, Input_file*, off_t,
                     const elfcpp::Ehdr<size, big_endian>&);
@@ -487,16 +589,144 @@ class Target_powerpc : public Sized_target<size, big_endian>
                      unsigned int dst_shndx,
                      Address dst_off) const;
 
+  typedef std::vector<Stub_table<size, big_endian>*> Stub_tables;
+  const Stub_tables&
+  stub_tables() const
+  { return this->stub_tables_; }
+
+  const Output_data_brlt_powerpc<size, big_endian>*
+  brlt_section() const
+  { return this->brlt_section_; }
+
+  void
+  add_branch_lookup_table(Address to)
+  {
+    unsigned int off = this->branch_lookup_table_.size() * (size / 8);
+    this->branch_lookup_table_.insert(std::make_pair(to, off));
+  }
+
+  Address
+  find_branch_lookup_table(Address to)
+  {
+    typename Branch_lookup_table::const_iterator p
+      = this->branch_lookup_table_.find(to);
+    return p == this->branch_lookup_table_.end() ? invalid_address : p->second;
+  }
+
+  void
+  write_branch_lookup_table(unsigned char *oview)
+  {
+    for (typename Branch_lookup_table::const_iterator p
+          = this->branch_lookup_table_.begin();
+        p != this->branch_lookup_table_.end();
+        ++p)
+      {
+       elfcpp::Swap<32, big_endian>::writeval(oview + p->second, p->first);
+      }
+  }
+
+  bool
+  plt_thread_safe() const
+  { return this->plt_thread_safe_; }
+
  private:
 
+  class Track_tls
+  {
+  public:
+    enum Tls_get_addr
+    {
+      NOT_EXPECTED = 0,
+      EXPECTED = 1,
+      SKIP = 2,
+      NORMAL = 3
+    };
+
+    Track_tls()
+      : tls_get_addr_(NOT_EXPECTED),
+       relinfo_(NULL), relnum_(0), r_offset_(0)
+    { }
+
+    ~Track_tls()
+    {
+      if (this->tls_get_addr_ != NOT_EXPECTED)
+       this->missing();
+    }
+
+    void
+    missing(void)
+    {
+      if (this->relinfo_ != NULL)
+       gold_error_at_location(this->relinfo_, this->relnum_, this->r_offset_,
+                              _("missing expected __tls_get_addr call"));
+    }
+
+    void
+    expect_tls_get_addr_call(
+       const Relocate_info<size, big_endian>* relinfo,
+       size_t relnum,
+       Address r_offset)
+    {
+      this->tls_get_addr_ = EXPECTED;
+      this->relinfo_ = relinfo;
+      this->relnum_ = relnum;
+      this->r_offset_ = r_offset;
+    }
+
+    void
+    expect_tls_get_addr_call()
+    { this->tls_get_addr_ = EXPECTED; }
+
+    void
+    skip_next_tls_get_addr_call()
+    {this->tls_get_addr_ = SKIP; }
+
+    Tls_get_addr
+    maybe_skip_tls_get_addr_call(unsigned int r_type, const Symbol* gsym)
+    {
+      bool is_tls_call = ((r_type == elfcpp::R_POWERPC_REL24
+                          || r_type == elfcpp::R_PPC_PLTREL24)
+                         && gsym != NULL
+                         && strcmp(gsym->name(), "__tls_get_addr") == 0);
+      Tls_get_addr last_tls = this->tls_get_addr_;
+      this->tls_get_addr_ = NOT_EXPECTED;
+      if (is_tls_call && last_tls != EXPECTED)
+       return last_tls;
+      else if (!is_tls_call && last_tls != NOT_EXPECTED)
+       {
+         this->missing();
+         return EXPECTED;
+       }
+      return NORMAL;
+    }
+
+  private:
+    // What we're up to regarding calls to __tls_get_addr.
+    // On powerpc, the branch and link insn making a call to
+    // __tls_get_addr is marked with a relocation, R_PPC64_TLSGD,
+    // R_PPC64_TLSLD, R_PPC_TLSGD or R_PPC_TLSLD, in addition to the
+    // usual R_POWERPC_REL24 or R_PPC_PLTREL25 relocation on a call.
+    // The marker relocation always comes first, and has the same
+    // symbol as the reloc on the insn setting up the __tls_get_addr
+    // argument.  This ties the arg setup insn with the call insn,
+    // allowing ld to safely optimize away the call.  We check that
+    // every call to __tls_get_addr has a marker relocation, and that
+    // every marker relocation is on a call to __tls_get_addr.
+    Tls_get_addr tls_get_addr_;
+    // Info about the last reloc for error message.
+    const Relocate_info<size, big_endian>* relinfo_;
+    size_t relnum_;
+    Address r_offset_;
+  };
+
   // The class which scans relocations.
-  class Scan
+  class Scan : protected Track_tls
   {
   public:
     typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
 
     Scan()
-      : issued_non_pic_error_(false)
+      : Track_tls(), issued_non_pic_error_(false)
     { }
 
     static inline int
@@ -571,33 +801,17 @@ class Target_powerpc : public Sized_target<size, big_endian>
                    unsigned int *dest_shndx);
 
   // The class which implements relocation.
-  class Relocate
+  class Relocate : protected Track_tls
   {
    public:
     // Use 'at' branch hints when true, 'y' when false.
     // FIXME maybe: set this with an option.
     static const bool is_isa_v2 = true;
 
-    enum skip_tls
-    {
-      CALL_NOT_EXPECTED = 0,
-      CALL_EXPECTED = 1,
-      CALL_SKIP = 2
-    };
-
     Relocate()
-      : call_tls_get_addr_(CALL_NOT_EXPECTED)
+      : Track_tls()
     { }
 
-    ~Relocate()
-    {
-      if (this->call_tls_get_addr_ != CALL_NOT_EXPECTED)
-       {
-         // FIXME: This needs to specify the location somehow.
-         gold_error(_("missing expected __tls_get_addr call"));
-       }
-    }
-
     // Do a relocation.  Return false if the caller should not issue
     // any warnings about this relocation.
     inline bool
@@ -609,10 +823,32 @@ class Target_powerpc : public Sized_target<size, big_endian>
             unsigned char*,
             typename elfcpp::Elf_types<size>::Elf_Addr,
             section_size_type);
+  };
 
-    // This is set if we should skip the next reloc, which should be a
-    // call to __tls_get_addr.
-    enum skip_tls call_tls_get_addr_;
+  class Relocate_comdat_behavior
+  {
+   public:
+    // Decide what the linker should do for relocations that refer to
+    // discarded comdat sections.
+    inline Comdat_behavior
+    get(const char* name)
+    {
+      gold::Default_comdat_behavior default_behavior;
+      Comdat_behavior ret = default_behavior.get(name);
+      if (ret == CB_WARNING)
+       {
+         if (size == 32
+             && (strcmp(name, ".fixup") == 0
+                 || strcmp(name, ".got2") == 0))
+           ret = CB_IGNORE;
+         if (size == 64
+             && (strcmp(name, ".opd") == 0
+                 || strcmp(name, ".toc") == 0
+                 || strcmp(name, ".toc1") == 0))
+           ret = CB_IGNORE;
+       }
+      return ret;
+    }
   };
 
   // A class which returns the size required for a relocation type,
@@ -663,32 +899,30 @@ class Target_powerpc : public Sized_target<size, big_endian>
     return tls::TLSOPT_TO_LE;
   }
 
-  // Get the GOT section, creating it if necessary.
-  Output_data_got_powerpc<size, big_endian>*
-  got_section(Symbol_table*, Layout*);
-
   // Create glink.
   void
   make_glink_section(Layout*);
 
   // Create the PLT section.
   void
-  make_plt_section(Layout*);
+  make_plt_section(Symbol_table*, Layout*);
 
   void
-  make_iplt_section(Layout*);
+  make_iplt_section(Symbol_table*, Layout*);
+
+  void
+  make_brlt_section(Layout*);
 
   // Create a PLT entry for a global symbol.
   void
-  make_plt_entry(Layout*, Symbol*,
-                const elfcpp::Rela<size, big_endian>&,
-                const Sized_relobj_file<size, big_endian>* object);
+  make_plt_entry(Symbol_table*, Layout*, Symbol*);
 
   // Create a PLT entry for a local IFUNC symbol.
   void
-  make_local_ifunc_plt_entry(Layout*,
-                            const elfcpp::Rela<size, big_endian>&,
-                            Sized_relobj_file<size, big_endian>*);
+  make_local_ifunc_plt_entry(Symbol_table*, Layout*,
+                            Sized_relobj_file<size, big_endian>*,
+                            unsigned int);
+
 
   // Create a GOT entry for local dynamic __tls_get_addr.
   unsigned int
@@ -718,6 +952,51 @@ class Target_powerpc : public Sized_target<size, big_endian>
                                  reloc, this->rela_dyn_section(layout));
   }
 
+  // Look over all the input sections, deciding where to place stub.
+  void
+  group_sections(Layout*, const Task*);
+
+  // Sort output sections by address.
+  struct Sort_sections
+  {
+    bool
+    operator()(const Output_section* sec1, const Output_section* sec2)
+    { return sec1->address() < sec2->address(); }
+  };
+
+  class Branch_info
+  {
+   public:
+    Branch_info(Powerpc_relobj<size, big_endian>* ppc_object,
+               unsigned int data_shndx,
+               Address r_offset,
+               unsigned int r_type,
+               unsigned int r_sym,
+               Address addend)
+      : object_(ppc_object), shndx_(data_shndx), offset_(r_offset),
+       r_type_(r_type), r_sym_(r_sym), addend_(addend)
+    { }
+
+    ~Branch_info()
+    { }
+
+    // If this branch needs a plt call stub, or a long branch stub, make one.
+    void
+    make_stub(Stub_table<size, big_endian>*,
+             Stub_table<size, big_endian>*,
+             Symbol_table*) const;
+
+   private:
+    // The branch location..
+    Powerpc_relobj<size, big_endian>* object_;
+    unsigned int shndx_;
+    Address offset_;
+    // ..and the branch type and destination.
+    unsigned int r_type_;
+    unsigned int r_sym_;
+    Address addend_;
+  };
+
   // Information about this specific target which we pass to the
   // general Target structure.
   static Target::Target_info powerpc_info;
@@ -734,15 +1013,17 @@ class Target_powerpc : public Sized_target<size, big_endian>
     GOT_TYPE_TPREL     // entry for @got@tprel
   };
 
-  // The GOT output section.
+  // The GOT section.
   Output_data_got_powerpc<size, big_endian>* got_;
-  // The PLT output section.
+  // The PLT section.
   Output_data_plt_powerpc<size, big_endian>* plt_;
-  // The IPLT output section.
+  // The IPLT section.
   Output_data_plt_powerpc<size, big_endian>* iplt_;
-  // The .glink output section.
+  // Section holding long branch destinations.
+  Output_data_brlt_powerpc<size, big_endian>* brlt_section_;
+  // The .glink section.
   Output_data_glink<size, big_endian>* glink_;
-  // The dynamic reloc output section.
+  // The dynamic reloc section.
   Reloc_section* rela_dyn_;
   // Relocs saved to avoid a COPY reloc.
   Copy_relocs<elfcpp::SHT_RELA, size, big_endian> copy_relocs_;
@@ -750,6 +1031,15 @@ class Target_powerpc : public Sized_target<size, big_endian>
   Output_data_space* dynbss_;
   // Offset of the GOT entry for local dynamic __tls_get_addr calls.
   unsigned int tlsld_got_offset_;
+
+  Stub_tables stub_tables_;
+  typedef Unordered_map<Address, unsigned int> Branch_lookup_table;
+  Branch_lookup_table branch_lookup_table_;
+
+  typedef std::vector<Branch_info> Branches;
+  Branches branch_info_;
+
+  bool plt_thread_safe_;
 };
 
 template<>
@@ -990,13 +1280,13 @@ use_plt_offset(const Symbol* gsym, int flags)
   if (gsym->is_from_dynobj())
     return true;
 
-  // If we are generating a shared object, and gsym symbol is
+  // If we are generating a shared object, and this symbol is
   // undefined or preemptible, we need to use the PLT entry.
   if (parameters->options().shared()
       && (gsym->is_undefined() || gsym->is_preemptible()))
     return true;
 
-  // If gsym is a call to a weak undefined symbol, we need to use
+  // If this is a call to a weak undefined symbol, we need to use
   // the PLT entry; the symbol may be defined by a library loaded
   // at runtime.
   if ((flags & Symbol::FUNCTION_CALL) && gsym->is_weak_undefined())
@@ -1343,6 +1633,56 @@ Powerpc_relobj<size, big_endian>::do_read_relocs(Read_relocs_data* rd)
     }
 }
 
+// Set up some symbols.
+
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::do_define_standard_symbols(
+    Symbol_table* symtab,
+    Layout* layout)
+{
+  if (size == 32)
+    {
+      // Define _GLOBAL_OFFSET_TABLE_ to ensure it isn't seen as
+      // undefined when scanning relocs (and thus requires
+      // non-relative dynamic relocs).  The proper value will be
+      // updated later.
+      Symbol *gotsym = symtab->lookup("_GLOBAL_OFFSET_TABLE_", 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("_GLOBAL_OFFSET_TABLE_", NULL,
+                                       Symbol_table::PREDEFINED,
+                                       got, 0, 0,
+                                       elfcpp::STT_OBJECT,
+                                       elfcpp::STB_LOCAL,
+                                       elfcpp::STV_HIDDEN, 0,
+                                       false, false);
+       }
+
+      // Define _SDA_BASE_ at the start of the .sdata section + 32768.
+      Symbol *sdasym = symtab->lookup("_SDA_BASE_", NULL);
+      if (sdasym != NULL && sdasym->is_undefined())
+       {
+         Output_data_space* sdata = new Output_data_space(4, "** sdata");
+         Output_section* os
+           = layout->add_output_section_data(".sdata", 0,
+                                             elfcpp::SHF_ALLOC
+                                             | elfcpp::SHF_WRITE,
+                                             sdata, ORDER_SMALL_DATA, false);
+         symtab->define_in_output_data("_SDA_BASE_", NULL,
+                                       Symbol_table::PREDEFINED,
+                                       os, 32768, 0, elfcpp::STT_OBJECT,
+                                       elfcpp::STB_LOCAL, elfcpp::STV_HIDDEN,
+                                       0, false, false);
+       }
+    }
+}
+
 // Set up PowerPC target specific relobj.
 
 template<int size, bool big_endian>
@@ -1389,7 +1729,7 @@ public:
       symtab_(symtab), layout_(layout),
       header_ent_cnt_(size == 32 ? 3 : 1),
       header_index_(size == 32 ? 0x2000 : 0)
-  {}
+  { }
 
   class Got_entry;
 
@@ -1428,7 +1768,7 @@ public:
 
   // Offset of base used to access the GOT/TOC.
   // The got/toc pointer reg will be set to this value.
-  typename elfcpp::Elf_types<size>::Elf_Off
+  Valtype
   got_base_offset(const Powerpc_relobj<size, big_endian>* object) const
   {
     if (size == 32)
@@ -1485,13 +1825,20 @@ private:
        Output_data_got<size, big_endian>::add_constant(0);
 
        // Define _GLOBAL_OFFSET_TABLE_ at the header
-       this->symtab_->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL,
-                                            Symbol_table::PREDEFINED,
-                                            this, this->g_o_t(), 0,
-                                            elfcpp::STT_OBJECT,
-                                            elfcpp::STB_LOCAL,
-                                            elfcpp::STV_HIDDEN,
-                                            0, false, false);
+       Symbol *gotsym = this->symtab_->lookup("_GLOBAL_OFFSET_TABLE_", NULL);
+       if (gotsym != NULL)
+         {
+           Sized_symbol<size>* sym = static_cast<Sized_symbol<size>*>(gotsym);
+           sym->set_value(this->g_o_t());
+         }
+       else
+         this->symtab_->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL,
+                                              Symbol_table::PREDEFINED,
+                                              this, this->g_o_t(), 0,
+                                              elfcpp::STT_OBJECT,
+                                              elfcpp::STB_LOCAL,
+                                              elfcpp::STV_HIDDEN, 0,
+                                              false, false);
       }
     else
       Output_data_got<size, big_endian>::add_constant(0);
@@ -1546,103 +1893,599 @@ Target_powerpc<size, big_endian>::rela_dyn_section(Layout* layout)
   return this->rela_dyn_;
 }
 
-// A class to handle the PLT data.
-
-template<int size, bool big_endian>
-class Output_data_plt_powerpc : public Output_section_data_build
+class Stub_control
 {
  public:
-  typedef Output_data_reloc<elfcpp::SHT_RELA, true,
-                           size, big_endian> Reloc_section;
-
-  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)
-  { }
-
-  // Add an entry to the PLT.
-  void
-  add_entry(Symbol*);
-
-  void
-  add_ifunc_entry(Symbol*);
-
-  void
-  add_local_ifunc_entry(Sized_relobj_file<size, big_endian>*, unsigned int);
-
-  // Return the .rela.plt section data.
-  Reloc_section*
-  rel_plt() const
-  {
-    return this->rel_;
-  }
-
-  // Return the number of PLT entries.
-  unsigned int
-  entry_count() const
+  // Determine the stub group size.  The group size is the absolute
+  // value of the parameter --stub-group-size.  If --stub-group-size
+  // is passed a negative value, we restrict stubs to be always before
+  // the stubbed branches.
+  Stub_control(int32_t size)
+    : state_(NO_GROUP), stub_group_size_(abs(size)),
+      stub14_group_size_(abs(size)),
+      stubs_always_before_branch_(size < 0), suppress_size_errors_(false),
+      group_end_addr_(0), owner_(NULL), output_section_(NULL)
   {
-    return ((this->current_data_size() - this->initial_plt_entry_size_)
-           / plt_entry_size);
+    if (stub_group_size_ == 1)
+      {
+       // Default values.
+       if (stubs_always_before_branch_)
+         {
+           stub_group_size_ = 0x1e00000;
+           stub14_group_size_ = 0x7800;
+         }
+       else
+         {
+           stub_group_size_ = 0x1c00000;
+           stub14_group_size_ = 0x7000;
+         }
+       suppress_size_errors_ = true;
+      }
   }
 
-  // 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; }
+  // Return true iff input section can be handled by current stub
+  // group.
+  bool
+  can_add_to_stub_group(Output_section* o,
+                       const Output_section::Input_section* i,
+                       bool has14);
 
- protected:
-  void
-  do_adjust_output_section(Output_section* os)
-  {
-    os->set_entsize(0);
-  }
+  const Output_section::Input_section*
+  owner()
+  { return owner_; }
 
-  // Write to a map file.
-  void
-  do_print_to_mapfile(Mapfile* mapfile) const
-  { mapfile->print_output_data(this, this->name_); }
+  Output_section*
+  output_section()
+  { return output_section_; }
 
  private:
-  // The size of an entry in the PLT.
-  static const int plt_entry_size = size == 32 ? 4 : 24;
-
-  // Write out the PLT data.
-  void
-  do_write(Output_file*);
-
-  // The reloc section.
-  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_;
+  typedef enum
+  {
+    NO_GROUP,
+    FINDING_STUB_SECTION,
+    HAS_STUB_SECTION
+  } State;
+
+  State state_;
+  uint32_t stub_group_size_;
+  uint32_t stub14_group_size_;
+  bool stubs_always_before_branch_;
+  bool suppress_size_errors_;
+  uint64_t group_end_addr_;
+  const Output_section::Input_section* owner_;
+  Output_section* output_section_;
 };
 
-// Add an entry to the PLT.
+// Return true iff input section can be handled by current stub/
+// group.
 
-template<int size, bool big_endian>
-void
-Output_data_plt_powerpc<size, big_endian>::add_entry(Symbol* gsym)
+bool
+Stub_control::can_add_to_stub_group(Output_section* o,
+                                   const Output_section::Input_section* i,
+                                   bool has14)
 {
-  if (!gsym->has_plt_offset())
+  uint32_t group_size
+    = has14 ? this->stub14_group_size_ : this->stub_group_size_;
+  bool whole_sec = o->order() == ORDER_INIT || o->order() == ORDER_FINI;
+  uint64_t this_size;
+  uint64_t start_addr = o->address();
+
+  if (whole_sec)
+    // .init and .fini sections are pasted together to form a single
+    // function.  We can't be adding stubs in the middle of the function.
+    this_size = o->data_size();
+  else
     {
-      off_t off = this->current_data_size();
-      if (off == 0)
-       off += this->first_plt_entry_offset();
-      gsym->set_plt_offset(off);
+      start_addr += i->relobj()->output_section_offset(i->shndx());
+      this_size = i->data_size();
+    }
+  uint64_t end_addr = start_addr + this_size;
+  bool toobig = this_size > group_size;
+
+  if (toobig && !this->suppress_size_errors_)
+    gold_warning(_("%s:%s exceeds group size"),
+                i->relobj()->name().c_str(),
+                i->relobj()->section_name(i->shndx()).c_str());
+
+  if (this->state_ != HAS_STUB_SECTION
+      && (!whole_sec || this->output_section_ != o))
+    {
+      this->owner_ = i;
+      this->output_section_ = o;
+    }
+
+  if (this->state_ == NO_GROUP)
+    {
+      this->state_ = FINDING_STUB_SECTION;
+      this->group_end_addr_ = end_addr;
+    }
+  else if (this->group_end_addr_ - start_addr < group_size)
+    ;
+  // Adding this section would make the group larger than GROUP_SIZE.
+  else if (this->state_ == FINDING_STUB_SECTION
+          && !this->stubs_always_before_branch_
+          && !toobig)
+    {
+      // But wait, there's more!  Input sections up to GROUP_SIZE
+      // bytes before the stub table can be handled by it too.
+      this->state_ = HAS_STUB_SECTION;
+      this->group_end_addr_ = end_addr;
+    }
+  else
+    {
+      this->state_ = NO_GROUP;
+      return false;
+    }
+  return true;
+}
+
+// Look over all the input sections, deciding where to place stubs.
+
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::group_sections(Layout* layout,
+                                                const Task*)
+{
+  Stub_control stub_control(parameters->options().stub_group_size());
+
+  // Group input sections and insert stub table
+  Stub_table<size, big_endian>* stub_table = NULL;
+  Layout::Section_list section_list;
+  layout->get_executable_sections(&section_list);
+  std::stable_sort(section_list.begin(), section_list.end(), Sort_sections());
+  for (Layout::Section_list::reverse_iterator o = section_list.rbegin();
+       o != section_list.rend();
+       ++o)
+    {
+      typedef Output_section::Input_section_list Input_section_list;
+      for (Input_section_list::const_reverse_iterator i
+            = (*o)->input_sections().rbegin();
+          i != (*o)->input_sections().rend();
+          ++i)
+       {
+         if (i->is_input_section())
+           {
+             Powerpc_relobj<size, big_endian>* ppcobj = static_cast
+               <Powerpc_relobj<size, big_endian>*>(i->relobj());
+             bool has14 = ppcobj->has_14bit_branch(i->shndx());
+             if (!stub_control.can_add_to_stub_group(*o, &*i, has14))
+               {
+                 stub_table->init(stub_control.owner(),
+                                  stub_control.output_section());
+                 stub_table = NULL;
+               }
+             if (stub_table == NULL)
+               stub_table = this->new_stub_table();
+             ppcobj->set_stub_table(i->shndx(), stub_table);
+           }
+       }
+    }
+  if (stub_table != NULL)
+    stub_table->init(stub_control.owner(), stub_control.output_section());
+}
+
+// If this branch needs a plt call stub, or a long branch stub, make one.
+
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::Branch_info::make_stub(
+    Stub_table<size, big_endian>* stub_table,
+    Stub_table<size, big_endian>* ifunc_stub_table,
+    Symbol_table* symtab) const
+{
+  Symbol* sym = this->object_->global_symbol(this->r_sym_);
+  if (sym != NULL && sym->is_forwarder())
+    sym = symtab->resolve_forwards(sym);
+  const Sized_symbol<size>* gsym = static_cast<const Sized_symbol<size>*>(sym);
+  if (gsym != NULL
+      ? use_plt_offset<size>(gsym, Scan::get_reference_flags(this->r_type_))
+      : this->object_->local_has_plt_offset(this->r_sym_))
+    {
+      if (stub_table == NULL)
+       stub_table = this->object_->stub_table(this->shndx_);
+      if (stub_table == NULL)
+       {
+         // This is a ref from a data section to an ifunc symbol.
+         stub_table = ifunc_stub_table;
+       }
+      gold_assert(stub_table != NULL);
+      if (gsym != NULL)
+       stub_table->add_plt_call_entry(this->object_, gsym,
+                                      this->r_type_, this->addend_);
+      else
+       stub_table->add_plt_call_entry(this->object_, this->r_sym_,
+                                      this->r_type_, this->addend_);
+    }
+  else
+    {
+      unsigned int 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)
+       max_branch_offset = 1 << 15;
+      else if (this->r_type_ == elfcpp::R_POWERPC_REL24
+              || this->r_type_ == elfcpp::R_PPC_PLTREL24
+              || this->r_type_ == elfcpp::R_PPC_LOCAL24PC)
+       max_branch_offset = 1 << 25;
+      else
+       return;
+      Address from = this->object_->get_output_section_offset(this->shndx_);
+      gold_assert(from != invalid_address);
+      from += (this->object_->output_section(this->shndx_)->address()
+              + this->offset_);
+      Address to;
+      if (gsym != NULL)
+       {
+         switch (gsym->source())
+           {
+           case Symbol::FROM_OBJECT:
+             {
+               Object* symobj = gsym->object();
+               if (symobj->is_dynamic()
+                   || symobj->pluginobj() != NULL)
+                 return;
+               bool is_ordinary;
+               unsigned int shndx = gsym->shndx(&is_ordinary);
+               if (shndx == elfcpp::SHN_UNDEF)
+                 return;
+             }
+             break;
+
+           case Symbol::IS_UNDEFINED:
+             return;
+
+           default:
+             break;
+           }
+         Symbol_table::Compute_final_value_status status;
+         to = symtab->compute_final_value<size>(gsym, &status);
+         if (status != Symbol_table::CFVS_OK)
+           return;
+       }
+      else
+       {
+         const Symbol_value<size>* psymval
+           = this->object_->local_symbol(this->r_sym_);
+         Symbol_value<size> symval;
+         typedef Sized_relobj_file<size, big_endian> ObjType;
+         typename ObjType::Compute_final_local_value_status status
+           = this->object_->compute_final_local_value(this->r_sym_, psymval,
+                                                      &symval, symtab);
+         if (status != ObjType::CFLV_OK
+             || !symval.has_output_value())
+           return;
+         to = symval.value(this->object_, 0);
+       }
+      to += this->addend_;
+      if (stub_table == NULL)
+       stub_table = this->object_->stub_table(this->shndx_);
+      gold_assert(stub_table != NULL);
+      if (size == 64 && is_branch_reloc(this->r_type_))
+       {
+         unsigned int dest_shndx;
+         to = stub_table->targ()->symval_for_branch(to, gsym, this->object_,
+                                                    &dest_shndx);
+       }
+      Address delta = to - from;
+      if (delta + max_branch_offset >= 2 * max_branch_offset)
+       {
+         stub_table->add_long_branch_entry(this->object_, to);
+       }
+    }
+}
+
+// Relaxation hook.  This is where we do stub generation.
+
+template<int size, bool big_endian>
+bool
+Target_powerpc<size, big_endian>::do_relax(int pass,
+                                          const Input_objects*,
+                                          Symbol_table* symtab,
+                                          Layout* layout,
+                                          const Task* task)
+{
+  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())
+       {
+         static const char* const thread_starter[] =
+           {
+             "pthread_create",
+             /* libstdc++ */
+             "_ZNSt6thread15_M_start_threadESt10shared_ptrINS_10_Impl_baseEE",
+             /* librt */
+             "aio_init", "aio_read", "aio_write", "aio_fsync", "lio_listio",
+             "mq_notify", "create_timer",
+             /* libanl */
+             "getaddrinfo_a",
+             /* libgomp */
+             "GOMP_parallel_start",
+             "GOMP_parallel_loop_static_start",
+             "GOMP_parallel_loop_dynamic_start",
+             "GOMP_parallel_loop_guided_start",
+             "GOMP_parallel_loop_runtime_start",
+             "GOMP_parallel_sections_start", 
+           };
+
+         if (parameters->options().shared())
+           thread_safe = true;
+         else
+           {
+             for (unsigned int i = 0;
+                  i < sizeof(thread_starter) / sizeof(thread_starter[0]);
+                  i++)
+               {
+                 Symbol* sym = symtab->lookup(thread_starter[i], NULL);
+                 thread_safe = (sym != NULL
+                                && sym->in_reg()
+                                && sym->in_real_elf());
+                 if (thread_safe)
+                   break;
+               }
+           }
+       }
+      this->plt_thread_safe_ = thread_safe;
+      this->group_sections(layout, task);
+    }
+
+  // We need address of stub tables valid for make_stub.
+  for (typename Stub_tables::iterator p = this->stub_tables_.begin();
+       p != this->stub_tables_.end();
+       ++p)
+    {
+      const Powerpc_relobj<size, big_endian>* object
+       = static_cast<const Powerpc_relobj<size, big_endian>*>((*p)->relobj());
+      Address off = object->get_output_section_offset((*p)->shndx());
+      gold_assert(off != invalid_address);
+      Output_section* os = (*p)->output_section();
+      (*p)->set_address_and_size(os, off);
+    }
+
+  if (pass != 1)
+    {
+      // Clear plt call stubs, long branch stubs and branch lookup table.
+      prev_brlt_size = this->branch_lookup_table_.size();
+      this->branch_lookup_table_.clear();
+      for (typename Stub_tables::iterator p = this->stub_tables_.begin();
+          p != this->stub_tables_.end();
+          ++p)
+       {
+         (*p)->clear_stubs();
+       }
+    }
+
+  // Build all the stubs.
+  Stub_table<size, big_endian>* ifunc_stub_table
+    = this->stub_tables_.size() == 0 ? NULL : this->stub_tables_[0];
+  Stub_table<size, big_endian>* one_stub_table
+    = this->stub_tables_.size() != 1 ? NULL : ifunc_stub_table;
+  for (typename Branches::const_iterator b = this->branch_info_.begin();
+       b != this->branch_info_.end();
+       b++)
+    {
+      b->make_stub(one_stub_table, ifunc_stub_table, symtab);
+    }
+
+  // Did anything change size?
+  unsigned int num_huge_branches = this->branch_lookup_table_.size();
+  bool again = num_huge_branches != prev_brlt_size;
+  if (size == 64 && num_huge_branches != 0)
+    this->make_brlt_section(layout);
+  if (size == 64 && again)
+    this->brlt_section_->set_current_size(num_huge_branches);
+
+  typedef Unordered_set<Output_section*> Output_sections;
+  Output_sections os_need_update;
+  for (typename Stub_tables::iterator p = this->stub_tables_.begin();
+       p != this->stub_tables_.end();
+       ++p)
+    {
+      if ((*p)->size_update())
+       {
+         again = true;
+         (*p)->add_eh_frame(layout);
+         os_need_update.insert((*p)->output_section());
+       }
+    }
+
+  // Set output section offsets for all input sections in an output
+  // section that just changed size.  Anything past the stubs will
+  // need updating.
+  for (typename Output_sections::iterator p = os_need_update.begin();
+       p != os_need_update.end();
+       p++)
+    {
+      Output_section* os = *p;
+      Address off = 0;
+      typedef Output_section::Input_section_list Input_section_list;
+      for (Input_section_list::const_iterator i = os->input_sections().begin();
+          i != os->input_sections().end();
+          ++i)
+       {
+         off = align_address(off, i->addralign());
+         if (i->is_input_section() || i->is_relaxed_input_section())
+           i->relobj()->set_section_offset(i->shndx(), off);
+         if (i->is_relaxed_input_section())
+           {
+             Stub_table<size, big_endian>* stub_table
+               = static_cast<Stub_table<size, big_endian>*>(
+                   i->relaxed_input_section());
+             off += stub_table->set_address_and_size(os, off);
+           }
+         else
+           off += i->data_size();
+       }
+      // If .brlt is part of this output section, then we have just
+      // done the offset adjustment.
+      os->clear_section_offsets_need_adjustment();
+    }
+
+  if (size == 64
+      && !again
+      && num_huge_branches != 0
+      && parameters->options().output_is_position_independent())
+    {
+      // Fill in the BRLT relocs.
+      this->brlt_section_->reset_data_size();
+      for (typename Branch_lookup_table::const_iterator p
+            = this->branch_lookup_table_.begin();
+          p != this->branch_lookup_table_.end();
+          ++p)
+       {
+         this->brlt_section_->add_reloc(p->first, p->second);
+       }
+      this->brlt_section_->finalize_data_size();
+    }
+  return again;
+}
+
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::do_plt_fde_location(const Output_data* plt,
+                                                     unsigned char* oview,
+                                                     uint64_t* paddress,
+                                                     off_t* plen) const
+{
+  uint64_t address = plt->address();
+  off_t len = plt->data_size();
+
+  if (plt == this->glink_)
+    {
+      // See Output_data_glink::do_write() for glink contents.
+      if (size == 64)
+       {
+         // There is one word before __glink_PLTresolve
+         address += 8;
+         len -= 8;
+       }
+      else if (parameters->options().output_is_position_independent())
+       {
+         // There are two FDEs for a position independent glink.
+         // The first covers the branch table, the second
+         // __glink_PLTresolve at the end of glink.
+         off_t resolve_size = this->glink_->pltresolve_size;
+         if (oview[9] == 0)
+           len -= resolve_size;
+         else
+           {
+             address += len - resolve_size;
+             len = resolve_size;
+           }
+       }
+    }
+  else
+    {
+      // Must be a stub table.
+      const Stub_table<size, big_endian>* stub_table
+       = static_cast<const Stub_table<size, big_endian>*>(plt);
+      uint64_t stub_address = stub_table->stub_address();
+      len -= stub_address - address;
+      address = stub_address;
+    }
+
+  *paddress = address;
+  *plen = len;
+}
+
+// A class to handle the PLT data.
+
+template<int size, bool big_endian>
+class Output_data_plt_powerpc : public Output_section_data_build
+{
+ public:
+  typedef Output_data_reloc<elfcpp::SHT_RELA, true,
+                           size, big_endian> Reloc_section;
+
+  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)
+  { }
+
+  // Add an entry to the PLT.
+  void
+  add_entry(Symbol*);
+
+  void
+  add_ifunc_entry(Symbol*);
+
+  void
+  add_local_ifunc_entry(Sized_relobj_file<size, big_endian>*, unsigned int);
+
+  // Return the .rela.plt section data.
+  Reloc_section*
+  rel_plt() const
+  {
+    return this->rel_;
+  }
+
+  // Return the number of PLT entries.
+  unsigned int
+  entry_count() const
+  {
+    return ((this->current_data_size() - this->initial_plt_entry_size_)
+           / 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)
+  {
+    os->set_entsize(0);
+  }
+
+  // Write to a map file.
+  void
+  do_print_to_mapfile(Mapfile* mapfile) const
+  { 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;
+
+  // Write out the PLT data.
+  void
+  do_write(Output_file*);
+
+  // The reloc section.
+  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_;
+};
+
+// Add an entry to the PLT.
+
+template<int size, bool big_endian>
+void
+Output_data_plt_powerpc<size, big_endian>::add_entry(Symbol* gsym)
+{
+  if (!gsym->has_plt_offset())
+    {
+      section_size_type off = this->current_data_size();
+      if (off == 0)
+       off += this->first_plt_entry_offset();
+      gsym->set_plt_offset(off);
       gsym->set_needs_dynsym_entry();
       unsigned int dynrel = elfcpp::R_POWERPC_JMP_SLOT;
       this->rel_->add_global(gsym, dynrel, this, off, 0);
@@ -1659,7 +2502,7 @@ Output_data_plt_powerpc<size, big_endian>::add_ifunc_entry(Symbol* gsym)
 {
   if (!gsym->has_plt_offset())
     {
-      off_t off = this->current_data_size();
+      section_size_type off = this->current_data_size();
       gsym->set_plt_offset(off);
       unsigned int dynrel = elfcpp::R_POWERPC_IRELATIVE;
       if (size == 64)
@@ -1680,7 +2523,7 @@ Output_data_plt_powerpc<size, big_endian>::add_local_ifunc_entry(
 {
   if (!relobj->local_has_plt_offset(local_sym_index))
     {
-      off_t off = this->current_data_size();
+      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)
@@ -1693,10 +2536,12 @@ Output_data_plt_powerpc<size, big_endian>::add_local_ifunc_entry(
 }
 
 static const uint32_t add_0_11_11      = 0x7c0b5a14;
+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 addi_2_2         = 0x38420000;
@@ -1713,16 +2558,23 @@ 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 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;
@@ -1731,17 +2583,24 @@ 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;
 static const uint32_t mtctr_0          = 0x7c0903a6;
 static const uint32_t mtctr_11         = 0x7d6903a6;
+static const uint32_t mtctr_12         = 0x7d8903a6;
 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 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;
 
 // Write out the PLT.
 
@@ -1751,7 +2610,7 @@ Output_data_plt_powerpc<size, big_endian>::do_write(Output_file* of)
 {
   if (size == 32)
     {
-      const off_t offset = this->offset();
+      const section_size_type offset = this->offset();
       const section_size_type oview_size
        = convert_to_section_size_type(this->data_size());
       unsigned char* const oview = of->get_output_view(offset, oview_size);
@@ -1761,8 +2620,7 @@ Output_data_plt_powerpc<size, big_endian>::do_write(Output_file* of)
       // The address of the .glink branch table
       const Output_data_glink<size, big_endian>* glink
        = this->targ_->glink_section();
-      elfcpp::Elf_types<32>::Elf_Addr branch_tab
-       = glink->address() + glink->pltresolve();
+      elfcpp::Elf_types<32>::Elf_Addr branch_tab = glink->address();
 
       while (pov < endpov)
        {
@@ -1779,10 +2637,14 @@ Output_data_plt_powerpc<size, big_endian>::do_write(Output_file* of)
 
 template<int size, bool big_endian>
 void
-Target_powerpc<size, big_endian>::make_plt_section(Layout* layout)
+Target_powerpc<size, big_endian>::make_plt_section(Symbol_table* symtab,
+                                                  Layout* layout)
 {
   if (this->plt_ == NULL)
     {
+      if (this->got_ == NULL)
+       this->got_section(symtab, layout);
+
       if (this->glink_ == NULL)
        make_glink_section(layout);
 
@@ -1816,11 +2678,12 @@ Target_powerpc<size, big_endian>::make_plt_section(Layout* layout)
 
 template<int size, bool big_endian>
 void
-Target_powerpc<size, big_endian>::make_iplt_section(Layout* layout)
+Target_powerpc<size, big_endian>::make_iplt_section(Symbol_table* symtab,
+                                                   Layout* layout)
 {
   if (this->iplt_ == NULL)
     {
-      this->make_plt_section(layout);
+      this->make_plt_section(symtab, layout);
 
       Reloc_section* iplt_rel = new Reloc_section(false);
       this->rela_dyn_->output_section()->add_output_section_data(iplt_rel);
@@ -1831,117 +2694,505 @@ Target_powerpc<size, big_endian>::make_iplt_section(Layout* layout)
     }
 }
 
-// A class to handle .glink.
+// A section for huge long branch addresses, similar to plt section.
 
 template<int size, bool big_endian>
-class Output_data_glink : public Output_section_data
+class Output_data_brlt_powerpc : public Output_section_data_build
 {
  public:
-  static const int pltresolve_size = 16*4;
+  typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
+  typedef Output_data_reloc<elfcpp::SHT_RELA, true,
+                           size, big_endian> Reloc_section;
 
-  Output_data_glink(Target_powerpc<size, big_endian>*);
+  Output_data_brlt_powerpc(Target_powerpc<size, big_endian>* targ,
+                          Reloc_section* brlt_rel)
+    : Output_section_data_build(size == 32 ? 4 : 8),
+      rel_(brlt_rel),
+      targ_(targ)
+  { }
 
-  // Add an entry
+  // Add a reloc for an entry in the BRLT.
   void
-  add_entry(const Sized_relobj_file<size, big_endian>*,
-           const Symbol*,
-           const elfcpp::Rela<size, big_endian>&);
+  add_reloc(Address to, unsigned int off)
+  { this->rel_->add_relative(elfcpp::R_POWERPC_RELATIVE, this, off, to); }
 
+  // Update section and reloc section size.
   void
-  add_entry(const Sized_relobj_file<size, big_endian>*,
-           unsigned int,
-           const elfcpp::Rela<size, big_endian>&);
-
-  unsigned int
-  find_entry(const Symbol*) const;
-
-  unsigned int
-  find_entry(const Sized_relobj_file<size, big_endian>*, unsigned int) const;
-
-  unsigned int
-  find_entry(const Sized_relobj_file<size, big_endian>*,
-            const Symbol*,
-            const elfcpp::Rela<size, big_endian>&) const;
-
-  unsigned int
-  find_entry(const Sized_relobj_file<size, big_endian>*,
-            unsigned int,
-            const elfcpp::Rela<size, big_endian>&) const;
-
-  unsigned int
-  glink_entry_size() const
+  set_current_size(unsigned int num_branches)
   {
-    if (size == 32)
-      return 4 * 4;
-    else
-      // FIXME: We should be using multiple glink sections for
-      // stubs to support > 33M applications.
-      return 8 * 4;
+    this->reset_address_and_file_offset();
+    this->set_current_data_size(num_branches * 16);
+    this->finalize_data_size();
+    Output_section* os = this->output_section();
+    os->set_section_offsets_need_adjustment();
+    if (this->rel_ != NULL)
+      {
+       unsigned int reloc_size
+         = Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
+       this->rel_->reset_address_and_file_offset();
+       this->rel_->set_current_data_size(num_branches * reloc_size);
+       this->rel_->finalize_data_size();
+       Output_section* os = this->rel_->output_section();
+       os->set_section_offsets_need_adjustment();
+      }
   }
 
-  off_t
-  pltresolve() const
-  {
-    return this->pltresolve_;
+ protected:
+  void
+  do_adjust_output_section(Output_section* os)
+  {
+    os->set_entsize(0);
   }
 
- protected:
   // Write to a map file.
   void
   do_print_to_mapfile(Mapfile* mapfile) const
-  { mapfile->print_output_data(this, _("** glink")); }
+  { mapfile->print_output_data(this, "** BRLT"); }
 
  private:
+  // Write out the BRLT data.
   void
-  set_final_data_size();
+  do_write(Output_file*);
 
-  // Write out .glink
+  // The reloc section.
+  Reloc_section* rel_;
+  Target_powerpc<size, big_endian>* targ_;
+};
+
+// Make the branch lookup table section.
+
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::make_brlt_section(Layout* layout)
+{
+  if (size == 64 && this->brlt_section_ == NULL)
+    {
+      Reloc_section* brlt_rel = NULL;
+      bool is_pic = parameters->options().output_is_position_independent();
+      if (is_pic)
+       {
+         // When PIC we can't fill in .brlt (like .plt it can be a
+         // bss style section) but must initialise at runtime via
+         // dynamic relocats.
+         this->rela_dyn_section(layout);
+         brlt_rel = new Reloc_section(false);
+         this->rela_dyn_->output_section()->add_output_section_data(brlt_rel);
+       }
+      this->brlt_section_
+       = new Output_data_brlt_powerpc<size, big_endian>(this, brlt_rel);
+      if (this->plt_ && is_pic)
+       this->plt_->output_section()
+         ->add_output_section_data(this->brlt_section_);
+      else
+       layout->add_output_section_data(".brlt",
+                                       (is_pic ? elfcpp::SHT_NOBITS
+                                        : elfcpp::SHT_PROGBITS),
+                                       elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE,
+                                       this->brlt_section_,
+                                       (is_pic ? ORDER_SMALL_BSS
+                                        : ORDER_SMALL_DATA),
+                                       false);
+    }
+}
+
+// Write out .brlt when non-PIC.
+
+template<int size, bool big_endian>
+void
+Output_data_brlt_powerpc<size, big_endian>::do_write(Output_file* of)
+{
+  if (size == 64 && !parameters->options().output_is_position_independent())
+    {
+      const section_size_type offset = this->offset();
+      const section_size_type oview_size
+       = convert_to_section_size_type(this->data_size());
+      unsigned char* const oview = of->get_output_view(offset, oview_size);
+
+      this->targ_->write_branch_lookup_table(oview);
+      of->write_output_view(offset, oview_size, oview);
+    }
+}
+
+static inline uint32_t
+l(uint32_t a)
+{
+  return a & 0xffff;
+}
+
+static inline uint32_t
+hi(uint32_t a)
+{
+  return l(a >> 16);
+}
+
+static inline uint32_t
+ha(uint32_t a)
+{
+  return hi(a + 0x8000);
+}
+
+template<int size>
+struct Eh_cie
+{
+  static const unsigned char eh_frame_cie[12];
+};
+
+template<int size>
+const unsigned char Eh_cie<size>::eh_frame_cie[] =
+{
+  1,                                   // CIE version.
+  'z', 'R', 0,                         // Augmentation string.
+  4,                                   // Code alignment.
+  0x80 - size / 8 ,                    // Data alignment.
+  65,                                  // RA reg.
+  1,                                   // Augmentation size.
+  (elfcpp::DW_EH_PE_pcrel
+   | elfcpp::DW_EH_PE_sdata4),         // FDE encoding.
+  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[] =
+{
+  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, 12,
+  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[] =
+{
+  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 + 2,
+  elfcpp::DW_CFA_register, 65, 0,
+  elfcpp::DW_CFA_advance_loc + 4,
+  elfcpp::DW_CFA_restore_extended, 65
+};
+
+static const unsigned char default_fde[] =
+{
+  0, 0, 0, 0,                          // Replaced with offset to stubs.
+  0, 0, 0, 0,                          // Replaced with size of stubs.
+  0,                                   // Augmentation size.
+  elfcpp::DW_CFA_nop,                  // Pad.
+  elfcpp::DW_CFA_nop,
+  elfcpp::DW_CFA_nop
+};
+
+template<bool big_endian>
+static inline void
+write_insn(unsigned char* p, uint32_t v)
+{
+  elfcpp::Swap<32, big_endian>::writeval(p, v);
+}
+
+// Stub_table holds information about plt and long branch stubs.
+// Stubs are built in an area following some input section determined
+// by group_sections().  This input section is converted to a relaxed
+// input section allowing it to be resized to accommodate the stubs
+
+template<int size, bool big_endian>
+class Stub_table : public Output_relaxed_input_section
+{
+ public:
+  typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
+  static const Address invalid_address = static_cast<Address>(0) - 1;
+
+  Stub_table(Target_powerpc<size, big_endian>* targ)
+    : Output_relaxed_input_section(NULL, 0, 0),
+      targ_(targ), plt_call_stubs_(), long_branch_stubs_(),
+      orig_data_size_(0), plt_size_(0), last_plt_size_(0),
+      branch_size_(0), last_branch_size_(0), eh_frame_added_(false)
+  { }
+
+  // Delayed Output_relaxed_input_section init.
+  void
+  init(const Output_section::Input_section*, Output_section*);
+
+  // Add a plt call stub.
+  void
+  add_plt_call_entry(const Sized_relobj_file<size, big_endian>*,
+                    const Symbol*,
+                    unsigned int,
+                    Address);
+
+  void
+  add_plt_call_entry(const Sized_relobj_file<size, big_endian>*,
+                    unsigned int,
+                    unsigned int,
+                    Address);
+
+  // Find a given plt call stub.
+  Address
+  find_plt_call_entry(const Symbol*) const;
+
+  Address
+  find_plt_call_entry(const Sized_relobj_file<size, big_endian>*,
+                     unsigned int) const;
+
+  Address
+  find_plt_call_entry(const Sized_relobj_file<size, big_endian>*,
+                     const Symbol*,
+                     unsigned int,
+                     Address) const;
+
+  Address
+  find_plt_call_entry(const Sized_relobj_file<size, big_endian>*,
+                     unsigned int,
+                     unsigned int,
+                     Address) const;
+
+  // Add a long branch stub.
+  void
+  add_long_branch_entry(const Powerpc_relobj<size, big_endian>*, Address);
+
+  Address
+  find_long_branch_entry(const Powerpc_relobj<size, big_endian>*,
+                        Address) const;
+
+  void
+  clear_stubs()
+  {
+    this->plt_call_stubs_.clear();
+    this->plt_size_ = 0;
+    this->long_branch_stubs_.clear();
+    this->branch_size_ = 0;
+  }
+
+  Address
+  set_address_and_size(const Output_section* os, Address off)
+  {
+    Address start_off = off;
+    off += this->orig_data_size_;
+    Address my_size = this->plt_size_ + this->branch_size_;
+    if (my_size != 0)
+      off = align_address(off, this->stub_align());
+    // Include original section size and alignment padding in size
+    my_size += off - start_off;
+    this->reset_address_and_file_offset();
+    this->set_current_data_size(my_size);
+    this->set_address_and_file_offset(os->address() + start_off,
+                                     os->offset() + start_off);
+    return my_size;
+  }
+
+  Address
+  stub_address() const
+  {
+    return align_address(this->address() + this->orig_data_size_,
+                        this->stub_align());
+  }
+
+  Address
+  stub_offset() const
+  {
+    return align_address(this->offset() + this->orig_data_size_,
+                        this->stub_align());
+  }
+
+  section_size_type
+  plt_size() const
+  { return this->plt_size_; }
+
+  bool
+  size_update()
+  {
+    Output_section* os = this->output_section();
+    if (os->addralign() < this->stub_align())
+      {
+       os->set_addralign(this->stub_align());
+       // FIXME: get rid of the insane checkpointing.
+       // We can't increase alignment of the input section to which
+       // stubs are attached;  The input section may be .init which
+       // is pasted together with other .init sections to form a
+       // function.  Aligning might insert zero padding resulting in
+       // sigill.  However we do need to increase alignment of the
+       // output section so that the align_address() on offset in
+       // set_address_and_size() adds the same padding as the
+       // align_address() on address in stub_address().
+       // What's more, we need this alignment for the layout done in
+       // relaxation_loop_body() so that the output section starts at
+       // a suitably aligned address.
+       os->checkpoint_set_addralign(this->stub_align());
+      }
+    if (this->last_plt_size_ != this->plt_size_
+       || this->last_branch_size_ != this->branch_size_)
+      {
+       this->last_plt_size_ = this->plt_size_;
+       this->last_branch_size_ = this->branch_size_;
+       return true;
+      }
+    return false;
+  }
+
+  // Add .eh_frame info for this stub section.  Unlike other linker
+  // generated .eh_frame this is added late in the link, because we
+  // only want the .eh_frame info if this particular stub section is
+  // non-empty.
+  void
+  add_eh_frame(Layout* layout)
+  {
+    if (!this->eh_frame_added_)
+      {
+       if (!parameters->options().ld_generated_unwind_info())
+         return;
+
+       // Since we add stub .eh_frame info late, it must be placed
+       // after all other linker generated .eh_frame info so that
+       // merge mapping need not be updated for input sections.
+       // There is no provision to use a different CIE to that used
+       // by .glink.
+       if (!this->targ_->has_glink())
+         return;
+
+       layout->add_eh_frame_for_plt(this,
+                                    Eh_cie<size>::eh_frame_cie,
+                                    sizeof (Eh_cie<size>::eh_frame_cie),
+                                    default_fde,
+                                    sizeof (default_fde));
+       this->eh_frame_added_ = true;
+      }
+  }
+
+  Target_powerpc<size, big_endian>*
+  targ() const
+  { return targ_; }
+
+ private:
+  class Plt_stub_ent;
+  class Plt_stub_ent_hash;
+  typedef Unordered_map<Plt_stub_ent, unsigned int,
+                       Plt_stub_ent_hash> Plt_stub_entries;
+
+  // Alignment of stub section.
+  unsigned int
+  stub_align() const
+  {
+    if (size == 32)
+      return 16;
+    unsigned int min_align = 32;
+    unsigned int user_align = 1 << parameters->options().plt_align();
+    return std::max(user_align, min_align);
+  }
+
+  // Return the plt offset for the given call stub.
+  Address
+  plt_off(typename Plt_stub_entries::const_iterator p, bool* is_iplt) const
+  {
+    const Symbol* gsym = p->first.sym_;
+    if (gsym != NULL)
+      {
+       *is_iplt = (gsym->type() == elfcpp::STT_GNU_IFUNC
+                   && gsym->can_use_relative_reloc(false));
+       return gsym->plt_offset();
+      }
+    else
+      {
+       *is_iplt = true;
+       const Sized_relobj_file<size, big_endian>* relobj = p->first.object_;
+       unsigned int local_sym_index = p->first.locsym_;
+       return relobj->local_plt_offset(local_sym_index);
+      }
+  }
+
+  // Size of a given plt call stub.
+  unsigned int
+  plt_call_size(typename Plt_stub_entries::const_iterator p) const
+  {
+    if (size == 32)
+      return 16;
+
+    bool is_iplt;
+    Address plt_addr = this->plt_off(p, &is_iplt);
+    if (is_iplt)
+      plt_addr += this->targ_->iplt_section()->address();
+    else
+      plt_addr += this->targ_->plt_section()->address();
+    Address got_addr = this->targ_->got_section()->output_section()->address();
+    const Powerpc_relobj<size, big_endian>* ppcobj = static_cast
+      <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 align = 1 << parameters->options().plt_align();
+    if (align > 1)
+      bytes = (bytes + align - 1) & -align;
+    return bytes;
+  }
+
+  // Return long branch stub size.
+  unsigned int
+  branch_stub_size(Address to)
+  {
+    Address loc
+      = this->stub_address() + this->last_plt_size_ + this->branch_size_;
+    if (to - loc + (1 << 25) < 2 << 25)
+      return 4;
+    if (size == 64 || !parameters->options().output_is_position_independent())
+      return 16;
+    return 32;
+  }
+
+  // Write out stubs.
   void
   do_write(Output_file*);
 
-  class Glink_sym_ent
+  // Plt call stub keys.
+  class Plt_stub_ent
   {
   public:
-    Glink_sym_ent(const Symbol* sym)
+    Plt_stub_ent(const Symbol* sym)
       : sym_(sym), object_(0), addend_(0), locsym_(0)
     { }
 
-    Glink_sym_ent(const Sized_relobj_file<size, big_endian>* object,
-                 unsigned int locsym_index)
+    Plt_stub_ent(const Sized_relobj_file<size, big_endian>* object,
+                unsigned int locsym_index)
       : sym_(NULL), object_(object), addend_(0), locsym_(locsym_index)
     { }
 
-    Glink_sym_ent(const Sized_relobj_file<size, big_endian>* object,
-                 const Symbol* sym,
-                 const elfcpp::Rela<size, big_endian>& reloc)
+    Plt_stub_ent(const Sized_relobj_file<size, big_endian>* object,
+                const Symbol* sym,
+                unsigned int r_type,
+                Address addend)
       : sym_(sym), object_(0), addend_(0), locsym_(0)
     {
       if (size != 32)
-       this->addend_ = reloc.get_r_addend();
+       this->addend_ = addend;
       else if (parameters->options().output_is_position_independent()
-              && (elfcpp::elf_r_type<size>(reloc.get_r_info())
-                  == elfcpp::R_PPC_PLTREL24))
+              && r_type == elfcpp::R_PPC_PLTREL24)
        {
-         this->addend_ = reloc.get_r_addend();
+         this->addend_ = addend;
          if (this->addend_ >= 32768)
            this->object_ = object;
        }
     }
 
-    Glink_sym_ent(const Sized_relobj_file<size, big_endian>* object,
-                 unsigned int locsym_index,
-                 const elfcpp::Rela<size, big_endian>& reloc)
+    Plt_stub_ent(const Sized_relobj_file<size, big_endian>* object,
+                unsigned int locsym_index,
+                unsigned int r_type,
+                Address addend)
       : sym_(NULL), object_(object), addend_(0), locsym_(locsym_index)
     {
       if (size != 32)
-       this->addend_ = reloc.get_r_addend();
+       this->addend_ = addend;
       else if (parameters->options().output_is_position_independent()
-              && (elfcpp::elf_r_type<size>(reloc.get_r_info())
-                  == elfcpp::R_PPC_PLTREL24))
-       this->addend_ = reloc.get_r_addend();
+              && r_type == elfcpp::R_PPC_PLTREL24)
+       this->addend_ = addend;
     }
 
-    bool operator==(const Glink_sym_ent& that) const
+    bool operator==(const Plt_stub_ent& that) const
     {
       return (this->sym_ == that.sym_
              && this->object_ == that.object_
@@ -1955,10 +3206,10 @@ class Output_data_glink : public Output_section_data
     unsigned int locsym_;
   };
 
-  class Glink_sym_ent_hash
+  class Plt_stub_ent_hash
   {
   public:
-    size_t operator()(const Glink_sym_ent& ent) const
+    size_t operator()(const Plt_stub_ent& ent) const
     {
       return (reinterpret_cast<uintptr_t>(ent.sym_)
              ^ reinterpret_cast<uintptr_t>(ent.object_)
@@ -1967,117 +3218,272 @@ class Output_data_glink : public Output_section_data
     }
   };
 
-  // Map sym/object/addend to index.
-  typedef Unordered_map<Glink_sym_ent, unsigned int,
-                       Glink_sym_ent_hash> Glink_entries;
-  Glink_entries glink_entries_;
+  // Long branch stub keys.
+  class Branch_stub_ent
+  {
+  public:
+    Branch_stub_ent(const Powerpc_relobj<size, big_endian>* obj, Address to)
+      : dest_(to), toc_base_off_(0)
+    {
+      if (size == 64)
+       toc_base_off_ = obj->toc_base_offset();
+    }
 
-  // Offset of pltresolve stub (actually, branch table for 32-bit)
-  off_t pltresolve_;
+    bool operator==(const Branch_stub_ent& that) const
+    {
+      return (this->dest_ == that.dest_
+             && (size == 32
+                 || this->toc_base_off_ == that.toc_base_off_));
+    }
 
-  // Allows access to .got and .plt for do_write.
+    Address dest_;
+    unsigned int toc_base_off_;
+  };
+
+  class Branch_stub_ent_hash
+  {
+  public:
+    size_t operator()(const Branch_stub_ent& ent) const
+    { return ent.dest_ ^ ent.toc_base_off_; }
+  };
+
+  // In a sane world this would be a global.
   Target_powerpc<size, big_endian>* targ_;
+  // Map sym/object/addend to stub offset.
+  Plt_stub_entries plt_call_stubs_;
+  // Map destination address to stub offset.
+  typedef Unordered_map<Branch_stub_ent, unsigned int,
+                       Branch_stub_ent_hash> Branch_stub_entries;
+  Branch_stub_entries long_branch_stubs_;
+  // size of input section
+  section_size_type orig_data_size_;
+  // size of stubs
+  section_size_type plt_size_, last_plt_size_, branch_size_, last_branch_size_;
+  // Whether .eh_frame info has been created for this stub section.
+  bool eh_frame_added_;
 };
 
-// Create the glink section.
+// Make a new stub table, and record.
 
 template<int size, bool big_endian>
-Output_data_glink<size, big_endian>::Output_data_glink(
-    Target_powerpc<size, big_endian>* targ)
-  : Output_section_data(16),
-    pltresolve_(0), targ_(targ)
+Stub_table<size, big_endian>*
+Target_powerpc<size, big_endian>::new_stub_table()
 {
+  Stub_table<size, big_endian>* stub_table
+    = new Stub_table<size, big_endian>(this);
+  this->stub_tables_.push_back(stub_table);
+  return stub_table;
 }
 
-// Add an entry to glink, if we do not already have one for this
+// Delayed stub table initialisation, because we create the stub table
+// before we know to which section it will be attached.
+
+template<int size, bool big_endian>
+void
+Stub_table<size, big_endian>::init(
+    const Output_section::Input_section* owner,
+    Output_section* output_section)
+{
+  this->set_relobj(owner->relobj());
+  this->set_shndx(owner->shndx());
+  this->set_addralign(this->relobj()->section_addralign(this->shndx()));
+  this->set_output_section(output_section);
+  this->orig_data_size_ = owner->current_data_size();
+
+  std::vector<Output_relaxed_input_section*> new_relaxed;
+  new_relaxed.push_back(this);
+  output_section->convert_input_sections_to_relaxed_sections(new_relaxed);
+}
+
+// Add a plt call stub, if we do not already have one for this
 // sym/object/addend combo.
 
 template<int size, bool big_endian>
 void
-Output_data_glink<size, big_endian>::add_entry(
+Stub_table<size, big_endian>::add_plt_call_entry(
     const Sized_relobj_file<size, big_endian>* object,
     const Symbol* gsym,
-    const elfcpp::Rela<size, big_endian>& reloc)
+    unsigned int r_type,
+    Address addend)
 {
-  Glink_sym_ent ent(object, gsym, reloc);
-  unsigned int indx = this->glink_entries_.size();
-  this->glink_entries_.insert(std::make_pair(ent, indx));
+  Plt_stub_ent ent(object, gsym, r_type, addend);
+  Address 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)
+    this->plt_size_ = off + this->plt_call_size(p.first);
 }
 
 template<int size, bool big_endian>
 void
-Output_data_glink<size, big_endian>::add_entry(
+Stub_table<size, big_endian>::add_plt_call_entry(
     const Sized_relobj_file<size, big_endian>* object,
     unsigned int locsym_index,
-    const elfcpp::Rela<size, big_endian>& reloc)
+    unsigned int r_type,
+    Address addend)
 {
-  Glink_sym_ent ent(object, locsym_index, reloc);
-  unsigned int indx = this->glink_entries_.size();
-  this->glink_entries_.insert(std::make_pair(ent, indx));
+  Plt_stub_ent ent(object, locsym_index, r_type, addend);
+  Address 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)
+    this->plt_size_ = off + this->plt_call_size(p.first);
 }
 
+// Find a plt call stub.
+
 template<int size, bool big_endian>
-unsigned int
-Output_data_glink<size, big_endian>::find_entry(
+typename Stub_table<size, big_endian>::Address
+Stub_table<size, big_endian>::find_plt_call_entry(
     const Sized_relobj_file<size, big_endian>* object,
     const Symbol* gsym,
-    const elfcpp::Rela<size, big_endian>& reloc) const
+    unsigned int r_type,
+    Address addend) const
 {
-  Glink_sym_ent ent(object, gsym, reloc);
-  typename Glink_entries::const_iterator p = this->glink_entries_.find(ent);
-  gold_assert(p != this->glink_entries_.end());
-  return p->second;
+  Plt_stub_ent ent(object, gsym, r_type, addend);
+  typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(ent);
+  return p == this->plt_call_stubs_.end() ? invalid_address : p->second;
 }
 
 template<int size, bool big_endian>
-unsigned int
-Output_data_glink<size, big_endian>::find_entry(const Symbol* gsym) const
+typename Stub_table<size, big_endian>::Address
+Stub_table<size, big_endian>::find_plt_call_entry(const Symbol* gsym) const
 {
-  Glink_sym_ent ent(gsym);
-  typename Glink_entries::const_iterator p = this->glink_entries_.find(ent);
-  gold_assert(p != this->glink_entries_.end());
-  return p->second;
+  Plt_stub_ent ent(gsym);
+  typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(ent);
+  return p == this->plt_call_stubs_.end() ? invalid_address : p->second;
 }
 
 template<int size, bool big_endian>
-unsigned int
-Output_data_glink<size, big_endian>::find_entry(
+typename Stub_table<size, big_endian>::Address
+Stub_table<size, big_endian>::find_plt_call_entry(
     const Sized_relobj_file<size, big_endian>* object,
     unsigned int locsym_index,
-    const elfcpp::Rela<size, big_endian>& reloc) const
+    unsigned int r_type,
+    Address addend) const
 {
-  Glink_sym_ent ent(object, locsym_index, reloc);
-  typename Glink_entries::const_iterator p = this->glink_entries_.find(ent);
-  gold_assert(p != this->glink_entries_.end());
-  return p->second;
+  Plt_stub_ent ent(object, locsym_index, r_type, addend);
+  typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(ent);
+  return p == this->plt_call_stubs_.end() ? invalid_address : p->second;
 }
 
 template<int size, bool big_endian>
-unsigned int
-Output_data_glink<size, big_endian>::find_entry(
+typename Stub_table<size, big_endian>::Address
+Stub_table<size, big_endian>::find_plt_call_entry(
     const Sized_relobj_file<size, big_endian>* object,
     unsigned int locsym_index) const
 {
-  Glink_sym_ent ent(object, locsym_index);
-  typename Glink_entries::const_iterator p = this->glink_entries_.find(ent);
-  gold_assert(p != this->glink_entries_.end());
-  return p->second;
+  Plt_stub_ent ent(object, locsym_index);
+  typename Plt_stub_entries::const_iterator p = this->plt_call_stubs_.find(ent);
+  return p == this->plt_call_stubs_.end() ? invalid_address : p->second;
 }
 
+// Add a long branch stub if we don't already have one to given
+// destination.
+
+template<int size, bool big_endian>
+void
+Stub_table<size, big_endian>::add_long_branch_entry(
+    const Powerpc_relobj<size, big_endian>* object,
+    Address to)
+{
+  Branch_stub_ent ent(object, to);
+  Address off = this->branch_size_;
+  if (this->long_branch_stubs_.insert(std::make_pair(ent, off)).second)
+    {
+      unsigned int stub_size = this->branch_stub_size(to);
+      this->branch_size_ = off + stub_size;
+      if (size == 64 && stub_size != 4)
+       this->targ_->add_branch_lookup_table(to);
+    }
+}
+
+// Find long branch stub.
+
+template<int size, bool big_endian>
+typename Stub_table<size, big_endian>::Address
+Stub_table<size, big_endian>::find_long_branch_entry(
+    const Powerpc_relobj<size, big_endian>* object,
+    Address to) const
+{
+  Branch_stub_ent ent(object, to);
+  typename Branch_stub_entries::const_iterator p
+    = this->long_branch_stubs_.find(ent);
+  return p == this->long_branch_stubs_.end() ? invalid_address : p->second;
+}
+
+// A class to handle .glink.
+
+template<int size, bool big_endian>
+class Output_data_glink : public Output_section_data
+{
+ public:
+  static const int pltresolve_size = 16*4;
+
+  Output_data_glink(Target_powerpc<size, big_endian>* targ)
+    : Output_section_data(16), targ_(targ)
+  { }
+
+  void
+  add_eh_frame(Layout* layout)
+  {
+    if (!parameters->options().ld_generated_unwind_info())
+      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));
+    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));
+      }
+  }
+
+ protected:
+  // Write to a map file.
+  void
+  do_print_to_mapfile(Mapfile* mapfile) const
+  { mapfile->print_output_data(this, _("** glink")); }
+
+ private:
+  void
+  set_final_data_size();
+
+  // Write out .glink
+  void
+  do_write(Output_file*);
+
+  // Allows access to .got and .plt for do_write.
+  Target_powerpc<size, big_endian>* targ_;
+};
+
 template<int size, bool big_endian>
 void
 Output_data_glink<size, big_endian>::set_final_data_size()
 {
-  unsigned int count = this->glink_entries_.size();
-  off_t total = count;
+  unsigned int count = this->targ_->plt_entry_count();
+  section_size_type total = 0;
 
   if (count != 0)
     {
       if (size == 32)
        {
-         total *= 16;
-         this->pltresolve_ = total;
-
          // space for branch table
          total += 4 * (count - 1);
 
@@ -2086,8 +3492,6 @@ Output_data_glink<size, big_endian>::set_final_data_size()
        }
       else
        {
-         total *= 32;
-         this->pltresolve_ = total;
          total += this->pltresolve_size;
 
          // space for branch table
@@ -2100,132 +3504,304 @@ Output_data_glink<size, big_endian>::set_final_data_size()
   this->set_data_size(total);
 }
 
-static inline uint32_t
-l(uint32_t a)
-{
-  return a & 0xffff;
-}
-
-static inline uint32_t
-hi(uint32_t a)
-{
-  return l(a >> 16);
-}
-
-static inline uint32_t
-ha(uint32_t a)
-{
-  return hi(a + 0x8000);
-}
-
-template<bool big_endian>
-static inline void
-write_insn(unsigned char* p, uint32_t v)
-{
-  elfcpp::Swap<32, big_endian>::writeval(p, v);
-}
-
-// Write out .glink.
+// Write out plt and long branch stub code.
 
 template<int size, bool big_endian>
 void
-Output_data_glink<size, big_endian>::do_write(Output_file* of)
+Stub_table<size, big_endian>::do_write(Output_file* of)
 {
-  const off_t off = this->offset();
+  if (this->plt_call_stubs_.empty()
+      && this->long_branch_stubs_.empty())
+    return;
+
+  const section_size_type start_off = this->offset();
+  const section_size_type off = this->stub_offset();
   const section_size_type oview_size =
-    convert_to_section_size_type(this->data_size());
+    convert_to_section_size_type(this->data_size() - (off - start_off));
   unsigned char* const oview = of->get_output_view(off, oview_size);
   unsigned char* p;
 
-  // The base address of the .plt section.
-  typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
-  static const Address invalid_address = static_cast<Address>(0) - 1;
-  Address plt_base = this->targ_->plt_section()->address();
-  Address iplt_base = invalid_address;
-
-  const Output_data_got_powerpc<size, big_endian>* got
-    = this->targ_->got_section();
-
   if (size == 64)
     {
+      const Output_data_got_powerpc<size, big_endian>* got
+       = this->targ_->got_section();
       Address got_os_addr = got->output_section()->address();
 
-      // Write out call stubs.
-      typename Glink_entries::const_iterator g;
-      for (g = this->glink_entries_.begin();
-          g != this->glink_entries_.end();
-          ++g)
+      if (!this->plt_call_stubs_.empty())
        {
-         Address plt_addr;
-         bool is_ifunc;
-         const Symbol* gsym = g->first.sym_;
-         if (gsym != NULL)
-           {
-             is_ifunc = (gsym->type() == elfcpp::STT_GNU_IFUNC
-                         && gsym->can_use_relative_reloc(false));
-             plt_addr = gsym->plt_offset();
-           }
-         else
-           {
-             is_ifunc = true;
-             const Sized_relobj_file<size, big_endian>* relobj
-               = g->first.object_;
-             unsigned int local_sym_index = g->first.locsym_;
-             plt_addr = relobj->local_plt_offset(local_sym_index);
-           }
-         if (is_ifunc)
+         // The base address of the .plt section.
+         Address plt_base = this->targ_->plt_section()->address();
+         Address iplt_base = invalid_address;
+
+         // Write out plt call stubs.
+         typename Plt_stub_entries::const_iterator cs;
+         for (cs = this->plt_call_stubs_.begin();
+              cs != this->plt_call_stubs_.end();
+              ++cs)
            {
-             if (iplt_base == invalid_address)
-               iplt_base = this->targ_->iplt_section()->address();
-             plt_addr += iplt_base;
+             bool is_iplt;
+             Address pltoff = this->plt_off(cs, &is_iplt);
+             Address plt_addr = pltoff;
+             if (is_iplt)
+               {
+                 if (iplt_base == invalid_address)
+                   iplt_base = this->targ_->iplt_section()->address();
+                 plt_addr += iplt_base;
+               }
+             else
+               plt_addr += plt_base;
+             const Powerpc_relobj<size, big_endian>* ppcobj = static_cast
+               <const Powerpc_relobj<size, big_endian>*>(cs->first.object_);
+             Address got_addr = got_os_addr + ppcobj->toc_base_offset();
+             Address off = plt_addr - got_addr;
+
+             if (off + 0x80008000 > 0xffffffff || (off & 7) != 0)
+               gold_error(_("%s: linkage table error against `%s'"),
+                          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 use_fake_dep = false;
+             Address cmp_branch_off = 0;
+             if (thread_safe)
+               {
+                 unsigned int pltindex
+                   = ((pltoff - this->targ_->first_plt_entry_offset())
+                      / this->targ_->plt_entry_size());
+                 Address glinkoff
+                   = (this->targ_->glink_section()->pltresolve_size
+                      + pltindex * 8);
+                 if (pltindex > 32768)
+                   glinkoff += (pltindex - 32768) * 4;
+                 Address to
+                   = this->targ_->glink_section()->address() + glinkoff;
+                 Address from
+                   = (this->stub_address() + cs->second + 24
+                      + 4 * (ha(off) != 0)
+                      + 4 * (ha(off + 8 + 8 * static_chain) != ha(off))
+                      + 4 * static_chain);
+                 cmp_branch_off = to - from;
+                 use_fake_dep = cmp_branch_off + (1 << 25) >= (1 << 26);
+               }
+
+             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, addi_12_12 + l(off)),   p += 4;
+                     off = 0;
+                   }
+                 write_insn<big_endian>(p, mtctr_11),                  p += 4;
+                 if (use_fake_dep)
+                   {
+                     write_insn<big_endian>(p, xor_11_11_11),          p += 4;
+                     write_insn<big_endian>(p, add_12_12_11),          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, 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, xor_11_11_11),          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 (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, b | (cmp_branch_off & 0x3fffffc));
+               }
+             else
+               write_insn<big_endian>(p, bctr);
            }
+       }
+
+      // Write out long branch stubs.
+      typename Branch_stub_entries::const_iterator bs;
+      for (bs = this->long_branch_stubs_.begin();
+          bs != this->long_branch_stubs_.end();
+          ++bs)
+       {
+         p = oview + this->plt_size_ + bs->second;
+         Address loc = this->stub_address() + this->plt_size_ + bs->second;
+         Address delta = bs->first.dest_ - loc;
+         if (delta + (1 << 25) < 2 << 25)
+           write_insn<big_endian>(p, b | (delta & 0x3fffffc));
          else
-           plt_addr += plt_base;
-         const Powerpc_relobj<size, big_endian>* ppcobj = static_cast
-           <const Powerpc_relobj<size, big_endian>*>(g->first.object_);
-         Address got_addr = got_os_addr + ppcobj->toc_base_offset();
-         Address pltoff = plt_addr - got_addr;
-
-         if (pltoff + 0x80008000 > 0xffffffff || (pltoff & 7) != 0)
-           gold_error(_("%s: linkage table error against `%s'"),
-                      g->first.object_->name().c_str(),
-                      g->first.sym_->demangled_name().c_str());
-
-         p = oview + g->second * this->glink_entry_size();
-         if (ha(pltoff) != 0)
            {
-             write_insn<big_endian>(p, addis_12_2 + ha(pltoff)),       p += 4;
-             write_insn<big_endian>(p, std_2_1 + 40),                  p += 4;
-             write_insn<big_endian>(p, ld_11_12 + l(pltoff)),          p += 4;
-             if (ha(pltoff + 16) != ha(pltoff))
+             Address brlt_addr
+               = this->targ_->find_branch_lookup_table(bs->first.dest_);
+             gold_assert(brlt_addr != invalid_address);
+             brlt_addr += this->targ_->brlt_section()->address();
+             Address got_addr = got_os_addr + bs->first.toc_base_off_;
+             Address brltoff = brlt_addr - got_addr;
+             if (ha(brltoff) == 0)
+               {
+                 write_insn<big_endian>(p, ld_11_2 + l(brltoff)),      p += 4;
+               }
+             else
                {
-                 write_insn<big_endian>(p, addi_12_12 + l(pltoff)),    p += 4;
-                 pltoff = 0;
+                 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, mtctr_11),                      p += 4;
-             write_insn<big_endian>(p, ld_2_12 + l(pltoff + 8)),       p += 4;
-             write_insn<big_endian>(p, ld_11_12 + l(pltoff + 16)),     p += 4;
-             write_insn<big_endian>(p, bctr),                          p += 4;
+             write_insn<big_endian>(p, bctr);
            }
-         else
+       }
+    }
+  else
+    {
+      if (!this->plt_call_stubs_.empty())
+       {
+         // The base address of the .plt section.
+         Address plt_base = this->targ_->plt_section()->address();
+         Address iplt_base = invalid_address;
+         // The address of _GLOBAL_OFFSET_TABLE_.
+         Address g_o_t = invalid_address;
+
+         // Write out plt call stubs.
+         typename Plt_stub_entries::const_iterator cs;
+         for (cs = this->plt_call_stubs_.begin();
+              cs != this->plt_call_stubs_.end();
+              ++cs)
            {
-             write_insn<big_endian>(p, std_2_1 + 40),                  p += 4;
-             write_insn<big_endian>(p, ld_11_2 + l(pltoff)),           p += 4;
-             if (ha(pltoff + 16) != ha(pltoff))
+             bool is_iplt;
+             Address plt_addr = this->plt_off(cs, &is_iplt);
+             if (is_iplt)
                {
-                 write_insn<big_endian>(p, addi_2_2 + l(pltoff)),      p += 4;
-                 pltoff = 0;
+                 if (iplt_base == invalid_address)
+                   iplt_base = this->targ_->iplt_section()->address();
+                 plt_addr += iplt_base;
                }
-             write_insn<big_endian>(p, mtctr_11),                      p += 4;
-             write_insn<big_endian>(p, ld_11_2 + l(pltoff + 16)),      p += 4;
-             write_insn<big_endian>(p, ld_2_2 + l(pltoff + 8)),        p += 4;
-             write_insn<big_endian>(p, bctr),                          p += 4;
+             else
+               plt_addr += plt_base;
+
+             p = oview + cs->second;
+             if (parameters->options().output_is_position_independent())
+               {
+                 Address got_addr;
+                 const Powerpc_relobj<size, big_endian>* ppcobj
+                   = (static_cast<const Powerpc_relobj<size, big_endian>*>
+                      (cs->first.object_));
+                 if (ppcobj != NULL && cs->first.addend_ >= 32768)
+                   {
+                     unsigned int got2 = ppcobj->got2_shndx();
+                     got_addr = ppcobj->get_output_section_offset(got2);
+                     gold_assert(got_addr != invalid_address);
+                     got_addr += (ppcobj->output_section(got2)->address()
+                                  + cs->first.addend_);
+                   }
+                 else
+                   {
+                     if (g_o_t == invalid_address)
+                       {
+                         const Output_data_got_powerpc<size, big_endian>* got
+                           = this->targ_->got_section();
+                         g_o_t = got->address() + got->g_o_t();
+                       }
+                     got_addr = g_o_t;
+                   }
+
+                 Address off = plt_addr - got_addr;
+                 if (ha(off) == 0)
+                   {
+                     write_insn<big_endian>(p +  0, lwz_11_30 + l(off));
+                     write_insn<big_endian>(p +  4, mtctr_11);
+                     write_insn<big_endian>(p +  8, bctr);
+                   }
+                 else
+                   {
+                     write_insn<big_endian>(p +  0, addis_11_30 + ha(off));
+                     write_insn<big_endian>(p +  4, lwz_11_11 + l(off));
+                     write_insn<big_endian>(p +  8, mtctr_11);
+                     write_insn<big_endian>(p + 12, bctr);
+                   }
+               }
+             else
+               {
+                 write_insn<big_endian>(p +  0, lis_11 + ha(plt_addr));
+                 write_insn<big_endian>(p +  4, lwz_11_11 + l(plt_addr));
+                 write_insn<big_endian>(p +  8, mtctr_11);
+                 write_insn<big_endian>(p + 12, bctr);
+               }
+           }
+       }
+
+      // Write out long branch stubs.
+      typename Branch_stub_entries::const_iterator bs;
+      for (bs = this->long_branch_stubs_.begin();
+          bs != this->long_branch_stubs_.end();
+          ++bs)
+       {
+         p = oview + this->plt_size_ + bs->second;
+         Address loc = this->stub_address() + this->plt_size_ + bs->second;
+         Address delta = bs->first.dest_ - loc;
+         if (delta + (1 << 25) < 2 << 25)
+           write_insn<big_endian>(p, b | (delta & 0x3fffffc));
+         else if (!parameters->options().output_is_position_independent())
+           {
+             write_insn<big_endian>(p +  0, lis_12 + ha(bs->first.dest_));
+             write_insn<big_endian>(p +  4, addi_12_12 + l(bs->first.dest_));
+             write_insn<big_endian>(p +  8, mtctr_12);
+             write_insn<big_endian>(p + 12, bctr);
+           }
+         else
+           {
+             delta -= 8;
+             write_insn<big_endian>(p +  0, mflr_0);
+             write_insn<big_endian>(p +  4, bcl_20_31);
+             write_insn<big_endian>(p +  8, mflr_12);
+             write_insn<big_endian>(p + 12, addis_12_12 + ha(delta));
+             write_insn<big_endian>(p + 16, addi_12_12 + l(delta));
+             write_insn<big_endian>(p + 20, mtlr_0);
+             write_insn<big_endian>(p + 24, mtctr_12);
+             write_insn<big_endian>(p + 28, bctr);
            }
        }
+    }
+}
+
+// Write out .glink.
+
+template<int size, bool big_endian>
+void
+Output_data_glink<size, big_endian>::do_write(Output_file* of)
+{
+  const section_size_type off = this->offset();
+  const section_size_type oview_size =
+    convert_to_section_size_type(this->data_size());
+  unsigned char* const oview = of->get_output_view(off, oview_size);
+  unsigned char* p;
+
+  // The base address of the .plt section.
+  typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
+  Address plt_base = this->targ_->plt_section()->address();
 
+  if (size == 64)
+    {
       // Write pltresolve stub.
-      p = oview + this->pltresolve_;
-      Address after_bcl = this->address() + this->pltresolve_ + 16;
+      p = oview;
+      Address after_bcl = this->address() + 16;
       Address pltoff = plt_base - after_bcl;
 
       elfcpp::Swap<64, big_endian>::writeval(p, pltoff),       p += 8;
@@ -2241,7 +3817,7 @@ Output_data_glink<size, big_endian>::do_write(Output_file* of)
       write_insn<big_endian>(p, mtctr_11),                     p += 4;
       write_insn<big_endian>(p, ld_11_12 + 16),                        p += 4;
       write_insn<big_endian>(p, bctr),                         p += 4;
-      while (p < oview + this->pltresolve_ + this->pltresolve_size)
+      while (p < oview + this->pltresolve_size)
        write_insn<big_endian>(p, nop), p += 4;
 
       // Write lazy link call stubs.
@@ -2257,91 +3833,20 @@ Output_data_glink<size, big_endian>::do_write(Output_file* of)
              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 = this->pltresolve_ + 8 - (p - oview);
+         uint32_t branch_off = 8 - (p - oview);
          write_insn<big_endian>(p, b + (branch_off & 0x3fffffc)),      p += 4;
          indx++;
        }
     }
   else
     {
+      const Output_data_got_powerpc<size, big_endian>* got
+       = this->targ_->got_section();
       // The address of _GLOBAL_OFFSET_TABLE_.
       Address g_o_t = got->address() + got->g_o_t();
 
-      // Write out call stubs.
-      typename Glink_entries::const_iterator g;
-      for (g = this->glink_entries_.begin();
-          g != this->glink_entries_.end();
-          ++g)
-       {
-         Address plt_addr;
-         bool is_ifunc;
-         const Symbol* gsym = g->first.sym_;
-         if (gsym != NULL)
-           {
-             is_ifunc = (gsym->type() == elfcpp::STT_GNU_IFUNC
-                         && gsym->can_use_relative_reloc(false));
-             plt_addr = gsym->plt_offset();
-           }
-         else
-           {
-             is_ifunc = true;
-             const Sized_relobj_file<size, big_endian>* relobj
-               = g->first.object_;
-             unsigned int local_sym_index = g->first.locsym_;
-             plt_addr = relobj->local_plt_offset(local_sym_index);
-           }
-         if (is_ifunc)
-           {
-             if (iplt_base == invalid_address)
-               iplt_base = this->targ_->iplt_section()->address();
-             plt_addr += iplt_base;
-           }
-         else
-           plt_addr += plt_base;
-
-         p = oview + g->second * this->glink_entry_size();
-         if (parameters->options().output_is_position_independent())
-           {
-             Address got_addr;
-             const Powerpc_relobj<size, big_endian>* object = static_cast
-               <const Powerpc_relobj<size, big_endian>*>(g->first.object_);
-             if (object != NULL && g->first.addend_ >= 32768)
-               {
-                 unsigned int got2 = object->got2_shndx();
-                 got_addr = g->first.object_->get_output_section_offset(got2);
-                 gold_assert(got_addr != invalid_address);
-                 got_addr += (g->first.object_->output_section(got2)->address()
-                              + g->first.addend_);
-               }
-             else
-               got_addr = g_o_t;
-
-             Address pltoff = plt_addr - got_addr;
-             if (ha(pltoff) == 0)
-               {
-                 write_insn<big_endian>(p +  0, lwz_11_30 + l(pltoff));
-                 write_insn<big_endian>(p +  4, mtctr_11);
-                 write_insn<big_endian>(p +  8, bctr);
-               }
-             else
-               {
-                 write_insn<big_endian>(p +  0, addis_11_30 + ha(pltoff));
-                 write_insn<big_endian>(p +  4, lwz_11_11 + l(pltoff));
-                 write_insn<big_endian>(p +  8, mtctr_11);
-                 write_insn<big_endian>(p + 12, bctr);
-               }
-           }
-         else
-           {
-             write_insn<big_endian>(p +  0, lis_11 + ha(plt_addr));
-             write_insn<big_endian>(p +  4, lwz_11_11 + l(plt_addr));
-             write_insn<big_endian>(p +  8, mtctr_11);
-             write_insn<big_endian>(p + 12, bctr);
-           }
-       }
-
       // Write out pltresolve branch table.
-      p = oview + this->pltresolve_;
+      p = oview;
       unsigned int the_end = oview_size - this->pltresolve_size;
       unsigned char* end_p = oview + the_end;
       while (p < end_p - 8 * 4)
@@ -2352,7 +3857,7 @@ Output_data_glink<size, big_endian>::do_write(Output_file* of)
       // Write out pltresolve call stub.
       if (parameters->options().output_is_position_independent())
        {
-         Address res0_off = this->pltresolve_;
+         Address res0_off = 0;
          Address after_bcl_off = the_end + 12;
          Address bcl_res0 = after_bcl_off - res0_off;
 
@@ -2386,7 +3891,7 @@ Output_data_glink<size, big_endian>::do_write(Output_file* of)
        }
       else
        {
-         Address res0 = this->pltresolve_ + this->address();
+         Address res0 = this->address();
 
          write_insn<big_endian>(p + 0, lis_12 + ha(g_o_t + 4));
          write_insn<big_endian>(p + 4, addis_11_11 + ha(-res0));
@@ -2417,6 +3922,335 @@ Output_data_glink<size, big_endian>::do_write(Output_file* of)
   of->write_output_view(off, oview_size, oview);
 }
 
+
+// A class to handle linker generated save/restore functions.
+
+template<int size, bool big_endian>
+class Output_data_save_res : public Output_section_data_build
+{
+ public:
+  Output_data_save_res(Symbol_table* symtab);
+
+ protected:
+  // Write to a map file.
+  void
+  do_print_to_mapfile(Mapfile* mapfile) const
+  { mapfile->print_output_data(this, _("** save/restore")); }
+
+  void
+  do_write(Output_file*);
+
+ private:
+  // The maximum size of save/restore contents.
+  static const unsigned int savres_max = 218*4;
+
+  void
+  savres_define(Symbol_table* symtab,
+               const char *name,
+               unsigned int lo, unsigned int hi,
+               unsigned char* write_ent(unsigned char*, int),
+               unsigned char* write_tail(unsigned char*, int));
+
+  unsigned char *contents_;
+};
+
+template<bool big_endian>
+static unsigned char*
+savegpr0(unsigned char* p, int r)
+{
+  uint32_t insn = std_0_1 + (r << 21) + (1 << 16) - (32 - r) * 8;
+  write_insn<big_endian>(p, insn);
+  return p + 4;
+}
+
+template<bool big_endian>
+static unsigned char*
+savegpr0_tail(unsigned char* p, int r)
+{
+  p = savegpr0<big_endian>(p, r);
+  uint32_t insn = std_0_1 + 16;
+  write_insn<big_endian>(p, insn);
+  p = p + 4;
+  write_insn<big_endian>(p, blr);
+  return p + 4;
+}
+
+template<bool big_endian>
+static unsigned char*
+restgpr0(unsigned char* p, int r)
+{
+  uint32_t insn = ld_0_1 + (r << 21) + (1 << 16) - (32 - r) * 8;
+  write_insn<big_endian>(p, insn);
+  return p + 4;
+}
+
+template<bool big_endian>
+static unsigned char*
+restgpr0_tail(unsigned char* p, int r)
+{
+  uint32_t insn = ld_0_1 + 16;
+  write_insn<big_endian>(p, insn);
+  p = p + 4;
+  p = restgpr0<big_endian>(p, r);
+  write_insn<big_endian>(p, mtlr_0);
+  p = p + 4;
+  if (r == 29)
+    {
+      p = restgpr0<big_endian>(p, 30);
+      p = restgpr0<big_endian>(p, 31);
+    }
+  write_insn<big_endian>(p, blr);
+  return p + 4;
+}
+
+template<bool big_endian>
+static unsigned char*
+savegpr1(unsigned char* p, int r)
+{
+  uint32_t insn = std_0_12 + (r << 21) + (1 << 16) - (32 - r) * 8;
+  write_insn<big_endian>(p, insn);
+  return p + 4;
+}
+
+template<bool big_endian>
+static unsigned char*
+savegpr1_tail(unsigned char* p, int r)
+{
+  p = savegpr1<big_endian>(p, r);
+  write_insn<big_endian>(p, blr);
+  return p + 4;
+}
+
+template<bool big_endian>
+static unsigned char*
+restgpr1(unsigned char* p, int r)
+{
+  uint32_t insn = ld_0_12 + (r << 21) + (1 << 16) - (32 - r) * 8;
+  write_insn<big_endian>(p, insn);
+  return p + 4;
+}
+
+template<bool big_endian>
+static unsigned char*
+restgpr1_tail(unsigned char* p, int r)
+{
+  p = restgpr1<big_endian>(p, r);
+  write_insn<big_endian>(p, blr);
+  return p + 4;
+}
+
+template<bool big_endian>
+static unsigned char*
+savefpr(unsigned char* p, int r)
+{
+  uint32_t insn = stfd_0_1 + (r << 21) + (1 << 16) - (32 - r) * 8;
+  write_insn<big_endian>(p, insn);
+  return p + 4;
+}
+
+template<bool big_endian>
+static unsigned char*
+savefpr0_tail(unsigned char* p, int r)
+{
+  p = savefpr<big_endian>(p, r);
+  write_insn<big_endian>(p, std_0_1 + 16);
+  p = p + 4;
+  write_insn<big_endian>(p, blr);
+  return p + 4;
+}
+
+template<bool big_endian>
+static unsigned char*
+restfpr(unsigned char* p, int r)
+{
+  uint32_t insn = lfd_0_1 + (r << 21) + (1 << 16) - (32 - r) * 8;
+  write_insn<big_endian>(p, insn);
+  return p + 4;
+}
+
+template<bool big_endian>
+static unsigned char*
+restfpr0_tail(unsigned char* p, int r)
+{
+  write_insn<big_endian>(p, ld_0_1 + 16);
+  p = p + 4;
+  p = restfpr<big_endian>(p, r);
+  write_insn<big_endian>(p, mtlr_0);
+  p = p + 4;
+  if (r == 29)
+    {
+      p = restfpr<big_endian>(p, 30);
+      p = restfpr<big_endian>(p, 31);
+    }
+  write_insn<big_endian>(p, blr);
+  return p + 4;
+}
+
+template<bool big_endian>
+static unsigned char*
+savefpr1_tail(unsigned char* p, int r)
+{
+  p = savefpr<big_endian>(p, r);
+  write_insn<big_endian>(p, blr);
+  return p + 4;
+}
+
+template<bool big_endian>
+static unsigned char*
+restfpr1_tail(unsigned char* p, int r)
+{
+  p = restfpr<big_endian>(p, r);
+  write_insn<big_endian>(p, blr);
+  return p + 4;
+}
+
+template<bool big_endian>
+static unsigned char*
+savevr(unsigned char* p, int r)
+{
+  uint32_t insn = li_12_0 + (1 << 16) - (32 - r) * 16;
+  write_insn<big_endian>(p, insn);
+  p = p + 4;
+  insn = stvx_0_12_0 + (r << 21);
+  write_insn<big_endian>(p, insn);
+  return p + 4;
+}
+
+template<bool big_endian>
+static unsigned char*
+savevr_tail(unsigned char* p, int r)
+{
+  p = savevr<big_endian>(p, r);
+  write_insn<big_endian>(p, blr);
+  return p + 4;
+}
+
+template<bool big_endian>
+static unsigned char*
+restvr(unsigned char* p, int r)
+{
+  uint32_t insn = li_12_0 + (1 << 16) - (32 - r) * 16;
+  write_insn<big_endian>(p, insn);
+  p = p + 4;
+  insn = lvx_0_12_0 + (r << 21);
+  write_insn<big_endian>(p, insn);
+  return p + 4;
+}
+
+template<bool big_endian>
+static unsigned char*
+restvr_tail(unsigned char* p, int r)
+{
+  p = restvr<big_endian>(p, r);
+  write_insn<big_endian>(p, blr);
+  return p + 4;
+}
+
+
+template<int size, bool big_endian>
+Output_data_save_res<size, big_endian>::Output_data_save_res(
+    Symbol_table* symtab)
+  : Output_section_data_build(4),
+    contents_(NULL)
+{
+  this->savres_define(symtab,
+                     "_savegpr0_", 14, 31,
+                     savegpr0<big_endian>, savegpr0_tail<big_endian>);
+  this->savres_define(symtab,
+                     "_restgpr0_", 14, 29,
+                     restgpr0<big_endian>, restgpr0_tail<big_endian>);
+  this->savres_define(symtab,
+                     "_restgpr0_", 30, 31,
+                     restgpr0<big_endian>, restgpr0_tail<big_endian>);
+  this->savres_define(symtab,
+                     "_savegpr1_", 14, 31,
+                     savegpr1<big_endian>, savegpr1_tail<big_endian>);
+  this->savres_define(symtab,
+                     "_restgpr1_", 14, 31,
+                     restgpr1<big_endian>, restgpr1_tail<big_endian>);
+  this->savres_define(symtab,
+                     "_savefpr_", 14, 31,
+                     savefpr<big_endian>, savefpr0_tail<big_endian>);
+  this->savres_define(symtab,
+                     "_restfpr_", 14, 29,
+                     restfpr<big_endian>, restfpr0_tail<big_endian>);
+  this->savres_define(symtab,
+                     "_restfpr_", 30, 31,
+                     restfpr<big_endian>, restfpr0_tail<big_endian>);
+  this->savres_define(symtab,
+                     "._savef", 14, 31,
+                     savefpr<big_endian>, savefpr1_tail<big_endian>);
+  this->savres_define(symtab,
+                     "._restf", 14, 31,
+                     restfpr<big_endian>, restfpr1_tail<big_endian>);
+  this->savres_define(symtab,
+                     "_savevr_", 20, 31,
+                     savevr<big_endian>, savevr_tail<big_endian>);
+  this->savres_define(symtab,
+                     "_restvr_", 20, 31,
+                     restvr<big_endian>, restvr_tail<big_endian>);
+}
+
+template<int size, bool big_endian>
+void
+Output_data_save_res<size, big_endian>::savres_define(
+    Symbol_table* symtab,
+    const char *name,
+    unsigned int lo, unsigned int hi,
+    unsigned char* write_ent(unsigned char*, int),
+    unsigned char* write_tail(unsigned char*, int))
+{
+  size_t len = strlen(name);
+  bool writing = false;
+  char sym[16];
+
+  memcpy(sym, name, len);
+  sym[len + 2] = 0;
+
+  for (unsigned int i = lo; i <= hi; i++)
+    {
+      sym[len + 0] = i / 10 + '0';
+      sym[len + 1] = i % 10 + '0';
+      Symbol* gsym = symtab->lookup(sym);
+      bool refd = gsym != NULL && gsym->is_undefined();
+      writing = writing || refd;
+      if (writing)
+       {
+         if (this->contents_ == NULL)
+           this->contents_ = new unsigned char[this->savres_max];
+
+         section_size_type value = this->current_data_size();
+         unsigned char* p = this->contents_ + value;
+         if (i != hi)
+           p = write_ent(p, i);
+         else
+           p = write_tail(p, i);
+         section_size_type cur_size = p - this->contents_;
+         this->set_current_data_size(cur_size);
+         if (refd)
+           symtab->define_in_output_data(sym, NULL, Symbol_table::PREDEFINED,
+                                         this, value, cur_size - value,
+                                         elfcpp::STT_FUNC, elfcpp::STB_GLOBAL,
+                                         elfcpp::STV_HIDDEN, 0, false, false);
+       }
+    }
+}
+
+// Write out save/restore.
+
+template<int size, bool big_endian>
+void
+Output_data_save_res<size, big_endian>::do_write(Output_file* of)
+{
+  const section_size_type off = this->offset();
+  const section_size_type oview_size =
+    convert_to_section_size_type(this->data_size());
+  unsigned char* const oview = of->get_output_view(off, oview_size);
+  memcpy(oview, this->contents_, oview_size);
+  of->write_output_view(off, oview_size, oview);
+}
+
+
 // Create the glink section.
 
 template<int size, bool big_endian>
@@ -2426,6 +4260,7 @@ Target_powerpc<size, big_endian>::make_glink_section(Layout* layout)
   if (this->glink_ == NULL)
     {
       this->glink_ = new Output_data_glink<size, big_endian>(this);
+      this->glink_->add_eh_frame(layout);
       layout->add_output_section_data(".text", elfcpp::SHT_PROGBITS,
                                      elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR,
                                      this->glink_, ORDER_TEXT, false);
@@ -2436,26 +4271,23 @@ Target_powerpc<size, big_endian>::make_glink_section(Layout* layout)
 
 template<int size, bool big_endian>
 void
-Target_powerpc<size, big_endian>::make_plt_entry(
-    Layout* layout,
-    Symbol* gsym,
-    const elfcpp::Rela<size, big_endian>& reloc,
-    const Sized_relobj_file<size, big_endian>* object)
+Target_powerpc<size, big_endian>::make_plt_entry(Symbol_table* symtab,
+                                                Layout* layout,
+                                                Symbol* gsym)
 {
   if (gsym->type() == elfcpp::STT_GNU_IFUNC
       && gsym->can_use_relative_reloc(false))
     {
       if (this->iplt_ == NULL)
-       this->make_iplt_section(layout);
+       this->make_iplt_section(symtab, layout);
       this->iplt_->add_ifunc_entry(gsym);
     }
   else
     {
       if (this->plt_ == NULL)
-       this->make_plt_section(layout);
+       this->make_plt_section(symtab, layout);
       this->plt_->add_entry(gsym);
     }
-  this->glink_->add_entry(object, gsym, reloc);
 }
 
 // Make a PLT entry for a local STT_GNU_IFUNC symbol.
@@ -2463,15 +4295,14 @@ Target_powerpc<size, big_endian>::make_plt_entry(
 template<int size, bool big_endian>
 void
 Target_powerpc<size, big_endian>::make_local_ifunc_plt_entry(
+    Symbol_table* symtab,
     Layout* layout,
-    const elfcpp::Rela<size, big_endian>& reloc,
-    Sized_relobj_file<size, big_endian>* relobj)
+    Sized_relobj_file<size, big_endian>* relobj,
+    unsigned int r_sym)
 {
   if (this->iplt_ == NULL)
-    this->make_iplt_section(layout);
-  unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
+    this->make_iplt_section(symtab, layout);
   this->iplt_->add_local_ifunc_entry(relobj, r_sym);
-  this->glink_->add_entry(relobj, r_sym, reloc);
 }
 
 // Return the number of entries in the PLT.
@@ -2806,6 +4637,25 @@ Target_powerpc<size, big_endian>::Scan::local(
     const elfcpp::Sym<size, big_endian>& lsym,
     bool is_discarded)
 {
+  this->maybe_skip_tls_get_addr_call(r_type, NULL);
+
+  if ((size == 64 && r_type == elfcpp::R_PPC64_TLSGD)
+      || (size == 32 && r_type == elfcpp::R_PPC_TLSGD))
+    {
+      this->expect_tls_get_addr_call();
+      const tls::Tls_optimization tls_type = target->optimize_tls_gd(true);
+      if (tls_type != tls::TLSOPT_NONE)
+       this->skip_next_tls_get_addr_call();
+    }
+  else if ((size == 64 && r_type == elfcpp::R_PPC64_TLSLD)
+          || (size == 32 && r_type == elfcpp::R_PPC_TLSLD))
+    {
+      this->expect_tls_get_addr_call();
+      const tls::Tls_optimization tls_type = target->optimize_tls_ld();
+      if (tls_type != tls::TLSOPT_NONE)
+       this->skip_next_tls_get_addr_call();
+    }
+
   Powerpc_relobj<size, big_endian>* ppc_object
     = static_cast<Powerpc_relobj<size, big_endian>*>(object);
 
@@ -2821,7 +4671,12 @@ 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))
-    target->make_local_ifunc_plt_entry(layout, reloc, object);
+    {
+      unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
+      target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
+                         r_type, r_sym, reloc.get_r_addend());
+      target->make_local_ifunc_plt_entry(symtab, layout, object, r_sym);
+    }
 
   switch (r_type)
     {
@@ -2909,18 +4764,28 @@ Target_powerpc<size, big_endian>::Scan::local(
        }
       break;
 
-    case elfcpp::R_PPC64_REL64:
-    case elfcpp::R_POWERPC_REL32:
     case elfcpp::R_POWERPC_REL24:
     case elfcpp::R_PPC_PLTREL24:
     case elfcpp::R_PPC_LOCAL24PC:
+      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());
+      break;
+
+    case elfcpp::R_POWERPC_REL14:
+    case elfcpp::R_POWERPC_REL14_BRTAKEN:
+    case elfcpp::R_POWERPC_REL14_BRNTAKEN:
+      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());
+      break;
+
+    case elfcpp::R_PPC64_REL64:
+    case elfcpp::R_POWERPC_REL32:
     case elfcpp::R_POWERPC_REL16:
     case elfcpp::R_POWERPC_REL16_LO:
     case elfcpp::R_POWERPC_REL16_HI:
     case elfcpp::R_POWERPC_REL16_HA:
-    case elfcpp::R_POWERPC_REL14:
-    case elfcpp::R_POWERPC_REL14_BRTAKEN:
-    case elfcpp::R_POWERPC_REL14_BRNTAKEN:
     case elfcpp::R_POWERPC_SECTOFF:
     case elfcpp::R_POWERPC_TPREL16:
     case elfcpp::R_POWERPC_DTPREL16:
@@ -3068,10 +4933,19 @@ Target_powerpc<size, big_endian>::Scan::local(
        const tls::Tls_optimization tls_type = target->optimize_tls_ie(true);
        if (tls_type == tls::TLSOPT_NONE)
          {
-           Output_data_got_powerpc<size, big_endian>* got
-             = target->got_section(symtab, layout);
            unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
-           got->add_local_tls(object, r_sym, GOT_TYPE_TPREL);
+           if (!object->local_has_got_offset(r_sym, GOT_TYPE_TPREL))
+             {
+               Output_data_got_powerpc<size, big_endian>* got
+                 = target->got_section(symtab, layout);
+               unsigned int off = got->add_constant(0);
+               object->set_local_got_offset(r_sym, GOT_TYPE_TPREL, off);
+
+               Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+               rela_dyn->add_symbolless_local_addend(object, r_sym,
+                                                     elfcpp::R_POWERPC_TPREL,
+                                                     got, off, 0);
+             }
          }
        else if (tls_type == tls::TLSOPT_TO_LE)
          {
@@ -3086,6 +4960,21 @@ Target_powerpc<size, big_endian>::Scan::local(
       unsupported_reloc_local(object, r_type);
       break;
     }
+
+  switch (r_type)
+    {
+    case elfcpp::R_POWERPC_GOT_TLSLD16:
+    case elfcpp::R_POWERPC_GOT_TLSGD16:
+    case elfcpp::R_POWERPC_GOT_TPREL16:
+    case elfcpp::R_POWERPC_GOT_DTPREL16:
+    case elfcpp::R_POWERPC_GOT16:
+    case elfcpp::R_PPC64_GOT16_DS:
+    case elfcpp::R_PPC64_TOC16:
+    case elfcpp::R_PPC64_TOC16_DS:
+      ppc_object->set_has_small_toc_reloc();
+    default:
+      break;
+    }
 }
 
 // Report an unsupported relocation against a global symbol.
@@ -3116,13 +5005,39 @@ Target_powerpc<size, big_endian>::Scan::global(
     unsigned int r_type,
     Symbol* gsym)
 {
+  if (this->maybe_skip_tls_get_addr_call(r_type, gsym) == Track_tls::SKIP)
+    return;
+
+  if ((size == 64 && r_type == elfcpp::R_PPC64_TLSGD)
+      || (size == 32 && r_type == elfcpp::R_PPC_TLSGD))
+    {
+      this->expect_tls_get_addr_call();
+      const bool final = gsym->final_value_is_known();
+      const tls::Tls_optimization tls_type = target->optimize_tls_gd(final);
+      if (tls_type != tls::TLSOPT_NONE)
+       this->skip_next_tls_get_addr_call();
+    }
+  else if ((size == 64 && r_type == elfcpp::R_PPC64_TLSLD)
+          || (size == 32 && r_type == elfcpp::R_PPC_TLSLD))
+    {
+      this->expect_tls_get_addr_call();
+      const tls::Tls_optimization tls_type = target->optimize_tls_ld();
+      if (tls_type != tls::TLSOPT_NONE)
+       this->skip_next_tls_get_addr_call();
+    }
+
   Powerpc_relobj<size, big_endian>* ppc_object
     = static_cast<Powerpc_relobj<size, big_endian>*>(object);
 
   // A STT_GNU_IFUNC symbol may require a PLT entry.
   if (gsym->type() == elfcpp::STT_GNU_IFUNC
       && this->reloc_needs_plt_for_ifunc(object, r_type))
-    target->make_plt_entry(layout, gsym, reloc, object);
+    {
+      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);
+    }
 
   switch (r_type)
     {
@@ -3192,7 +5107,11 @@ Target_powerpc<size, big_endian>::Scan::global(
        // Make a PLT entry if necessary.
        if (gsym->needs_plt_entry())
          {
-           target->make_plt_entry(layout, gsym, reloc, 0);
+           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);
            // 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
@@ -3211,11 +5130,15 @@ Target_powerpc<size, big_endian>::Scan::global(
                target->copy_reloc(symtab, layout, object,
                                   data_shndx, output_section, gsym, reloc);
              }
-           else if (((size == 32 && r_type == elfcpp::R_POWERPC_ADDR32)
-                     || (size == 64 && r_type == elfcpp::R_PPC64_ADDR64))
-                    && (gsym->can_use_relative_reloc(false)
-                        || (size == 64
-                            && data_shndx == ppc_object->opd_shndx())))
+           else if ((size == 32
+                     && r_type == elfcpp::R_POWERPC_ADDR32
+                     && gsym->can_use_relative_reloc(false)
+                     && !(gsym->visibility() == elfcpp::STV_PROTECTED
+                          && parameters->options().shared()))
+                    || (size == 64
+                        && r_type == elfcpp::R_PPC64_ADDR64
+                        && (gsym->can_use_relative_reloc(false)
+                            || data_shndx == ppc_object->opd_shndx())))
              {
                Reloc_section* rela_dyn = target->rela_dyn_section(layout);
                unsigned int dynrel = elfcpp::R_POWERPC_RELATIVE;
@@ -3243,12 +5166,15 @@ Target_powerpc<size, big_endian>::Scan::global(
 
     case elfcpp::R_PPC_PLTREL24:
     case elfcpp::R_POWERPC_REL24:
+      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());
       if (gsym->needs_plt_entry()
          || (!gsym->final_value_is_known()
              && (gsym->is_undefined()
                  || gsym->is_from_dynobj()
                  || gsym->is_preemptible())))
-       target->make_plt_entry(layout, gsym, reloc, object);
+       target->make_plt_entry(symtab, layout, gsym);
       // Fall thru
 
     case elfcpp::R_PPC64_REL64:
@@ -3273,13 +5199,18 @@ Target_powerpc<size, big_endian>::Scan::global(
        }
       break;
 
+    case elfcpp::R_POWERPC_REL14:
+    case elfcpp::R_POWERPC_REL14_BRTAKEN:
+    case elfcpp::R_POWERPC_REL14_BRNTAKEN:
+      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());
+      break;
+
     case elfcpp::R_POWERPC_REL16:
     case elfcpp::R_POWERPC_REL16_LO:
     case elfcpp::R_POWERPC_REL16_HI:
     case elfcpp::R_POWERPC_REL16_HA:
-    case elfcpp::R_POWERPC_REL14:
-    case elfcpp::R_POWERPC_REL14_BRTAKEN:
-    case elfcpp::R_POWERPC_REL14_BRNTAKEN:
     case elfcpp::R_POWERPC_SECTOFF:
     case elfcpp::R_POWERPC_TPREL16:
     case elfcpp::R_POWERPC_DTPREL16:
@@ -3386,11 +5317,26 @@ Target_powerpc<size, big_endian>::Scan::global(
          }
        else if (tls_type == tls::TLSOPT_TO_IE)
          {
-           Output_data_got_powerpc<size, big_endian>* got
-             = target->got_section(symtab, layout);
-           got->add_global_with_rel(gsym, GOT_TYPE_TPREL,
-                                    target->rela_dyn_section(layout),
-                                    elfcpp::R_POWERPC_TPREL);
+           if (!gsym->has_got_offset(GOT_TYPE_TPREL))
+             {
+               Output_data_got_powerpc<size, big_endian>* got
+                 = target->got_section(symtab, layout);
+               Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+               if (gsym->is_undefined()
+                   || gsym->is_from_dynobj())
+                 {
+                   got->add_global_with_rel(gsym, GOT_TYPE_TPREL, rela_dyn,
+                                            elfcpp::R_POWERPC_TPREL);
+                 }
+               else
+                 {
+                   unsigned int off = got->add_constant(0);
+                   gsym->set_got_offset(GOT_TYPE_TPREL, off);
+                   unsigned int dynrel = elfcpp::R_POWERPC_TPREL;
+                   rela_dyn->add_symbolless_global_addend(gsym, dynrel,
+                                                          got, off, 0);
+                 }
+             }
          }
        else if (tls_type == tls::TLSOPT_TO_LE)
          {
@@ -3452,17 +5398,26 @@ Target_powerpc<size, big_endian>::Scan::global(
        const tls::Tls_optimization tls_type = target->optimize_tls_ie(final);
        if (tls_type == tls::TLSOPT_NONE)
          {
-           Output_data_got_powerpc<size, big_endian>* got
-             = target->got_section(symtab, layout);
-           if (!gsym->final_value_is_known()
-               && (gsym->is_from_dynobj()
-                   || gsym->is_undefined()
-                   || gsym->is_preemptible()))
-             got->add_global_with_rel(gsym, GOT_TYPE_TPREL,
-                                      target->rela_dyn_section(layout),
-                                      elfcpp::R_POWERPC_TPREL);
-           else
-             got->add_global_tls(gsym, GOT_TYPE_TPREL);
+           if (!gsym->has_got_offset(GOT_TYPE_TPREL))
+             {
+               Output_data_got_powerpc<size, big_endian>* got
+                 = target->got_section(symtab, layout);
+               Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+               if (gsym->is_undefined()
+                   || gsym->is_from_dynobj())
+                 {
+                   got->add_global_with_rel(gsym, GOT_TYPE_TPREL, rela_dyn,
+                                            elfcpp::R_POWERPC_TPREL);
+                 }
+               else
+                 {
+                   unsigned int off = got->add_constant(0);
+                   gsym->set_got_offset(GOT_TYPE_TPREL, off);
+                   unsigned int dynrel = elfcpp::R_POWERPC_TPREL;
+                   rela_dyn->add_symbolless_global_addend(gsym, dynrel,
+                                                          got, off, 0);
+                 }
+             }
          }
        else if (tls_type == tls::TLSOPT_TO_LE)
          {
@@ -3477,6 +5432,21 @@ Target_powerpc<size, big_endian>::Scan::global(
       unsupported_reloc_global(object, r_type, gsym);
       break;
     }
+
+  switch (r_type)
+    {
+    case elfcpp::R_POWERPC_GOT_TLSLD16:
+    case elfcpp::R_POWERPC_GOT_TLSGD16:
+    case elfcpp::R_POWERPC_GOT_TPREL16:
+    case elfcpp::R_POWERPC_GOT_DTPREL16:
+    case elfcpp::R_POWERPC_GOT16:
+    case elfcpp::R_PPC64_GOT16_DS:
+    case elfcpp::R_PPC64_TOC16:
+    case elfcpp::R_PPC64_TOC16_DS:
+      ppc_object->set_has_small_toc_reloc();
+    default:
+      break;
+    }
 }
 
 // Process relocations for gc.
@@ -3559,7 +5529,9 @@ Target_powerpc<size, big_endian>::do_gc_add_reference(
 {
   Powerpc_relobj<size, big_endian>* ppc_object
     = static_cast<Powerpc_relobj<size, big_endian>*>(dst_obj);
-  if (size == 64 && dst_shndx == ppc_object->opd_shndx())
+  if (size == 64
+      && !ppc_object->is_dynamic()
+      && dst_shndx == ppc_object->opd_shndx())
     {
       if (ppc_object->opd_valid())
        {
@@ -3633,28 +5605,6 @@ Target_powerpc<size, big_endian>::scan_relocs(
       return;
     }
 
-  if (size == 32)
-    {
-      static Output_data_space* sdata;
-
-      // Define _SDA_BASE_ at the start of the .sdata section.
-      if (sdata == NULL)
-       {
-         // layout->find_output_section(".sdata") == NULL
-         sdata = new Output_data_space(4, "** sdata");
-         Output_section* os
-           = layout->add_output_section_data(".sdata", 0,
-                                             elfcpp::SHF_ALLOC
-                                             | elfcpp::SHF_WRITE,
-                                             sdata, ORDER_SMALL_DATA, false);
-         symtab->define_in_output_data("_SDA_BASE_", NULL,
-                                       Symbol_table::PREDEFINED,
-                                       os, 32768, 0, elfcpp::STT_OBJECT,
-                                       elfcpp::STB_LOCAL, elfcpp::STV_HIDDEN,
-                                       0, false, false);
-       }
-    }
-
   gold::scan_relocs<size, big_endian, Powerpc, elfcpp::SHT_RELA, Scan>(
     symtab,
     layout,
@@ -3701,6 +5651,47 @@ class Global_symbol_visitor_opd
   }
 };
 
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::define_save_restore_funcs(
+    Layout* layout,
+    Symbol_table* symtab)
+{
+  if (size == 64)
+    {
+      Output_data_save_res<64, big_endian>* savres
+       = new Output_data_save_res<64, big_endian>(symtab);
+      layout->add_output_section_data(".text", elfcpp::SHT_PROGBITS,
+                                     elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR,
+                                     savres, ORDER_TEXT, false);
+    }
+}
+
+// Sort linker created .got section first (for the header), then input
+// sections belonging to files using small model code.
+
+template<bool big_endian>
+class Sort_toc_sections
+{
+ public:
+  bool
+  operator()(const Output_section::Input_section& is1,
+            const Output_section::Input_section& is2) const
+  {
+    if (!is1.is_input_section() && is2.is_input_section())
+      return true;
+    bool small1
+      = (is1.is_input_section()
+        && (static_cast<const Powerpc_relobj<64, big_endian>*>(is1.relobj())
+            ->has_small_toc_reloc()));
+    bool small2
+      = (is2.is_input_section()
+        && (static_cast<const Powerpc_relobj<64, big_endian>*>(is2.relobj())
+            ->has_small_toc_reloc()));
+    return small1 && !small2;
+  }
+};
+
 // Finalize the sections.
 
 template<int size, bool big_endian>
@@ -3744,6 +5735,26 @@ Target_powerpc<size, big_endian>::do_finalize_sections(
     {
       typedef Global_symbol_visitor_opd<big_endian> Symbol_visitor;
       symtab->for_all_symbols<64, Symbol_visitor>(Symbol_visitor());
+
+      if (!parameters->options().relocatable())
+       {
+         this->define_save_restore_funcs(layout, symtab);
+
+         // Annoyingly, we need to make these sections now whether or
+         // not we need them.  If we delay until do_relax then we
+         // need to mess with the relaxation machinery checkpointing.
+         this->got_section(symtab, layout);
+         this->make_brlt_section(layout);
+
+         if (parameters->options().toc_sort())
+           {
+             Output_section* os = this->got_->output_section();
+             if (os != NULL && os->input_sections().size() > 1)
+               std::stable_sort(os->input_sections().begin(),
+                                os->input_sections().end(),
+                                Sort_toc_sections<big_endian>());
+           }
+       }
     }
 
   // Fill in some more dynamic tags.
@@ -3772,8 +5783,7 @@ Target_powerpc<size, big_endian>::do_finalize_sections(
              this->glink_->finalize_data_size();
              odyn->add_section_plus_offset(elfcpp::DT_PPC64_GLINK,
                                            this->glink_,
-                                           (this->glink_->pltresolve()
-                                            + this->glink_->pltresolve_size
+                                           (this->glink_->pltresolve_size
                                             - 32));
            }
        }
@@ -3785,10 +5795,37 @@ Target_powerpc<size, big_endian>::do_finalize_sections(
     this->copy_relocs_.emit(this->rela_dyn_section(layout));
 }
 
+// Return TRUE iff INSN is one we expect on a _LO variety toc/got
+// reloc.
+
+static bool
+ok_lo_toc_insn(uint32_t insn)
+{
+  return ((insn & (0x3f << 26)) == 14u << 26 /* addi */
+         || (insn & (0x3f << 26)) == 32u << 26 /* lwz */
+         || (insn & (0x3f << 26)) == 34u << 26 /* lbz */
+         || (insn & (0x3f << 26)) == 36u << 26 /* stw */
+         || (insn & (0x3f << 26)) == 38u << 26 /* stb */
+         || (insn & (0x3f << 26)) == 40u << 26 /* lhz */
+         || (insn & (0x3f << 26)) == 42u << 26 /* lha */
+         || (insn & (0x3f << 26)) == 44u << 26 /* sth */
+         || (insn & (0x3f << 26)) == 46u << 26 /* lmw */
+         || (insn & (0x3f << 26)) == 47u << 26 /* stmw */
+         || (insn & (0x3f << 26)) == 48u << 26 /* lfs */
+         || (insn & (0x3f << 26)) == 50u << 26 /* lfd */
+         || (insn & (0x3f << 26)) == 52u << 26 /* stfs */
+         || (insn & (0x3f << 26)) == 54u << 26 /* stfd */
+         || ((insn & (0x3f << 26)) == 58u << 26 /* lwa,ld,lmd */
+             && (insn & 3) != 1)
+         || ((insn & (0x3f << 26)) == 62u << 26 /* std, stmd */
+             && ((insn & 3) == 0 || (insn & 3) == 3))
+         || (insn & (0x3f << 26)) == 12u << 26 /* addic */);
+}
+
 // Return the value to use for a branch relocation.
 
 template<int size, bool big_endian>
-typename elfcpp::Elf_types<size>::Elf_Addr
+typename Target_powerpc<size, big_endian>::Address
 Target_powerpc<size, big_endian>::symval_for_branch(
     Address value,
     const Sized_symbol<size>* gsym,
@@ -3802,6 +5839,9 @@ Target_powerpc<size, big_endian>::symval_for_branch(
   // If the symbol is defined in an opd section, ie. is a function
   // descriptor, use the function descriptor code entry address
   Powerpc_relobj<size, big_endian>* symobj = object;
+  if (gsym != NULL
+      && gsym->source() != Symbol::FROM_OBJECT)
+    return value;
   if (gsym != NULL)
     symobj = static_cast<Powerpc_relobj<size, big_endian>*>(gsym->object());
   unsigned int shndx = symobj->opd_shndx();
@@ -3839,24 +5879,20 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
     Address address,
     section_size_type view_size)
 {
-
-  bool is_tls_call = ((r_type == elfcpp::R_POWERPC_REL24
-                      || r_type == elfcpp::R_PPC_PLTREL24)
-                     && gsym != NULL
-                     && strcmp(gsym->name(), "__tls_get_addr") == 0);
-  enum skip_tls last_tls = this->call_tls_get_addr_;
-  this->call_tls_get_addr_ = CALL_NOT_EXPECTED;
-  if (is_tls_call)
+  switch (this->maybe_skip_tls_get_addr_call(r_type, gsym))
     {
-      if (last_tls == CALL_NOT_EXPECTED)
-       gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
-                              _("__tls_get_addr call lacks marker reloc"));
-      else if (last_tls == CALL_SKIP)
-       return false;
+    case Track_tls::NOT_EXPECTED:
+      gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
+                            _("__tls_get_addr call lacks marker reloc"));
+      break;
+    case Track_tls::EXPECTED:
+      // We have already complained.
+      break;
+    case Track_tls::SKIP:
+      return true;
+    case Track_tls::NORMAL:
+      break;
     }
-  else if (last_tls != CALL_NOT_EXPECTED)
-    gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
-                          _("missing expected __tls_get_addr call"));
 
   typedef Powerpc_relocate_functions<size, big_endian> Reloc;
   typedef typename elfcpp::Swap<32, big_endian>::Valtype Insn;
@@ -3869,14 +5905,24 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       ? use_plt_offset<size>(gsym, Scan::get_reference_flags(r_type))
       : object->local_has_plt_offset(r_sym))
     {
-      const Output_data_glink<size, big_endian>* glink
-       = target->glink_section();
-      unsigned int glink_index;
+      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)
-       glink_index = glink->find_entry(object, gsym, rela);
+       off = stub_table->find_plt_call_entry(object, gsym, r_type,
+                                             rela.get_r_addend());
       else
-       glink_index = glink->find_entry(object, r_sym, rela);
-      value = glink->address() + glink_index * glink->glink_entry_size();
+       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;
     }
 
@@ -4143,7 +6189,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
     {
       // Second instruction of a global dynamic sequence,
       // the __tls_get_addr call
-      this->call_tls_get_addr_ = CALL_EXPECTED;
+      this->expect_tls_get_addr_call(relinfo, relnum, rela.get_r_offset());
       const bool final = gsym == NULL || gsym->final_value_is_known();
       const tls::Tls_optimization tls_type = target->optimize_tls_gd(final);
       if (tls_type != tls::TLSOPT_NONE)
@@ -4166,7 +6212,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
              view += 2 * big_endian;
              value = psymval->value(object, rela.get_r_addend());
            }
-         this->call_tls_get_addr_ = CALL_SKIP;
+         this->skip_next_tls_get_addr_call();
        }
     }
   else if ((size == 64 && r_type == elfcpp::R_PPC64_TLSLD)
@@ -4174,14 +6220,14 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
     {
       // Second instruction of a local dynamic sequence,
       // the __tls_get_addr call
-      this->call_tls_get_addr_ = CALL_EXPECTED;
+      this->expect_tls_get_addr_call(relinfo, relnum, rela.get_r_offset());
       const tls::Tls_optimization tls_type = target->optimize_tls_ld();
       if (tls_type == tls::TLSOPT_TO_LE)
        {
          Insn* iview = reinterpret_cast<Insn*>(view);
          Insn insn = addi_3_3;
          elfcpp::Swap<32, big_endian>::writeval(iview, insn);
-         this->call_tls_get_addr_ = CALL_SKIP;
+         this->skip_next_tls_get_addr_call();
          r_type = elfcpp::R_POWERPC_TPREL16_LO;
          view += 2 * big_endian;
          value = dtp_offset;
@@ -4214,6 +6260,25 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       value = psymval->value(object, addend);
       if (size == 64 && is_branch_reloc(r_type))
        value = target->symval_for_branch(value, gsym, object, &dest_shndx);
+      unsigned int max_branch_offset = 0;
+      if (r_type == elfcpp::R_POWERPC_REL24
+         || r_type == elfcpp::R_PPC_PLTREL24
+         || r_type == elfcpp::R_PPC_LOCAL24PC)
+       max_branch_offset = 1 << 25;
+      else if (r_type == elfcpp::R_POWERPC_REL14
+              || r_type == elfcpp::R_POWERPC_REL14_BRTAKEN
+              || r_type == elfcpp::R_POWERPC_REL14_BRNTAKEN)
+       max_branch_offset = 1 << 15;
+      if (max_branch_offset != 0
+         && value - address + max_branch_offset >= 2 * max_branch_offset)
+       {
+         Stub_table<size, big_endian>* stub_table
+           = object->stub_table(relinfo->data_shndx);
+         gold_assert(stub_table != NULL);
+         Address off = stub_table->find_long_branch_entry(object, value);
+         if (off != invalid_address)
+           value = stub_table->stub_address() + stub_table->plt_size() + off;
+       }
     }
 
   switch (r_type)
@@ -4334,6 +6399,75 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       break;
     }
 
+  if (size == 64)
+    {
+      // Multi-instruction sequences that access the TOC can be
+      // optimized, eg. addis ra,r2,0; addi rb,ra,x;
+      // to             nop;           addi rb,r2,x;
+      switch (r_type)
+       {
+       default:
+         break;
+
+       case elfcpp::R_POWERPC_GOT_TLSLD16_HA:
+       case elfcpp::R_POWERPC_GOT_TLSGD16_HA:
+       case elfcpp::R_POWERPC_GOT_TPREL16_HA:
+       case elfcpp::R_POWERPC_GOT_DTPREL16_HA:
+       case elfcpp::R_POWERPC_GOT16_HA:
+       case elfcpp::R_PPC64_TOC16_HA:
+         if (parameters->options().toc_optimize())
+           {
+             Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+             Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+             if ((insn & ((0x3f << 26) | 0x1f << 16))
+                 != ((15u << 26) | (2 << 16)) /* addis rt,2,imm */)
+               gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
+                                      _("toc optimization is not supported "
+                                        "for %#08x instruction"), insn);
+             else if (value + 0x8000 < 0x10000)
+               {
+                 elfcpp::Swap<32, big_endian>::writeval(iview, nop);
+                 return true;
+               }
+           }
+         break;
+
+       case elfcpp::R_POWERPC_GOT_TLSLD16_LO:
+       case elfcpp::R_POWERPC_GOT_TLSGD16_LO:
+       case elfcpp::R_POWERPC_GOT_TPREL16_LO:
+       case elfcpp::R_POWERPC_GOT_DTPREL16_LO:
+       case elfcpp::R_POWERPC_GOT16_LO:
+       case elfcpp::R_PPC64_GOT16_LO_DS:
+       case elfcpp::R_PPC64_TOC16_LO:
+       case elfcpp::R_PPC64_TOC16_LO_DS:
+         if (parameters->options().toc_optimize())
+           {
+             Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+             Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+             if (!ok_lo_toc_insn(insn))
+               gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
+                                      _("toc optimization is not supported "
+                                        "for %#08x instruction"), insn);
+             else if (value + 0x8000 < 0x10000)
+               {
+                 if ((insn & (0x3f << 26)) == 12u << 26 /* addic */)
+                   {
+                     // Transform addic to addi when we change reg.
+                     insn &= ~((0x3f << 26) | (0x1f << 16));
+                     insn |= (14u << 26) | (2 << 16);
+                   }
+                 else
+                   {
+                     insn &= ~(0x1f << 16);
+                     insn |= 2 << 16;
+                   }
+                 elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+               }
+           }
+         break;
+       }
+    }
+
   typename Reloc::Overflow_check overflow = Reloc::CHECK_NONE;
   switch (r_type)
     {
@@ -4413,10 +6547,10 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       break;
 
     case elfcpp::R_POWERPC_ADDR32:
-    case elfcpp::R_POWERPC_REL32:
       status = Reloc::addr32(view, value, overflow);
       break;
 
+    case elfcpp::R_POWERPC_REL32:
     case elfcpp::R_POWERPC_UADDR32:
       status = Reloc::addr32_u(view, value, overflow);
       break;
@@ -4639,48 +6773,13 @@ Target_powerpc<size, big_endian>::relocate_section(
 {
   typedef Target_powerpc<size, big_endian> Powerpc;
   typedef typename Target_powerpc<size, big_endian>::Relocate Powerpc_relocate;
+  typedef typename Target_powerpc<size, big_endian>::Relocate_comdat_behavior
+    Powerpc_comdat_behavior;
 
   gold_assert(sh_type == elfcpp::SHT_RELA);
 
-  unsigned char *opd_rel = NULL;
-  Powerpc_relobj<size, big_endian>* const object
-    = static_cast<Powerpc_relobj<size, big_endian>*>(relinfo->object);
-  if (size == 64
-      && relinfo->data_shndx == object->opd_shndx())
-    {
-      // Rewrite opd relocs, omitting those for discarded sections
-      // to silence gold::relocate_section errors.
-      const int reloc_size
-       = Reloc_types<elfcpp::SHT_RELA, size, big_endian>::reloc_size;
-      opd_rel = new unsigned char[reloc_count * reloc_size];
-      const unsigned char* rrel = prelocs;
-      unsigned char* wrel = opd_rel;
-
-      for (size_t i = 0;
-          i < reloc_count;
-          ++i, rrel += reloc_size, wrel += reloc_size)
-       {
-         typename Reloc_types<elfcpp::SHT_RELA, size, big_endian>::Reloc
-           reloc(rrel);
-         typename elfcpp::Elf_types<size>::Elf_WXword r_info
-           = reloc.get_r_info();
-         unsigned int r_type = elfcpp::elf_r_type<size>(r_info);
-         Address r_off = reloc.get_r_offset();
-         if (r_type == elfcpp::R_PPC64_TOC)
-           r_off -= 8;
-         bool is_discarded = object->get_opd_discard(r_off);
-
-         // Reloc number is reported in some errors, so keep all relocs.
-         if (is_discarded)
-           memset(wrel, 0, reloc_size);
-         else
-           memcpy(wrel, rrel, reloc_size);
-       }
-      prelocs = opd_rel;
-    }
-
   gold::relocate_section<size, big_endian, Powerpc, elfcpp::SHT_RELA,
-                        Powerpc_relocate>(
+                        Powerpc_relocate, Powerpc_comdat_behavior>(
     relinfo,
     this,
     prelocs,
@@ -4691,9 +6790,6 @@ Target_powerpc<size, big_endian>::relocate_section(
     address,
     view_size,
     reloc_symbol_changes);
-
-  if (opd_rel != NULL)
-    delete[] opd_rel;
 }
 
 class Powerpc_scan_relocatable_reloc
@@ -4777,7 +6873,7 @@ Target_powerpc<size, big_endian>::relocate_relocs(
     const unsigned char* prelocs,
     size_t reloc_count,
     Output_section* output_section,
-    off_t offset_in_output_section,
+    typename elfcpp::Elf_types<size>::Elf_Off offset_in_output_section,
     const Relocatable_relocs* rr,
     unsigned char*,
     Address view_address,
@@ -5060,7 +7156,7 @@ Target_powerpc<size, big_endian>::relocate_relocs(
              == reloc_view_size);
 }
 
-// Return the value to use for a dynamic which requires special
+// Return the value to use for a dynamic symbol which requires special
 // treatment.  This is how we support equality comparisons of function
 // pointers across shared library boundaries, as described in the
 // processor specific ABI supplement.
@@ -5072,12 +7168,16 @@ Target_powerpc<size, big_endian>::do_dynsym_value(const Symbol* gsym) const
   if (size == 32)
     {
       gold_assert(gsym->is_from_dynobj() && gsym->has_plt_offset());
-      const Output_data_glink<size, big_endian>* glink = this->glink_section();
-      unsigned int glink_index = glink->find_entry(gsym);
-      return glink->address() + glink_index * glink->glink_entry_size();
+      for (typename Stub_tables::const_iterator p = this->stub_tables_.begin();
+          p != this->stub_tables_.end();
+          ++p)
+       {
+         Address off = (*p)->find_plt_call_entry(gsym);
+         if (off != invalid_address)
+           return (*p)->stub_address() + off;
+       }
     }
-  else
-    gold_unreachable();
+  gold_unreachable();
 }
 
 // Return the PLT address to use for a local symbol.
@@ -5091,13 +7191,17 @@ Target_powerpc<size, big_endian>::do_plt_address_for_local(
     {
       const Sized_relobj<size, big_endian>* relobj
        = static_cast<const Sized_relobj<size, big_endian>*>(object);
-      const Output_data_glink<size, big_endian>* glink = this->glink_section();
-      unsigned int glink_index = glink->find_entry(relobj->sized_relobj(),
-                                                  symndx);
-      return glink->address() + glink_index * glink->glink_entry_size();
+      for (typename Stub_tables::const_iterator p = this->stub_tables_.begin();
+          p != this->stub_tables_.end();
+          ++p)
+       {
+         Address off = (*p)->find_plt_call_entry(relobj->sized_relobj(),
+                                                 symndx);
+         if (off != invalid_address)
+           return (*p)->stub_address() + off;
+       }
     }
-  else
-    gold_unreachable();
+  gold_unreachable();
 }
 
 // Return the PLT address to use for a global symbol.
@@ -5108,12 +7212,16 @@ Target_powerpc<size, big_endian>::do_plt_address_for_global(
 {
   if (size == 32)
     {
-      const Output_data_glink<size, big_endian>* glink = this->glink_section();
-      unsigned int glink_index = glink->find_entry(gsym);
-      return glink->address() + glink_index * glink->glink_entry_size();
+      for (typename Stub_tables::const_iterator p = this->stub_tables_.begin();
+          p != this->stub_tables_.end();
+          ++p)
+       {
+         Address off = (*p)->find_plt_call_entry(gsym);
+         if (off != invalid_address)
+           return (*p)->stub_address() + off;
+       }
     }
-  else
-    gold_unreachable();
+  gold_unreachable();
 }
 
 // Return the offset to use for the GOT_INDX'th got entry which is
@@ -5186,7 +7294,8 @@ class Target_selector_powerpc : public Target_selector
 {
 public:
   Target_selector_powerpc()
-    : Target_selector(elfcpp::EM_NONE, size, big_endian,
+    : Target_selector(size == 64 ? elfcpp::EM_PPC64 : elfcpp::EM_PPC,
+                     size, big_endian,
                      (size == 64
                       ? (big_endian ? "elf64-powerpc" : "elf64-powerpcle")
                       : (big_endian ? "elf32-powerpc" : "elf32-powerpcle")),
@@ -5195,28 +7304,6 @@ public:
                       : (big_endian ? "elf32ppc" : "elf32lppc")))
   { }
 
-  virtual Target*
-  do_recognize(Input_file*, off_t, int machine, int, int)
-  {
-    switch (size)
-      {
-      case 64:
-       if (machine != elfcpp::EM_PPC64)
-         return NULL;
-       break;
-
-      case 32:
-       if (machine != elfcpp::EM_PPC)
-         return NULL;
-       break;
-
-      default:
-       return NULL;
-      }
-
-    return this->instantiate_target();
-  }
-
   virtual Target*
   do_instantiate_target()
   { return new Target_powerpc<size, big_endian>(); }
@@ -5227,4 +7314,14 @@ Target_selector_powerpc<32, false> target_selector_ppc32le;
 Target_selector_powerpc<64, true> target_selector_ppc64;
 Target_selector_powerpc<64, false> target_selector_ppc64le;
 
+// Instantiate these constants for -O0
+template<int size, bool big_endian>
+const int Output_data_glink<size, big_endian>::pltresolve_size;
+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>
+const typename Target_powerpc<size, big_endian>::Address
+  Target_powerpc<size, big_endian>::invalid_address;
+
 } // End anonymous namespace.
This page took 0.067022 seconds and 4 git commands to generate.