OSDN Git Service

2009-12-03 Tristan Gingold <gingold@adacore.com>
[pf3gnuchains/pf3gnuchains3x.git] / bfd / elf-ifunc.c
index 4bfc31b..0de236f 100644 (file)
@@ -103,3 +103,186 @@ _bfd_elf_create_ifunc_sections (bfd *abfd, struct bfd_link_info *info)
 
   return TRUE;
 }
+
+/* For a STT_GNU_IFUNC symbol, create a dynamic reloc section, SRELOC,
+   for the input section, SEC, and append this reloc to HEAD.  */
+
+asection *
+_bfd_elf_create_ifunc_dyn_reloc (bfd *abfd, struct bfd_link_info *info,
+                                asection *sec, asection *sreloc,
+                                struct elf_dyn_relocs **head)
+{
+  struct elf_dyn_relocs *p;
+  struct elf_link_hash_table *htab = elf_hash_table (info);
+
+  if (sreloc == NULL)
+    {
+      const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+
+      if (htab->dynobj == NULL)
+       htab->dynobj = abfd;
+
+      sreloc = _bfd_elf_make_dynamic_reloc_section (sec, htab->dynobj,
+                                                   bed->s->log_file_align,
+                                                   abfd,
+                                                   bed->rela_plts_and_copies_p); 
+      if (sreloc == NULL)
+       return NULL;
+    }
+                     
+  p = *head;
+  if (p == NULL || p->sec != sec)
+    {
+      bfd_size_type amt = sizeof *p;
+
+      p = ((struct elf_dyn_relocs *) bfd_alloc (htab->dynobj, amt));
+      if (p == NULL)
+       return NULL;
+      p->next = *head;
+      *head = p;
+      p->sec = sec;
+      p->count = 0;
+      p->pc_count = 0;
+    }
+  p->count += 1;
+
+  return sreloc;
+}
+
+/* Allocate space in .plt, .got and associated reloc sections for
+   dynamic relocs against a STT_GNU_IFUNC symbol definition.  */
+
+bfd_boolean
+_bfd_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info,
+                                   struct elf_link_hash_entry *h,
+                                   struct elf_dyn_relocs **head,
+                                   unsigned int plt_entry_size,
+                                   unsigned int got_entry_size)
+{
+  asection *plt, *gotplt, *relplt;
+  struct elf_dyn_relocs *p;
+  unsigned int sizeof_reloc;
+  const struct elf_backend_data *bed;
+  struct elf_link_hash_table *htab;
+
+  /* When a shared library references a STT_GNU_IFUNC symbol defined
+     in executable, the address of the resolved function may be used.
+     But in non-shared executable, the address of its .plt slot may
+     be used.  Pointer equality may not work correctly.  PIE should
+     be used if pointer equality is required here.  */
+  if (!info->shared
+      && (h->dynindx != -1
+         || info->export_dynamic)
+      && h->pointer_equality_needed)
+    {
+      info->callbacks->einfo 
+       (_("%F%P: dynamic STT_GNU_IFUNC symbol `%s' with pointer "
+          "equality in `%B' can not be used when making an "
+          "executable; recompile with -fPIE and relink with -pie\n"),
+        h->root.root.string,
+        h->root.u.def.section->owner);
+      bfd_set_error (bfd_error_bad_value);
+      return FALSE;
+    }
+
+  htab = elf_hash_table (info);
+
+  /* Return and discard space for dynamic relocations against it if
+     it is never referenced in a non-shared object.  */
+  if (!h->ref_regular)
+    {
+      if (h->plt.refcount > 0
+         || h->got.refcount > 0)
+       abort ();
+      h->got = htab->init_got_offset;
+      h->plt = htab->init_plt_offset;
+      *head = NULL;
+      return TRUE;
+    }
+
+  bed = get_elf_backend_data (info->output_bfd);
+  if (bed->rela_plts_and_copies_p)
+    sizeof_reloc = bed->s->sizeof_rela;
+  else
+    sizeof_reloc = bed->s->sizeof_rel;
+
+  /* When building a static executable, use .iplt, .igot.plt and
+     .rel[a].iplt sections for STT_GNU_IFUNC symbols.  */
+  if (htab->splt != NULL)
+    {
+      plt = htab->splt;
+      gotplt = htab->sgotplt;
+      relplt = htab->srelplt;
+
+      /* If this is the first .plt entry, make room for the special
+        first entry.  */
+      if (plt->size == 0)
+       plt->size += plt_entry_size;
+    }
+  else
+    {
+      plt = htab->iplt;
+      gotplt = htab->igotplt;
+      relplt = htab->irelplt;
+    }
+
+  /* Don't update value of STT_GNU_IFUNC symbol to PLT.  We need
+     the original value for R_*_IRELATIVE.  */  
+  h->plt.offset = plt->size;
+
+  /* Make room for this entry in the .plt/.iplt section.  */
+  plt->size += plt_entry_size;
+
+  /* We also need to make an entry in the .got.plt/.got.iplt section,
+     which will be placed in the .got section by the linker script.  */
+  gotplt->size += got_entry_size;
+
+  /* We also need to make an entry in the .rel[a].plt/.rel[a].iplt
+     section.  */
+  relplt->size += sizeof_reloc;
+  relplt->reloc_count++;
+
+  /* We need dynamic relocation for STT_GNU_IFUNC symbol only when
+     there is a non-GOT reference in a shared object.  */
+  if (!info->shared
+      || !h->non_got_ref)
+    *head = NULL;
+
+  /* Finally, allocate space.  */
+  for (p = *head; p != NULL; p = p->next)
+    htab->irelifunc->size += p->count * sizeof_reloc;
+
+  /* For STT_GNU_IFUNC symbol, .got.plt has the real function addres
+     and .got has the PLT entry adddress.  We will load the GOT entry
+     with the PLT entry in finish_dynamic_symbol if it is used.  For
+     branch, it uses .got.plt.  For symbol value,
+     1. Use .got.plt in a shared object if it is forced local or not
+     dynamic.
+     2. Use .got.plt in a non-shared object if pointer equality isn't
+     needed.
+     3. Use .got.plt in PIE.
+     4. Use .got.plt if .got isn't used.
+     5. Otherwise use .got so that it can be shared among different
+     objects at run-time.
+     We only need to relocate .got entry in shared object.  */
+  if ((info->shared
+       && (h->dynindx == -1
+          || h->forced_local))
+      || (!info->shared
+         && !h->pointer_equality_needed)
+      || (info->executable && info->shared)
+      || htab->sgot == NULL)
+    {
+      /* Use .got.plt.  */
+      h->got.offset = (bfd_vma) -1;
+    }
+  else
+    {
+      h->got.offset = htab->sgot->size;
+      htab->sgot->size += got_entry_size;
+      if (info->shared)
+       htab->srelgot->size += sizeof_reloc;
+    }
+
+  return TRUE;
+}