+ ret->sdata[0].name = ".sdata";
+ ret->sdata[0].sym_name = "_SDA_BASE_";
+ ret->sdata[0].bss_name = ".sbss";
+
+ ret->sdata[1].name = ".sdata2";
+ ret->sdata[1].sym_name = "_SDA2_BASE_";
+ ret->sdata[1].bss_name = ".sbss2";
+
+ ret->plt_entry_size = 12;
+ ret->plt_slot_size = 8;
+ ret->plt_initial_entry_size = 72;
+
+ return &ret->elf.root;
+}
+
+/* Create .got and the related sections. */
+
+static bfd_boolean
+ppc_elf_create_got (bfd *abfd, struct bfd_link_info *info)
+{
+ struct ppc_elf_link_hash_table *htab;
+ asection *s;
+ flagword flags;
+
+ if (!_bfd_elf_create_got_section (abfd, info))
+ return FALSE;
+
+ htab = ppc_elf_hash_table (info);
+ htab->got = s = bfd_get_linker_section (abfd, ".got");
+ if (s == NULL)
+ abort ();
+
+ if (htab->is_vxworks)
+ {
+ htab->sgotplt = bfd_get_linker_section (abfd, ".got.plt");
+ if (!htab->sgotplt)
+ abort ();
+ }
+ else
+ {
+ /* The powerpc .got has a blrl instruction in it. Mark it
+ executable. */
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS
+ | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+ if (!bfd_set_section_flags (abfd, s, flags))
+ return FALSE;
+ }
+
+ htab->relgot = bfd_get_linker_section (abfd, ".rela.got");
+ if (!htab->relgot)
+ abort ();
+
+ return TRUE;
+}
+
+static bfd_boolean
+ppc_elf_create_glink (bfd *abfd, struct bfd_link_info *info)
+{
+ struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info);
+ asection *s;
+ flagword flags;
+
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_READONLY | SEC_HAS_CONTENTS
+ | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+ s = bfd_make_section_anyway_with_flags (abfd, ".glink", flags);
+ htab->glink = s;
+ if (s == NULL
+ || !bfd_set_section_alignment (abfd, s, 4))
+ return FALSE;
+
+ if (!info->no_ld_generated_unwind_info)
+ {
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS
+ | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+ s = bfd_make_section_anyway_with_flags (abfd, ".eh_frame", flags);
+ htab->glink_eh_frame = s;
+ if (s == NULL
+ || !bfd_set_section_alignment (abfd, s, 2))
+ return FALSE;
+ }
+
+ flags = SEC_ALLOC | SEC_LINKER_CREATED;
+ s = bfd_make_section_anyway_with_flags (abfd, ".iplt", flags);
+ htab->iplt = s;
+ if (s == NULL
+ || !bfd_set_section_alignment (abfd, s, 4))
+ return FALSE;
+
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS
+ | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+ s = bfd_make_section_anyway_with_flags (abfd, ".rela.iplt", flags);
+ htab->reliplt = s;
+ if (s == NULL
+ || ! bfd_set_section_alignment (abfd, s, 2))
+ return FALSE;
+ return TRUE;
+}
+
+/* We have to create .dynsbss and .rela.sbss here so that they get mapped
+ to output sections (just like _bfd_elf_create_dynamic_sections has
+ to create .dynbss and .rela.bss). */
+
+static bfd_boolean
+ppc_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
+{
+ struct ppc_elf_link_hash_table *htab;
+ asection *s;
+ flagword flags;
+
+ htab = ppc_elf_hash_table (info);
+
+ if (htab->got == NULL
+ && !ppc_elf_create_got (abfd, info))
+ return FALSE;
+
+ if (!_bfd_elf_create_dynamic_sections (abfd, info))
+ return FALSE;
+
+ if (htab->glink == NULL
+ && !ppc_elf_create_glink (abfd, info))
+ return FALSE;
+
+ htab->dynbss = bfd_get_linker_section (abfd, ".dynbss");
+ s = bfd_make_section_anyway_with_flags (abfd, ".dynsbss",
+ SEC_ALLOC | SEC_LINKER_CREATED);
+ htab->dynsbss = s;
+ if (s == NULL)
+ return FALSE;
+
+ if (! info->shared)
+ {
+ htab->relbss = bfd_get_linker_section (abfd, ".rela.bss");
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS
+ | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+ s = bfd_make_section_anyway_with_flags (abfd, ".rela.sbss", flags);
+ htab->relsbss = s;
+ if (s == NULL
+ || ! bfd_set_section_alignment (abfd, s, 2))
+ return FALSE;
+ }
+
+ if (htab->is_vxworks
+ && !elf_vxworks_create_dynamic_sections (abfd, info, &htab->srelplt2))
+ return FALSE;
+
+ htab->relplt = bfd_get_linker_section (abfd, ".rela.plt");
+ htab->plt = s = bfd_get_linker_section (abfd, ".plt");
+ if (s == NULL)
+ abort ();
+
+ flags = SEC_ALLOC | SEC_CODE | SEC_LINKER_CREATED;
+ if (htab->plt_type == PLT_VXWORKS)
+ /* The VxWorks PLT is a loaded section with contents. */
+ flags |= SEC_HAS_CONTENTS | SEC_LOAD | SEC_READONLY;
+ return bfd_set_section_flags (abfd, s, flags);
+}
+
+/* Copy the extra info we tack onto an elf_link_hash_entry. */
+
+static void
+ppc_elf_copy_indirect_symbol (struct bfd_link_info *info,
+ struct elf_link_hash_entry *dir,
+ struct elf_link_hash_entry *ind)
+{
+ struct ppc_elf_link_hash_entry *edir, *eind;
+
+ edir = (struct ppc_elf_link_hash_entry *) dir;
+ eind = (struct ppc_elf_link_hash_entry *) ind;
+
+ edir->tls_mask |= eind->tls_mask;
+ edir->has_sda_refs |= eind->has_sda_refs;
+
+ /* If called to transfer flags for a weakdef during processing
+ of elf_adjust_dynamic_symbol, don't copy non_got_ref.
+ We clear it ourselves for ELIMINATE_COPY_RELOCS. */
+ if (!(ELIMINATE_COPY_RELOCS
+ && eind->elf.root.type != bfd_link_hash_indirect
+ && edir->elf.dynamic_adjusted))
+ edir->elf.non_got_ref |= eind->elf.non_got_ref;
+
+ edir->elf.ref_dynamic |= eind->elf.ref_dynamic;
+ edir->elf.ref_regular |= eind->elf.ref_regular;
+ edir->elf.ref_regular_nonweak |= eind->elf.ref_regular_nonweak;
+ edir->elf.needs_plt |= eind->elf.needs_plt;
+ edir->elf.pointer_equality_needed |= eind->elf.pointer_equality_needed;
+
+ if (eind->dyn_relocs != NULL)
+ {
+ if (edir->dyn_relocs != NULL)
+ {
+ struct elf_dyn_relocs **pp;
+ struct elf_dyn_relocs *p;
+
+ /* Add reloc counts against the indirect sym to the direct sym
+ list. Merge any entries against the same section. */
+ for (pp = &eind->dyn_relocs; (p = *pp) != NULL; )
+ {
+ struct elf_dyn_relocs *q;
+
+ for (q = edir->dyn_relocs; q != NULL; q = q->next)
+ if (q->sec == p->sec)
+ {
+ q->pc_count += p->pc_count;
+ q->count += p->count;
+ *pp = p->next;
+ break;
+ }
+ if (q == NULL)
+ pp = &p->next;
+ }
+ *pp = edir->dyn_relocs;
+ }
+
+ edir->dyn_relocs = eind->dyn_relocs;
+ eind->dyn_relocs = NULL;
+ }
+
+ /* If we were called to copy over info for a weak sym, that's all.
+ You might think dyn_relocs need not be copied over; After all,
+ both syms will be dynamic or both non-dynamic so we're just
+ moving reloc accounting around. However, ELIMINATE_COPY_RELOCS
+ code in ppc_elf_adjust_dynamic_symbol needs to check for
+ dyn_relocs in read-only sections, and it does so on what is the
+ DIR sym here. */
+ if (eind->elf.root.type != bfd_link_hash_indirect)
+ return;
+
+ /* Copy over the GOT refcount entries that we may have already seen to
+ the symbol which just became indirect. */
+ edir->elf.got.refcount += eind->elf.got.refcount;
+ eind->elf.got.refcount = 0;
+
+ /* And plt entries. */
+ if (eind->elf.plt.plist != NULL)
+ {
+ if (edir->elf.plt.plist != NULL)
+ {
+ struct plt_entry **entp;
+ struct plt_entry *ent;
+
+ for (entp = &eind->elf.plt.plist; (ent = *entp) != NULL; )
+ {
+ struct plt_entry *dent;
+
+ for (dent = edir->elf.plt.plist; dent != NULL; dent = dent->next)
+ if (dent->sec == ent->sec && dent->addend == ent->addend)
+ {
+ dent->plt.refcount += ent->plt.refcount;
+ *entp = ent->next;
+ break;
+ }
+ if (dent == NULL)
+ entp = &ent->next;
+ }
+ *entp = edir->elf.plt.plist;
+ }
+
+ edir->elf.plt.plist = eind->elf.plt.plist;
+ eind->elf.plt.plist = NULL;
+ }
+
+ if (eind->elf.dynindx != -1)
+ {
+ if (edir->elf.dynindx != -1)
+ _bfd_elf_strtab_delref (elf_hash_table (info)->dynstr,
+ edir->elf.dynstr_index);
+ edir->elf.dynindx = eind->elf.dynindx;
+ edir->elf.dynstr_index = eind->elf.dynstr_index;
+ eind->elf.dynindx = -1;
+ eind->elf.dynstr_index = 0;
+ }
+}
+
+/* Hook called by the linker routine which adds symbols from an object
+ file. We use it to put .comm items in .sbss, and not .bss. */
+
+static bfd_boolean
+ppc_elf_add_symbol_hook (bfd *abfd,
+ struct bfd_link_info *info,
+ Elf_Internal_Sym *sym,
+ const char **namep ATTRIBUTE_UNUSED,
+ flagword *flagsp ATTRIBUTE_UNUSED,
+ asection **secp,
+ bfd_vma *valp)
+{
+ if (sym->st_shndx == SHN_COMMON
+ && !info->relocatable
+ && is_ppc_elf (info->output_bfd)
+ && sym->st_size <= elf_gp_size (abfd))
+ {
+ /* Common symbols less than or equal to -G nn bytes are automatically
+ put into .sbss. */
+ struct ppc_elf_link_hash_table *htab;
+
+ htab = ppc_elf_hash_table (info);
+ if (htab->sbss == NULL)
+ {
+ flagword flags = SEC_IS_COMMON | SEC_LINKER_CREATED;
+
+ if (!htab->elf.dynobj)
+ htab->elf.dynobj = abfd;
+
+ htab->sbss = bfd_make_section_anyway_with_flags (htab->elf.dynobj,
+ ".sbss",
+ flags);
+ if (htab->sbss == NULL)
+ return FALSE;
+ }
+
+ *secp = htab->sbss;
+ *valp = sym->st_size;
+ }
+
+ if ((abfd->flags & DYNAMIC) == 0
+ && (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC
+ || ELF_ST_BIND (sym->st_info) == STB_GNU_UNIQUE))
+ elf_tdata (info->output_bfd)->has_gnu_symbols = TRUE;
+
+ return TRUE;
+}
+\f
+static bfd_boolean
+create_sdata_sym (struct bfd_link_info *info, elf_linker_section_t *lsect)
+{
+ struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info);
+
+ lsect->sym = elf_link_hash_lookup (&htab->elf, lsect->sym_name,
+ TRUE, FALSE, TRUE);
+ if (lsect->sym == NULL)
+ return FALSE;
+ if (lsect->sym->root.type == bfd_link_hash_new)
+ lsect->sym->non_elf = 0;
+ lsect->sym->ref_regular = 1;
+ _bfd_elf_link_hash_hide_symbol (info, lsect->sym, TRUE);
+ return TRUE;
+}
+
+/* Create a special linker section. */
+
+static bfd_boolean
+ppc_elf_create_linker_section (bfd *abfd,
+ struct bfd_link_info *info,
+ flagword flags,
+ elf_linker_section_t *lsect)
+{
+ struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info);
+ asection *s;
+
+ flags |= (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
+ | SEC_LINKER_CREATED);
+
+ /* Record the first bfd that needs the special sections. */
+ if (!htab->elf.dynobj)
+ htab->elf.dynobj = abfd;
+
+ s = bfd_make_section_anyway_with_flags (htab->elf.dynobj,
+ lsect->name,
+ flags);
+ if (s == NULL
+ || !bfd_set_section_alignment (htab->elf.dynobj, s, 2))
+ return FALSE;
+ lsect->section = s;
+
+ return create_sdata_sym (info, lsect);
+}
+
+/* Find a linker generated pointer with a given addend and type. */
+
+static elf_linker_section_pointers_t *
+elf_find_pointer_linker_section
+ (elf_linker_section_pointers_t *linker_pointers,
+ bfd_vma addend,
+ elf_linker_section_t *lsect)
+{
+ for ( ; linker_pointers != NULL; linker_pointers = linker_pointers->next)
+ if (lsect == linker_pointers->lsect && addend == linker_pointers->addend)
+ return linker_pointers;
+
+ return NULL;
+}
+
+/* Allocate a pointer to live in a linker created section. */
+
+static bfd_boolean
+elf_create_pointer_linker_section (bfd *abfd,
+ elf_linker_section_t *lsect,
+ struct elf_link_hash_entry *h,
+ const Elf_Internal_Rela *rel)
+{
+ elf_linker_section_pointers_t **ptr_linker_section_ptr = NULL;
+ elf_linker_section_pointers_t *linker_section_ptr;
+ unsigned long r_symndx = ELF32_R_SYM (rel->r_info);
+ bfd_size_type amt;
+
+ BFD_ASSERT (lsect != NULL);
+
+ /* Is this a global symbol? */
+ if (h != NULL)
+ {
+ struct ppc_elf_link_hash_entry *eh;
+
+ /* Has this symbol already been allocated? If so, our work is done. */
+ eh = (struct ppc_elf_link_hash_entry *) h;
+ if (elf_find_pointer_linker_section (eh->linker_section_pointer,
+ rel->r_addend,
+ lsect))
+ return TRUE;
+
+ ptr_linker_section_ptr = &eh->linker_section_pointer;
+ }
+ else
+ {
+ BFD_ASSERT (is_ppc_elf (abfd));
+
+ /* Allocation of a pointer to a local symbol. */
+ elf_linker_section_pointers_t **ptr = elf_local_ptr_offsets (abfd);
+
+ /* Allocate a table to hold the local symbols if first time. */
+ if (!ptr)
+ {
+ unsigned int num_symbols = elf_symtab_hdr (abfd).sh_info;
+
+ amt = num_symbols;
+ amt *= sizeof (elf_linker_section_pointers_t *);
+ ptr = bfd_zalloc (abfd, amt);
+
+ if (!ptr)
+ return FALSE;
+
+ elf_local_ptr_offsets (abfd) = ptr;
+ }
+
+ /* Has this symbol already been allocated? If so, our work is done. */
+ if (elf_find_pointer_linker_section (ptr[r_symndx],
+ rel->r_addend,
+ lsect))
+ return TRUE;
+
+ ptr_linker_section_ptr = &ptr[r_symndx];
+ }
+
+ /* Allocate space for a pointer in the linker section, and allocate
+ a new pointer record from internal memory. */
+ BFD_ASSERT (ptr_linker_section_ptr != NULL);
+ amt = sizeof (elf_linker_section_pointers_t);
+ linker_section_ptr = bfd_alloc (abfd, amt);
+
+ if (!linker_section_ptr)
+ return FALSE;
+
+ linker_section_ptr->next = *ptr_linker_section_ptr;
+ linker_section_ptr->addend = rel->r_addend;
+ linker_section_ptr->lsect = lsect;
+ *ptr_linker_section_ptr = linker_section_ptr;
+
+ linker_section_ptr->offset = lsect->section->size;
+ lsect->section->size += 4;
+
+#ifdef DEBUG
+ fprintf (stderr,
+ "Create pointer in linker section %s, offset = %ld, section size = %ld\n",
+ lsect->name, (long) linker_section_ptr->offset,
+ (long) lsect->section->size);
+#endif
+
+ return TRUE;
+}
+
+static struct plt_entry **
+update_local_sym_info (bfd *abfd,
+ Elf_Internal_Shdr *symtab_hdr,
+ unsigned long r_symndx,
+ int tls_type)
+{
+ bfd_signed_vma *local_got_refcounts = elf_local_got_refcounts (abfd);
+ struct plt_entry **local_plt;
+ char *local_got_tls_masks;
+
+ if (local_got_refcounts == NULL)
+ {
+ bfd_size_type size = symtab_hdr->sh_info;
+
+ size *= (sizeof (*local_got_refcounts)
+ + sizeof (*local_plt)
+ + sizeof (*local_got_tls_masks));
+ local_got_refcounts = bfd_zalloc (abfd, size);
+ if (local_got_refcounts == NULL)
+ return NULL;
+ elf_local_got_refcounts (abfd) = local_got_refcounts;
+ }
+
+ local_plt = (struct plt_entry **) (local_got_refcounts + symtab_hdr->sh_info);
+ local_got_tls_masks = (char *) (local_plt + symtab_hdr->sh_info);
+ local_got_tls_masks[r_symndx] |= tls_type;
+ if (tls_type != PLT_IFUNC)
+ local_got_refcounts[r_symndx] += 1;
+ return local_plt + r_symndx;
+}
+
+static bfd_boolean
+update_plt_info (bfd *abfd, struct plt_entry **plist,
+ asection *sec, bfd_vma addend)
+{
+ struct plt_entry *ent;
+
+ if (addend < 32768)
+ sec = NULL;
+ for (ent = *plist; ent != NULL; ent = ent->next)
+ if (ent->sec == sec && ent->addend == addend)
+ break;
+ if (ent == NULL)
+ {
+ bfd_size_type amt = sizeof (*ent);
+ ent = bfd_alloc (abfd, amt);
+ if (ent == NULL)
+ return FALSE;
+ ent->next = *plist;
+ ent->sec = sec;
+ ent->addend = addend;
+ ent->plt.refcount = 0;
+ *plist = ent;
+ }
+ ent->plt.refcount += 1;
+ return TRUE;
+}
+
+static struct plt_entry *
+find_plt_ent (struct plt_entry **plist, asection *sec, bfd_vma addend)
+{
+ struct plt_entry *ent;
+
+ if (addend < 32768)
+ sec = NULL;
+ for (ent = *plist; ent != NULL; ent = ent->next)
+ if (ent->sec == sec && ent->addend == addend)
+ break;
+ return ent;
+}
+
+static bfd_boolean
+is_branch_reloc (enum elf_ppc_reloc_type r_type)
+{
+ return (r_type == R_PPC_PLTREL24
+ || r_type == R_PPC_LOCAL24PC
+ || r_type == R_PPC_REL24
+ || r_type == R_PPC_REL14
+ || r_type == R_PPC_REL14_BRTAKEN
+ || r_type == R_PPC_REL14_BRNTAKEN
+ || r_type == R_PPC_ADDR24
+ || r_type == R_PPC_ADDR14
+ || r_type == R_PPC_ADDR14_BRTAKEN
+ || r_type == R_PPC_ADDR14_BRNTAKEN);
+}
+
+static void
+bad_shared_reloc (bfd *abfd, enum elf_ppc_reloc_type r_type)
+{
+ (*_bfd_error_handler)
+ (_("%B: relocation %s cannot be used when making a shared object"),
+ abfd,
+ ppc_elf_howto_table[r_type]->name);
+ bfd_set_error (bfd_error_bad_value);
+}
+
+/* Look through the relocs for a section during the first phase, and
+ allocate space in the global offset table or procedure linkage
+ table. */
+
+static bfd_boolean
+ppc_elf_check_relocs (bfd *abfd,
+ struct bfd_link_info *info,
+ asection *sec,
+ const Elf_Internal_Rela *relocs)
+{
+ struct ppc_elf_link_hash_table *htab;
+ Elf_Internal_Shdr *symtab_hdr;
+ struct elf_link_hash_entry **sym_hashes;
+ const Elf_Internal_Rela *rel;
+ const Elf_Internal_Rela *rel_end;
+ asection *got2, *sreloc;
+ struct elf_link_hash_entry *tga;
+
+ if (info->relocatable)
+ return TRUE;
+
+ /* Don't do anything special with non-loaded, non-alloced sections.
+ In particular, any relocs in such sections should not affect GOT
+ and PLT reference counting (ie. we don't allow them to create GOT
+ or PLT entries), there's no possibility or desire to optimize TLS
+ relocs, and there's not much point in propagating relocs to shared
+ libs that the dynamic linker won't relocate. */
+ if ((sec->flags & SEC_ALLOC) == 0)
+ return TRUE;
+
+#ifdef DEBUG
+ _bfd_error_handler ("ppc_elf_check_relocs called for section %A in %B",
+ sec, abfd);
+#endif
+
+ BFD_ASSERT (is_ppc_elf (abfd));
+
+ /* Initialize howto table if not already done. */
+ if (!ppc_elf_howto_table[R_PPC_ADDR32])
+ ppc_elf_howto_init ();
+
+ htab = ppc_elf_hash_table (info);
+ if (htab->glink == NULL)
+ {
+ if (htab->elf.dynobj == NULL)
+ htab->elf.dynobj = abfd;
+ if (!ppc_elf_create_glink (htab->elf.dynobj, info))
+ return FALSE;
+ }
+ tga = elf_link_hash_lookup (&htab->elf, "__tls_get_addr",
+ FALSE, FALSE, TRUE);
+ symtab_hdr = &elf_symtab_hdr (abfd);
+ sym_hashes = elf_sym_hashes (abfd);
+ got2 = bfd_get_section_by_name (abfd, ".got2");
+ sreloc = NULL;
+
+ rel_end = relocs + sec->reloc_count;
+ for (rel = relocs; rel < rel_end; rel++)
+ {
+ unsigned long r_symndx;
+ enum elf_ppc_reloc_type r_type;
+ struct elf_link_hash_entry *h;
+ int tls_type;
+
+ r_symndx = ELF32_R_SYM (rel->r_info);
+ if (r_symndx < symtab_hdr->sh_info)
+ h = NULL;
+ else
+ {
+ h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+ while (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+ /* PR15323, ref flags aren't set for references in the same
+ object. */
+ h->root.non_ir_ref = 1;
+ }
+
+ /* If a relocation refers to _GLOBAL_OFFSET_TABLE_, create the .got.
+ This shows up in particular in an R_PPC_ADDR32 in the eabi
+ startup code. */
+ if (h != NULL
+ && htab->got == NULL
+ && strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0)
+ {
+ if (htab->elf.dynobj == NULL)
+ htab->elf.dynobj = abfd;
+ if (!ppc_elf_create_got (htab->elf.dynobj, info))
+ return FALSE;
+ BFD_ASSERT (h == htab->elf.hgot);
+ }
+
+ tls_type = 0;
+ r_type = ELF32_R_TYPE (rel->r_info);
+ if (h == NULL && !htab->is_vxworks)
+ {
+ Elf_Internal_Sym *isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+ abfd, r_symndx);
+ if (isym == NULL)
+ return FALSE;
+
+ if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+ {
+ struct plt_entry **ifunc;
+
+ /* Set PLT_IFUNC flag for this sym, no GOT entry yet. */
+ ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx,
+ PLT_IFUNC);
+ if (ifunc == NULL)
+ return FALSE;
+
+ /* STT_GNU_IFUNC symbols must have a PLT entry;
+ In a non-pie executable even when there are
+ no plt calls. */
+ if (!info->shared
+ || is_branch_reloc (r_type))
+ {
+ bfd_vma addend = 0;
+ if (r_type == R_PPC_PLTREL24)
+ {
+ ppc_elf_tdata (abfd)->makes_plt_call = 1;
+ if (info->shared)
+ addend = rel->r_addend;
+ }
+ if (!update_plt_info (abfd, ifunc, got2, addend))
+ return FALSE;
+ }
+ }
+ }
+
+ if (!htab->is_vxworks
+ && is_branch_reloc (r_type)
+ && h != NULL
+ && h == tga)
+ {
+ if (rel != relocs
+ && (ELF32_R_TYPE (rel[-1].r_info) == R_PPC_TLSGD
+ || ELF32_R_TYPE (rel[-1].r_info) == R_PPC_TLSLD))
+ /* We have a new-style __tls_get_addr call with a marker
+ reloc. */
+ ;
+ else
+ /* Mark this section as having an old-style call. */
+ sec->has_tls_get_addr_call = 1;
+ }
+
+ switch (r_type)
+ {
+ case R_PPC_TLSGD:
+ case R_PPC_TLSLD:
+ /* These special tls relocs tie a call to __tls_get_addr with
+ its parameter symbol. */
+ break;
+
+ case R_PPC_GOT_TLSLD16:
+ case R_PPC_GOT_TLSLD16_LO:
+ case R_PPC_GOT_TLSLD16_HI:
+ case R_PPC_GOT_TLSLD16_HA:
+ tls_type = TLS_TLS | TLS_LD;
+ goto dogottls;
+
+ case R_PPC_GOT_TLSGD16:
+ case R_PPC_GOT_TLSGD16_LO:
+ case R_PPC_GOT_TLSGD16_HI:
+ case R_PPC_GOT_TLSGD16_HA:
+ tls_type = TLS_TLS | TLS_GD;
+ goto dogottls;
+
+ case R_PPC_GOT_TPREL16:
+ case R_PPC_GOT_TPREL16_LO:
+ case R_PPC_GOT_TPREL16_HI:
+ case R_PPC_GOT_TPREL16_HA:
+ if (!info->executable)
+ info->flags |= DF_STATIC_TLS;
+ tls_type = TLS_TLS | TLS_TPREL;
+ goto dogottls;
+
+ case R_PPC_GOT_DTPREL16:
+ case R_PPC_GOT_DTPREL16_LO:
+ case R_PPC_GOT_DTPREL16_HI:
+ case R_PPC_GOT_DTPREL16_HA:
+ tls_type = TLS_TLS | TLS_DTPREL;
+ dogottls:
+ sec->has_tls_reloc = 1;
+ /* Fall thru */
+
+ /* GOT16 relocations */
+ case R_PPC_GOT16:
+ case R_PPC_GOT16_LO:
+ case R_PPC_GOT16_HI:
+ case R_PPC_GOT16_HA:
+ /* This symbol requires a global offset table entry. */
+ if (htab->got == NULL)
+ {
+ if (htab->elf.dynobj == NULL)
+ htab->elf.dynobj = abfd;
+ if (!ppc_elf_create_got (htab->elf.dynobj, info))
+ return FALSE;
+ }
+ if (h != NULL)
+ {
+ h->got.refcount += 1;
+ ppc_elf_hash_entry (h)->tls_mask |= tls_type;
+ }
+ else
+ /* This is a global offset table entry for a local symbol. */
+ if (!update_local_sym_info (abfd, symtab_hdr, r_symndx, tls_type))
+ return FALSE;
+
+ /* We may also need a plt entry if the symbol turns out to be
+ an ifunc. */
+ if (h != NULL && !info->shared)
+ {
+ if (!update_plt_info (abfd, &h->plt.plist, NULL, 0))
+ return FALSE;
+ }
+ break;
+
+ /* Indirect .sdata relocation. */
+ case R_PPC_EMB_SDAI16:
+ if (info->shared)
+ {
+ bad_shared_reloc (abfd, r_type);
+ return FALSE;
+ }
+ if (htab->sdata[0].section == NULL
+ && !ppc_elf_create_linker_section (abfd, info, 0,
+ &htab->sdata[0]))
+ return FALSE;
+ if (!elf_create_pointer_linker_section (abfd, &htab->sdata[0],
+ h, rel))
+ return FALSE;
+ if (h != NULL)
+ {
+ ppc_elf_hash_entry (h)->has_sda_refs = TRUE;
+ h->non_got_ref = TRUE;
+ }
+ break;
+
+ /* Indirect .sdata2 relocation. */
+ case R_PPC_EMB_SDA2I16:
+ if (info->shared)
+ {
+ bad_shared_reloc (abfd, r_type);
+ return FALSE;
+ }
+ if (htab->sdata[1].section == NULL
+ && !ppc_elf_create_linker_section (abfd, info, SEC_READONLY,
+ &htab->sdata[1]))
+ return FALSE;
+ if (!elf_create_pointer_linker_section (abfd, &htab->sdata[1],
+ h, rel))
+ return FALSE;
+ if (h != NULL)
+ {
+ ppc_elf_hash_entry (h)->has_sda_refs = TRUE;
+ h->non_got_ref = TRUE;
+ }
+ break;
+
+ case R_PPC_VLE_SDAREL_LO16A:
+ case R_PPC_VLE_SDAREL_LO16D:
+ case R_PPC_VLE_SDAREL_HI16A:
+ case R_PPC_VLE_SDAREL_HI16D:
+ case R_PPC_VLE_SDAREL_HA16A:
+ case R_PPC_VLE_SDAREL_HA16D:
+ case R_PPC_SDAREL16:
+ if (htab->sdata[0].sym == NULL
+ && !create_sdata_sym (info, &htab->sdata[0]))
+ return FALSE;
+
+ if (htab->sdata[1].sym == NULL
+ && !create_sdata_sym (info, &htab->sdata[1]))
+ return FALSE;
+
+ if (h != NULL)
+ {
+ ppc_elf_hash_entry (h)->has_sda_refs = TRUE;
+ h->non_got_ref = TRUE;
+ }
+ break;
+
+ case R_PPC_VLE_REL8:
+ case R_PPC_VLE_REL15:
+ case R_PPC_VLE_REL24:
+ case R_PPC_VLE_LO16A:
+ case R_PPC_VLE_LO16D:
+ case R_PPC_VLE_HI16A:
+ case R_PPC_VLE_HI16D:
+ case R_PPC_VLE_HA16A:
+ case R_PPC_VLE_HA16D:
+ break;
+
+ case R_PPC_EMB_SDA2REL:
+ if (info->shared)
+ {
+ bad_shared_reloc (abfd, r_type);
+ return FALSE;
+ }
+ if (htab->sdata[1].sym == NULL
+ && !create_sdata_sym (info, &htab->sdata[1]))
+ return FALSE;
+ if (h != NULL)
+ {
+ ppc_elf_hash_entry (h)->has_sda_refs = TRUE;
+ h->non_got_ref = TRUE;
+ }
+ break;
+
+ case R_PPC_VLE_SDA21_LO:
+ case R_PPC_VLE_SDA21:
+ case R_PPC_EMB_SDA21:
+ case R_PPC_EMB_RELSDA:
+ if (info->shared)
+ {
+ bad_shared_reloc (abfd, r_type);
+ return FALSE;
+ }
+ if (htab->sdata[0].sym == NULL
+ && !create_sdata_sym (info, &htab->sdata[0]))
+ return FALSE;
+ if (htab->sdata[1].sym == NULL
+ && !create_sdata_sym (info, &htab->sdata[1]))
+ return FALSE;
+ if (h != NULL)
+ {
+ ppc_elf_hash_entry (h)->has_sda_refs = TRUE;
+ h->non_got_ref = TRUE;
+ }
+ break;
+
+ case R_PPC_EMB_NADDR32:
+ case R_PPC_EMB_NADDR16:
+ case R_PPC_EMB_NADDR16_LO:
+ case R_PPC_EMB_NADDR16_HI:
+ case R_PPC_EMB_NADDR16_HA:
+ if (info->shared)
+ {
+ bad_shared_reloc (abfd, r_type);
+ return FALSE;
+ }
+ if (h != NULL)
+ h->non_got_ref = TRUE;
+ break;
+
+ case R_PPC_PLTREL24:
+ if (h == NULL)
+ break;
+ /* Fall through */
+ case R_PPC_PLT32:
+ case R_PPC_PLTREL32:
+ case R_PPC_PLT16_LO:
+ case R_PPC_PLT16_HI:
+ case R_PPC_PLT16_HA:
+#ifdef DEBUG
+ fprintf (stderr, "Reloc requires a PLT entry\n");
+#endif
+ /* This symbol requires a procedure linkage table entry. We
+ actually build the entry in finish_dynamic_symbol,
+ because this might be a case of linking PIC code without
+ linking in any dynamic objects, in which case we don't
+ need to generate a procedure linkage table after all. */
+
+ if (h == NULL)
+ {
+ /* It does not make sense to have a procedure linkage
+ table entry for a local symbol. */
+ info->callbacks->einfo (_("%P: %H: %s reloc against local symbol\n"),
+ abfd, sec, rel->r_offset,
+ ppc_elf_howto_table[r_type]->name);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+ else
+ {
+ bfd_vma addend = 0;
+
+ if (r_type == R_PPC_PLTREL24)
+ {
+ ppc_elf_tdata (abfd)->makes_plt_call = 1;
+ if (info->shared)
+ addend = rel->r_addend;
+ }
+ h->needs_plt = 1;
+ if (!update_plt_info (abfd, &h->plt.plist, got2, addend))
+ return FALSE;
+ }
+ break;
+
+ /* The following relocations don't need to propagate the
+ relocation if linking a shared object since they are
+ section relative. */
+ case R_PPC_SECTOFF:
+ case R_PPC_SECTOFF_LO:
+ case R_PPC_SECTOFF_HI:
+ case R_PPC_SECTOFF_HA:
+ case R_PPC_DTPREL16:
+ case R_PPC_DTPREL16_LO:
+ case R_PPC_DTPREL16_HI:
+ case R_PPC_DTPREL16_HA:
+ case R_PPC_TOC16:
+ break;
+
+ case R_PPC_REL16:
+ case R_PPC_REL16_LO:
+ case R_PPC_REL16_HI:
+ case R_PPC_REL16_HA:
+ ppc_elf_tdata (abfd)->has_rel16 = 1;
+ break;
+
+ /* These are just markers. */
+ case R_PPC_TLS:
+ case R_PPC_EMB_MRKREF:
+ case R_PPC_NONE:
+ case R_PPC_max:
+ case R_PPC_RELAX:
+ case R_PPC_RELAX_PLT:
+ case R_PPC_RELAX_PLTREL24:
+ break;
+
+ /* These should only appear in dynamic objects. */
+ case R_PPC_COPY:
+ case R_PPC_GLOB_DAT:
+ case R_PPC_JMP_SLOT:
+ case R_PPC_RELATIVE:
+ case R_PPC_IRELATIVE:
+ break;
+
+ /* These aren't handled yet. We'll report an error later. */
+ case R_PPC_ADDR30:
+ case R_PPC_EMB_RELSEC16:
+ case R_PPC_EMB_RELST_LO:
+ case R_PPC_EMB_RELST_HI:
+ case R_PPC_EMB_RELST_HA:
+ case R_PPC_EMB_BIT_FLD:
+ break;
+
+ /* This refers only to functions defined in the shared library. */
+ case R_PPC_LOCAL24PC:
+ if (h != NULL && h == htab->elf.hgot && htab->plt_type == PLT_UNSET)
+ {
+ htab->plt_type = PLT_OLD;
+ htab->old_bfd = abfd;
+ }
+ break;
+
+ /* This relocation describes the C++ object vtable hierarchy.
+ Reconstruct it for later use during GC. */
+ case R_PPC_GNU_VTINHERIT:
+ if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
+ return FALSE;
+ break;
+
+ /* This relocation describes which C++ vtable entries are actually
+ used. Record for later use during GC. */
+ case R_PPC_GNU_VTENTRY:
+ BFD_ASSERT (h != NULL);
+ if (h != NULL
+ && !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
+ return FALSE;
+ break;
+
+ /* We shouldn't really be seeing these. */
+ case R_PPC_TPREL32:
+ case R_PPC_TPREL16:
+ case R_PPC_TPREL16_LO:
+ case R_PPC_TPREL16_HI:
+ case R_PPC_TPREL16_HA:
+ if (!info->executable)
+ info->flags |= DF_STATIC_TLS;
+ goto dodyn;
+
+ /* Nor these. */
+ case R_PPC_DTPMOD32:
+ case R_PPC_DTPREL32:
+ goto dodyn;
+
+ case R_PPC_REL32:
+ if (h == NULL
+ && got2 != NULL
+ && (sec->flags & SEC_CODE) != 0
+ && info->shared
+ && htab->plt_type == PLT_UNSET)
+ {
+ /* Old -fPIC gcc code has .long LCTOC1-LCFx just before
+ the start of a function, which assembles to a REL32
+ reference to .got2. If we detect one of these, then
+ force the old PLT layout because the linker cannot
+ reliably deduce the GOT pointer value needed for
+ PLT call stubs. */
+ asection *s;
+ Elf_Internal_Sym *isym;
+
+ isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+ abfd, r_symndx);
+ if (isym == NULL)
+ return FALSE;
+
+ s = bfd_section_from_elf_index (abfd, isym->st_shndx);
+ if (s == got2)
+ {
+ htab->plt_type = PLT_OLD;
+ htab->old_bfd = abfd;
+ }
+ }
+ if (h == NULL || h == htab->elf.hgot)
+ break;
+ /* fall through */
+
+ case R_PPC_ADDR32:
+ case R_PPC_ADDR16:
+ case R_PPC_ADDR16_LO:
+ case R_PPC_ADDR16_HI:
+ case R_PPC_ADDR16_HA:
+ case R_PPC_UADDR32:
+ case R_PPC_UADDR16:
+ if (h != NULL && !info->shared)
+ {
+ /* We may need a plt entry if the symbol turns out to be
+ a function defined in a dynamic object. */
+ if (!update_plt_info (abfd, &h->plt.plist, NULL, 0))
+ return FALSE;
+
+ /* We may need a copy reloc too. */
+ h->non_got_ref = 1;
+ h->pointer_equality_needed = 1;
+ }
+ goto dodyn;
+
+ case R_PPC_REL24:
+ case R_PPC_REL14:
+ case R_PPC_REL14_BRTAKEN:
+ case R_PPC_REL14_BRNTAKEN:
+ if (h == NULL)
+ break;
+ if (h == htab->elf.hgot)
+ {
+ if (htab->plt_type == PLT_UNSET)
+ {
+ htab->plt_type = PLT_OLD;
+ htab->old_bfd = abfd;
+ }
+ break;
+ }
+ /* fall through */
+
+ case R_PPC_ADDR24:
+ case R_PPC_ADDR14:
+ case R_PPC_ADDR14_BRTAKEN:
+ case R_PPC_ADDR14_BRNTAKEN:
+ if (h != NULL && !info->shared)
+ {
+ /* We may need a plt entry if the symbol turns out to be
+ a function defined in a dynamic object. */
+ h->needs_plt = 1;
+ if (!update_plt_info (abfd, &h->plt.plist, NULL, 0))
+ return FALSE;
+ break;
+ }
+
+ dodyn:
+ /* If we are creating a shared library, and this is a reloc
+ against a global symbol, or a non PC relative reloc
+ against a local symbol, then we need to copy the reloc
+ into the shared library. However, if we are linking with
+ -Bsymbolic, we do not need to copy a reloc against a
+ global symbol which is defined in an object we are
+ including in the link (i.e., DEF_REGULAR is set). At
+ this point we have not seen all the input files, so it is
+ possible that DEF_REGULAR is not set now but will be set
+ later (it is never cleared). In case of a weak definition,
+ DEF_REGULAR may be cleared later by a strong definition in
+ a shared library. We account for that possibility below by
+ storing information in the dyn_relocs field of the hash
+ table entry. A similar situation occurs when creating
+ shared libraries and symbol visibility changes render the
+ symbol local.
+
+ If on the other hand, we are creating an executable, we
+ may need to keep relocations for symbols satisfied by a
+ dynamic library if we manage to avoid copy relocs for the
+ symbol. */
+ if ((info->shared
+ && (must_be_dyn_reloc (info, 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)))
+ {
+#ifdef DEBUG
+ fprintf (stderr,
+ "ppc_elf_check_relocs needs to "
+ "create relocation for %s\n",
+ (h && h->root.root.string
+ ? h->root.root.string : "<unknown>"));
+#endif
+ if (sreloc == NULL)
+ {
+ if (htab->elf.dynobj == NULL)
+ htab->elf.dynobj = abfd;
+
+ sreloc = _bfd_elf_make_dynamic_reloc_section
+ (sec, htab->elf.dynobj, 2, abfd, /*rela?*/ TRUE);
+
+ if (sreloc == NULL)
+ return FALSE;
+ }
+
+ /* If this is a global symbol, we count the number of
+ relocations we need for this symbol. */
+ if (h != NULL)
+ {
+ struct elf_dyn_relocs *p;
+ struct elf_dyn_relocs **rel_head;
+
+ rel_head = &ppc_elf_hash_entry (h)->dyn_relocs;
+ p = *rel_head;
+ if (p == NULL || p->sec != sec)
+ {
+ p = bfd_alloc (htab->elf.dynobj, sizeof *p);
+ if (p == NULL)
+ return FALSE;
+ p->next = *rel_head;
+ *rel_head = p;
+ p->sec = sec;
+ p->count = 0;
+ p->pc_count = 0;
+ }
+ p->count += 1;
+ if (!must_be_dyn_reloc (info, r_type))
+ p->pc_count += 1;
+ }
+ else
+ {
+ /* Track dynamic relocs needed for local syms too.
+ We really need local syms available to do this
+ easily. Oh well. */
+ struct ppc_dyn_relocs *p;
+ struct ppc_dyn_relocs **rel_head;
+ bfd_boolean is_ifunc;
+ asection *s;
+ void *vpp;
+ Elf_Internal_Sym *isym;
+
+ isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+ abfd, r_symndx);
+ if (isym == NULL)
+ return FALSE;
+
+ s = bfd_section_from_elf_index (abfd, isym->st_shndx);
+ if (s == NULL)
+ s = sec;
+
+ vpp = &elf_section_data (s)->local_dynrel;
+ rel_head = (struct ppc_dyn_relocs **) vpp;
+ is_ifunc = ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC;
+ p = *rel_head;
+ if (p != NULL && p->sec == sec && p->ifunc != is_ifunc)
+ p = p->next;
+ if (p == NULL || p->sec != sec || p->ifunc != is_ifunc)
+ {
+ p = bfd_alloc (htab->elf.dynobj, sizeof *p);
+ if (p == NULL)
+ return FALSE;
+ p->next = *rel_head;
+ *rel_head = p;
+ p->sec = sec;
+ p->ifunc = is_ifunc;
+ p->count = 0;
+ }
+ p->count += 1;
+ }
+ }
+
+ break;
+ }
+ }
+
+ return TRUE;
+}
+\f
+
+/* Merge object attributes from IBFD into OBFD. Raise an error if
+ there are conflicting attributes. */
+static bfd_boolean
+ppc_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
+{
+ obj_attribute *in_attr, *in_attrs;
+ obj_attribute *out_attr, *out_attrs;
+
+ if (!elf_known_obj_attributes_proc (obfd)[0].i)
+ {
+ /* This is the first object. Copy the attributes. */
+ _bfd_elf_copy_obj_attributes (ibfd, obfd);
+
+ /* Use the Tag_null value to indicate the attributes have been
+ initialized. */
+ elf_known_obj_attributes_proc (obfd)[0].i = 1;
+
+ return TRUE;
+ }
+
+ in_attrs = elf_known_obj_attributes (ibfd)[OBJ_ATTR_GNU];
+ out_attrs = elf_known_obj_attributes (obfd)[OBJ_ATTR_GNU];
+
+ /* Check for conflicting Tag_GNU_Power_ABI_FP attributes and merge
+ non-conflicting ones. */
+ in_attr = &in_attrs[Tag_GNU_Power_ABI_FP];
+ out_attr = &out_attrs[Tag_GNU_Power_ABI_FP];
+ if (in_attr->i != out_attr->i)
+ {
+ out_attr->type = 1;
+ if (out_attr->i == 0)
+ out_attr->i = in_attr->i;
+ else if (in_attr->i == 0)
+ ;
+ else if (out_attr->i == 1 && in_attr->i == 2)
+ _bfd_error_handler
+ (_("Warning: %B uses hard float, %B uses soft float"), obfd, ibfd);
+ else if (out_attr->i == 1 && in_attr->i == 3)
+ _bfd_error_handler
+ (_("Warning: %B uses double-precision hard float, %B uses single-precision hard float"),
+ obfd, ibfd);
+ else if (out_attr->i == 3 && in_attr->i == 1)
+ _bfd_error_handler
+ (_("Warning: %B uses double-precision hard float, %B uses single-precision hard float"),
+ ibfd, obfd);
+ else if (out_attr->i == 3 && in_attr->i == 2)
+ _bfd_error_handler
+ (_("Warning: %B uses soft float, %B uses single-precision hard float"),
+ ibfd, obfd);
+ else if (out_attr->i == 2 && (in_attr->i == 1 || in_attr->i == 3))
+ _bfd_error_handler
+ (_("Warning: %B uses hard float, %B uses soft float"), ibfd, obfd);
+ else if (in_attr->i > 3)
+ _bfd_error_handler
+ (_("Warning: %B uses unknown floating point ABI %d"), ibfd,
+ in_attr->i);
+ else
+ _bfd_error_handler
+ (_("Warning: %B uses unknown floating point ABI %d"), obfd,
+ out_attr->i);
+ }
+
+ /* Check for conflicting Tag_GNU_Power_ABI_Vector attributes and
+ merge non-conflicting ones. */
+ in_attr = &in_attrs[Tag_GNU_Power_ABI_Vector];
+ out_attr = &out_attrs[Tag_GNU_Power_ABI_Vector];
+ if (in_attr->i != out_attr->i)
+ {
+ const char *in_abi = NULL, *out_abi = NULL;
+
+ switch (in_attr->i)
+ {
+ case 1: in_abi = "generic"; break;
+ case 2: in_abi = "AltiVec"; break;
+ case 3: in_abi = "SPE"; break;
+ }
+
+ switch (out_attr->i)
+ {
+ case 1: out_abi = "generic"; break;
+ case 2: out_abi = "AltiVec"; break;
+ case 3: out_abi = "SPE"; break;
+ }
+
+ out_attr->type = 1;
+ if (out_attr->i == 0)
+ out_attr->i = in_attr->i;
+ else if (in_attr->i == 0)
+ ;
+ /* For now, allow generic to transition to AltiVec or SPE
+ without a warning. If GCC marked files with their stack
+ alignment and used don't-care markings for files which are
+ not affected by the vector ABI, we could warn about this
+ case too. */
+ else if (out_attr->i == 1)
+ out_attr->i = in_attr->i;
+ else if (in_attr->i == 1)
+ ;
+ else if (in_abi == NULL)
+ _bfd_error_handler
+ (_("Warning: %B uses unknown vector ABI %d"), ibfd,
+ in_attr->i);
+ else if (out_abi == NULL)
+ _bfd_error_handler
+ (_("Warning: %B uses unknown vector ABI %d"), obfd,
+ in_attr->i);
+ else
+ _bfd_error_handler
+ (_("Warning: %B uses vector ABI \"%s\", %B uses \"%s\""),
+ ibfd, obfd, in_abi, out_abi);
+ }
+
+ /* Check for conflicting Tag_GNU_Power_ABI_Struct_Return attributes
+ and merge non-conflicting ones. */
+ in_attr = &in_attrs[Tag_GNU_Power_ABI_Struct_Return];
+ out_attr = &out_attrs[Tag_GNU_Power_ABI_Struct_Return];
+ if (in_attr->i != out_attr->i)
+ {
+ out_attr->type = 1;
+ if (out_attr->i == 0)
+ out_attr->i = in_attr->i;
+ else if (in_attr->i == 0)
+ ;
+ else if (out_attr->i == 1 && in_attr->i == 2)
+ _bfd_error_handler
+ (_("Warning: %B uses r3/r4 for small structure returns, %B uses memory"), obfd, ibfd);
+ else if (out_attr->i == 2 && in_attr->i == 1)
+ _bfd_error_handler
+ (_("Warning: %B uses r3/r4 for small structure returns, %B uses memory"), ibfd, obfd);
+ else if (in_attr->i > 2)
+ _bfd_error_handler
+ (_("Warning: %B uses unknown small structure return convention %d"), ibfd,
+ in_attr->i);
+ else
+ _bfd_error_handler
+ (_("Warning: %B uses unknown small structure return convention %d"), obfd,
+ out_attr->i);
+ }
+
+ /* Merge Tag_compatibility attributes and any common GNU ones. */
+ _bfd_elf_merge_object_attributes (ibfd, obfd);
+
+ return TRUE;
+}
+
+/* Merge backend specific data from an object file to the output
+ object file when linking. */
+
+static bfd_boolean
+ppc_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
+{
+ flagword old_flags;
+ flagword new_flags;
+ bfd_boolean error;
+
+ if (!is_ppc_elf (ibfd) || !is_ppc_elf (obfd))
+ return TRUE;
+
+ /* Check if we have the same endianness. */
+ if (! _bfd_generic_verify_endian_match (ibfd, obfd))
+ return FALSE;
+
+ if (!ppc_elf_merge_obj_attributes (ibfd, obfd))
+ return FALSE;
+
+ new_flags = elf_elfheader (ibfd)->e_flags;
+ old_flags = elf_elfheader (obfd)->e_flags;
+ if (!elf_flags_init (obfd))
+ {
+ /* First call, no flags set. */
+ elf_flags_init (obfd) = TRUE;
+ elf_elfheader (obfd)->e_flags = new_flags;
+ }
+
+ /* Compatible flags are ok. */
+ else if (new_flags == old_flags)
+ ;
+
+ /* Incompatible flags. */
+ else
+ {
+ /* Warn about -mrelocatable mismatch. Allow -mrelocatable-lib
+ to be linked with either. */
+ error = FALSE;
+ if ((new_flags & EF_PPC_RELOCATABLE) != 0
+ && (old_flags & (EF_PPC_RELOCATABLE | EF_PPC_RELOCATABLE_LIB)) == 0)
+ {
+ error = TRUE;
+ (*_bfd_error_handler)
+ (_("%B: compiled with -mrelocatable and linked with "
+ "modules compiled normally"), ibfd);
+ }
+ else if ((new_flags & (EF_PPC_RELOCATABLE | EF_PPC_RELOCATABLE_LIB)) == 0
+ && (old_flags & EF_PPC_RELOCATABLE) != 0)
+ {
+ error = TRUE;
+ (*_bfd_error_handler)
+ (_("%B: compiled normally and linked with "
+ "modules compiled with -mrelocatable"), ibfd);
+ }
+
+ /* The output is -mrelocatable-lib iff both the input files are. */
+ if (! (new_flags & EF_PPC_RELOCATABLE_LIB))
+ elf_elfheader (obfd)->e_flags &= ~EF_PPC_RELOCATABLE_LIB;
+
+ /* The output is -mrelocatable iff it can't be -mrelocatable-lib,
+ but each input file is either -mrelocatable or -mrelocatable-lib. */
+ if (! (elf_elfheader (obfd)->e_flags & EF_PPC_RELOCATABLE_LIB)
+ && (new_flags & (EF_PPC_RELOCATABLE_LIB | EF_PPC_RELOCATABLE))
+ && (old_flags & (EF_PPC_RELOCATABLE_LIB | EF_PPC_RELOCATABLE)))
+ elf_elfheader (obfd)->e_flags |= EF_PPC_RELOCATABLE;
+
+ /* Do not warn about eabi vs. V.4 mismatch, just or in the bit if
+ any module uses it. */
+ elf_elfheader (obfd)->e_flags |= (new_flags & EF_PPC_EMB);
+
+ new_flags &= ~(EF_PPC_RELOCATABLE | EF_PPC_RELOCATABLE_LIB | EF_PPC_EMB);
+ old_flags &= ~(EF_PPC_RELOCATABLE | EF_PPC_RELOCATABLE_LIB | EF_PPC_EMB);
+
+ /* Warn about any other mismatches. */
+ if (new_flags != old_flags)
+ {
+ error = TRUE;
+ (*_bfd_error_handler)
+ (_("%B: uses different e_flags (0x%lx) fields "
+ "than previous modules (0x%lx)"),
+ ibfd, (long) new_flags, (long) old_flags);
+ }
+
+ if (error)
+ {
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+ppc_elf_vle_split16 (bfd *output_bfd, bfd_byte *contents,
+ bfd_vma offset, bfd_vma relocation,
+ split16_format_type split16_format)
+
+{
+ bfd_vma insn, top5, bottom11;
+
+ insn = bfd_get_32 (output_bfd, contents + offset);
+ top5 = relocation >> 11;
+ top5 = top5 << (split16_format == split16a_type ? 20 : 16);
+ bottom11 = relocation & 0x7ff;
+ insn |= top5;
+ insn |= bottom11;
+ bfd_put_32 (output_bfd, insn, contents + offset);
+}
+
+\f
+/* Choose which PLT scheme to use, and set .plt flags appropriately.
+ Returns -1 on error, 0 for old PLT, 1 for new PLT. */
+int
+ppc_elf_select_plt_layout (bfd *output_bfd ATTRIBUTE_UNUSED,
+ struct bfd_link_info *info,
+ enum ppc_elf_plt_type plt_style,
+ int emit_stub_syms)
+{
+ struct ppc_elf_link_hash_table *htab;
+ flagword flags;
+
+ htab = ppc_elf_hash_table (info);
+
+ htab->emit_stub_syms = emit_stub_syms;
+
+ if (htab->plt_type == PLT_UNSET)
+ {
+ struct elf_link_hash_entry *h;
+
+ if (plt_style == PLT_OLD)
+ htab->plt_type = PLT_OLD;
+ else if (info->shared
+ && htab->elf.dynamic_sections_created
+ && (h = elf_link_hash_lookup (&htab->elf, "_mcount",
+ FALSE, FALSE, TRUE)) != NULL
+ && (h->type == STT_FUNC
+ || h->needs_plt)
+ && h->ref_regular
+ && !(SYMBOL_CALLS_LOCAL (info, h)
+ || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+ && h->root.type == bfd_link_hash_undefweak)))
+ {
+ /* Profiling of shared libs (and pies) is not supported with
+ secure plt, because ppc32 does profiling before a
+ function prologue and a secure plt pic call stubs needs
+ r30 to be set up. */
+ htab->plt_type = PLT_OLD;
+ }
+ else
+ {
+ bfd *ibfd;
+ enum ppc_elf_plt_type plt_type = plt_style;
+
+ /* Look through the reloc flags left by ppc_elf_check_relocs.
+ Use the old style bss plt if a file makes plt calls
+ without using the new relocs, and if ld isn't given
+ --secure-plt and we never see REL16 relocs. */
+ if (plt_type == PLT_UNSET)
+ plt_type = PLT_OLD;
+ for (ibfd = info->input_bfds; ibfd; ibfd = ibfd->link_next)
+ if (is_ppc_elf (ibfd))
+ {
+ if (ppc_elf_tdata (ibfd)->has_rel16)
+ plt_type = PLT_NEW;
+ else if (ppc_elf_tdata (ibfd)->makes_plt_call)
+ {
+ plt_type = PLT_OLD;
+ htab->old_bfd = ibfd;
+ break;
+ }
+ }
+ htab->plt_type = plt_type;
+ }
+ }
+ if (htab->plt_type == PLT_OLD && plt_style == PLT_NEW)
+ {
+ if (htab->old_bfd != NULL)
+ info->callbacks->einfo (_("%P: bss-plt forced due to %B\n"),
+ htab->old_bfd);
+ else
+ info->callbacks->einfo (_("%P: bss-plt forced by profiling\n"));
+ }
+
+ BFD_ASSERT (htab->plt_type != PLT_VXWORKS);
+
+ if (htab->plt_type == PLT_NEW)
+ {
+ flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
+ | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+
+ /* The new PLT is a loaded section. */
+ if (htab->plt != NULL
+ && !bfd_set_section_flags (htab->elf.dynobj, htab->plt, flags))
+ return -1;
+
+ /* The new GOT is not executable. */
+ if (htab->got != NULL
+ && !bfd_set_section_flags (htab->elf.dynobj, htab->got, flags))
+ return -1;
+ }
+ else
+ {
+ /* Stop an unused .glink section from affecting .text alignment. */
+ if (htab->glink != NULL
+ && !bfd_set_section_alignment (htab->elf.dynobj, htab->glink, 0))
+ return -1;
+ }
+ return htab->plt_type == PLT_NEW;
+}
+\f
+/* Return the section that should be marked against GC for a given
+ relocation. */
+
+static asection *
+ppc_elf_gc_mark_hook (asection *sec,
+ struct bfd_link_info *info,
+ Elf_Internal_Rela *rel,
+ struct elf_link_hash_entry *h,
+ Elf_Internal_Sym *sym)
+{
+ if (h != NULL)
+ switch (ELF32_R_TYPE (rel->r_info))
+ {
+ case R_PPC_GNU_VTINHERIT:
+ case R_PPC_GNU_VTENTRY:
+ return NULL;
+ }
+
+ return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
+}
+
+/* Update the got, plt and dynamic reloc reference counts for the
+ section being removed. */
+
+static bfd_boolean
+ppc_elf_gc_sweep_hook (bfd *abfd,
+ struct bfd_link_info *info,
+ asection *sec,
+ const Elf_Internal_Rela *relocs)
+{
+ struct ppc_elf_link_hash_table *htab;
+ Elf_Internal_Shdr *symtab_hdr;
+ struct elf_link_hash_entry **sym_hashes;
+ bfd_signed_vma *local_got_refcounts;
+ const Elf_Internal_Rela *rel, *relend;
+ asection *got2;
+
+ if (info->relocatable)
+ return TRUE;
+
+ if ((sec->flags & SEC_ALLOC) == 0)
+ return TRUE;
+
+ elf_section_data (sec)->local_dynrel = NULL;
+
+ htab = ppc_elf_hash_table (info);
+ symtab_hdr = &elf_symtab_hdr (abfd);
+ sym_hashes = elf_sym_hashes (abfd);
+ local_got_refcounts = elf_local_got_refcounts (abfd);
+ got2 = bfd_get_section_by_name (abfd, ".got2");
+
+ relend = relocs + sec->reloc_count;
+ for (rel = relocs; rel < relend; rel++)
+ {
+ unsigned long r_symndx;
+ enum elf_ppc_reloc_type r_type;
+ struct elf_link_hash_entry *h = NULL;
+
+ r_symndx = ELF32_R_SYM (rel->r_info);
+ if (r_symndx >= symtab_hdr->sh_info)
+ {
+ struct elf_dyn_relocs **pp, *p;
+ struct ppc_elf_link_hash_entry *eh;
+
+ h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+ while (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+ eh = (struct ppc_elf_link_hash_entry *) h;
+
+ for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next)
+ if (p->sec == sec)
+ {
+ /* Everything must go for SEC. */
+ *pp = p->next;
+ break;
+ }
+ }
+
+ r_type = ELF32_R_TYPE (rel->r_info);
+ if (!htab->is_vxworks
+ && h == NULL
+ && local_got_refcounts != NULL
+ && (!info->shared
+ || is_branch_reloc (r_type)))
+ {
+ struct plt_entry **local_plt = (struct plt_entry **)
+ (local_got_refcounts + symtab_hdr->sh_info);
+ char *local_got_tls_masks = (char *)
+ (local_plt + symtab_hdr->sh_info);
+ if ((local_got_tls_masks[r_symndx] & PLT_IFUNC) != 0)
+ {
+ struct plt_entry **ifunc = local_plt + r_symndx;
+ bfd_vma addend = 0;
+ struct plt_entry *ent;
+
+ if (r_type == R_PPC_PLTREL24 && info->shared)
+ addend = rel->r_addend;
+ ent = find_plt_ent (ifunc, got2, addend);
+ if (ent->plt.refcount > 0)
+ ent->plt.refcount -= 1;
+ continue;
+ }
+ }
+
+ switch (r_type)
+ {
+ case R_PPC_GOT_TLSLD16:
+ case R_PPC_GOT_TLSLD16_LO:
+ case R_PPC_GOT_TLSLD16_HI:
+ case R_PPC_GOT_TLSLD16_HA:
+ case R_PPC_GOT_TLSGD16:
+ case R_PPC_GOT_TLSGD16_LO:
+ case R_PPC_GOT_TLSGD16_HI:
+ case R_PPC_GOT_TLSGD16_HA:
+ case R_PPC_GOT_TPREL16:
+ case R_PPC_GOT_TPREL16_LO:
+ case R_PPC_GOT_TPREL16_HI:
+ case R_PPC_GOT_TPREL16_HA:
+ case R_PPC_GOT_DTPREL16:
+ case R_PPC_GOT_DTPREL16_LO:
+ case R_PPC_GOT_DTPREL16_HI:
+ case R_PPC_GOT_DTPREL16_HA:
+ case R_PPC_GOT16:
+ case R_PPC_GOT16_LO:
+ case R_PPC_GOT16_HI:
+ case R_PPC_GOT16_HA:
+ if (h != NULL)
+ {
+ if (h->got.refcount > 0)
+ h->got.refcount--;
+ if (!info->shared)
+ {
+ struct plt_entry *ent;
+
+ ent = find_plt_ent (&h->plt.plist, NULL, 0);
+ if (ent != NULL && ent->plt.refcount > 0)
+ ent->plt.refcount -= 1;
+ }
+ }
+ else if (local_got_refcounts != NULL)
+ {
+ if (local_got_refcounts[r_symndx] > 0)
+ local_got_refcounts[r_symndx]--;
+ }
+ break;
+
+ case R_PPC_REL24:
+ case R_PPC_REL14:
+ case R_PPC_REL14_BRTAKEN:
+ case R_PPC_REL14_BRNTAKEN:
+ case R_PPC_REL32:
+ if (h == NULL || h == htab->elf.hgot)
+ break;
+ /* Fall thru */
+
+ case R_PPC_ADDR32:
+ case R_PPC_ADDR24:
+ case R_PPC_ADDR16:
+ case R_PPC_ADDR16_LO:
+ case R_PPC_ADDR16_HI:
+ case R_PPC_ADDR16_HA:
+ case R_PPC_ADDR14:
+ case R_PPC_ADDR14_BRTAKEN:
+ case R_PPC_ADDR14_BRNTAKEN:
+ case R_PPC_UADDR32:
+ case R_PPC_UADDR16:
+ if (info->shared)
+ break;
+
+ case R_PPC_PLT32:
+ case R_PPC_PLTREL24:
+ case R_PPC_PLTREL32:
+ case R_PPC_PLT16_LO:
+ case R_PPC_PLT16_HI:
+ case R_PPC_PLT16_HA:
+ if (h != NULL)
+ {
+ bfd_vma addend = 0;
+ struct plt_entry *ent;
+
+ if (r_type == R_PPC_PLTREL24 && info->shared)
+ addend = rel->r_addend;
+ ent = find_plt_ent (&h->plt.plist, got2, addend);
+ if (ent != NULL && ent->plt.refcount > 0)
+ ent->plt.refcount -= 1;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ return TRUE;
+}
+\f
+/* Set plt output section type, htab->tls_get_addr, and call the
+ generic ELF tls_setup function. */
+
+asection *
+ppc_elf_tls_setup (bfd *obfd,
+ struct bfd_link_info *info,
+ int no_tls_get_addr_opt)
+{
+ struct ppc_elf_link_hash_table *htab;
+
+ htab = ppc_elf_hash_table (info);
+ htab->tls_get_addr = elf_link_hash_lookup (&htab->elf, "__tls_get_addr",
+ FALSE, FALSE, TRUE);
+ if (!no_tls_get_addr_opt)
+ {
+ struct elf_link_hash_entry *opt, *tga;
+ opt = elf_link_hash_lookup (&htab->elf, "__tls_get_addr_opt",
+ FALSE, FALSE, TRUE);
+ if (opt != NULL
+ && (opt->root.type == bfd_link_hash_defined
+ || opt->root.type == bfd_link_hash_defweak))
+ {
+ /* If glibc supports an optimized __tls_get_addr call stub,
+ signalled by the presence of __tls_get_addr_opt, and we'll
+ be calling __tls_get_addr via a plt call stub, then
+ make __tls_get_addr point to __tls_get_addr_opt. */
+ tga = htab->tls_get_addr;
+ if (htab->elf.dynamic_sections_created
+ && tga != NULL
+ && (tga->type == STT_FUNC
+ || tga->needs_plt)
+ && !(SYMBOL_CALLS_LOCAL (info, tga)
+ || (ELF_ST_VISIBILITY (tga->other) != STV_DEFAULT
+ && tga->root.type == bfd_link_hash_undefweak)))
+ {
+ struct plt_entry *ent;
+ for (ent = tga->plt.plist; ent != NULL; ent = ent->next)
+ if (ent->plt.refcount > 0)
+ break;
+ if (ent != NULL)
+ {
+ tga->root.type = bfd_link_hash_indirect;
+ tga->root.u.i.link = &opt->root;
+ ppc_elf_copy_indirect_symbol (info, opt, tga);
+ if (opt->dynindx != -1)
+ {
+ /* Use __tls_get_addr_opt in dynamic relocations. */
+ opt->dynindx = -1;
+ _bfd_elf_strtab_delref (elf_hash_table (info)->dynstr,
+ opt->dynstr_index);
+ if (!bfd_elf_link_record_dynamic_symbol (info, opt))
+ return FALSE;
+ }
+ htab->tls_get_addr = opt;
+ }
+ }
+ }
+ else
+ no_tls_get_addr_opt = TRUE;
+ }
+ htab->no_tls_get_addr_opt = no_tls_get_addr_opt;
+ if (htab->plt_type == PLT_NEW
+ && htab->plt != NULL
+ && htab->plt->output_section != NULL)
+ {
+ elf_section_type (htab->plt->output_section) = SHT_PROGBITS;
+ elf_section_flags (htab->plt->output_section) = SHF_ALLOC + SHF_WRITE;
+ }
+
+ return _bfd_elf_tls_setup (obfd, info);
+}
+
+/* Return TRUE iff REL is a branch reloc with a global symbol matching
+ HASH. */
+
+static bfd_boolean
+branch_reloc_hash_match (const bfd *ibfd,
+ const Elf_Internal_Rela *rel,
+ const struct elf_link_hash_entry *hash)
+{
+ Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (ibfd);
+ enum elf_ppc_reloc_type r_type = ELF32_R_TYPE (rel->r_info);
+ unsigned int r_symndx = ELF32_R_SYM (rel->r_info);
+
+ if (r_symndx >= symtab_hdr->sh_info && is_branch_reloc (r_type))
+ {
+ struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (ibfd);
+ struct elf_link_hash_entry *h;
+
+ h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+ while (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+ if (h == hash)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* Run through all the TLS relocs looking for optimization
+ opportunities. */
+
+bfd_boolean
+ppc_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED,
+ struct bfd_link_info *info)
+{
+ bfd *ibfd;
+ asection *sec;
+ struct ppc_elf_link_hash_table *htab;
+ int pass;
+
+ if (info->relocatable || !info->executable)
+ return TRUE;
+
+ htab = ppc_elf_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
+
+ /* Make two passes through the relocs. First time check that tls
+ relocs involved in setting up a tls_get_addr call are indeed
+ followed by such a call. If they are not, don't do any tls
+ optimization. On the second pass twiddle tls_mask flags to
+ notify relocate_section that optimization can be done, and
+ adjust got and plt refcounts. */
+ for (pass = 0; pass < 2; ++pass)
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+ {
+ Elf_Internal_Sym *locsyms = NULL;
+ Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (ibfd);
+ asection *got2 = bfd_get_section_by_name (ibfd, ".got2");
+
+ for (sec = ibfd->sections; sec != NULL; sec = sec->next)
+ if (sec->has_tls_reloc && !bfd_is_abs_section (sec->output_section))
+ {
+ Elf_Internal_Rela *relstart, *rel, *relend;
+ int expecting_tls_get_addr = 0;
+
+ /* Read the relocations. */
+ relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
+ info->keep_memory);
+ if (relstart == NULL)
+ return FALSE;
+
+ relend = relstart + sec->reloc_count;
+ for (rel = relstart; rel < relend; rel++)
+ {
+ enum elf_ppc_reloc_type r_type;
+ unsigned long r_symndx;
+ struct elf_link_hash_entry *h = NULL;
+ char *tls_mask;
+ char tls_set, tls_clear;
+ bfd_boolean is_local;
+ bfd_signed_vma *got_count;
+
+ r_symndx = ELF32_R_SYM (rel->r_info);
+ if (r_symndx >= symtab_hdr->sh_info)
+ {
+ struct elf_link_hash_entry **sym_hashes;
+
+ sym_hashes = elf_sym_hashes (ibfd);
+ h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+ while (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+ }
+
+ is_local = FALSE;
+ if (h == NULL
+ || !h->def_dynamic)
+ is_local = TRUE;
+
+ r_type = ELF32_R_TYPE (rel->r_info);
+ /* If this section has old-style __tls_get_addr calls
+ without marker relocs, then check that each
+ __tls_get_addr call reloc is preceded by a reloc
+ that conceivably belongs to the __tls_get_addr arg
+ setup insn. If we don't find matching arg setup
+ relocs, don't do any tls optimization. */
+ if (pass == 0
+ && sec->has_tls_get_addr_call
+ && h != NULL
+ && h == htab->tls_get_addr
+ && !expecting_tls_get_addr
+ && is_branch_reloc (r_type))
+ {
+ info->callbacks->minfo ("%H __tls_get_addr lost arg, "
+ "TLS optimization disabled\n",
+ ibfd, sec, rel->r_offset);
+ if (elf_section_data (sec)->relocs != relstart)
+ free (relstart);
+ return TRUE;
+ }
+
+ expecting_tls_get_addr = 0;
+ switch (r_type)
+ {
+ case R_PPC_GOT_TLSLD16:
+ case R_PPC_GOT_TLSLD16_LO:
+ expecting_tls_get_addr = 1;
+ /* Fall thru */
+
+ case R_PPC_GOT_TLSLD16_HI:
+ case R_PPC_GOT_TLSLD16_HA:
+ /* These relocs should never be against a symbol
+ defined in a shared lib. Leave them alone if
+ that turns out to be the case. */
+ if (!is_local)
+ continue;
+
+ /* LD -> LE */
+ tls_set = 0;
+ tls_clear = TLS_LD;
+ break;
+
+ case R_PPC_GOT_TLSGD16:
+ case R_PPC_GOT_TLSGD16_LO:
+ expecting_tls_get_addr = 1;
+ /* Fall thru */
+
+ case R_PPC_GOT_TLSGD16_HI:
+ case R_PPC_GOT_TLSGD16_HA:
+ if (is_local)
+ /* GD -> LE */
+ tls_set = 0;
+ else
+ /* GD -> IE */
+ tls_set = TLS_TLS | TLS_TPRELGD;
+ tls_clear = TLS_GD;
+ break;
+
+ case R_PPC_GOT_TPREL16:
+ case R_PPC_GOT_TPREL16_LO:
+ case R_PPC_GOT_TPREL16_HI:
+ case R_PPC_GOT_TPREL16_HA:
+ if (is_local)
+ {
+ /* IE -> LE */
+ tls_set = 0;
+ tls_clear = TLS_TPREL;
+ break;
+ }
+ else
+ continue;
+
+ case R_PPC_TLSGD:
+ case R_PPC_TLSLD:
+ expecting_tls_get_addr = 2;
+ tls_set = 0;
+ tls_clear = 0;
+ break;
+
+ default:
+ continue;
+ }
+
+ if (pass == 0)
+ {
+ if (!expecting_tls_get_addr
+ || (expecting_tls_get_addr == 1
+ && !sec->has_tls_get_addr_call))
+ continue;
+
+ if (rel + 1 < relend
+ && branch_reloc_hash_match (ibfd, rel + 1,
+ htab->tls_get_addr))
+ continue;
+
+ /* Uh oh, we didn't find the expected call. We
+ could just mark this symbol to exclude it
+ from tls optimization but it's safer to skip
+ the entire optimization. */
+ info->callbacks->minfo (_("%H arg lost __tls_get_addr, "
+ "TLS optimization disabled\n"),
+ ibfd, sec, rel->r_offset);
+ if (elf_section_data (sec)->relocs != relstart)
+ free (relstart);
+ return TRUE;
+ }
+
+ if (expecting_tls_get_addr)
+ {
+ struct plt_entry *ent;
+ bfd_vma addend = 0;
+
+ if (info->shared
+ && ELF32_R_TYPE (rel[1].r_info) == R_PPC_PLTREL24)
+ addend = rel[1].r_addend;
+ ent = find_plt_ent (&htab->tls_get_addr->plt.plist,
+ got2, addend);
+ if (ent != NULL && ent->plt.refcount > 0)
+ ent->plt.refcount -= 1;
+
+ if (expecting_tls_get_addr == 2)
+ continue;
+ }
+
+ if (h != NULL)
+ {
+ tls_mask = &ppc_elf_hash_entry (h)->tls_mask;
+ got_count = &h->got.refcount;
+ }
+ else
+ {
+ bfd_signed_vma *lgot_refs;
+ struct plt_entry **local_plt;
+ char *lgot_masks;
+
+ if (locsyms == NULL)
+ {
+ locsyms = (Elf_Internal_Sym *) symtab_hdr->contents;
+ if (locsyms == NULL)
+ locsyms = bfd_elf_get_elf_syms (ibfd, symtab_hdr,
+ symtab_hdr->sh_info,
+ 0, NULL, NULL, NULL);
+ if (locsyms == NULL)
+ {
+ if (elf_section_data (sec)->relocs != relstart)
+ free (relstart);
+ return FALSE;
+ }
+ }
+ lgot_refs = elf_local_got_refcounts (ibfd);
+ if (lgot_refs == NULL)
+ abort ();
+ local_plt = (struct plt_entry **)
+ (lgot_refs + symtab_hdr->sh_info);
+ lgot_masks = (char *) (local_plt + symtab_hdr->sh_info);
+ tls_mask = &lgot_masks[r_symndx];
+ got_count = &lgot_refs[r_symndx];
+ }
+
+ if (tls_set == 0)
+ {
+ /* We managed to get rid of a got entry. */
+ if (*got_count > 0)
+ *got_count -= 1;
+ }
+
+ *tls_mask |= tls_set;
+ *tls_mask &= ~tls_clear;
+ }
+
+ if (elf_section_data (sec)->relocs != relstart)
+ free (relstart);
+ }
+
+ if (locsyms != NULL
+ && (symtab_hdr->contents != (unsigned char *) locsyms))
+ {
+ if (!info->keep_memory)
+ free (locsyms);
+ else
+ symtab_hdr->contents = (unsigned char *) locsyms;
+ }
+ }
+ return TRUE;
+}
+\f
+/* Return true if we have dynamic relocs that apply to read-only sections. */
+
+static bfd_boolean
+readonly_dynrelocs (struct elf_link_hash_entry *h)
+{
+ struct elf_dyn_relocs *p;
+
+ for (p = ppc_elf_hash_entry (h)->dyn_relocs; p != NULL; p = p->next)
+ {
+ asection *s = p->sec->output_section;
+
+ if (s != NULL
+ && ((s->flags & (SEC_READONLY | SEC_ALLOC))
+ == (SEC_READONLY | SEC_ALLOC)))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* Adjust a symbol defined by a dynamic object and referenced by a
+ regular object. The current definition is in some section of the
+ dynamic object, but we're not including those sections. We have to
+ change the definition to something the rest of the link can
+ understand. */
+
+static bfd_boolean
+ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
+ struct elf_link_hash_entry *h)
+{
+ struct ppc_elf_link_hash_table *htab;
+ asection *s;
+
+#ifdef DEBUG
+ fprintf (stderr, "ppc_elf_adjust_dynamic_symbol called for %s\n",
+ h->root.root.string);
+#endif
+
+ /* Make sure we know what is going on here. */
+ htab = ppc_elf_hash_table (info);
+ BFD_ASSERT (htab->elf.dynobj != NULL
+ && (h->needs_plt
+ || h->type == STT_GNU_IFUNC
+ || h->u.weakdef != NULL
+ || (h->def_dynamic
+ && h->ref_regular
+ && !h->def_regular)));
+
+ /* Deal with function syms. */
+ if (h->type == STT_FUNC
+ || h->type == STT_GNU_IFUNC
+ || h->needs_plt)
+ {
+ /* Clear procedure linkage table information for any symbol that
+ won't need a .plt entry. */
+ struct plt_entry *ent;
+ for (ent = h->plt.plist; ent != NULL; ent = ent->next)
+ if (ent->plt.refcount > 0)
+ break;
+ if (ent == NULL
+ || (h->type != STT_GNU_IFUNC
+ && (SYMBOL_CALLS_LOCAL (info, h)
+ || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+ && h->root.type == bfd_link_hash_undefweak))))
+ {
+ /* A PLT entry is not required/allowed when:
+
+ 1. We are not using ld.so; because then the PLT entry
+ can't be set up, so we can't use one. In this case,
+ ppc_elf_adjust_dynamic_symbol won't even be called.
+
+ 2. GC has rendered the entry unused.
+
+ 3. We know for certain that a call to this symbol
+ will go to this object, or will remain undefined. */
+ h->plt.plist = NULL;
+ h->needs_plt = 0;
+ }
+ else
+ {
+ /* After adjust_dynamic_symbol, non_got_ref set in the
+ non-shared case means that we have allocated space in
+ .dynbss for the symbol and thus dyn_relocs for this
+ symbol should be discarded.
+ If we get here we know we are making a PLT entry for this
+ symbol, and in an executable we'd normally resolve
+ relocations against this symbol to the PLT entry. Allow
+ dynamic relocs if the reference is weak, and the dynamic
+ relocs will not cause text relocation. */
+ if (!h->ref_regular_nonweak
+ && h->non_got_ref
+ && h->type != STT_GNU_IFUNC
+ && !htab->is_vxworks
+ && !ppc_elf_hash_entry (h)->has_sda_refs
+ && !readonly_dynrelocs (h))
+ h->non_got_ref = 0;
+ }
+ return TRUE;
+ }
+ else
+ h->plt.plist = NULL;
+
+ /* If this is a weak symbol, and there is a real definition, the
+ processor independent code will have arranged for us to see the
+ real definition first, and we can just use the same value. */
+ if (h->u.weakdef != NULL)
+ {
+ BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined
+ || h->u.weakdef->root.type == bfd_link_hash_defweak);
+ h->root.u.def.section = h->u.weakdef->root.u.def.section;
+ h->root.u.def.value = h->u.weakdef->root.u.def.value;
+ if (ELIMINATE_COPY_RELOCS)
+ h->non_got_ref = h->u.weakdef->non_got_ref;
+ return TRUE;
+ }
+
+ /* This is a reference to a symbol defined by a dynamic object which
+ is not a function. */
+
+ /* If we are creating a shared library, we must presume that the
+ only references to the symbol are via the global offset table.
+ For such cases we need not do anything here; the relocations will
+ be handled correctly by relocate_section. */
+ if (info->shared)
+ return TRUE;
+
+ /* If there are no references to this symbol that do not use the
+ GOT, we don't need to generate a copy reloc. */
+ if (!h->non_got_ref)
+ return TRUE;
+
+ /* If we didn't find any dynamic relocs in read-only sections, then
+ we'll be keeping the dynamic relocs and avoiding the copy reloc.
+ We can't do this if there are any small data relocations. This
+ doesn't work on VxWorks, where we can not have dynamic
+ relocations (other than copy and jump slot relocations) in an
+ executable. */
+ if (ELIMINATE_COPY_RELOCS
+ && !ppc_elf_hash_entry (h)->has_sda_refs
+ && !htab->is_vxworks
+ && !h->def_regular
+ && !readonly_dynrelocs (h))
+ {
+ h->non_got_ref = 0;
+ return TRUE;
+ }
+
+ /* We must allocate the symbol in our .dynbss section, which will
+ become part of the .bss section of the executable. There will be
+ an entry for this symbol in the .dynsym section. The dynamic
+ object will contain position independent code, so all references
+ from the dynamic object to this symbol will go through the global
+ offset table. The dynamic linker will use the .dynsym entry to
+ determine the address it must put in the global offset table, so
+ both the dynamic object and the regular object will refer to the
+ same memory location for the variable.
+
+ Of course, if the symbol is referenced using SDAREL relocs, we
+ must instead allocate it in .sbss. */
+
+ if (ppc_elf_hash_entry (h)->has_sda_refs)
+ s = htab->dynsbss;
+ else
+ s = htab->dynbss;
+ BFD_ASSERT (s != NULL);
+
+ /* We must generate a R_PPC_COPY reloc to tell the dynamic linker to
+ copy the initial value out of the dynamic object and into the
+ runtime process image. We need to remember the offset into the
+ .rela.bss section we are going to use. */
+ if ((h->root.u.def.section->flags & SEC_ALLOC) != 0 && h->size != 0)
+ {
+ asection *srel;
+
+ if (ppc_elf_hash_entry (h)->has_sda_refs)
+ srel = htab->relsbss;
+ else
+ srel = htab->relbss;
+ BFD_ASSERT (srel != NULL);
+ srel->size += sizeof (Elf32_External_Rela);
+ h->needs_copy = 1;
+ }
+
+ return _bfd_elf_adjust_dynamic_copy (h, s);
+}
+\f
+/* Generate a symbol to mark plt call stubs. For non-PIC code the sym is
+ xxxxxxxx.plt_call32.<callee> where xxxxxxxx is a hex number, usually 0,
+ specifying the addend on the plt relocation. For -fpic code, the sym
+ is xxxxxxxx.plt_pic32.<callee>, and for -fPIC
+ xxxxxxxx.got2.plt_pic32.<callee>. */
+
+static bfd_boolean
+add_stub_sym (struct plt_entry *ent,
+ struct elf_link_hash_entry *h,
+ struct bfd_link_info *info)
+{
+ struct elf_link_hash_entry *sh;
+ size_t len1, len2, len3;
+ char *name;
+ const char *stub;
+ struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info);
+
+ if (info->shared)
+ stub = ".plt_pic32.";
+ else
+ stub = ".plt_call32.";
+
+ len1 = strlen (h->root.root.string);
+ len2 = strlen (stub);
+ len3 = 0;
+ if (ent->sec)
+ len3 = strlen (ent->sec->name);
+ name = bfd_malloc (len1 + len2 + len3 + 9);
+ if (name == NULL)
+ return FALSE;
+ sprintf (name, "%08x", (unsigned) ent->addend & 0xffffffff);
+ if (ent->sec)
+ memcpy (name + 8, ent->sec->name, len3);
+ memcpy (name + 8 + len3, stub, len2);
+ memcpy (name + 8 + len3 + len2, h->root.root.string, len1 + 1);
+ sh = elf_link_hash_lookup (&htab->elf, name, TRUE, FALSE, FALSE);
+ if (sh == NULL)
+ return FALSE;
+ if (sh->root.type == bfd_link_hash_new)
+ {
+ sh->root.type = bfd_link_hash_defined;
+ sh->root.u.def.section = htab->glink;
+ sh->root.u.def.value = ent->glink_offset;
+ sh->ref_regular = 1;
+ sh->def_regular = 1;
+ sh->ref_regular_nonweak = 1;
+ sh->forced_local = 1;
+ sh->non_elf = 0;
+ }
+ return TRUE;
+}
+
+/* Allocate NEED contiguous space in .got, and return the offset.
+ Handles allocation of the got header when crossing 32k. */
+
+static bfd_vma
+allocate_got (struct ppc_elf_link_hash_table *htab, unsigned int need)
+{
+ bfd_vma where;
+ unsigned int max_before_header;
+
+ if (htab->plt_type == PLT_VXWORKS)
+ {
+ where = htab->got->size;
+ htab->got->size += need;
+ }
+ else
+ {
+ max_before_header = htab->plt_type == PLT_NEW ? 32768 : 32764;
+ if (need <= htab->got_gap)
+ {
+ where = max_before_header - htab->got_gap;
+ htab->got_gap -= need;
+ }
+ else
+ {
+ if (htab->got->size + need > max_before_header
+ && htab->got->size <= max_before_header)
+ {
+ htab->got_gap = max_before_header - htab->got->size;
+ htab->got->size = max_before_header + htab->got_header_size;
+ }
+ where = htab->got->size;
+ htab->got->size += need;
+ }
+ }
+ return where;
+}
+
+/* Allocate space in associated reloc sections for dynamic relocs. */
+
+static bfd_boolean
+allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
+{
+ struct bfd_link_info *info = inf;
+ struct ppc_elf_link_hash_entry *eh;
+ struct ppc_elf_link_hash_table *htab;
+ struct elf_dyn_relocs *p;
+
+ if (h->root.type == bfd_link_hash_indirect)
+ return TRUE;
+
+ htab = ppc_elf_hash_table (info);
+ if (htab->elf.dynamic_sections_created
+ || h->type == STT_GNU_IFUNC)
+ {
+ struct plt_entry *ent;
+ bfd_boolean doneone = FALSE;
+ bfd_vma plt_offset = 0, glink_offset = 0;
+ bfd_boolean dyn;
+
+ for (ent = h->plt.plist; ent != NULL; ent = ent->next)
+ if (ent->plt.refcount > 0)
+ {
+ /* Make sure this symbol is output as a dynamic symbol. */
+ if (h->dynindx == -1
+ && !h->forced_local
+ && !h->def_regular
+ && htab->elf.dynamic_sections_created)
+ {
+ if (! bfd_elf_link_record_dynamic_symbol (info, h))
+ return FALSE;
+ }
+
+ dyn = htab->elf.dynamic_sections_created;
+ if (info->shared
+ || h->type == STT_GNU_IFUNC
+ || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h))
+ {
+ asection *s = htab->plt;
+ if (!dyn || h->dynindx == -1)
+ s = htab->iplt;
+
+ if (htab->plt_type == PLT_NEW || !dyn || h->dynindx == -1)
+ {
+ if (!doneone)
+ {
+ plt_offset = s->size;
+ s->size += 4;
+ }
+ ent->plt.offset = plt_offset;
+
+ s = htab->glink;
+ if (!doneone || info->shared)
+ {
+ glink_offset = s->size;
+ s->size += GLINK_ENTRY_SIZE;
+ if (h == htab->tls_get_addr
+ && !htab->no_tls_get_addr_opt)
+ s->size += TLS_GET_ADDR_GLINK_SIZE - GLINK_ENTRY_SIZE;
+ }
+ if (!doneone
+ && !info->shared
+ && h->def_dynamic
+ && !h->def_regular)
+ {
+ h->root.u.def.section = s;
+ h->root.u.def.value = glink_offset;
+ }
+ ent->glink_offset = glink_offset;
+
+ if (htab->emit_stub_syms
+ && !add_stub_sym (ent, h, info))
+ return FALSE;
+ }
+ else
+ {
+ if (!doneone)
+ {
+ /* If this is the first .plt entry, make room
+ for the special first entry. */
+ if (s->size == 0)
+ s->size += htab->plt_initial_entry_size;
+
+ /* The PowerPC PLT is actually composed of two
+ parts, the first part is 2 words (for a load
+ and a jump), and then there is a remaining
+ word available at the end. */
+ plt_offset = (htab->plt_initial_entry_size
+ + (htab->plt_slot_size
+ * ((s->size
+ - htab->plt_initial_entry_size)
+ / htab->plt_entry_size)));
+
+ /* If this symbol is not defined in a regular
+ file, and we are not generating a shared
+ library, then set the symbol to this location
+ in the .plt. This is to avoid text
+ relocations, and is required to make
+ function pointers compare as equal between
+ the normal executable and the shared library. */
+ if (! info->shared
+ && h->def_dynamic
+ && !h->def_regular)
+ {
+ h->root.u.def.section = s;
+ h->root.u.def.value = plt_offset;
+ }
+
+ /* Make room for this entry. */
+ s->size += htab->plt_entry_size;
+ /* After the 8192nd entry, room for two entries
+ is allocated. */
+ if (htab->plt_type == PLT_OLD
+ && (s->size - htab->plt_initial_entry_size)
+ / htab->plt_entry_size
+ > PLT_NUM_SINGLE_ENTRIES)
+ s->size += htab->plt_entry_size;
+ }
+ ent->plt.offset = plt_offset;
+ }
+
+ /* We also need to make an entry in the .rela.plt section. */
+ if (!doneone)
+ {
+ if (!htab->elf.dynamic_sections_created
+ || h->dynindx == -1)
+ htab->reliplt->size += sizeof (Elf32_External_Rela);
+ else
+ {
+ htab->relplt->size += sizeof (Elf32_External_Rela);
+
+ if (htab->plt_type == PLT_VXWORKS)
+ {
+ /* Allocate space for the unloaded relocations. */
+ if (!info->shared
+ && htab->elf.dynamic_sections_created)
+ {
+ if (ent->plt.offset
+ == (bfd_vma) htab->plt_initial_entry_size)
+ {
+ htab->srelplt2->size
+ += (sizeof (Elf32_External_Rela)
+ * VXWORKS_PLTRESOLVE_RELOCS);
+ }
+
+ htab->srelplt2->size
+ += (sizeof (Elf32_External_Rela)
+ * VXWORKS_PLT_NON_JMP_SLOT_RELOCS);
+ }
+
+ /* Every PLT entry has an associated GOT entry in
+ .got.plt. */
+ htab->sgotplt->size += 4;
+ }
+ }
+ doneone = TRUE;
+ }
+ }
+ else
+ ent->plt.offset = (bfd_vma) -1;
+ }
+ else
+ ent->plt.offset = (bfd_vma) -1;
+
+ if (!doneone)
+ {
+ h->plt.plist = NULL;
+ h->needs_plt = 0;
+ }
+ }
+ else
+ {
+ h->plt.plist = NULL;
+ h->needs_plt = 0;
+ }
+
+ eh = (struct ppc_elf_link_hash_entry *) h;
+ if (eh->elf.got.refcount > 0)
+ {
+ bfd_boolean dyn;
+ unsigned int need;
+
+ /* Make sure this symbol is output as a dynamic symbol. */
+ if (eh->elf.dynindx == -1
+ && !eh->elf.forced_local
+ && eh->elf.type != STT_GNU_IFUNC
+ && htab->elf.dynamic_sections_created)
+ {
+ if (!bfd_elf_link_record_dynamic_symbol (info, &eh->elf))
+ return FALSE;
+ }
+
+ need = 0;
+ if ((eh->tls_mask & TLS_TLS) != 0)
+ {
+ if ((eh->tls_mask & TLS_LD) != 0)
+ {
+ if (!eh->elf.def_dynamic)
+ /* We'll just use htab->tlsld_got.offset. This should
+ always be the case. It's a little odd if we have
+ a local dynamic reloc against a non-local symbol. */
+ htab->tlsld_got.refcount += 1;
+ else
+ need += 8;
+ }
+ if ((eh->tls_mask & TLS_GD) != 0)
+ need += 8;
+ if ((eh->tls_mask & (TLS_TPREL | TLS_TPRELGD)) != 0)
+ need += 4;
+ if ((eh->tls_mask & TLS_DTPREL) != 0)
+ need += 4;
+ }
+ else
+ need += 4;
+ if (need == 0)
+ eh->elf.got.offset = (bfd_vma) -1;
+ else
+ {
+ eh->elf.got.offset = allocate_got (htab, need);
+ dyn = htab->elf.dynamic_sections_created;
+ if ((info->shared
+ || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, &eh->elf))
+ && (ELF_ST_VISIBILITY (eh->elf.other) == STV_DEFAULT
+ || eh->elf.root.type != bfd_link_hash_undefweak))
+ {
+ asection *rsec = htab->relgot;
+
+ if (eh->elf.type == STT_GNU_IFUNC)
+ rsec = htab->reliplt;
+ /* All the entries we allocated need relocs.
+ Except LD only needs one. */
+ if ((eh->tls_mask & TLS_LD) != 0
+ && eh->elf.def_dynamic)
+ need -= 4;
+ rsec->size += need * (sizeof (Elf32_External_Rela) / 4);
+ }
+ }
+ }
+ else
+ eh->elf.got.offset = (bfd_vma) -1;
+
+ if (eh->dyn_relocs == NULL
+ || !htab->elf.dynamic_sections_created)
+ return TRUE;
+
+ /* In the shared -Bsymbolic case, discard space allocated for
+ dynamic pc-relative relocs against symbols which turn out to be
+ defined in regular objects. For the normal shared case, discard
+ space for relocs that have become local due to symbol visibility
+ changes. */
+
+ if (info->shared)
+ {
+ /* Relocs that use pc_count are those that appear on a call insn,
+ or certain REL relocs (see must_be_dyn_reloc) that can be
+ generated via assembly. We want calls to protected symbols to
+ resolve directly to the function rather than going via the plt.
+ If people want function pointer comparisons to work as expected
+ then they should avoid writing weird assembly. */
+ if (SYMBOL_CALLS_LOCAL (info, h))
+ {
+ struct elf_dyn_relocs **pp;
+
+ for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
+ {
+ p->count -= p->pc_count;
+ p->pc_count = 0;
+ if (p->count == 0)
+ *pp = p->next;
+ else
+ pp = &p->next;
+ }
+ }
+
+ if (htab->is_vxworks)
+ {
+ struct elf_dyn_relocs **pp;
+
+ for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
+ {
+ if (strcmp (p->sec->output_section->name, ".tls_vars") == 0)
+ *pp = p->next;
+ else
+ pp = &p->next;
+ }
+ }
+
+ /* Discard relocs on undefined symbols that must be local. */
+ if (eh->dyn_relocs != NULL
+ && h->root.type == bfd_link_hash_undefined
+ && (ELF_ST_VISIBILITY (h->other) == STV_HIDDEN
+ || ELF_ST_VISIBILITY (h->other) == STV_INTERNAL))
+ eh->dyn_relocs = NULL;
+
+ /* Also discard relocs on undefined weak syms with non-default
+ visibility. */
+ if (eh->dyn_relocs != NULL
+ && h->root.type == bfd_link_hash_undefweak)