+/* Handles decrementing dynamic reloc counts for the reloc specified by
+ R_INFO in section SEC. If LOCAL_SYMS is NULL, then H and SYM_SEC
+ have already been determined. */
+
+static bfd_boolean
+dec_dynrel_count (bfd_vma r_info,
+ asection *sec,
+ struct bfd_link_info *info,
+ Elf_Internal_Sym **local_syms,
+ struct elf_link_hash_entry *h,
+ asection *sym_sec)
+{
+ enum elf_ppc64_reloc_type r_type;
+ struct ppc_dyn_relocs *p;
+ struct ppc_dyn_relocs **pp;
+
+ /* Can this reloc be dynamic? This switch, and later tests here
+ should be kept in sync with the code in check_relocs. */
+ r_type = ELF64_R_TYPE (r_info);
+ switch (r_type)
+ {
+ default:
+ return TRUE;
+
+ case R_PPC64_TPREL16:
+ case R_PPC64_TPREL16_LO:
+ case R_PPC64_TPREL16_HI:
+ case R_PPC64_TPREL16_HA:
+ case R_PPC64_TPREL16_DS:
+ case R_PPC64_TPREL16_LO_DS:
+ case R_PPC64_TPREL16_HIGHER:
+ case R_PPC64_TPREL16_HIGHERA:
+ case R_PPC64_TPREL16_HIGHEST:
+ case R_PPC64_TPREL16_HIGHESTA:
+ if (!info->shared)
+ return TRUE;
+
+ case R_PPC64_TPREL64:
+ case R_PPC64_DTPMOD64:
+ case R_PPC64_DTPREL64:
+ case R_PPC64_ADDR64:
+ case R_PPC64_REL30:
+ case R_PPC64_REL32:
+ case R_PPC64_REL64:
+ case R_PPC64_ADDR14:
+ case R_PPC64_ADDR14_BRNTAKEN:
+ case R_PPC64_ADDR14_BRTAKEN:
+ case R_PPC64_ADDR16:
+ case R_PPC64_ADDR16_DS:
+ case R_PPC64_ADDR16_HA:
+ case R_PPC64_ADDR16_HI:
+ case R_PPC64_ADDR16_HIGHER:
+ case R_PPC64_ADDR16_HIGHERA:
+ case R_PPC64_ADDR16_HIGHEST:
+ case R_PPC64_ADDR16_HIGHESTA:
+ case R_PPC64_ADDR16_LO:
+ case R_PPC64_ADDR16_LO_DS:
+ case R_PPC64_ADDR24:
+ case R_PPC64_ADDR32:
+ case R_PPC64_UADDR16:
+ case R_PPC64_UADDR32:
+ case R_PPC64_UADDR64:
+ case R_PPC64_TOC:
+ break;
+ }
+
+ if (local_syms != NULL)
+ {
+ unsigned long r_symndx;
+ Elf_Internal_Sym *sym;
+ bfd *ibfd = sec->owner;
+
+ r_symndx = ELF64_R_SYM (r_info);
+ if (!get_sym_h (&h, &sym, &sym_sec, NULL, local_syms, r_symndx, ibfd))
+ return FALSE;
+ }
+
+ if ((info->shared
+ && (MUST_BE_DYN_RELOC (r_type)
+ || (h != NULL
+ && (!info->symbolic
+ || h->root.type == bfd_link_hash_defweak
+ || !h->def_regular))))
+ || (ELIMINATE_COPY_RELOCS
+ && !info->shared
+ && h != NULL
+ && (h->root.type == bfd_link_hash_defweak
+ || !h->def_regular)))
+ ;
+ else
+ return TRUE;
+
+ if (h != NULL)
+ pp = &((struct ppc_link_hash_entry *) h)->dyn_relocs;
+ else
+ {
+ if (sym_sec != NULL)
+ {
+ void *vpp = &elf_section_data (sym_sec)->local_dynrel;
+ pp = (struct ppc_dyn_relocs **) vpp;
+ }
+ else
+ {
+ void *vpp = &elf_section_data (sec)->local_dynrel;
+ pp = (struct ppc_dyn_relocs **) vpp;
+ }
+
+ /* elf_gc_sweep may have already removed all dyn relocs associated
+ with local syms for a given section. Don't report a dynreloc
+ miscount. */
+ if (*pp == NULL)
+ return TRUE;
+ }
+
+ while ((p = *pp) != NULL)
+ {
+ if (p->sec == sec)
+ {
+ if (!MUST_BE_DYN_RELOC (r_type))
+ p->pc_count -= 1;
+ p->count -= 1;
+ if (p->count == 0)
+ *pp = p->next;
+ return TRUE;
+ }
+ pp = &p->next;
+ }
+
+ (*_bfd_error_handler) (_("dynreloc miscount for %B, section %A"),
+ sec->owner, sec);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+}
+