Beispiel #1
0
/* Helper to size leading part of tv array and set it up. */
static void
set_tv_header (struct ld_plugin_tv *tv)
{
    size_t i;

    /* Version info.  */
    static const unsigned int major = (unsigned)(BFD_VERSION / 100000000UL);
    static const unsigned int minor = (unsigned)(BFD_VERSION / 1000000UL) % 100;

    for (i = 0; i < tv_header_size; i++)
    {
        tv[i].tv_tag = tv_header_tags[i];
#define TVU(x) tv[i].tv_u.tv_ ## x
        switch (tv[i].tv_tag)
        {
        case LDPT_MESSAGE:
            TVU(message) = message;
            break;
        case LDPT_API_VERSION:
            TVU(val) = LD_PLUGIN_API_VERSION;
            break;
        case LDPT_GNU_LD_VERSION:
            TVU(val) = major * 100 + minor;
            break;
        case LDPT_LINKER_OUTPUT:
            TVU(val) = (bfd_link_relocatable (&link_info) ? LDPO_REL
                        : bfd_link_pde (&link_info) ? LDPO_EXEC
                        : bfd_link_pie (&link_info) ? LDPO_PIE
                        : LDPO_DYN);
            break;
        case LDPT_OUTPUT_NAME:
            TVU(string) = output_filename;
            break;
        case LDPT_REGISTER_CLAIM_FILE_HOOK:
            TVU(register_claim_file) = register_claim_file;
            break;
        case LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK:
            TVU(register_all_symbols_read) = register_all_symbols_read;
            break;
        case LDPT_REGISTER_CLEANUP_HOOK:
            TVU(register_cleanup) = register_cleanup;
            break;
        case LDPT_ADD_SYMBOLS:
            TVU(add_symbols) = add_symbols;
            break;
        case LDPT_GET_INPUT_FILE:
            TVU(get_input_file) = get_input_file;
            break;
        case LDPT_GET_VIEW:
            TVU(get_view) = get_view;
            break;
        case LDPT_RELEASE_INPUT_FILE:
            TVU(release_input_file) = release_input_file;
            break;
        case LDPT_GET_SYMBOLS:
            TVU(get_symbols) = get_symbols_v1;
            break;
        case LDPT_GET_SYMBOLS_V2:
            TVU(get_symbols) = get_symbols_v2;
            break;
        case LDPT_ADD_INPUT_FILE:
            TVU(add_input_file) = add_input_file;
            break;
        case LDPT_ADD_INPUT_LIBRARY:
            TVU(add_input_library) = add_input_library;
            break;
        case LDPT_SET_EXTRA_LIBRARY_PATH:
            TVU(set_extra_library_path) = set_extra_library_path;
            break;
        default:
            /* Added a new entry to the array without adding
               a new case to set up its value is a bug.  */
            FAIL ();
        }
#undef TVU
    }
}
Beispiel #2
0
static bfd_boolean
s390_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info,
				    struct elf_link_hash_entry *h)
{
  struct elf_dyn_relocs *p;
  struct elf_link_hash_table *htab;
  struct elf_s390_link_hash_entry *eh = (struct elf_s390_link_hash_entry*)h;
  struct elf_dyn_relocs **head = &eh->dyn_relocs;

  htab = elf_hash_table (info);
  eh->ifunc_resolver_address = h->root.u.def.value;
  eh->ifunc_resolver_section = h->root.u.def.section;

  /* Support garbage collection against STT_GNU_IFUNC symbols.  */
  if (h->plt.refcount <= 0 && h->got.refcount <= 0)
    {
      /* When building shared library, we need to handle the case
	 where it is marked with regular reference, but not non-GOT
	 reference.  It may happen if we didn't see STT_GNU_IFUNC
	 symbol at the time when checking relocations.  */
      if (bfd_link_pic (info)
	  && !h->non_got_ref
	  && h->ref_regular)
	for (p = *head; p != NULL; p = p->next)
	  if (p->count)
	    {
	      h->non_got_ref = 1;
	      goto keep;
	    }

      h->got = htab->init_got_offset;
      h->plt = htab->init_plt_offset;
      *head = NULL;
      return TRUE;
    }

  /* 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;
    }

keep:
  /* Without checking h->plt.refcount here we allocate a PLT slot.
     When setting plt.refcount in check_relocs it might not have been
     known that this will be an IFUNC symol.  */
  h->plt.offset = htab->iplt->size;
  h->needs_plt = 1;
  htab->iplt->size += PLT_ENTRY_SIZE;
  htab->igotplt->size += GOT_ENTRY_SIZE;
  htab->irelplt->size += RELA_ENTRY_SIZE;
  htab->irelplt->reloc_count++;

  /* In order to make pointer equality work with IFUNC symbols defined
     in a non-PIE executable and referenced in a shared lib, we turn
     the symbol into a STT_FUNC symbol and make the symbol value to
     point to the IPLT slot.  That way the referencing shared lib will
     always get the PLT slot address when resolving the respective
     R_390_GLOB_DAT/R_390_64 relocs on that symbol.  */
  if (bfd_link_pde (info)
      && h->def_regular
      && h->ref_dynamic)
    {
      h->root.u.def.section = htab->iplt;
      h->root.u.def.value = h->plt.offset;
      h->size = PLT_ENTRY_SIZE;
      h->type = STT_FUNC;
    }

  if (!bfd_link_pic (info))
    *head = NULL;

  /* Finally, allocate space.  */
  p = *head;
  if (p != NULL)
    {
      bfd_size_type count = 0;
      do
	{
	  count += p->count;
	  p = p->next;
	}
      while (p != NULL);
      htab->irelifunc->size += count * RELA_ENTRY_SIZE;
    }

  /* Decide whether the got.iplt slot can be used.  This has to be
     avoided if the values in the GOT slots could differ for pointer
     equality reasons.  */
  if (h->got.refcount <= 0
      || (bfd_link_pic (info)
	  && (h->dynindx == -1 || h->forced_local))
      || bfd_link_pie (info)
      || htab->sgot == NULL)
    {
      /* Use .got.iplt.  */
      h->got.offset = (bfd_vma) -1;
    }
  else
    {
      h->got.offset = htab->sgot->size;
      htab->sgot->size += GOT_ENTRY_SIZE;
      if (bfd_link_pic (info))
	htab->srelgot->size += RELA_ENTRY_SIZE;
    }

  return TRUE;
}
Beispiel #3
0
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 plt_header_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 (!bfd_link_pic (info)
      && (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);

  /* When building shared library, we need to handle the case where it is
     marked with regular reference, but not non-GOT reference since the
     non-GOT reference bit may not be set here.  */
  if (bfd_link_pic (info) && !h->non_got_ref && h->ref_regular)
    for (p = *head; p != NULL; p = p->next)
      if (p->count)
	{
	  h->non_got_ref = 1;
	  goto keep;
	}

  /* Support garbage collection against STT_GNU_IFUNC symbols.  */
  if (h->plt.refcount <= 0 && h->got.refcount <= 0)
    {
      h->got = htab->init_got_offset;
      h->plt = htab->init_plt_offset;
      *head = NULL;
      return TRUE;
    }

  /* 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;
    }

keep:
  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_header_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 (!bfd_link_pic (info)
      || !h->non_got_ref)
    *head = NULL;

  /* Finally, allocate space.  */
  p = *head;
  if (p != NULL)
    {
      bfd_size_type count = 0;
      do
	{
	  count += p->count;
	  p = p->next;
	}
      while (p != NULL);
      htab->irelifunc->size += count * sizeof_reloc;
    }

  /* For STT_GNU_IFUNC symbol, .got.plt has the real function address
     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 (h->got.refcount <= 0
      || (bfd_link_pic (info)
	  && (h->dynindx == -1
	      || h->forced_local))
      || (!bfd_link_pic (info)
	  && !h->pointer_equality_needed)
      || bfd_link_pie (info)
      || 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 (bfd_link_pic (info))
	htab->srelgot->size += sizeof_reloc;
    }

  return TRUE;
}