From e03a8ed8034fdfbbf7f73ae2fbf7fe29b60c6c5a Mon Sep 17 00:00:00 2001 From: "H.J. Lu" Date: Thu, 18 Jun 2009 00:45:14 +0000 Subject: 2009-06-17 H.J. Lu * elf-bfd.h (_bfd_elf_allocate_ifunc_dyn_relocs): New. * elf-ifunc.c (_bfd_elf_allocate_ifunc_dyn_relocs): Likewise. * elf32-i386.c (elf_i386_allocate_dynrelocs): Use it. * elf64-x86-64.c (elf64_x86_64_allocate_dynrelocs): Likewise. 2009-06-17 H.J. Lu * elf-bfd.h (_bfd_elf_create_ifunc_dyn_reloc): New. * elf-ifunc.c (_bfd_elf_create_ifunc_dyn_reloc): Likewise. * elf32-i386.c (elf_i386_check_relocs): Use it. * elf64-x86-64.c (elf64_x86_64_check_relocs): Likewise. 2009-06-17 H.J. Lu * elf-bfd.h (elf_dyn_relocs): New. * elf32-i386.c (elf_i386_dyn_relocs): Removed. (elf_i386_link_hash_entry): Replace elf_i386_dyn_relocs with elf_dyn_relocs. (elf_i386_copy_indirect_symbol): Likewise. (elf_i386_check_relocs): Likewise. (elf_i386_gc_sweep_hook): Likewise. (elf_i386_allocate_dynrelocs): Likewise. (elf_i386_readonly_dynrelocs): Likewise. (elf_i386_size_dynamic_sections): Likewise. * elf64-x86-64.c (elf64_x86_64_dyn_relocs): Removed. (elf64_x86_64_link_hash_entry): Replace elf64_x86_64_dyn_relocs with elf_dyn_relocs. (elf64_x86_64_copy_indirect_symbol): Updated. (elf64_x86_64_check_relocs): Likewise. (elf64_x86_64_gc_sweep_hook): Likewise. (elf64_x86_64_adjust_dynamic_symbol): Likewise. (elf64_x86_64_allocate_dynrelocs): Likewise. (elf64_x86_64_readonly_dynrelocs): Likewise. (elf64_x86_64_size_dynamic_sections): Likewise. --- bfd/elf-ifunc.c | 182 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) (limited to 'bfd/elf-ifunc.c') diff --git a/bfd/elf-ifunc.c b/bfd/elf-ifunc.c index 4bfc31b..b9b50c3 100644 --- a/bfd/elf-ifunc.c +++ b/bfd/elf-ifunc.c @@ -103,3 +103,185 @@ _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; + } + + /* 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.offset = (bfd_vma) -1; + *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; + + htab = elf_hash_table (info); + + /* 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; +} -- cgit v1.1