From d8ee9e44cc9a986b063a6ea6c91d39217cce65a1 Mon Sep 17 00:00:00 2001 From: Andreas Krebbel Date: Fri, 16 Oct 2015 21:49:43 +0200 Subject: [PATCH] S/390: ifunc: Fix function pointers to hidden ifunc symbols. bfd/ChangeLog: * elf32-s390.c (elf_s390_adjust_dynamic_symbol): Set the PLT reference counters for local IFUNC calls. * elf64-s390.c (elf_s390_adjust_dynamic_symbol): Likewise. --- bfd/ChangeLog | 6 ++++++ bfd/elf32-s390.c | 42 +++++++++++++++++++++++++++++++++++++++++- bfd/elf64-s390.c | 42 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 88 insertions(+), 2 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 86a8ce818aa..b7f42cd6d3c 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,9 @@ +2015-10-22 Andreas Krebbel + + * elf32-s390.c (elf_s390_adjust_dynamic_symbol): Set the PLT + reference counters for local IFUNC calls. + * elf64-s390.c (elf_s390_adjust_dynamic_symbol): Likewise. + 2015-10-22 Andreas Krebbel * elf32-s390.c (elf_s390_check_relocs): Fallthrough to the PLT diff --git a/bfd/elf32-s390.c b/bfd/elf32-s390.c index fcdade0461e..d154fb78aff 100644 --- a/bfd/elf32-s390.c +++ b/bfd/elf32-s390.c @@ -1658,7 +1658,47 @@ elf_s390_adjust_dynamic_symbol (struct bfd_link_info *info, /* STT_GNU_IFUNC symbol must go through PLT. */ if (s390_is_ifunc_symbol_p (h)) - return TRUE; + { + /* All local STT_GNU_IFUNC references must be treated as local + calls via local PLT. */ + if (h->ref_regular && SYMBOL_CALLS_LOCAL (info, h)) + { + bfd_size_type pc_count = 0, count = 0; + struct elf_dyn_relocs **pp; + struct elf_s390_link_hash_entry *eh; + struct elf_dyn_relocs *p; + + eh = (struct elf_s390_link_hash_entry *) h; + for (pp = &eh->dyn_relocs; (p = *pp) != NULL; ) + { + pc_count += p->pc_count; + p->count -= p->pc_count; + p->pc_count = 0; + count += p->count; + if (p->count == 0) + *pp = p->next; + else + pp = &p->next; + } + + if (pc_count || count) + { + h->needs_plt = 1; + h->non_got_ref = 1; + if (h->plt.refcount <= 0) + h->plt.refcount = 1; + else + h->plt.refcount += 1; + } + } + + if (h->plt.refcount <= 0) + { + h->plt.offset = (bfd_vma) -1; + h->needs_plt = 0; + } + return TRUE; + } /* If this is a function, put it in the procedure linkage table. We will fill in the contents of the procedure linkage table later diff --git a/bfd/elf64-s390.c b/bfd/elf64-s390.c index 588892fb8cf..2b6227148d2 100644 --- a/bfd/elf64-s390.c +++ b/bfd/elf64-s390.c @@ -1592,7 +1592,47 @@ elf_s390_adjust_dynamic_symbol (struct bfd_link_info *info, /* STT_GNU_IFUNC symbol must go through PLT. */ if (s390_is_ifunc_symbol_p (h)) - return TRUE; + { + /* All local STT_GNU_IFUNC references must be treated as local + calls via local PLT. */ + if (h->ref_regular && SYMBOL_CALLS_LOCAL (info, h)) + { + bfd_size_type pc_count = 0, count = 0; + struct elf_dyn_relocs **pp; + struct elf_s390_link_hash_entry *eh; + struct elf_dyn_relocs *p; + + eh = (struct elf_s390_link_hash_entry *) h; + for (pp = &eh->dyn_relocs; (p = *pp) != NULL; ) + { + pc_count += p->pc_count; + p->count -= p->pc_count; + p->pc_count = 0; + count += p->count; + if (p->count == 0) + *pp = p->next; + else + pp = &p->next; + } + + if (pc_count || count) + { + h->needs_plt = 1; + h->non_got_ref = 1; + if (h->plt.refcount <= 0) + h->plt.refcount = 1; + else + h->plt.refcount += 1; + } + } + + if (h->plt.refcount <= 0) + { + h->plt.offset = (bfd_vma) -1; + h->needs_plt = 0; + } + return TRUE; + } /* If this is a function, put it in the procedure linkage table. We will fill in the contents of the procedure linkage table later -- 2.30.2