* powerpc.cc (Target_powerpc::do_plt_address_for_local,
authorAlan Modra <amodra@gmail.com>
Fri, 5 Oct 2012 08:18:07 +0000 (08:18 +0000)
committerAlan Modra <amodra@gmail.com>
Fri, 5 Oct 2012 08:18:07 +0000 (08:18 +0000)
do_plt_address_for_global): New functions.
(Output_data_got_powerpc::do_write): Don't segfault when linking
statically.
(Output_data_plt_powerpc::add_entry, add_ifunc_entry,
add_local_ifunc_entry): Return true on adding entry..
(Target_powerpc::make_plt_entry): ..use to avoid unnecessary
glink->add_entry call.  Remove unused symtab param.  Adjust calls.
(Target_powerpc::make_local_ifunc_plt_entry): Likewise.
(Target_powerpc::make_iplt_section): Remove symtab param.  Don't
set up symbols here.
(Target_powerpc::do_finalize_sections): Instead set up __rela_iplt
syms here.  Do so even when no .iplt.  Don't segfault when linking
statically.
(Output_data_glink::add_entry, find_entry): Rearrange params.  Add
new variants without reloc param.
(Glink_sym_ent::Glink_sym_ent): Likewise.
(Target_powerpc::Scan::reloc_needs_plt_for_ifunc): Accept any
reloc when refs will resolve to plt call stub.
(Target_powerpc::Scan::local): Correct ifunc handling.  Allow
R_PPC_PLTREL24 to resolve locally.
(Target_powerpc::Scan::global): Correct ifunc handling.
(Target_powerpc::Relocate::relocate): Correct local sym glink
lookup.  Don't destroy "value" when we have a plt call stub,
and when checking plt call validity.
(Target_powerpc::do_dynsym_value): Simplify.

gold/ChangeLog
gold/powerpc.cc

index 7b3bc9252845707a1abc42934af84d3ca832fd35..d9a5307c23ca202fd32a3cb9845adfb0a57ac70c 100644 (file)
@@ -1,3 +1,32 @@
+2012-10-05  Alan Modra  <amodra@gmail.com>
+
+       * powerpc.cc (Target_powerpc::do_plt_address_for_local,
+       do_plt_address_for_global): New functions.
+       (Output_data_got_powerpc::do_write): Don't segfault when linking
+       statically.
+       (Output_data_plt_powerpc::add_entry, add_ifunc_entry,
+       add_local_ifunc_entry): Return true on adding entry..
+       (Target_powerpc::make_plt_entry): ..use to avoid unnecessary
+       glink->add_entry call.  Remove unused symtab param.  Adjust calls.
+       (Target_powerpc::make_local_ifunc_plt_entry): Likewise.
+       (Target_powerpc::make_iplt_section): Remove symtab param.  Don't
+       set up symbols here.
+       (Target_powerpc::do_finalize_sections): Instead set up __rela_iplt
+       syms here.  Do so even when no .iplt.  Don't segfault when linking
+       statically.
+       (Output_data_glink::add_entry, find_entry): Rearrange params.  Add
+       new variants without reloc param.
+       (Glink_sym_ent::Glink_sym_ent): Likewise.
+       (Target_powerpc::Scan::reloc_needs_plt_for_ifunc): Accept any
+       reloc when refs will resolve to plt call stub.
+       (Target_powerpc::Scan::local): Correct ifunc handling.  Allow
+       R_PPC_PLTREL24 to resolve locally.
+       (Target_powerpc::Scan::global): Correct ifunc handling.
+       (Target_powerpc::Relocate::relocate): Correct local sym glink
+       lookup.  Don't destroy "value" when we have a plt call stub,
+       and when checking plt call validity.
+       (Target_powerpc::do_dynsym_value): Simplify.
+
 2012-10-05  Alan Modra  <amodra@gmail.com>
 
        * i386.cc (Output_data_plt_i386::address_for_global,
index d59b30c092913f53c1354891a8faa983eb7a07d0..7f9774f634a779d6262f3c1f738c210ab887290c 100644 (file)
@@ -311,6 +311,14 @@ class Target_powerpc : public Sized_target<size, big_endian>
   uint64_t
   do_dynsym_value(const Symbol*) const;
 
+  // Return the PLT address to use for a local symbol.
+  uint64_t
+  do_plt_address_for_local(const Relobj*, unsigned int) const;
+
+  // Return the PLT address to use for a global symbol.
+  uint64_t
+  do_plt_address_for_global(const Symbol*) const;
+
   // Return the offset to use for the GOT_INDX'th got entry which is
   // for a local tls symbol specified by OBJECT, SYMNDX.
   int64_t
@@ -645,17 +653,17 @@ class Target_powerpc : public Sized_target<size, big_endian>
   make_plt_section(Layout*);
 
   void
-  make_iplt_section(Layout*, Symbol_table*);
+  make_iplt_section(Layout*);
 
   // Create a PLT entry for a global symbol.
   void
-  make_plt_entry(Layout*, Symbol_table*, Symbol*,
+  make_plt_entry(Layout*, Symbol*,
                 const elfcpp::Rela<size, big_endian>&,
                 const Sized_relobj_file<size, big_endian>* object);
 
   // Create a PLT entry for a local IFUNC symbol.
   void
-  make_local_ifunc_plt_entry(Layout*, Symbol_table*,
+  make_local_ifunc_plt_entry(Layout*,
                             const elfcpp::Rela<size, big_endian>&,
                             Sized_relobj_file<size, big_endian>*);
 
@@ -1423,11 +1431,12 @@ public:
   void
   do_write(Output_file* of)
   {
-    this->replace_constant(this->header_index_,
-                          (size == 32
-                           ? this->layout_->dynamic_section()->address()
-                           : this->output_section()->address() + 0x8000));
-
+    Valtype val = 0;
+    if (size == 32 && this->layout_->dynamic_data() != NULL)
+      val = this->layout_->dynamic_section()->address();
+    if (size == 64)
+      val = this->output_section()->address() + 0x8000;
+    this->replace_constant(this->header_index_, val);
     Output_data_got<size, big_endian>::do_write(of);
   }
 
@@ -1535,13 +1544,13 @@ class Output_data_plt_powerpc : public Output_section_data_build
   { }
 
   // Add an entry to the PLT.
-  void
+  bool
   add_entry(Symbol*);
 
-  void
+  bool
   add_ifunc_entry(Symbol*);
 
-  void
+  bool
   add_local_ifunc_entry(Sized_relobj_file<size, big_endian>*, unsigned int);
 
   // Return the .rela.plt section data.
@@ -1602,7 +1611,7 @@ class Output_data_plt_powerpc : public Output_section_data_build
 // Add an entry to the PLT.
 
 template<int size, bool big_endian>
-void
+bool
 Output_data_plt_powerpc<size, big_endian>::add_entry(Symbol* gsym)
 {
   if (!gsym->has_plt_offset())
@@ -1616,13 +1625,15 @@ Output_data_plt_powerpc<size, big_endian>::add_entry(Symbol* gsym)
       this->rel_->add_global(gsym, dynrel, this, off, 0);
       off += plt_entry_size;
       this->set_current_data_size(off);
+      return true;
     }
+  return false;
 }
 
 // Add an entry for a global ifunc symbol that resolves locally, to the IPLT.
 
 template<int size, bool big_endian>
-void
+bool
 Output_data_plt_powerpc<size, big_endian>::add_ifunc_entry(Symbol* gsym)
 {
   if (!gsym->has_plt_offset())
@@ -1635,13 +1646,15 @@ Output_data_plt_powerpc<size, big_endian>::add_ifunc_entry(Symbol* gsym)
       this->rel_->add_symbolless_global_addend(gsym, dynrel, this, off, 0);
       off += plt_entry_size;
       this->set_current_data_size(off);
+      return true;
     }
+  return false;
 }
 
 // Add an entry for a local ifunc symbol to the IPLT.
 
 template<int size, bool big_endian>
-void
+bool
 Output_data_plt_powerpc<size, big_endian>::add_local_ifunc_entry(
     Sized_relobj_file<size, big_endian>* relobj,
     unsigned int local_sym_index)
@@ -1657,7 +1670,9 @@ Output_data_plt_powerpc<size, big_endian>::add_local_ifunc_entry(
                                              this, off, 0);
       off += plt_entry_size;
       this->set_current_data_size(off);
+      return true;
     }
+  return false;
 }
 
 static const uint32_t add_0_11_11      = 0x7c0b5a14;
@@ -1784,8 +1799,7 @@ 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,
-                                                   Symbol_table* symtab)
+Target_powerpc<size, big_endian>::make_iplt_section(Layout* layout)
 {
   if (this->iplt_ == NULL)
     {
@@ -1797,19 +1811,6 @@ Target_powerpc<size, big_endian>::make_iplt_section(Layout* layout,
        = new Output_data_plt_powerpc<size, big_endian>(this, iplt_rel,
                                                        0, "** IPLT");
       this->plt_->output_section()->add_output_section_data(this->iplt_);
-      if (parameters->doing_static_link())
-       {
-         symtab->define_in_output_data("__rela_iplt_start", NULL,
-                                       Symbol_table::PREDEFINED,
-                                       iplt_rel, 0, 0,
-                                       elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
-                                       elfcpp::STV_HIDDEN, 0, false, true);
-         symtab->define_in_output_data("__rela_iplt_end", NULL,
-                                       Symbol_table::PREDEFINED,
-                                       iplt_rel, 0, 0,
-                                       elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
-                                       elfcpp::STV_HIDDEN, 0, true, true);
-       }
     }
 }
 
@@ -1825,20 +1826,30 @@ class Output_data_glink : public Output_section_data
 
   // Add an entry
   void
-  add_entry(const Symbol*, const elfcpp::Rela<size, big_endian>&,
-           const Sized_relobj_file<size, big_endian>*);
+  add_entry(const Sized_relobj_file<size, big_endian>*,
+           const Symbol*,
+           const elfcpp::Rela<size, big_endian>&);
 
   void
-  add_entry(unsigned int, const elfcpp::Rela<size, big_endian>&,
-           const Sized_relobj_file<size, big_endian>*);
+  add_entry(const Sized_relobj_file<size, big_endian>*,
+           unsigned int,
+           const elfcpp::Rela<size, big_endian>&);
 
   unsigned int
-  find_entry(const Symbol*, const elfcpp::Rela<size, big_endian>&,
-            const Sized_relobj_file<size, big_endian>*) const;
+  find_entry(const Symbol*) const;
 
   unsigned int
-  find_entry(unsigned int, const elfcpp::Rela<size, big_endian>&,
-            const Sized_relobj_file<size, big_endian>*) const;
+  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
@@ -1874,9 +1885,18 @@ class Output_data_glink : public Output_section_data
   class Glink_sym_ent
   {
   public:
-    Glink_sym_ent(const Symbol* sym,
-                 const elfcpp::Rela<size, big_endian>& reloc,
-                 const Sized_relobj_file<size, big_endian>* object)
+    Glink_sym_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)
+      : 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)
       : sym_(sym), object_(0), addend_(0), locsym_(0)
     {
       if (size != 32)
@@ -1891,9 +1911,9 @@ class Output_data_glink : public Output_section_data
        }
     }
 
-    Glink_sym_ent(unsigned int locsym_index,
-                 const elfcpp::Rela<size, big_endian>& reloc,
-                 const Sized_relobj_file<size, big_endian>* object)
+    Glink_sym_ent(const Sized_relobj_file<size, big_endian>* object,
+                 unsigned int locsym_index,
+                 const elfcpp::Rela<size, big_endian>& reloc)
       : sym_(NULL), object_(object), addend_(0), locsym_(locsym_index)
     {
       if (size != 32)
@@ -1958,11 +1978,11 @@ Output_data_glink<size, big_endian>::Output_data_glink(
 template<int size, bool big_endian>
 void
 Output_data_glink<size, big_endian>::add_entry(
+    const Sized_relobj_file<size, big_endian>* object,
     const Symbol* gsym,
-    const elfcpp::Rela<size, big_endian>& reloc,
-    const Sized_relobj_file<size, big_endian>* object)
+    const elfcpp::Rela<size, big_endian>& reloc)
 {
-  Glink_sym_ent ent(gsym, reloc, object);
+  Glink_sym_ent ent(object, gsym, reloc);
   unsigned int indx = this->glink_entries_.size();
   this->glink_entries_.insert(std::make_pair(ent, indx));
 }
@@ -1970,11 +1990,11 @@ Output_data_glink<size, big_endian>::add_entry(
 template<int size, bool big_endian>
 void
 Output_data_glink<size, big_endian>::add_entry(
+    const Sized_relobj_file<size, big_endian>* object,
     unsigned int locsym_index,
-    const elfcpp::Rela<size, big_endian>& reloc,
-    const Sized_relobj_file<size, big_endian>* object)
+    const elfcpp::Rela<size, big_endian>& reloc)
 {
-  Glink_sym_ent ent(locsym_index, reloc, object);
+  Glink_sym_ent ent(object, locsym_index, reloc);
   unsigned int indx = this->glink_entries_.size();
   this->glink_entries_.insert(std::make_pair(ent, indx));
 }
@@ -1982,11 +2002,21 @@ Output_data_glink<size, big_endian>::add_entry(
 template<int size, bool big_endian>
 unsigned int
 Output_data_glink<size, big_endian>::find_entry(
+    const Sized_relobj_file<size, big_endian>* object,
     const Symbol* gsym,
-    const elfcpp::Rela<size, big_endian>& reloc,
-    const Sized_relobj_file<size, big_endian>* object) const
+    const elfcpp::Rela<size, big_endian>& reloc) 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;
+}
+
+template<int size, bool big_endian>
+unsigned int
+Output_data_glink<size, big_endian>::find_entry(const Symbol* gsym) const
 {
-  Glink_sym_ent ent(gsym, reloc, object);
+  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;
@@ -1995,11 +2025,23 @@ Output_data_glink<size, big_endian>::find_entry(
 template<int size, bool big_endian>
 unsigned int
 Output_data_glink<size, big_endian>::find_entry(
+    const Sized_relobj_file<size, big_endian>* object,
     unsigned int locsym_index,
-    const elfcpp::Rela<size, big_endian>& reloc,
-    const Sized_relobj_file<size, big_endian>* object) const
+    const elfcpp::Rela<size, big_endian>& reloc) const
 {
-  Glink_sym_ent ent(locsym_index, reloc, object);
+  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;
+}
+
+template<int size, bool big_endian>
+unsigned int
+Output_data_glink<size, big_endian>::find_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;
@@ -2379,7 +2421,6 @@ template<int size, bool big_endian>
 void
 Target_powerpc<size, big_endian>::make_plt_entry(
     Layout* layout,
-    Symbol_table* symtab,
     Symbol* gsym,
     const elfcpp::Rela<size, big_endian>& reloc,
     const Sized_relobj_file<size, big_endian>* object)
@@ -2388,16 +2429,17 @@ Target_powerpc<size, big_endian>::make_plt_entry(
       && gsym->can_use_relative_reloc(false))
     {
       if (this->iplt_ == NULL)
-       this->make_iplt_section(layout, symtab);
-      this->iplt_->add_ifunc_entry(gsym);
+       this->make_iplt_section(layout);
+      if (this->iplt_->add_ifunc_entry(gsym))
+       this->glink_->add_entry(object, gsym, reloc);
     }
   else
     {
       if (this->plt_ == NULL)
        this->make_plt_section(layout);
-      this->plt_->add_entry(gsym);
+      if (this->plt_->add_entry(gsym))
+       this->glink_->add_entry(object, gsym, reloc);
     }
-  this->glink_->add_entry(gsym, reloc, object);
 }
 
 // Make a PLT entry for a local STT_GNU_IFUNC symbol.
@@ -2406,15 +2448,14 @@ template<int size, bool big_endian>
 void
 Target_powerpc<size, big_endian>::make_local_ifunc_plt_entry(
     Layout* layout,
-    Symbol_table* symtab,
     const elfcpp::Rela<size, big_endian>& reloc,
     Sized_relobj_file<size, big_endian>* relobj)
 {
   if (this->iplt_ == NULL)
-    this->make_iplt_section(layout, symtab);
+    this->make_iplt_section(layout);
   unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
-  this->iplt_->add_local_ifunc_entry(relobj, r_sym);
-  this->glink_->add_entry(r_sym, reloc, relobj);
+  if (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.
@@ -2674,6 +2715,11 @@ Target_powerpc<size, big_endian>::Scan::reloc_needs_plt_for_ifunc(
      Sized_relobj_file<size, big_endian>* object,
      unsigned int r_type)
 {
+  // In non-pic code any reference will resolve to the plt call stub
+  // for the ifunc symbol.
+  if (size == 32 && !parameters->options().output_is_position_independent())
+    return true;
+
   switch (r_type)
     {
     // Word size refs from data sections are OK.
@@ -2759,7 +2805,7 @@ Target_powerpc<size, big_endian>::Scan::local(
   // A local STT_GNU_IFUNC symbol may require a PLT entry.
   bool is_ifunc = lsym.get_st_type() == elfcpp::STT_GNU_IFUNC;
   if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type))
-    target->make_local_ifunc_plt_entry(layout, symtab, reloc, object);
+    target->make_local_ifunc_plt_entry(layout, reloc, object);
 
   switch (r_type)
     {
@@ -2816,7 +2862,8 @@ Target_powerpc<size, big_endian>::Scan::local(
       // If building a shared library (or a position-independent
       // executable), we need to create a dynamic relocation for
       // this location.
-      if (parameters->options().output_is_position_independent())
+      if (parameters->options().output_is_position_independent()
+         || (size == 64 && is_ifunc))
        {
          Reloc_section* rela_dyn = target->rela_dyn_section(layout);
 
@@ -2833,7 +2880,7 @@ Target_powerpc<size, big_endian>::Scan::local(
              rela_dyn->add_local_relative(object, r_sym, dynrel,
                                           output_section, data_shndx,
                                           reloc.get_r_offset(),
-                                          reloc.get_r_addend(), is_ifunc);
+                                          reloc.get_r_addend(), false);
            }
          else
            {
@@ -2849,6 +2896,7 @@ Target_powerpc<size, big_endian>::Scan::local(
     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:
     case elfcpp::R_POWERPC_REL16:
     case elfcpp::R_POWERPC_REL16_LO:
@@ -2922,7 +2970,7 @@ Target_powerpc<size, big_endian>::Scan::local(
                dynrel = elfcpp::R_POWERPC_IRELATIVE;
              }
            rela_dyn->add_local_relative(object, r_sym, dynrel,
-                                        got, off, 0, is_ifunc);
+                                        got, off, 0, false);
          }
       }
       break;
@@ -3058,7 +3106,7 @@ Target_powerpc<size, big_endian>::Scan::global(
   // 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, symtab, gsym, reloc, object);
+    target->make_plt_entry(layout, gsym, reloc, object);
 
   switch (r_type)
     {
@@ -3128,7 +3176,7 @@ Target_powerpc<size, big_endian>::Scan::global(
        // Make a PLT entry if necessary.
        if (gsym->needs_plt_entry())
          {
-           target->make_plt_entry(layout, symtab, gsym, reloc, 0);
+           target->make_plt_entry(layout, gsym, reloc, 0);
            // 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
@@ -3139,7 +3187,8 @@ Target_powerpc<size, big_endian>::Scan::global(
              gsym->set_needs_dynsym_value();
          }
        // Make a dynamic relocation if necessary.
-       if (needs_dynamic_reloc<size>(gsym, Scan::get_reference_flags(r_type)))
+       if (needs_dynamic_reloc<size>(gsym, Scan::get_reference_flags(r_type))
+           || (size == 64 && gsym->type() == elfcpp::STT_GNU_IFUNC))
          {
            if (gsym->may_need_copy_reloc())
              {
@@ -3183,7 +3232,7 @@ Target_powerpc<size, big_endian>::Scan::global(
              && (gsym->is_undefined()
                  || gsym->is_from_dynobj()
                  || gsym->is_preemptible())))
-       target->make_plt_entry(layout, symtab, gsym, reloc, object);
+       target->make_plt_entry(layout, gsym, reloc, object);
       // Fall thru
 
     case elfcpp::R_PPC64_REL64:
@@ -3639,6 +3688,36 @@ Target_powerpc<size, big_endian>::do_finalize_sections(
     const Input_objects*,
     Symbol_table* symtab)
 {
+  if (parameters->doing_static_link())
+    {
+      // At least some versions of glibc elf-init.o have a strong
+      // reference to __rela_iplt marker syms.  A weak ref would be
+      // better..
+      if (this->iplt_ != NULL)
+       {
+         Reloc_section* rel = this->iplt_->rel_plt();
+         symtab->define_in_output_data("__rela_iplt_start", NULL,
+                                       Symbol_table::PREDEFINED, rel, 0, 0,
+                                       elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
+                                       elfcpp::STV_HIDDEN, 0, false, true);
+         symtab->define_in_output_data("__rela_iplt_end", NULL,
+                                       Symbol_table::PREDEFINED, rel, 0, 0,
+                                       elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
+                                       elfcpp::STV_HIDDEN, 0, true, true);
+       }
+      else
+       {
+         symtab->define_as_constant("__rela_iplt_start", NULL,
+                                    Symbol_table::PREDEFINED, 0, 0,
+                                    elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
+                                    elfcpp::STV_HIDDEN, 0, true, false);
+         symtab->define_as_constant("__rela_iplt_end", NULL,
+                                    Symbol_table::PREDEFINED, 0, 0,
+                                    elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
+                                    elfcpp::STV_HIDDEN, 0, true, false);
+       }
+    }
+
   if (size == 64)
     {
       typedef Global_symbol_visitor_opd<big_endian> Symbol_visitor;
@@ -3646,31 +3725,35 @@ Target_powerpc<size, big_endian>::do_finalize_sections(
     }
 
   // Fill in some more dynamic tags.
-  const Reloc_section* rel_plt = (this->plt_ == NULL
-                                 ? NULL
-                                 : this->plt_->rel_plt());
-  layout->add_target_dynamic_tags(false, this->plt_, rel_plt,
-                                 this->rela_dyn_, true, size == 32);
-
   Output_data_dynamic* odyn = layout->dynamic_data();
-  if (size == 32)
+  if (odyn != NULL)
     {
-      if (this->got_ != NULL)
+      const Reloc_section* rel_plt = (this->plt_ == NULL
+                                     ? NULL
+                                     : this->plt_->rel_plt());
+      layout->add_target_dynamic_tags(false, this->plt_, rel_plt,
+                                     this->rela_dyn_, true, size == 32);
+
+      if (size == 32)
        {
-         this->got_->finalize_data_size();
-         odyn->add_section_plus_offset(elfcpp::DT_PPC_GOT,
-                                       this->got_, this->got_->g_o_t());
+         if (this->got_ != NULL)
+           {
+             this->got_->finalize_data_size();
+             odyn->add_section_plus_offset(elfcpp::DT_PPC_GOT,
+                                           this->got_, this->got_->g_o_t());
+           }
        }
-    }
-  else
-    {
-      if (this->glink_ != NULL)
+      else
        {
-         this->glink_->finalize_data_size();
-         odyn->add_section_plus_offset(elfcpp::DT_PPC64_GLINK,
-                                       this->glink_,
-                                       (this->glink_->pltresolve()
-                                        + this->glink_->pltresolve_size - 32));
+         if (this->glink_ != NULL)
+           {
+             this->glink_->finalize_data_size();
+             odyn->add_section_plus_offset(elfcpp::DT_PPC64_GLINK,
+                                           this->glink_,
+                                           (this->glink_->pltresolve()
+                                            + this->glink_->pltresolve_size
+                                            - 32));
+           }
        }
     }
 
@@ -3766,7 +3849,11 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
     {
       const Output_data_glink<size, big_endian>* glink
        = target->glink_section();
-      unsigned int glink_index = glink->find_entry(gsym, rela, object);
+      unsigned int glink_index;
+      if (gsym != NULL)
+       glink_index = glink->find_entry(object, gsym, rela);
+      else
+       glink_index = glink->find_entry(object, r_sym, rela);
       value = glink->address() + glink_index * glink->glink_entry_size();
       has_plt_value = true;
     }
@@ -3834,23 +3921,27 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
              // such a call so just check for a call to the same
              // section.
              bool ok = false;
+             Address code = value;
              if (gsym->source() == Symbol::FROM_OBJECT
                  && gsym->object() == object)
                {
                  Address addend = rela.get_r_addend();
                  unsigned int dest_shndx;
-                 value = psymval->value(object, addend);
-                 value = target->symval_for_branch(value, gsym, object,
-                                                   &dest_shndx);
+                 Address opdent = psymval->value(object, addend);
+                 code = target->symval_for_branch(opdent, gsym, object,
+                                                  &dest_shndx);
                  bool is_ordinary;
                  if (dest_shndx == 0)
                    dest_shndx = gsym->shndx(&is_ordinary);
                  ok = dest_shndx == relinfo->data_shndx;
                }
              if (!ok)
-               gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
-                                      _("call lacks nop, can't restore toc; "
-                                        "recompile with -fPIC"));
+               {
+                 gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
+                                        _("call lacks nop, can't restore toc; "
+                                          "recompile with -fPIC"));
+                 value = code;
+               }
            }
        }
     }
@@ -4092,14 +4183,13 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
          value = psymval->value(object, rela.get_r_addend());
        }
     }
-  else
+  else if (!has_plt_value)
     {
       Address addend = 0;
       unsigned int dest_shndx;
       if (r_type != elfcpp::R_PPC_PLTREL24)
        addend = rela.get_r_addend();
-      if (size == 64 || !has_plt_value)
-       value = psymval->value(object, addend);
+      value = psymval->value(object, addend);
       if (size == 64 && is_branch_reloc(r_type))
        value = target->symval_for_branch(value, gsym, object, &dest_shndx);
     }
@@ -4961,9 +5051,43 @@ Target_powerpc<size, big_endian>::do_dynsym_value(const Symbol* gsym) const
     {
       gold_assert(gsym->is_from_dynobj() && gsym->has_plt_offset());
       const Output_data_glink<size, big_endian>* glink = this->glink_section();
-      static const unsigned char zeros[elfcpp::Elf_sizes<32>::rela_size] = {0};
-      const elfcpp::Rela<size, big_endian> zero_reloc(zeros);
-      unsigned int glink_index = glink->find_entry(gsym, zero_reloc, NULL);
+      unsigned int glink_index = glink->find_entry(gsym);
+      return glink->address() + glink_index * glink->glink_entry_size();
+    }
+  else
+    gold_unreachable();
+}
+
+// Return the PLT address to use for a local symbol.
+template<int size, bool big_endian>
+uint64_t
+Target_powerpc<size, big_endian>::do_plt_address_for_local(
+    const Relobj* object,
+    unsigned int symndx) const
+{
+  if (size == 32)
+    {
+      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();
+    }
+  else
+    gold_unreachable();
+}
+
+// Return the PLT address to use for a global symbol.
+template<int size, bool big_endian>
+uint64_t
+Target_powerpc<size, big_endian>::do_plt_address_for_global(
+    const Symbol* gsym) const
+{
+  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();
     }
   else