+ return TRUE;
+}
+
+/* Add room for N relocations to the .rel(a).dyn section in ABFD. */
+
+static void
+mips_elf_allocate_dynamic_relocations (bfd *abfd, struct bfd_link_info *info,
+ unsigned int n)
+{
+ asection *s;
+ struct mips_elf_link_hash_table *htab;
+
+ htab = mips_elf_hash_table (info);
+ BFD_ASSERT (htab != NULL);
+
+ s = mips_elf_rel_dyn_section (info, FALSE);
+ BFD_ASSERT (s != NULL);
+
+ if (htab->is_vxworks)
+ s->size += n * MIPS_ELF_RELA_SIZE (abfd);
+ else
+ {
+ if (s->size == 0)
+ {
+ /* Make room for a null element. */
+ s->size += MIPS_ELF_REL_SIZE (abfd);
+ ++s->reloc_count;
+ }
+ s->size += n * MIPS_ELF_REL_SIZE (abfd);
+ }
+}
+\f
+/* A htab_traverse callback for GOT entries. Set boolean *DATA to true
+ if the GOT entry is for an indirect or warning symbol. */
+
+static int
+mips_elf_check_recreate_got (void **entryp, void *data)
+{
+ struct mips_got_entry *entry;
+ bfd_boolean *must_recreate;
+
+ entry = (struct mips_got_entry *) *entryp;
+ must_recreate = (bfd_boolean *) data;
+ if (entry->abfd != NULL && entry->symndx == -1)
+ {
+ struct mips_elf_link_hash_entry *h;
+
+ h = entry->d.h;
+ if (h->root.root.type == bfd_link_hash_indirect
+ || h->root.root.type == bfd_link_hash_warning)
+ {
+ *must_recreate = TRUE;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* A htab_traverse callback for GOT entries. Add all entries to
+ hash table *DATA, converting entries for indirect and warning
+ symbols into entries for the target symbol. Set *DATA to null
+ on error. */
+
+static int
+mips_elf_recreate_got (void **entryp, void *data)
+{
+ htab_t *new_got;
+ struct mips_got_entry *entry;
+ void **slot;
+
+ new_got = (htab_t *) data;
+ entry = (struct mips_got_entry *) *entryp;
+ if (entry->abfd != NULL && entry->symndx == -1)
+ {
+ struct mips_elf_link_hash_entry *h;
+
+ h = entry->d.h;
+ while (h->root.root.type == bfd_link_hash_indirect
+ || h->root.root.type == bfd_link_hash_warning)
+ {
+ BFD_ASSERT (h->global_got_area == GGA_NONE);
+ h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
+ }
+ entry->d.h = h;
+ }
+ slot = htab_find_slot (*new_got, entry, INSERT);
+ if (slot == NULL)
+ {
+ *new_got = NULL;
+ return 0;
+ }
+ if (*slot == NULL)
+ *slot = entry;
+ else
+ free (entry);
+ return 1;
+}
+
+/* If any entries in G->got_entries are for indirect or warning symbols,
+ replace them with entries for the target symbol. */
+
+static bfd_boolean
+mips_elf_resolve_final_got_entries (struct mips_got_info *g)
+{
+ bfd_boolean must_recreate;
+ htab_t new_got;
+
+ must_recreate = FALSE;
+ htab_traverse (g->got_entries, mips_elf_check_recreate_got, &must_recreate);
+ if (must_recreate)
+ {
+ new_got = htab_create (htab_size (g->got_entries),
+ mips_elf_got_entry_hash,
+ mips_elf_got_entry_eq, NULL);
+ htab_traverse (g->got_entries, mips_elf_recreate_got, &new_got);
+ if (new_got == NULL)
+ return FALSE;
+
+ /* Each entry in g->got_entries has either been copied to new_got
+ or freed. Now delete the hash table itself. */
+ htab_delete (g->got_entries);
+ g->got_entries = new_got;
+ }
+ return TRUE;
+}
+
+/* A mips_elf_link_hash_traverse callback for which DATA points
+ to a mips_got_info. Count the number of type (3) entries. */
+
+static int
+mips_elf_count_got_symbols (struct mips_elf_link_hash_entry *h, void *data)
+{
+ struct mips_got_info *g;
+
+ g = (struct mips_got_info *) data;
+ if (h->global_got_area != GGA_NONE)
+ {
+ if (h->root.forced_local || h->root.dynindx == -1)
+ {
+ /* We no longer need this entry if it was only used for
+ relocations; those relocations will be against the
+ null or section symbol instead of H. */
+ if (h->global_got_area != GGA_RELOC_ONLY)
+ g->local_gotno++;
+ h->global_got_area = GGA_NONE;
+ }
+ else
+ {
+ g->global_gotno++;
+ if (h->global_got_area == GGA_RELOC_ONLY)
+ g->reloc_only_gotno++;
+ }
+ }
+ return 1;
+}
+\f
+/* Compute the hash value of the bfd in a bfd2got hash entry. */
+
+static hashval_t
+mips_elf_bfd2got_entry_hash (const void *entry_)
+{
+ const struct mips_elf_bfd2got_hash *entry
+ = (struct mips_elf_bfd2got_hash *)entry_;
+
+ return entry->bfd->id;
+}
+
+/* Check whether two hash entries have the same bfd. */
+
+static int
+mips_elf_bfd2got_entry_eq (const void *entry1, const void *entry2)
+{
+ const struct mips_elf_bfd2got_hash *e1
+ = (const struct mips_elf_bfd2got_hash *)entry1;
+ const struct mips_elf_bfd2got_hash *e2
+ = (const struct mips_elf_bfd2got_hash *)entry2;
+
+ return e1->bfd == e2->bfd;
+}
+
+/* In a multi-got link, determine the GOT to be used for IBFD. G must
+ be the master GOT data. */
+
+static struct mips_got_info *
+mips_elf_got_for_ibfd (struct mips_got_info *g, bfd *ibfd)
+{
+ struct mips_elf_bfd2got_hash e, *p;
+
+ if (! g->bfd2got)
+ return g;
+
+ e.bfd = ibfd;
+ p = htab_find (g->bfd2got, &e);
+ return p ? p->g : NULL;
+}
+
+/* Use BFD2GOT to find ABFD's got entry, creating one if none exists.
+ Return NULL if an error occured. */
+
+static struct mips_got_info *
+mips_elf_get_got_for_bfd (struct htab *bfd2got, bfd *output_bfd,
+ bfd *input_bfd)
+{
+ struct mips_elf_bfd2got_hash bfdgot_entry, *bfdgot;
+ struct mips_got_info *g;
+ void **bfdgotp;
+
+ bfdgot_entry.bfd = input_bfd;
+ bfdgotp = htab_find_slot (bfd2got, &bfdgot_entry, INSERT);
+ bfdgot = (struct mips_elf_bfd2got_hash *) *bfdgotp;
+
+ if (bfdgot == NULL)
+ {
+ bfdgot = ((struct mips_elf_bfd2got_hash *)
+ bfd_alloc (output_bfd, sizeof (struct mips_elf_bfd2got_hash)));
+ if (bfdgot == NULL)
+ return NULL;
+
+ *bfdgotp = bfdgot;
+
+ g = ((struct mips_got_info *)
+ bfd_alloc (output_bfd, sizeof (struct mips_got_info)));
+ if (g == NULL)
+ return NULL;
+
+ bfdgot->bfd = input_bfd;
+ bfdgot->g = g;
+
+ g->global_gotsym = NULL;
+ g->global_gotno = 0;
+ g->reloc_only_gotno = 0;
+ g->local_gotno = 0;
+ g->page_gotno = 0;
+ g->assigned_gotno = -1;
+ g->tls_gotno = 0;
+ g->tls_assigned_gotno = 0;
+ g->tls_ldm_offset = MINUS_ONE;
+ g->got_entries = htab_try_create (1, mips_elf_multi_got_entry_hash,
+ mips_elf_multi_got_entry_eq, NULL);
+ if (g->got_entries == NULL)
+ return NULL;
+
+ g->got_page_entries = htab_try_create (1, mips_got_page_entry_hash,
+ mips_got_page_entry_eq, NULL);
+ if (g->got_page_entries == NULL)
+ return NULL;
+
+ g->bfd2got = NULL;
+ g->next = NULL;
+ }
+
+ return bfdgot->g;
+}
+
+/* A htab_traverse callback for the entries in the master got.
+ Create one separate got for each bfd that has entries in the global
+ got, such that we can tell how many local and global entries each
+ bfd requires. */
+
+static int
+mips_elf_make_got_per_bfd (void **entryp, void *p)
+{
+ struct mips_got_entry *entry = (struct mips_got_entry *)*entryp;
+ struct mips_elf_got_per_bfd_arg *arg = (struct mips_elf_got_per_bfd_arg *)p;
+ struct mips_got_info *g;
+
+ g = mips_elf_get_got_for_bfd (arg->bfd2got, arg->obfd, entry->abfd);
+ if (g == NULL)
+ {
+ arg->obfd = NULL;
+ return 0;
+ }
+
+ /* Insert the GOT entry in the bfd's got entry hash table. */
+ entryp = htab_find_slot (g->got_entries, entry, INSERT);