* powerpc.cc (Target_powerpc::Scan::reloc_needs_plt_for_ifunc): Make
authorAlan Modra <amodra@gmail.com>
Fri, 15 Mar 2013 08:40:49 +0000 (08:40 +0000)
committerAlan Modra <amodra@gmail.com>
Fri, 15 Mar 2013 08:40:49 +0000 (08:40 +0000)
static and public.  Add report_err param.  Return false for data refs.
(Target_powerpc::rela_dyn_section): New overloaded function.
(Target_powerpc::plt_, iplt_): Elucidate.
(Output_data_plt_powerpc::entry_count): Handle current_data_size()==0.
(Output_data_plt_powerpc::do_write): Don't write .iplt.
(Output_data_plt_powerpc::plt_entry_count): Don't add .iplt entries.
(Target_powerpc::Scan::local, global): Adjust reloc_needs_plt_for_ifunc
calls.  Put ifunc dynamic relocs in irela_dyn_section.  Only
push_branch and make_plt_entry for ifunc syms when
reloc_needs_plt_for_ifunc.
(Target_powerpc::Relocate::relocate): Don't use plt entry value
for ifunc unless reloc_needs_plt_for_ifunc.

gold/ChangeLog
gold/powerpc.cc

index 6be58e967518d77432154b58b95d742c1afe890f..ee6b2686f782a8ed7c3d154030aea7248af6d087 100644 (file)
@@ -1,3 +1,19 @@
+2013-03-15  Alan Modra  <amodra@gmail.com>
+
+       * powerpc.cc (Target_powerpc::Scan::reloc_needs_plt_for_ifunc): Make
+       static and public.  Add report_err param.  Return false for data refs.
+       (Target_powerpc::rela_dyn_section): New overloaded function.
+       (Target_powerpc::plt_, iplt_): Elucidate.
+       (Output_data_plt_powerpc::entry_count): Handle current_data_size()==0.
+       (Output_data_plt_powerpc::do_write): Don't write .iplt.
+       (Output_data_plt_powerpc::plt_entry_count): Don't add .iplt entries.
+       (Target_powerpc::Scan::local, global): Adjust reloc_needs_plt_for_ifunc
+       calls.  Put ifunc dynamic relocs in irela_dyn_section.  Only
+       push_branch and make_plt_entry for ifunc syms when
+       reloc_needs_plt_for_ifunc.
+       (Target_powerpc::Relocate::relocate): Don't use plt entry value
+       for ifunc unless reloc_needs_plt_for_ifunc.
+
 2013-03-15  Alan Modra  <amodra@gmail.com>
 
        * gc.h (gc_process_relocs): Don't look through function descriptors.
index 66109d2ab1a65362526c51247dc2db7ee29f774a..d9db9b9f5236a6ebbfe2f6229413b98f7aaf4cf6 100644 (file)
@@ -899,6 +899,10 @@ class Target_powerpc : public Sized_target<size, big_endian>
       return !is_branch_reloc(r_type);
     }
 
+    static bool
+    reloc_needs_plt_for_ifunc(Sized_relobj_file<size, big_endian>* object,
+                             unsigned int r_type, bool report_err);
+
   private:
     static void
     unsupported_reloc_local(Sized_relobj_file<size, big_endian>*,
@@ -915,10 +919,6 @@ class Target_powerpc : public Sized_target<size, big_endian>
     void
     check_non_pic(Relobj*, unsigned int r_type);
 
-    bool
-    reloc_needs_plt_for_ifunc(Sized_relobj_file<size, big_endian>* object,
-                             unsigned int r_type);
-
     // Whether we have issued an error about a non-PIC compilation.
     bool issued_non_pic_error_;
   };
@@ -1068,6 +1068,10 @@ class Target_powerpc : public Sized_target<size, big_endian>
   Reloc_section*
   rela_dyn_section(Layout*);
 
+  // Similarly, but for ifunc symbols get the one for ifunc.
+  Reloc_section*
+  rela_dyn_section(Symbol_table*, Layout*, bool for_ifunc);
+
   // Copy a relocation against a global symbol.
   void
   copy_reloc(Symbol_table* symtab, Layout* layout,
@@ -1144,9 +1148,30 @@ class Target_powerpc : public Sized_target<size, big_endian>
 
   // The GOT section.
   Output_data_got_powerpc<size, big_endian>* got_;
-  // The PLT section.
+  // The PLT section.  This is a container for a table of addresses,
+  // and their relocations.  Each address in the PLT has a dynamic
+  // relocation (R_*_JMP_SLOT) and each address will have a
+  // corresponding entry in .glink for lazy resolution of the PLT.
+  // ppc32 initialises the PLT to point at the .glink entry, while
+  // ppc64 leaves this to ld.so.  To make a call via the PLT, the
+  // linker adds a stub that loads the PLT entry into ctr then
+  // branches to ctr.  There may be more than one stub for each PLT
+  // entry.  DT_JMPREL points at the first PLT dynamic relocation and
+  // DT_PLTRELSZ gives the total size of PLT dynamic relocations.
   Output_data_plt_powerpc<size, big_endian>* plt_;
-  // The IPLT section.
+  // The IPLT section.  Like plt_, this is a container for a table of
+  // addresses and their relocations, specifically for STT_GNU_IFUNC
+  // functions that resolve locally (STT_GNU_IFUNC functions that
+  // don't resolve locally go in PLT).  Unlike plt_, these have no
+  // entry in .glink for lazy resolution, and the relocation section
+  // does not have a 1-1 correspondence with IPLT addresses.  In fact,
+  // the relocation section may contain relocations against
+  // STT_GNU_IFUNC symbols at locations outside of IPLT.  The
+  // relocation section will appear at the end of other dynamic
+  // relocations, so that ld.so applies these relocations after other
+  // dynamic relocations.  In a static executable, the relocation
+  // section is emitted and marked with __rela_iplt_start and
+  // __rela_iplt_end symbols.
   Output_data_plt_powerpc<size, big_endian>* iplt_;
   // Section holding long branch destinations.
   Output_data_brlt_powerpc<size, big_endian>* brlt_section_;
@@ -2122,6 +2147,22 @@ Target_powerpc<size, big_endian>::rela_dyn_section(Layout* layout)
   return this->rela_dyn_;
 }
 
+// Similarly, but for ifunc symbols get the one for ifunc.
+
+template<int size, bool big_endian>
+typename Target_powerpc<size, big_endian>::Reloc_section*
+Target_powerpc<size, big_endian>::rela_dyn_section(Symbol_table* symtab,
+                                                  Layout* layout,
+                                                  bool for_ifunc)
+{
+  if (!for_ifunc)
+    return this->rela_dyn_section(layout);
+
+  if (this->iplt_ == NULL)
+    this->make_iplt_section(symtab, layout);
+  return this->iplt_->rel_plt();
+}
+
 class Stub_control
 {
  public:
@@ -2660,6 +2701,8 @@ class Output_data_plt_powerpc : public Output_section_data_build
   unsigned int
   entry_count() const
   {
+    if (this->current_data_size() == 0)
+      return 0;
     return ((this->current_data_size() - this->initial_plt_entry_size_)
            / plt_entry_size);
   }
@@ -2838,7 +2881,7 @@ template<int size, bool big_endian>
 void
 Output_data_plt_powerpc<size, big_endian>::do_write(Output_file* of)
 {
-  if (size == 32)
+  if (size == 32 && this->name_[3] != 'I')
     {
       const section_size_type offset = this->offset();
       const section_size_type oview_size
@@ -2880,7 +2923,7 @@ Target_powerpc<size, big_endian>::make_plt_section(Symbol_table* symtab,
 
       // Ensure that .rela.dyn always appears before .rela.plt  This is
       // necessary due to how, on PowerPC and some other targets, .rela.dyn
-      // needs to include .rela.plt in it's range.
+      // needs to include .rela.plt in its range.
       this->rela_dyn_section(layout);
 
       Reloc_section* plt_rel = new Reloc_section(false);
@@ -4543,10 +4586,7 @@ Target_powerpc<size, big_endian>::plt_entry_count() const
 {
   if (this->plt_ == NULL)
     return 0;
-  unsigned int count = this->plt_->entry_count();
-  if (this->iplt_ != NULL)
-    count += this->iplt_->entry_count();
-  return count;
+  return this->plt_->entry_count();
 }
 
 // Return the offset of the first non-reserved PLT entry.
@@ -4790,7 +4830,8 @@ template<int size, bool big_endian>
 bool
 Target_powerpc<size, big_endian>::Scan::reloc_needs_plt_for_ifunc(
      Sized_relobj_file<size, big_endian>* object,
-     unsigned int r_type)
+     unsigned int r_type,
+     bool report_err)
 {
   // In non-pic code any reference will resolve to the plt call stub
   // for the ifunc symbol.
@@ -4799,29 +4840,29 @@ Target_powerpc<size, big_endian>::Scan::reloc_needs_plt_for_ifunc(
 
   switch (r_type)
     {
-    // Word size refs from data sections are OK.
+    // Word size refs from data sections are OK, but don't need a PLT entry.
     case elfcpp::R_POWERPC_ADDR32:
     case elfcpp::R_POWERPC_UADDR32:
       if (size == 32)
-       return true;
+       return false;
       break;
 
     case elfcpp::R_PPC64_ADDR64:
     case elfcpp::R_PPC64_UADDR64:
       if (size == 64)
-       return true;
+       return false;
       break;
 
-    // GOT refs are good.
+    // GOT refs are good, but also don't need a PLT entry.
     case elfcpp::R_POWERPC_GOT16:
     case elfcpp::R_POWERPC_GOT16_LO:
     case elfcpp::R_POWERPC_GOT16_HI:
     case elfcpp::R_POWERPC_GOT16_HA:
     case elfcpp::R_PPC64_GOT16_DS:
     case elfcpp::R_PPC64_GOT16_LO_DS:
-      return true;
+      return false;
 
-    // So are function calls.
+    // Function calls are good, and these do need a PLT entry.
     case elfcpp::R_POWERPC_ADDR24:
     case elfcpp::R_POWERPC_ADDR14:
     case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
@@ -4846,7 +4887,8 @@ Target_powerpc<size, big_endian>::Scan::reloc_needs_plt_for_ifunc(
   // writable and non-executable to apply text relocations.  So we'll
   // segfault when trying to run the indirection function to resolve
   // the reloc.
-  gold_error(_("%s: unsupported reloc %u for IFUNC symbol"),
+  if (report_err)
+    gold_error(_("%s: unsupported reloc %u for IFUNC symbol"),
               object->name().c_str(), r_type);
   return false;
 }
@@ -4900,7 +4942,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))
+  if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type, true))
     {
       unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
       target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
@@ -4966,18 +5008,14 @@ Target_powerpc<size, big_endian>::Scan::local(
       if (parameters->options().output_is_position_independent()
          || (size == 64 && is_ifunc))
        {
-         Reloc_section* rela_dyn = target->rela_dyn_section(layout);
-
+         Reloc_section* rela_dyn = target->rela_dyn_section(symtab, layout,
+                                                            is_ifunc);
          if ((size == 32 && r_type == elfcpp::R_POWERPC_ADDR32)
              || (size == 64 && r_type == elfcpp::R_PPC64_ADDR64))
            {
              unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
-             unsigned int dynrel = elfcpp::R_POWERPC_RELATIVE;
-             if (is_ifunc)
-               {
-                 rela_dyn = target->iplt_section()->rel_plt();
-                 dynrel = elfcpp::R_POWERPC_IRELATIVE;
-               }
+             unsigned int dynrel = (is_ifunc ? elfcpp::R_POWERPC_IRELATIVE
+                                    : elfcpp::R_POWERPC_RELATIVE);
              rela_dyn->add_local_relative(object, r_sym, dynrel,
                                           output_section, data_shndx,
                                           reloc.get_r_offset(),
@@ -4997,17 +5035,13 @@ Target_powerpc<size, big_endian>::Scan::local(
     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());
+      if (!is_ifunc)
+       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:
@@ -5073,13 +5107,10 @@ Target_powerpc<size, big_endian>::Scan::local(
            off = got->add_constant(0);
            object->set_local_got_offset(r_sym, GOT_TYPE_STANDARD, off);
 
-           Reloc_section* rela_dyn = target->rela_dyn_section(layout);
-           unsigned int dynrel = elfcpp::R_POWERPC_RELATIVE;
-           if (is_ifunc)
-             {
-               rela_dyn = target->iplt_section()->rel_plt();
-               dynrel = elfcpp::R_POWERPC_IRELATIVE;
-             }
+           Reloc_section* rela_dyn = target->rela_dyn_section(symtab, layout,
+                                                              is_ifunc);
+           unsigned int dynrel = (is_ifunc ? elfcpp::R_POWERPC_IRELATIVE
+                                  : elfcpp::R_POWERPC_RELATIVE);
            rela_dyn->add_local_relative(object, r_sym, dynrel,
                                         got, off, 0, false);
          }
@@ -5260,8 +5291,8 @@ Target_powerpc<size, big_endian>::Scan::global(
     = 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))
+  bool is_ifunc = gsym->type() == elfcpp::STT_GNU_IFUNC;
+  if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type, true))
     {
       target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
                          r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
@@ -5337,11 +5368,14 @@ Target_powerpc<size, big_endian>::Scan::global(
        // Make a PLT entry if necessary.
        if (gsym->needs_plt_entry())
          {
-           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);
+           if (!is_ifunc)
+             {
+               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
@@ -5353,7 +5387,7 @@ Target_powerpc<size, big_endian>::Scan::global(
          }
        // Make a dynamic relocation if necessary.
        if (needs_dynamic_reloc<size>(gsym, Scan::get_reference_flags(r_type))
-           || (size == 64 && gsym->type() == elfcpp::STT_GNU_IFUNC))
+           || (size == 64 && is_ifunc))
          {
            if (gsym->may_need_copy_reloc())
              {
@@ -5370,20 +5404,18 @@ Target_powerpc<size, big_endian>::Scan::global(
                         && (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;
-               if (gsym->type() == elfcpp::STT_GNU_IFUNC)
-                 {
-                   rela_dyn = target->iplt_section()->rel_plt();
-                   dynrel = elfcpp::R_POWERPC_IRELATIVE;
-                 }
+               Reloc_section* rela_dyn
+                 = target->rela_dyn_section(symtab, layout, is_ifunc);
+               unsigned int dynrel = (is_ifunc ? elfcpp::R_POWERPC_IRELATIVE
+                                      : elfcpp::R_POWERPC_RELATIVE);
                rela_dyn->add_symbolless_global_addend(
                    gsym, dynrel, output_section, object, data_shndx,
                    reloc.get_r_offset(), reloc.get_r_addend());
              }
            else
              {
-               Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+               Reloc_section* rela_dyn
+                 = target->rela_dyn_section(symtab, layout, is_ifunc);
                check_non_pic(object, r_type);
                rela_dyn->add_global(gsym, r_type, output_section,
                                     object, data_shndx,
@@ -5396,15 +5428,19 @@ 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(symtab, layout, gsym);
+      if (!is_ifunc)
+       {
+         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(symtab, layout, gsym);
+       }
       // Fall thru
 
     case elfcpp::R_PPC64_REL64:
@@ -5420,7 +5456,8 @@ Target_powerpc<size, big_endian>::Scan::global(
            }
          else
            {
-             Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+             Reloc_section* rela_dyn
+               = target->rela_dyn_section(symtab, layout, is_ifunc);
              check_non_pic(object, r_type);
              rela_dyn->add_global(gsym, r_type, output_section, object,
                                   data_shndx, reloc.get_r_offset(),
@@ -5432,9 +5469,10 @@ Target_powerpc<size, big_endian>::Scan::global(
     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());
+      if (!is_ifunc)
+       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:
@@ -5484,7 +5522,7 @@ Target_powerpc<size, big_endian>::Scan::global(
        got = target->got_section(symtab, layout);
        if (gsym->final_value_is_known())
          {
-           if (size == 32 && gsym->type() == elfcpp::STT_GNU_IFUNC)
+           if (size == 32 && is_ifunc)
              got->add_global_plt(gsym, GOT_TYPE_STANDARD);
            else
              got->add_global(gsym, GOT_TYPE_STANDARD);
@@ -5496,18 +5534,16 @@ Target_powerpc<size, big_endian>::Scan::global(
            unsigned int off = got->add_constant(0);
            gsym->set_got_offset(GOT_TYPE_STANDARD, off);
 
-           Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+           Reloc_section* rela_dyn
+             = target->rela_dyn_section(symtab, layout, is_ifunc);
+
            if (gsym->can_use_relative_reloc(false)
                && !(size == 32
                     && gsym->visibility() == elfcpp::STV_PROTECTED
                     && parameters->options().shared()))
              {
-               unsigned int dynrel = elfcpp::R_POWERPC_RELATIVE;
-               if (gsym->type() == elfcpp::STT_GNU_IFUNC)
-                 {
-                   rela_dyn = target->iplt_section()->rel_plt();
-                   dynrel = elfcpp::R_POWERPC_IRELATIVE;
-                 }
+               unsigned int dynrel = (is_ifunc ? elfcpp::R_POWERPC_IRELATIVE
+                                      : elfcpp::R_POWERPC_RELATIVE);
                rela_dyn->add_global_relative(gsym, dynrel, got, off, 0, false);
              }
            else
@@ -5540,8 +5576,8 @@ Target_powerpc<size, big_endian>::Scan::global(
          {
            Output_data_got_powerpc<size, big_endian>* got
              = target->got_section(symtab, layout);
-           got->add_global_pair_with_rel(gsym, GOT_TYPE_TLSGD,
-                                         target->rela_dyn_section(layout),
+           Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+           got->add_global_pair_with_rel(gsym, GOT_TYPE_TLSGD, rela_dyn,
                                          elfcpp::R_POWERPC_DTPMOD,
                                          elfcpp::R_POWERPC_DTPREL);
          }
@@ -6178,9 +6214,11 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
   Address value = 0;
   bool has_plt_value = false;
   unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
-  if (gsym != NULL
-      ? use_plt_offset<size>(gsym, Scan::get_reference_flags(r_type))
-      : object->local_has_plt_offset(r_sym))
+  if ((gsym != NULL
+       ? use_plt_offset<size>(gsym, Scan::get_reference_flags(r_type))
+       : object->local_has_plt_offset(r_sym))
+      && (!psymval->is_ifunc_symbol()
+         || Scan::reloc_needs_plt_for_ifunc(object, r_type, false)))
     {
       Stub_table<size, big_endian>* stub_table
        = object->stub_table(relinfo->data_shndx);