2009-06-05 Doug Kwan <dougkwan@google.com>
[deliverable/binutils-gdb.git] / bfd / elf64-x86-64.c
index 86715865b6a166b6b0e1b01a04219ba24cf06df4..041e28519e83e2a567e4e854de0a31308181e628 100644 (file)
@@ -1126,6 +1126,7 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
                case R_X86_64_GOTPCREL:
                case R_X86_64_GOTPCREL64:
+                 h->got.refcount += 1;
                  if (htab->sgot == NULL
                      && !elf64_x86_64_create_got_section (htab->elf.dynobj,
                                                           info))
@@ -1792,9 +1793,6 @@ elf64_x86_64_allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
          return TRUE;
        }
 
-      if (h->plt.refcount <= 0)
-       abort ();
-
       /* When building a static executable, use .iplt, .igot.plt and
         .rela.iplt sections for STT_GNU_IFUNC symbols.  */
       if (htab->splt != 0)
@@ -1837,8 +1835,29 @@ elf64_x86_64_allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
       if (h->dynindx == -1 || h->forced_local)
        eh->dyn_relocs = NULL;
 
-      /* STT_GNU_IFUNC symbol uses .got.plt, not .got.  */
-      h->got.refcount = 0;
+      /* STT_GNU_IFUNC symbol uses .got.plt, not .got.  But for
+        shared library, we must go through GOT and we can't
+        use R_X86_64_IRELATIVE unless it is forced local.   */
+      if (info->executable
+         || info->symbolic
+         || h->forced_local)
+       {
+         if (h->pointer_equality_needed
+             && htab->sgot != NULL)
+           {
+             /* We can't use .got.plt, which contains the real
+                function addres, since we need pointer equality.
+                We will load the GOT entry with the PLT entry
+                in elf64_x86_64_finish_dynamic_symbol and don't
+                need GOT relocation.  */
+             h->got.offset = htab->sgot->size;
+             htab->sgot->size += GOT_ENTRY_SIZE;
+             eh->tlsdesc_got = (bfd_vma) -1;
+             goto skip_relgot;
+           }
+         else
+           h->got.refcount = 0;
+       }
     }
   else if (htab->elf.dynamic_sections_created
           && h->plt.refcount > 0)
@@ -1965,6 +1984,7 @@ elf64_x86_64_allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
   else
     h->got.offset = (bfd_vma) -1;
 
+skip_relgot:
   if (eh->dyn_relocs == NULL)
     return TRUE;
 
@@ -2616,49 +2636,51 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
              base_got = htab->sgot;
              off = h->got.offset;
 
-             if (base_got == NULL
-                 || off != (bfd_vma) -1)
+             if (base_got == NULL)
                abort ();
 
-             /* We can't use h->got.offset here to save state, or
-                even just remember the offset, as finish_dynamic_symbol
-                would use that as offset into .got.  */
-
-             if (htab->splt != NULL)
+             if (off == (bfd_vma) -1)
                {
-                 plt_index = h->plt.offset / PLT_ENTRY_SIZE - 1;
-                 off = (plt_index + 3) * GOT_ENTRY_SIZE;
-                 base_got = htab->sgotplt;
-               }
-             else
-               {
-                 plt_index = h->plt.offset / PLT_ENTRY_SIZE;
-                 off = plt_index * GOT_ENTRY_SIZE;
-                 base_got = htab->igotplt;
-               }
+                 /* We can't use h->got.offset here to save state, or
+                    even just remember the offset, as finish_dynamic_symbol
+                    would use that as offset into .got.  */
 
-             if (h->dynindx == -1
-                 || h->forced_local
-                 || info->symbolic)
-               {
-                 /* This references the local defitionion.  We must 
-                    initialize this entry in the global offset table.
-                    Since the offset must always be a multiple of 8, we
-                    use the least significant bit to record whether we
-                    have initialized it already.
-
-                    When doing a dynamic link, we create a .rela.got
-                    relocation entry to initialize the value.  This is
-                    done in the finish_dynamic_symbol routine.  */
-                 if ((off & 1) != 0)
-                   off &= ~1;
+                 if (htab->splt != NULL)
+                   {
+                     plt_index = h->plt.offset / PLT_ENTRY_SIZE - 1;
+                     off = (plt_index + 3) * GOT_ENTRY_SIZE;
+                     base_got = htab->sgotplt;
+                   }
                  else
                    {
-                     bfd_put_64 (output_bfd, relocation,
-                                 base_got->contents + off);
-                     /* Note that this is harmless for the GOTPLT64 case,
-                        as -1 | 1 still is -1.  */
-                     h->got.offset |= 1;
+                     plt_index = h->plt.offset / PLT_ENTRY_SIZE;
+                     off = plt_index * GOT_ENTRY_SIZE;
+                     base_got = htab->igotplt;
+                   }
+
+                 if (h->dynindx == -1
+                     || h->forced_local
+                     || info->symbolic)
+                   {
+                     /* This references the local defitionion.  We must 
+                        initialize this entry in the global offset table.
+                        Since the offset must always be a multiple of 8, 
+                        we use the least significant bit to record
+                        whether we have initialized it already.
+
+                        When doing a dynamic link, we create a .rela.got
+                        relocation entry to initialize the value.  This
+                        is done in the finish_dynamic_symbol routine.   */
+                     if ((off & 1) != 0)
+                       off &= ~1;
+                     else
+                       {
+                         bfd_put_64 (output_bfd, relocation,
+                                     base_got->contents + off);
+                         /* Note that this is harmless for the GOTPLT64
+                            case, as -1 | 1 still is -1.  */
+                         h->got.offset |= 1;
+                       }
                    }
                }
 
@@ -3719,8 +3741,28 @@ elf64_x86_64_finish_dynamic_symbol (bfd *output_bfd,
         of a version file, we just want to emit a RELATIVE reloc.
         The entry in the global offset table will already have been
         initialized in the relocate_section function.  */
-      if (info->shared
-         && SYMBOL_REFERENCES_LOCAL (info, h))
+      if ((info->executable
+          || info->symbolic
+          || h->forced_local)
+         && h->def_regular
+         && h->pointer_equality_needed
+         && h->type == STT_GNU_IFUNC)
+       {
+         /* The STT_GNU_IFUNC symbol is locally defined.  But we can't
+            use .got.plt, which contains the real function addres,
+            since we need pointer equality.  We load the GOT entry
+            with the PLT entry without relocation.  */
+         asection *plt = htab->splt ? htab->splt : htab->iplt;
+         if (htab->sgot == NULL
+             || h->plt.offset == (bfd_vma) -1)
+           abort ();
+         bfd_put_64 (output_bfd, (plt->output_section->vma
+                                  + plt->output_offset + h->plt.offset),
+                     htab->sgot->contents + h->got.offset);
+         return TRUE;
+       }
+      else if (info->shared
+              && SYMBOL_REFERENCES_LOCAL (info, h))
        {
          if (!h->def_regular)
            return FALSE;
This page took 0.026456 seconds and 4 git commands to generate.