DT_TEXTREL vs IFUNC
authorAlan Modra <amodra@gmail.com>
Thu, 16 Mar 2017 22:07:15 +0000 (08:37 +1030)
committerAlan Modra <amodra@gmail.com>
Fri, 17 Mar 2017 22:08:15 +0000 (08:38 +1030)
If you should somehow link non-pic objects into a PIE or shared
library, resulting in an object with DT_TEXTREL (text relocations)
set, and your executable or shared library also contains GNU indirect
functions, then you're in trouble.  To apply dynamic relocations
ld.so will make the text segment writable.  On most systems this will
make the text segment non-executable, which will then result in a
segfault when ld.so tries to run ifunc resolvers when applying
relocations against ifuncs.

This patch teaches PowerPC ld to detect the situation, and warn.

* elf64-ppc.c (struct ppc_link_hash_table): Add
local_ifunc_resolver and maybe_local_ifunc_resolver.
(ppc_build_one_stub): Set flags on emitting dynamic
relocation to ifunc.
(ppc64_elf_relocate_section): Likewise.
(ppc64_elf_finish_dynamic_symbol): Likewise.
(ppc64_elf_finish_dynamic_sections): Error on DT_TEXTREL with
local dynamic relocs to ifuncs.
* elf32-ppc.c (struct ppc_elf_link_hash_table): Add
local_ifunc_resolver and maybe_local_ifunc_resolver.
(ppc_elf_relocate_section): Set flag on emitting dynamic
relocation to ifuncs.
(ppc_elf_finish_dynamic_symbol): Likewise.
(ppc_elf_finish_dynamic_sections): Error on DT_TEXTREL with local
dynamic relocs to ifuncs.

bfd/ChangeLog
bfd/elf32-ppc.c
bfd/elf64-ppc.c

index 664285b813f0a0b5a4c2167e80076510f9c321be..5dea352b44df2aadb587cf8d0b73aad967c36380 100644 (file)
@@ -1,3 +1,21 @@
+2017-03-18  Alan Modra  <amodra@gmail.com>
+
+       * elf64-ppc.c (struct ppc_link_hash_table): Add
+       local_ifunc_resolver and maybe_local_ifunc_resolver.
+       (ppc_build_one_stub): Set flags on emitting dynamic
+       relocation to ifunc.
+       (ppc64_elf_relocate_section): Likewise.
+       (ppc64_elf_finish_dynamic_symbol): Likewise.
+       (ppc64_elf_finish_dynamic_sections): Error on DT_TEXTREL with
+       local dynamic relocs to ifuncs.
+       * elf32-ppc.c (struct ppc_elf_link_hash_table): Add
+       local_ifunc_resolver and maybe_local_ifunc_resolver.
+       (ppc_elf_relocate_section): Set flag on emitting dynamic
+       relocation to ifuncs.
+       (ppc_elf_finish_dynamic_symbol): Likewise.
+       (ppc_elf_finish_dynamic_sections): Error on DT_TEXTREL with local
+       dynamic relocs to ifuncs.
+
 2017-03-13  Nick Clifton  <nickc@redhat.com>
 
        PR binutils/21202
index 72ead480c29db04a5f0335f5aafdb75932ccf4e9..4bd58e2696efaf6a6ccc9fa4e3af5a93be778761 100644 (file)
@@ -3327,6 +3327,11 @@ struct ppc_elf_link_hash_table
   /* True if the target system is VxWorks.  */
   unsigned int is_vxworks:1;
 
+  /* Whether there exist local gnu indirect function resolvers,
+     referenced by dynamic relocations.  */
+  unsigned int local_ifunc_resolver:1;
+  unsigned int maybe_local_ifunc_resolver:1;
+
   /* The size of PLT entries.  */
   int plt_entry_size;
   /* The distance between adjacent PLT slots.  */
@@ -8386,6 +8391,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
                  loc += (htab->elf.irelplt->reloc_count++
                          * sizeof (Elf32_External_Rela));
                  bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
+                 htab->local_ifunc_resolver = 1;
 
                  ent->plt.offset |= 1;
                }
@@ -8574,7 +8580,13 @@ ppc_elf_relocate_section (bfd *output_bfd,
                        bfd_byte * loc;
 
                        if (ifunc != NULL)
-                         rsec = htab->elf.irelplt;
+                         {
+                           rsec = htab->elf.irelplt;
+                           if (indx == 0)
+                             htab->local_ifunc_resolver = 1;
+                           else if (is_static_defined (h))
+                             htab->maybe_local_ifunc_resolver = 1;
+                         }
                        outrel.r_offset = (htab->elf.sgot->output_section->vma
                                           + htab->elf.sgot->output_offset
                                           + off);
@@ -8867,6 +8879,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
              int skip;
              bfd_byte *loc;
              asection *sreloc;
+             long indx = 0;
+
 #ifdef DEBUG
              fprintf (stderr, "ppc_elf_relocate_section needs to "
                       "create relocation for %s\n",
@@ -8877,12 +8891,6 @@ ppc_elf_relocate_section (bfd *output_bfd,
              /* When generating a shared object, these relocations
                 are copied into the output file to be resolved at run
                 time.  */
-             sreloc = elf_section_data (input_section)->sreloc;
-             if (ifunc)
-               sreloc = htab->elf.irelplt;
-             if (sreloc == NULL)
-               return FALSE;
-
              skip = 0;
              outrel.r_offset = _bfd_elf_section_offset (output_bfd, info,
                                                         input_section,
@@ -8900,9 +8908,10 @@ ppc_elf_relocate_section (bfd *output_bfd,
                            || h->root.type == bfd_link_hash_undefweak))
                       || !SYMBOL_REFERENCES_LOCAL (info, h))
                {
-                 BFD_ASSERT (h->dynindx != -1);
+                 indx = h->dynindx;
+                 BFD_ASSERT (indx != -1);
                  unresolved_reloc = FALSE;
-                 outrel.r_info = ELF32_R_INFO (h->dynindx, r_type);
+                 outrel.r_info = ELF32_R_INFO (indx, r_type);
                  outrel.r_addend = rel->r_addend;
                }
              else
@@ -8911,8 +8920,6 @@ ppc_elf_relocate_section (bfd *output_bfd,
 
                  if (r_type != R_PPC_ADDR32)
                    {
-                     long indx = 0;
-
                      if (ifunc != NULL)
                        {
                          /* If we get here when building a static
@@ -8977,6 +8984,18 @@ ppc_elf_relocate_section (bfd *output_bfd,
                    outrel.r_info = ELF32_R_INFO (0, R_PPC_RELATIVE);
                }
 
+             sreloc = elf_section_data (input_section)->sreloc;
+             if (ifunc)
+               {
+                 sreloc = htab->elf.irelplt;
+                 if (indx == 0)
+                   htab->local_ifunc_resolver = 1;
+                 else if (is_static_defined (h))
+                   htab->maybe_local_ifunc_resolver = 1;
+               }
+             if (sreloc == NULL)
+               return FALSE;
+
              loc = sreloc->contents;
              loc += sreloc->reloc_count++ * sizeof (Elf32_External_Rela);
              bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
@@ -10267,12 +10286,19 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
 
            if (!htab->elf.dynamic_sections_created
                || h->dynindx == -1)
-             loc = (htab->elf.irelplt->contents
-                    + (htab->elf.irelplt->reloc_count++
-                       * sizeof (Elf32_External_Rela)));
+             {
+               loc = (htab->elf.irelplt->contents
+                      + (htab->elf.irelplt->reloc_count++
+                         * sizeof (Elf32_External_Rela)));
+               htab->local_ifunc_resolver = 1;
+             }
            else
-             loc = (htab->elf.srelplt->contents
-                    + reloc_index * sizeof (Elf32_External_Rela));
+             {
+               loc = (htab->elf.srelplt->contents
+                      + reloc_index * sizeof (Elf32_External_Rela));
+               if (h->type == STT_GNU_IFUNC && is_static_defined (h))
+                 htab->maybe_local_ifunc_resolver = 1;
+             }
            bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
 
            if (!h->def_regular)
@@ -10477,6 +10503,17 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
              dyn.d_un.d_ptr = got;
              break;
 
+           case DT_TEXTREL:
+             if (htab->local_ifunc_resolver)
+               info->callbacks->einfo
+                 (_("%X%P: text relocations and GNU indirect "
+                    "functions will result in a segfault at runtime\n"));
+             else if (htab->maybe_local_ifunc_resolver)
+               info->callbacks->einfo
+                 (_("%P: warning: text relocations and GNU indirect "
+                    "functions may result in a segfault at runtime\n"));
+             continue;
+
            default:
              if (htab->is_vxworks
                  && elf_vxworks_finish_dynamic_entry (output_bfd, &dyn))
index c9a3f2121bb23c1865db3133fd0bb01e7033870e..1193c1f5d798406f308c1733854d00e988ae0895 100644 (file)
@@ -4105,6 +4105,11 @@ struct ppc_link_hash_table
   /* Whether func_desc_adjust needs to be run over symbols.  */
   unsigned int need_func_desc_adj:1;
 
+  /* Whether there exist local gnu indirect function resolvers,
+     referenced by dynamic relocations.  */
+  unsigned int local_ifunc_resolver:1;
+  unsigned int maybe_local_ifunc_resolver:1;
+
   /* Incremented every time we size stubs.  */
   unsigned int stub_iteration;
 
@@ -11164,6 +11169,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
                   * sizeof (Elf64_External_Rela)));
          bfd_elf64_swap_reloca_out (info->output_bfd, &rela, rl);
          stub_entry->plt_ent->plt.offset |= 1;
+         htab->local_ifunc_resolver = 1;
        }
 
       off = (dest
@@ -14485,7 +14491,13 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                         ? h->elf.type == STT_GNU_IFUNC
                         : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC);
                if (ifunc)
-                 relgot = htab->elf.irelplt;
+                 {
+                   relgot = htab->elf.irelplt;
+                   if (indx == 0)
+                     htab->local_ifunc_resolver = 1;
+                   else if (is_static_defined (&h->elf))
+                     htab->maybe_local_ifunc_resolver = 1;
+                 }
                else if (indx != 0
                         || (bfd_link_pic (info)
                             && (h == NULL
@@ -14815,6 +14827,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              bfd_boolean skip, relocate;
              asection *sreloc;
              bfd_vma out_off;
+             long indx = 0;
 
              /* When generating a dynamic object, these relocations
                 are copied into the output file to be resolved at run
@@ -14851,8 +14864,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                       && !is_opd
                       && r_type != R_PPC64_TOC)
                {
-                 BFD_ASSERT (h->elf.dynindx != -1);
-                 outrel.r_info = ELF64_R_INFO (h->elf.dynindx, r_type);
+                 indx = h->elf.dynindx;
+                 BFD_ASSERT (indx != -1);
+                 outrel.r_info = ELF64_R_INFO (indx, r_type);
                }
              else
                {
@@ -14895,8 +14909,6 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                    }
                  else
                    {
-                     long indx = 0;
-
                      if (h != NULL
                          ? h->elf.type == STT_GNU_IFUNC
                          : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
@@ -14951,7 +14963,13 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              if (h != NULL
                  ? h->elf.type == STT_GNU_IFUNC
                  : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
-               sreloc = htab->elf.irelplt;
+               {
+                 sreloc = htab->elf.irelplt;
+                 if (indx == 0)
+                   htab->local_ifunc_resolver = 1;
+                 else if (is_static_defined (&h->elf))
+                   htab->maybe_local_ifunc_resolver = 1;
+               }
              if (sreloc == NULL)
                abort ();
 
@@ -15437,6 +15455,7 @@ ppc64_elf_finish_dynamic_symbol (bfd *output_bfd,
            loc = (htab->elf.irelplt->contents
                   + (htab->elf.irelplt->reloc_count++
                      * sizeof (Elf64_External_Rela)));
+           htab->local_ifunc_resolver = 1;
          }
        else
          {
@@ -15448,6 +15467,8 @@ ppc64_elf_finish_dynamic_symbol (bfd *output_bfd,
            loc = (htab->elf.srelplt->contents
                   + ((ent->plt.offset - PLT_INITIAL_ENTRY_SIZE (htab))
                      / PLT_ENTRY_SIZE (htab) * sizeof (Elf64_External_Rela)));
+           if (h->type == STT_GNU_IFUNC && is_static_defined (h))
+             htab->maybe_local_ifunc_resolver = 1;
          }
        bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
 
@@ -15613,6 +15634,17 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
            case DT_PLTRELSZ:
              dyn.d_un.d_val = htab->elf.srelplt->size;
              break;
+
+           case DT_TEXTREL:
+             if (htab->local_ifunc_resolver)
+               info->callbacks->einfo
+                 (_("%X%P: text relocations and GNU indirect "
+                    "functions will result in a segfault at runtime\n"));
+             else if (htab->maybe_local_ifunc_resolver)
+               info->callbacks->einfo
+                 (_("%P: warning: text relocations and GNU indirect "
+                    "functions may result in a segfault at runtime\n"));
+             continue;
            }
 
          bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon);