X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=bfd%2Felf-ifunc.c;h=1377fedfd3599a07b92adf475cbfe6eeb1815926;hb=57e7d118480f4adf8cf1c8a82f234463d15d509d;hp=81429b84334ca067439f3942e1cfdd1e8b335a08;hpb=7be867377db57e62bc2dc7f0756b484bdac30fda;p=deliverable%2Fbinutils-gdb.git diff --git a/bfd/elf-ifunc.c b/bfd/elf-ifunc.c index 81429b8433..1377fedfd3 100644 --- a/bfd/elf-ifunc.c +++ b/bfd/elf-ifunc.c @@ -1,6 +1,5 @@ /* ELF STT_GNU_IFUNC support. - Copyright 2009 - Free Software Foundation, Inc. + Copyright (C) 2009-2016 Free Software Foundation, Inc. This file is part of BFD, the Binary File Descriptor library. @@ -54,7 +53,7 @@ _bfd_elf_create_ifunc_sections (bfd *abfd, struct bfd_link_info *info) if (bed->plt_readonly) pltflags |= SEC_READONLY; - if (info->shared) + if (bfd_link_pic (info)) { /* We need to create .rel[a].ifunc for shared objects. */ const char *rel_sec = (bed->rela_plts_and_copies_p @@ -104,51 +103,6 @@ _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. */ @@ -156,7 +110,9 @@ bfd_boolean _bfd_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info, struct elf_link_hash_entry *h, struct elf_dyn_relocs **head, + bfd_boolean *readonly_dynrelocs_against_ifunc_p, unsigned int plt_entry_size, + unsigned int plt_header_size, unsigned int got_entry_size) { asection *plt, *gotplt, *relplt; @@ -164,18 +120,19 @@ _bfd_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info, unsigned int sizeof_reloc; const struct elf_backend_data *bed; struct elf_link_hash_table *htab; + bfd_boolean readonly_dynrelocs_against_ifunc; /* 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 + if (!bfd_link_pic (info) && (h->dynindx != -1 || info->export_dynamic) && h->pointer_equality_needed) { - info->callbacks->einfo + 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"), @@ -187,23 +144,20 @@ _bfd_elf_allocate_ifunc_dyn_relocs (struct bfd_link_info *info, 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) { - /* 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 (info->shared - && !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; @@ -241,7 +195,7 @@ keep: /* If this is the first .plt entry, make room for the special first entry. */ if (plt->size == 0) - plt->size += plt_entry_size; + plt->size += plt_header_size; } else { @@ -251,7 +205,7 @@ keep: } /* Don't update value of STT_GNU_IFUNC symbol to PLT. We need - the original value for R_*_IRELATIVE. */ + the original value for R_*_IRELATIVE. */ h->plt.offset = plt->size; /* Make room for this entry in the .plt/.iplt section. */ @@ -268,10 +222,12 @@ keep: /* We need dynamic relocation for STT_GNU_IFUNC symbol only when there is a non-GOT reference in a shared object. */ - if (!info->shared + if (!bfd_link_pic (info) || !h->non_got_ref) *head = NULL; + readonly_dynrelocs_against_ifunc = FALSE; + /* Finally, allocate space. */ p = *head; if (p != NULL) @@ -279,6 +235,12 @@ keep: bfd_size_type count = 0; do { + if (!readonly_dynrelocs_against_ifunc) + { + asection *s = p->sec->output_section; + if (s != NULL && (s->flags & SEC_READONLY) != 0) + readonly_dynrelocs_against_ifunc = TRUE; + } count += p->count; p = p->next; } @@ -286,6 +248,9 @@ keep: htab->irelifunc->size += count * sizeof_reloc; } + if (readonly_dynrelocs_against_ifunc_p) + *readonly_dynrelocs_against_ifunc_p = readonly_dynrelocs_against_ifunc; + /* 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 @@ -299,12 +264,13 @@ keep: 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 + if (h->got.refcount <= 0 + || (bfd_link_pic (info) + && (h->dynindx == -1 + || h->forced_local)) + || (!bfd_link_pic (info) && !h->pointer_equality_needed) - || (info->executable && info->shared) + || bfd_link_pie (info) || htab->sgot == NULL) { /* Use .got.plt. */ @@ -314,9 +280,134 @@ keep: { h->got.offset = htab->sgot->size; htab->sgot->size += got_entry_size; - if (info->shared) + if (bfd_link_pic (info)) htab->srelgot->size += sizeof_reloc; } return TRUE; } + +/* Similar to _bfd_elf_get_synthetic_symtab, optimized for unsorted PLT + entries. PLT is the PLT section. PLT_SYM_VAL is a function pointer + which returns an array of PLT entry symbol values. */ + +long +_bfd_elf_ifunc_get_synthetic_symtab + (bfd *abfd, long symcount ATTRIBUTE_UNUSED, + asymbol **syms ATTRIBUTE_UNUSED, long dynsymcount, asymbol **dynsyms, + asymbol **ret, asection *plt, + bfd_vma *(*get_plt_sym_val) (bfd *, asymbol **, asection *, asection *)) +{ + const struct elf_backend_data *bed = get_elf_backend_data (abfd); + asection *relplt; + asymbol *s; + const char *relplt_name; + bfd_boolean (*slurp_relocs) (bfd *, asection *, asymbol **, bfd_boolean); + arelent *p; + long count, i, n; + size_t size; + Elf_Internal_Shdr *hdr; + char *names; + bfd_vma *plt_sym_val; + + *ret = NULL; + + if (plt == NULL) + return 0; + + if ((abfd->flags & (DYNAMIC | EXEC_P)) == 0) + return 0; + + if (dynsymcount <= 0) + return 0; + + relplt_name = bed->relplt_name; + if (relplt_name == NULL) + relplt_name = bed->rela_plts_and_copies_p ? ".rela.plt" : ".rel.plt"; + relplt = bfd_get_section_by_name (abfd, relplt_name); + if (relplt == NULL) + return 0; + + hdr = &elf_section_data (relplt)->this_hdr; + if (hdr->sh_link != elf_dynsymtab (abfd) + || (hdr->sh_type != SHT_REL && hdr->sh_type != SHT_RELA)) + return 0; + + slurp_relocs = get_elf_backend_data (abfd)->s->slurp_reloc_table; + if (! (*slurp_relocs) (abfd, relplt, dynsyms, TRUE)) + return -1; + + count = relplt->size / hdr->sh_entsize; + size = count * sizeof (asymbol); + p = relplt->relocation; + for (i = 0; i < count; i++, p += bed->s->int_rels_per_ext_rel) + { + size += strlen ((*p->sym_ptr_ptr)->name) + sizeof ("@plt"); + if (p->addend != 0) + { +#ifdef BFD64 + size += sizeof ("+0x") - 1 + 8 + 8 * (bed->s->elfclass == ELFCLASS64); +#else + size += sizeof ("+0x") - 1 + 8; +#endif + } + } + + plt_sym_val = get_plt_sym_val (abfd, dynsyms, plt, relplt); + if (plt_sym_val == NULL) + return -1; + + s = *ret = (asymbol *) bfd_malloc (size); + if (s == NULL) + { + free (plt_sym_val); + return -1; + } + + names = (char *) (s + count); + p = relplt->relocation; + n = 0; + for (i = 0; i < count; i++, p += bed->s->int_rels_per_ext_rel) + { + size_t len; + bfd_vma addr; + + addr = plt_sym_val[i]; + if (addr == (bfd_vma) -1) + continue; + + *s = **p->sym_ptr_ptr; + /* Undefined syms won't have BSF_LOCAL or BSF_GLOBAL set. Since + we are defining a symbol, ensure one of them is set. */ + if ((s->flags & BSF_LOCAL) == 0) + s->flags |= BSF_GLOBAL; + s->flags |= BSF_SYNTHETIC; + s->section = plt; + s->value = addr - plt->vma; + s->name = names; + s->udata.p = NULL; + len = strlen ((*p->sym_ptr_ptr)->name); + memcpy (names, (*p->sym_ptr_ptr)->name, len); + names += len; + if (p->addend != 0) + { + char buf[30], *a; + + memcpy (names, "+0x", sizeof ("+0x") - 1); + names += sizeof ("+0x") - 1; + bfd_sprintf_vma (abfd, buf, p->addend); + for (a = buf; *a == '0'; ++a) + ; + len = strlen (a); + memcpy (names, a, len); + names += len; + } + memcpy (names, "@plt", sizeof ("@plt")); + names += sizeof ("@plt"); + ++s, ++n; + } + + free (plt_sym_val); + + return n; +}