+ return TRUE;
+}
+
+/* Actually merge two .got tables. */
+
+static void
+elf64_alpha_merge_gots (bfd *a, bfd *b)
+{
+ int total = alpha_elf_tdata (a)->total_got_size;
+ bfd *bsub;
+
+ /* Remember local expansion. */
+ {
+ int e = alpha_elf_tdata (b)->local_got_size;
+ total += e;
+ alpha_elf_tdata (a)->local_got_size += e;
+ }
+
+ for (bsub = b; bsub ; bsub = alpha_elf_tdata (bsub)->in_got_link_next)
+ {
+ struct alpha_elf_got_entry **local_got_entries;
+ struct alpha_elf_link_hash_entry **hashes;
+ Elf_Internal_Shdr *symtab_hdr;
+ int i, n;
+
+ /* Let the local .got entries know they are part of a new subsegment. */
+ local_got_entries = alpha_elf_tdata (bsub)->local_got_entries;
+ if (local_got_entries)
+ {
+ n = elf_tdata (bsub)->symtab_hdr.sh_info;
+ for (i = 0; i < n; ++i)
+ {
+ struct alpha_elf_got_entry *ent;
+ for (ent = local_got_entries[i]; ent; ent = ent->next)
+ ent->gotobj = a;
+ }
+ }
+
+ /* Merge the global .got entries. */
+ hashes = alpha_elf_sym_hashes (bsub);
+ symtab_hdr = &elf_tdata (bsub)->symtab_hdr;
+
+ n = NUM_SHDR_ENTRIES (symtab_hdr) - symtab_hdr->sh_info;
+ for (i = 0; i < n; ++i)
+ {
+ struct alpha_elf_got_entry *ae, *be, **pbe, **start;
+ struct alpha_elf_link_hash_entry *h;
+
+ h = hashes[i];
+ while (h->root.root.type == bfd_link_hash_indirect
+ || h->root.root.type == bfd_link_hash_warning)
+ h = (struct alpha_elf_link_hash_entry *)h->root.root.u.i.link;
+
+ pbe = start = &h->got_entries;
+ while ((be = *pbe) != NULL)
+ {
+ if (be->use_count == 0)
+ {
+ *pbe = be->next;
+ memset (be, 0xa5, sizeof (*be));
+ goto kill;
+ }
+ if (be->gotobj != b)
+ goto next;
+
+ for (ae = *start; ae ; ae = ae->next)
+ if (ae->gotobj == a
+ && ae->reloc_type == be->reloc_type
+ && ae->addend == be->addend)
+ {
+ ae->flags |= be->flags;
+ ae->use_count += be->use_count;
+ *pbe = be->next;
+ memset (be, 0xa5, sizeof (*be));
+ goto kill;
+ }
+ be->gotobj = a;
+ total += alpha_got_entry_size (be->reloc_type);
+
+ next:;
+ pbe = &be->next;
+ kill:;
+ }
+ }
+
+ alpha_elf_tdata (bsub)->gotobj = a;
+ }
+ alpha_elf_tdata (a)->total_got_size = total;
+
+ /* Merge the two in_got chains. */
+ {
+ bfd *next;
+
+ bsub = a;
+ while ((next = alpha_elf_tdata (bsub)->in_got_link_next) != NULL)
+ bsub = next;
+
+ alpha_elf_tdata (bsub)->in_got_link_next = b;
+ }
+}
+
+/* Calculate the offsets for the got entries. */
+
+static bfd_boolean
+elf64_alpha_calc_got_offsets_for_symbol (struct alpha_elf_link_hash_entry *h,
+ void * arg ATTRIBUTE_UNUSED)
+{
+ struct alpha_elf_got_entry *gotent;
+
+ for (gotent = h->got_entries; gotent; gotent = gotent->next)
+ if (gotent->use_count > 0)
+ {
+ struct alpha_elf_obj_tdata *td;
+ bfd_size_type *plge;
+
+ td = alpha_elf_tdata (gotent->gotobj);
+ plge = &td->got->size;
+ gotent->got_offset = *plge;
+ *plge += alpha_got_entry_size (gotent->reloc_type);
+ }
+
+ return TRUE;
+}
+
+static void
+elf64_alpha_calc_got_offsets (struct bfd_link_info *info)
+{
+ bfd *i, *got_list;
+ struct alpha_elf_link_hash_table * htab;
+
+ htab = alpha_elf_hash_table (info);
+ if (htab == NULL)
+ return;
+ got_list = htab->got_list;
+
+ /* First, zero out the .got sizes, as we may be recalculating the
+ .got after optimizing it. */
+ for (i = got_list; i ; i = alpha_elf_tdata(i)->got_link_next)
+ alpha_elf_tdata(i)->got->size = 0;
+
+ /* Next, fill in the offsets for all the global entries. */
+ alpha_elf_link_hash_traverse (htab,
+ elf64_alpha_calc_got_offsets_for_symbol,
+ NULL);
+
+ /* Finally, fill in the offsets for the local entries. */
+ for (i = got_list; i ; i = alpha_elf_tdata(i)->got_link_next)
+ {
+ bfd_size_type got_offset = alpha_elf_tdata(i)->got->size;
+ bfd *j;
+
+ for (j = i; j ; j = alpha_elf_tdata(j)->in_got_link_next)
+ {
+ struct alpha_elf_got_entry **local_got_entries, *gotent;
+ int k, n;
+
+ local_got_entries = alpha_elf_tdata(j)->local_got_entries;
+ if (!local_got_entries)
+ continue;
+
+ for (k = 0, n = elf_tdata(j)->symtab_hdr.sh_info; k < n; ++k)
+ for (gotent = local_got_entries[k]; gotent; gotent = gotent->next)
+ if (gotent->use_count > 0)
+ {
+ gotent->got_offset = got_offset;
+ got_offset += alpha_got_entry_size (gotent->reloc_type);
+ }
+ }
+
+ alpha_elf_tdata(i)->got->size = got_offset;
+ }
+}
+
+/* Constructs the gots. */
+
+static bfd_boolean
+elf64_alpha_size_got_sections (struct bfd_link_info *info,
+ bfd_boolean may_merge)
+{
+ bfd *i, *got_list, *cur_got_obj = NULL;
+ struct alpha_elf_link_hash_table * htab;
+
+ htab = alpha_elf_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
+ got_list = htab->got_list;
+
+ /* On the first time through, pretend we have an existing got list
+ consisting of all of the input files. */
+ if (got_list == NULL)
+ {
+ for (i = info->input_bfds; i ; i = i->link.next)
+ {
+ bfd *this_got;
+
+ if (! is_alpha_elf (i))
+ continue;
+
+ this_got = alpha_elf_tdata (i)->gotobj;
+ if (this_got == NULL)
+ continue;
+
+ /* We are assuming no merging has yet occurred. */
+ BFD_ASSERT (this_got == i);
+
+ if (alpha_elf_tdata (this_got)->total_got_size > MAX_GOT_SIZE)
+ {
+ /* Yikes! A single object file has too many entries. */
+ (*_bfd_error_handler)
+ (_("%B: .got subsegment exceeds 64K (size %d)"),
+ i, alpha_elf_tdata (this_got)->total_got_size);
+ return FALSE;
+ }
+
+ if (got_list == NULL)
+ got_list = this_got;
+ else
+ alpha_elf_tdata(cur_got_obj)->got_link_next = this_got;
+ cur_got_obj = this_got;
+ }
+
+ /* Strange degenerate case of no got references. */
+ if (got_list == NULL)
+ return TRUE;
+
+ htab->got_list = got_list;
+ }
+
+ cur_got_obj = got_list;
+ if (cur_got_obj == NULL)
+ return FALSE;
+
+ if (may_merge)
+ {
+ i = alpha_elf_tdata(cur_got_obj)->got_link_next;
+ while (i != NULL)
+ {
+ if (elf64_alpha_can_merge_gots (cur_got_obj, i))
+ {
+ elf64_alpha_merge_gots (cur_got_obj, i);
+
+ alpha_elf_tdata(i)->got->size = 0;
+ i = alpha_elf_tdata(i)->got_link_next;
+ alpha_elf_tdata(cur_got_obj)->got_link_next = i;
+ }
+ else
+ {
+ cur_got_obj = i;
+ i = alpha_elf_tdata(i)->got_link_next;
+ }
+ }
+ }
+
+ /* Once the gots have been merged, fill in the got offsets for
+ everything therein. */
+ elf64_alpha_calc_got_offsets (info);
+
+ return TRUE;