+ got->entries = NULL;
+ got->n_slots[R_8] = 0;
+ got->n_slots[R_16] = 0;
+ got->n_slots[R_32] = 0;
+ got->local_n_slots = 0;
+ got->offset = (bfd_vma) -1;
+}
+
+/* Destruct GOT. */
+
+static void
+elf_m68k_clear_got (struct elf_m68k_got *got)
+{
+ if (got->entries != NULL)
+ {
+ htab_delete (got->entries);
+ got->entries = NULL;
+ }
+}
+
+/* Create and empty GOT structure. INFO is the context where memory
+ should be allocated. */
+
+static struct elf_m68k_got *
+elf_m68k_create_empty_got (struct bfd_link_info *info)
+{
+ struct elf_m68k_got *got;
+
+ got = bfd_alloc (elf_hash_table (info)->dynobj, sizeof (*got));
+ if (got == NULL)
+ return NULL;
+
+ elf_m68k_init_got (got);
+
+ return got;
+}
+
+/* Initialize KEY. */
+
+static void
+elf_m68k_init_got_entry_key (struct elf_m68k_got_entry_key *key,
+ struct elf_link_hash_entry *h,
+ const bfd *abfd, unsigned long symndx,
+ enum elf_m68k_reloc_type reloc_type)
+{
+ if (elf_m68k_reloc_got_type (reloc_type) == R_68K_TLS_LDM32)
+ /* All TLS_LDM relocations share a single GOT entry. */
+ {
+ key->bfd = NULL;
+ key->symndx = 0;
+ }
+ else if (h != NULL)
+ /* Global symbols are identified with their got_entry_key. */
+ {
+ key->bfd = NULL;
+ key->symndx = elf_m68k_hash_entry (h)->got_entry_key;
+ BFD_ASSERT (key->symndx != 0);
+ }
+ else
+ /* Local symbols are identified by BFD they appear in and symndx. */
+ {
+ key->bfd = abfd;
+ key->symndx = symndx;
+ }
+
+ key->type = reloc_type;
+}
+
+/* Calculate hash of got_entry.
+ ??? Is it good? */
+
+static hashval_t
+elf_m68k_got_entry_hash (const void *_entry)
+{
+ const struct elf_m68k_got_entry_key *key;
+
+ key = &((const struct elf_m68k_got_entry *) _entry)->key_;
+
+ return (key->symndx
+ + (key->bfd != NULL ? (int) key->bfd->id : -1)
+ + elf_m68k_reloc_got_type (key->type));
+}
+
+/* Check if two got entries are equal. */
+
+static int
+elf_m68k_got_entry_eq (const void *_entry1, const void *_entry2)
+{
+ const struct elf_m68k_got_entry_key *key1;
+ const struct elf_m68k_got_entry_key *key2;
+
+ key1 = &((const struct elf_m68k_got_entry *) _entry1)->key_;
+ key2 = &((const struct elf_m68k_got_entry *) _entry2)->key_;
+
+ return (key1->bfd == key2->bfd
+ && key1->symndx == key2->symndx
+ && (elf_m68k_reloc_got_type (key1->type)
+ == elf_m68k_reloc_got_type (key2->type)));
+}
+
+/* When using negative offsets, we allocate one extra R_8, one extra R_16
+ and one extra R_32 slots to simplify handling of 2-slot entries during
+ offset allocation -- hence -1 for R_8 slots and -2 for R_16 slots. */
+
+/* Maximal number of R_8 slots in a single GOT. */
+#define ELF_M68K_R_8_MAX_N_SLOTS_IN_GOT(INFO) \
+ (elf_m68k_hash_table (INFO)->use_neg_got_offsets_p \
+ ? (0x40 - 1) \
+ : 0x20)
+
+/* Maximal number of R_8 and R_16 slots in a single GOT. */
+#define ELF_M68K_R_8_16_MAX_N_SLOTS_IN_GOT(INFO) \
+ (elf_m68k_hash_table (INFO)->use_neg_got_offsets_p \
+ ? (0x4000 - 2) \
+ : 0x2000)
+
+/* SEARCH - simply search the hashtable, don't insert new entries or fail when
+ the entry cannot be found.
+ FIND_OR_CREATE - search for an existing entry, but create new if there's
+ no such.
+ MUST_FIND - search for an existing entry and assert that it exist.
+ MUST_CREATE - assert that there's no such entry and create new one. */
+enum elf_m68k_get_entry_howto
+ {
+ SEARCH,
+ FIND_OR_CREATE,
+ MUST_FIND,
+ MUST_CREATE
+ };
+
+/* Get or create (depending on HOWTO) entry with KEY in GOT.
+ INFO is context in which memory should be allocated (can be NULL if
+ HOWTO is SEARCH or MUST_FIND). */
+
+static struct elf_m68k_got_entry *
+elf_m68k_get_got_entry (struct elf_m68k_got *got,
+ const struct elf_m68k_got_entry_key *key,
+ enum elf_m68k_get_entry_howto howto,
+ struct bfd_link_info *info)
+{
+ struct elf_m68k_got_entry entry_;
+ struct elf_m68k_got_entry *entry;
+ void **ptr;
+
+ BFD_ASSERT ((info == NULL) == (howto == SEARCH || howto == MUST_FIND));
+
+ if (got->entries == NULL)
+ /* This is the first entry in ABFD. Initialize hashtable. */
+ {
+ if (howto == SEARCH)
+ return NULL;
+
+ got->entries = htab_try_create (ELF_M68K_R_8_MAX_N_SLOTS_IN_GOT
+ (info),
+ elf_m68k_got_entry_hash,
+ elf_m68k_got_entry_eq, NULL);
+ if (got->entries == NULL)
+ {
+ bfd_set_error (bfd_error_no_memory);
+ return NULL;
+ }
+ }
+
+ entry_.key_ = *key;
+ ptr = htab_find_slot (got->entries, &entry_, (howto != SEARCH
+ ? INSERT : NO_INSERT));
+ if (ptr == NULL)
+ {
+ if (howto == SEARCH)
+ /* Entry not found. */
+ return NULL;
+
+ /* We're out of memory. */
+ bfd_set_error (bfd_error_no_memory);
+ return NULL;
+ }
+
+ if (*ptr == NULL)
+ /* We didn't find the entry and we're asked to create a new one. */
+ {
+ BFD_ASSERT (howto != MUST_FIND && howto != SEARCH);
+
+ entry = bfd_alloc (elf_hash_table (info)->dynobj, sizeof (*entry));
+ if (entry == NULL)
+ return NULL;
+
+ /* Initialize new entry. */
+ entry->key_ = *key;
+
+ entry->u.s1.refcount = 0;
+
+ /* Mark the entry as not initialized. */
+ entry->key_.type = R_68K_max;
+
+ *ptr = entry;
+ }
+ else
+ /* We found the entry. */
+ {
+ BFD_ASSERT (howto != MUST_CREATE);
+
+ entry = *ptr;
+ }
+
+ return entry;
+}
+
+/* Update GOT counters when merging entry of WAS type with entry of NEW type.
+ Return the value to which ENTRY's type should be set. */
+
+static enum elf_m68k_reloc_type
+elf_m68k_update_got_entry_type (struct elf_m68k_got *got,
+ enum elf_m68k_reloc_type was,
+ enum elf_m68k_reloc_type new_reloc)
+{
+ enum elf_m68k_got_offset_size was_size;
+ enum elf_m68k_got_offset_size new_size;
+ bfd_vma n_slots;
+
+ if (was == R_68K_max)
+ /* The type of the entry is not initialized yet. */
+ {
+ /* Update all got->n_slots counters, including n_slots[R_32]. */
+ was_size = R_LAST;
+
+ was = new_reloc;
+ }
+ else
+ {
+ /* !!! We, probably, should emit an error rather then fail on assert
+ in such a case. */
+ BFD_ASSERT (elf_m68k_reloc_got_type (was)
+ == elf_m68k_reloc_got_type (new_reloc));
+
+ was_size = elf_m68k_reloc_got_offset_size (was);
+ }
+
+ new_size = elf_m68k_reloc_got_offset_size (new_reloc);
+ n_slots = elf_m68k_reloc_got_n_slots (new_reloc);
+
+ while (was_size > new_size)
+ {
+ --was_size;
+ got->n_slots[was_size] += n_slots;
+ }
+
+ if (new_reloc > was)
+ /* Relocations are ordered from bigger got offset size to lesser,
+ so choose the relocation type with lesser offset size. */
+ was = new_reloc;
+
+ return was;
+}
+
+/* Update GOT counters when removing an entry of type TYPE. */
+
+static void
+elf_m68k_remove_got_entry_type (struct elf_m68k_got *got,
+ enum elf_m68k_reloc_type type)
+{
+ enum elf_m68k_got_offset_size os;
+ bfd_vma n_slots;
+
+ n_slots = elf_m68k_reloc_got_n_slots (type);
+
+ /* Decrese counter of slots with offset size corresponding to TYPE
+ and all greater offset sizes. */
+ for (os = elf_m68k_reloc_got_offset_size (type); os <= R_32; ++os)
+ {
+ BFD_ASSERT (got->n_slots[os] >= n_slots);
+
+ got->n_slots[os] -= n_slots;
+ }
+}
+
+/* Add new or update existing entry to GOT.
+ H, ABFD, TYPE and SYMNDX is data for the entry.
+ INFO is a context where memory should be allocated. */
+
+static struct elf_m68k_got_entry *
+elf_m68k_add_entry_to_got (struct elf_m68k_got *got,
+ struct elf_link_hash_entry *h,
+ const bfd *abfd,
+ enum elf_m68k_reloc_type reloc_type,
+ unsigned long symndx,
+ struct bfd_link_info *info)
+{
+ struct elf_m68k_got_entry_key key_;
+ struct elf_m68k_got_entry *entry;
+
+ if (h != NULL && elf_m68k_hash_entry (h)->got_entry_key == 0)
+ elf_m68k_hash_entry (h)->got_entry_key
+ = elf_m68k_multi_got (info)->global_symndx++;
+
+ elf_m68k_init_got_entry_key (&key_, h, abfd, symndx, reloc_type);
+
+ entry = elf_m68k_get_got_entry (got, &key_, FIND_OR_CREATE, info);
+ if (entry == NULL)
+ return NULL;
+
+ /* Determine entry's type and update got->n_slots counters. */
+ entry->key_.type = elf_m68k_update_got_entry_type (got,
+ entry->key_.type,
+ reloc_type);
+
+ /* Update refcount. */
+ ++entry->u.s1.refcount;
+
+ if (entry->u.s1.refcount == 1)
+ /* We see this entry for the first time. */
+ {
+ if (entry->key_.bfd != NULL)
+ got->local_n_slots += elf_m68k_reloc_got_n_slots (entry->key_.type);
+ }
+
+ BFD_ASSERT (got->n_slots[R_32] >= got->local_n_slots);
+
+ if ((got->n_slots[R_8]
+ > ELF_M68K_R_8_MAX_N_SLOTS_IN_GOT (info))
+ || (got->n_slots[R_16]
+ > ELF_M68K_R_8_16_MAX_N_SLOTS_IN_GOT (info)))
+ /* This BFD has too many relocation. */
+ {
+ if (got->n_slots[R_8] > ELF_M68K_R_8_MAX_N_SLOTS_IN_GOT (info))
+ (*_bfd_error_handler) (_("%B: GOT overflow: "
+ "Number of relocations with 8-bit "
+ "offset > %d"),
+ abfd,
+ ELF_M68K_R_8_MAX_N_SLOTS_IN_GOT (info));
+ else
+ (*_bfd_error_handler) (_("%B: GOT overflow: "
+ "Number of relocations with 8- or 16-bit "
+ "offset > %d"),
+ abfd,
+ ELF_M68K_R_8_16_MAX_N_SLOTS_IN_GOT (info));
+
+ return NULL;
+ }
+
+ return entry;
+}
+
+/* Compute the hash value of the bfd in a bfd2got hash entry. */
+
+static hashval_t
+elf_m68k_bfd2got_entry_hash (const void *entry)
+{
+ const struct elf_m68k_bfd2got_entry *e;
+
+ e = (const struct elf_m68k_bfd2got_entry *) entry;
+
+ return e->bfd->id;
+}
+
+/* Check whether two hash entries have the same bfd. */
+
+static int
+elf_m68k_bfd2got_entry_eq (const void *entry1, const void *entry2)
+{
+ const struct elf_m68k_bfd2got_entry *e1;
+ const struct elf_m68k_bfd2got_entry *e2;
+
+ e1 = (const struct elf_m68k_bfd2got_entry *) entry1;
+ e2 = (const struct elf_m68k_bfd2got_entry *) entry2;
+
+ return e1->bfd == e2->bfd;
+}
+
+/* Destruct a bfd2got entry. */
+
+static void
+elf_m68k_bfd2got_entry_del (void *_entry)
+{
+ struct elf_m68k_bfd2got_entry *entry;
+
+ entry = (struct elf_m68k_bfd2got_entry *) _entry;
+
+ BFD_ASSERT (entry->got != NULL);
+ elf_m68k_clear_got (entry->got);
+}
+
+/* Find existing or create new (depending on HOWTO) bfd2got entry in
+ MULTI_GOT. ABFD is the bfd we need a GOT for. INFO is a context where
+ memory should be allocated. */
+
+static struct elf_m68k_bfd2got_entry *
+elf_m68k_get_bfd2got_entry (struct elf_m68k_multi_got *multi_got,
+ const bfd *abfd,
+ enum elf_m68k_get_entry_howto howto,
+ struct bfd_link_info *info)
+{
+ struct elf_m68k_bfd2got_entry entry_;
+ void **ptr;
+ struct elf_m68k_bfd2got_entry *entry;
+
+ BFD_ASSERT ((info == NULL) == (howto == SEARCH || howto == MUST_FIND));
+
+ if (multi_got->bfd2got == NULL)
+ /* This is the first GOT. Initialize bfd2got. */
+ {
+ if (howto == SEARCH)
+ return NULL;
+
+ multi_got->bfd2got = htab_try_create (1, elf_m68k_bfd2got_entry_hash,
+ elf_m68k_bfd2got_entry_eq,
+ elf_m68k_bfd2got_entry_del);
+ if (multi_got->bfd2got == NULL)
+ {
+ bfd_set_error (bfd_error_no_memory);
+ return NULL;
+ }
+ }
+
+ entry_.bfd = abfd;
+ ptr = htab_find_slot (multi_got->bfd2got, &entry_, (howto != SEARCH
+ ? INSERT : NO_INSERT));
+ if (ptr == NULL)
+ {
+ if (howto == SEARCH)
+ /* Entry not found. */
+ return NULL;
+
+ /* We're out of memory. */
+ bfd_set_error (bfd_error_no_memory);
+ return NULL;
+ }
+
+ if (*ptr == NULL)
+ /* Entry was not found. Create new one. */
+ {
+ BFD_ASSERT (howto != MUST_FIND && howto != SEARCH);
+
+ entry = ((struct elf_m68k_bfd2got_entry *)
+ bfd_alloc (elf_hash_table (info)->dynobj, sizeof (*entry)));
+ if (entry == NULL)
+ return NULL;
+
+ entry->bfd = abfd;
+
+ entry->got = elf_m68k_create_empty_got (info);
+ if (entry->got == NULL)
+ return NULL;
+
+ *ptr = entry;
+ }
+ else
+ {
+ BFD_ASSERT (howto != MUST_CREATE);
+
+ /* Return existing entry. */
+ entry = *ptr;
+ }
+
+ return entry;
+}
+
+struct elf_m68k_can_merge_gots_arg
+{
+ /* A current_got that we constructing a DIFF against. */
+ struct elf_m68k_got *big;
+
+ /* GOT holding entries not present or that should be changed in
+ BIG. */
+ struct elf_m68k_got *diff;
+
+ /* Context where to allocate memory. */
+ struct bfd_link_info *info;
+
+ /* Error flag. */
+ bfd_boolean error_p;
+};
+
+/* Process a single entry from the small GOT to see if it should be added
+ or updated in the big GOT. */
+
+static int
+elf_m68k_can_merge_gots_1 (void **_entry_ptr, void *_arg)
+{
+ const struct elf_m68k_got_entry *entry1;
+ struct elf_m68k_can_merge_gots_arg *arg;
+ const struct elf_m68k_got_entry *entry2;
+ enum elf_m68k_reloc_type type;
+
+ entry1 = (const struct elf_m68k_got_entry *) *_entry_ptr;
+ arg = (struct elf_m68k_can_merge_gots_arg *) _arg;
+
+ entry2 = elf_m68k_get_got_entry (arg->big, &entry1->key_, SEARCH, NULL);
+
+ if (entry2 != NULL)
+ /* We found an existing entry. Check if we should update it. */
+ {
+ type = elf_m68k_update_got_entry_type (arg->diff,
+ entry2->key_.type,
+ entry1->key_.type);
+
+ if (type == entry2->key_.type)
+ /* ENTRY1 doesn't update data in ENTRY2. Skip it.
+ To skip creation of difference entry we use the type,
+ which we won't see in GOT entries for sure. */
+ type = R_68K_max;
+ }
+ else
+ /* We didn't find the entry. Add entry1 to DIFF. */
+ {
+ BFD_ASSERT (entry1->key_.type != R_68K_max);
+
+ type = elf_m68k_update_got_entry_type (arg->diff,
+ R_68K_max, entry1->key_.type);
+
+ if (entry1->key_.bfd != NULL)
+ arg->diff->local_n_slots += elf_m68k_reloc_got_n_slots (type);
+ }
+
+ if (type != R_68K_max)
+ /* Create an entry in DIFF. */
+ {
+ struct elf_m68k_got_entry *entry;
+
+ entry = elf_m68k_get_got_entry (arg->diff, &entry1->key_, MUST_CREATE,
+ arg->info);
+ if (entry == NULL)
+ {
+ arg->error_p = TRUE;
+ return 0;
+ }
+
+ entry->key_.type = type;
+ }
+
+ return 1;
+}
+
+/* Return TRUE if SMALL GOT can be added to BIG GOT without overflowing it.
+ Construct DIFF GOT holding the entries which should be added or updated
+ in BIG GOT to accumulate information from SMALL.
+ INFO is the context where memory should be allocated. */
+
+static bfd_boolean
+elf_m68k_can_merge_gots (struct elf_m68k_got *big,
+ const struct elf_m68k_got *small,
+ struct bfd_link_info *info,
+ struct elf_m68k_got *diff)
+{
+ struct elf_m68k_can_merge_gots_arg arg_;
+
+ BFD_ASSERT (small->offset == (bfd_vma) -1);
+
+ arg_.big = big;
+ arg_.diff = diff;
+ arg_.info = info;
+ arg_.error_p = FALSE;
+ htab_traverse_noresize (small->entries, elf_m68k_can_merge_gots_1, &arg_);
+ if (arg_.error_p)
+ {
+ diff->offset = 0;
+ return FALSE;
+ }
+
+ /* Check for overflow. */
+ if ((big->n_slots[R_8] + arg_.diff->n_slots[R_8]
+ > ELF_M68K_R_8_MAX_N_SLOTS_IN_GOT (info))
+ || (big->n_slots[R_16] + arg_.diff->n_slots[R_16]
+ > ELF_M68K_R_8_16_MAX_N_SLOTS_IN_GOT (info)))
+ return FALSE;
+
+ return TRUE;
+}
+
+struct elf_m68k_merge_gots_arg
+{
+ /* The BIG got. */
+ struct elf_m68k_got *big;
+
+ /* Context where memory should be allocated. */
+ struct bfd_link_info *info;
+
+ /* Error flag. */
+ bfd_boolean error_p;
+};
+
+/* Process a single entry from DIFF got. Add or update corresponding
+ entry in the BIG got. */
+
+static int
+elf_m68k_merge_gots_1 (void **entry_ptr, void *_arg)
+{
+ const struct elf_m68k_got_entry *from;
+ struct elf_m68k_merge_gots_arg *arg;
+ struct elf_m68k_got_entry *to;
+
+ from = (const struct elf_m68k_got_entry *) *entry_ptr;
+ arg = (struct elf_m68k_merge_gots_arg *) _arg;
+
+ to = elf_m68k_get_got_entry (arg->big, &from->key_, FIND_OR_CREATE,
+ arg->info);
+ if (to == NULL)
+ {
+ arg->error_p = TRUE;
+ return 0;
+ }
+
+ BFD_ASSERT (to->u.s1.refcount == 0);
+ /* All we need to merge is TYPE. */
+ to->key_.type = from->key_.type;
+
+ return 1;
+}
+
+/* Merge data from DIFF to BIG. INFO is context where memory should be
+ allocated. */
+
+static bfd_boolean
+elf_m68k_merge_gots (struct elf_m68k_got *big,
+ struct elf_m68k_got *diff,
+ struct bfd_link_info *info)
+{
+ if (diff->entries != NULL)
+ /* DIFF is not empty. Merge it into BIG GOT. */
+ {
+ struct elf_m68k_merge_gots_arg arg_;
+
+ /* Merge entries. */
+ arg_.big = big;
+ arg_.info = info;
+ arg_.error_p = FALSE;
+ htab_traverse_noresize (diff->entries, elf_m68k_merge_gots_1, &arg_);
+ if (arg_.error_p)
+ return FALSE;
+
+ /* Merge counters. */
+ big->n_slots[R_8] += diff->n_slots[R_8];
+ big->n_slots[R_16] += diff->n_slots[R_16];
+ big->n_slots[R_32] += diff->n_slots[R_32];
+ big->local_n_slots += diff->local_n_slots;
+ }
+ else
+ /* DIFF is empty. */
+ {
+ BFD_ASSERT (diff->n_slots[R_8] == 0);
+ BFD_ASSERT (diff->n_slots[R_16] == 0);
+ BFD_ASSERT (diff->n_slots[R_32] == 0);
+ BFD_ASSERT (diff->local_n_slots == 0);
+ }
+
+ BFD_ASSERT (!elf_m68k_hash_table (info)->allow_multigot_p
+ || ((big->n_slots[R_8]
+ <= ELF_M68K_R_8_MAX_N_SLOTS_IN_GOT (info))
+ && (big->n_slots[R_16]
+ <= ELF_M68K_R_8_16_MAX_N_SLOTS_IN_GOT (info))));
+
+ return TRUE;
+}
+
+struct elf_m68k_finalize_got_offsets_arg
+{
+ /* Ranges of the offsets for GOT entries.
+ R_x entries receive offsets between offset1[R_x] and offset2[R_x].
+ R_x is R_8, R_16 and R_32. */
+ bfd_vma *offset1;
+ bfd_vma *offset2;
+
+ /* Mapping from global symndx to global symbols.
+ This is used to build lists of got entries for global symbols. */
+ struct elf_m68k_link_hash_entry **symndx2h;
+
+ bfd_vma n_ldm_entries;
+};
+
+/* Assign ENTRY an offset. Build list of GOT entries for global symbols
+ along the way. */
+
+static int
+elf_m68k_finalize_got_offsets_1 (void **entry_ptr, void *_arg)
+{
+ struct elf_m68k_got_entry *entry;
+ struct elf_m68k_finalize_got_offsets_arg *arg;
+
+ enum elf_m68k_got_offset_size got_offset_size;
+ bfd_vma entry_size;
+
+ entry = (struct elf_m68k_got_entry *) *entry_ptr;
+ arg = (struct elf_m68k_finalize_got_offsets_arg *) _arg;
+
+ /* This should be a fresh entry created in elf_m68k_can_merge_gots. */
+ BFD_ASSERT (entry->u.s1.refcount == 0);
+
+ /* Get GOT offset size for the entry . */
+ got_offset_size = elf_m68k_reloc_got_offset_size (entry->key_.type);
+
+ /* Calculate entry size in bytes. */
+ entry_size = 4 * elf_m68k_reloc_got_n_slots (entry->key_.type);
+
+ /* Check if we should switch to negative range of the offsets. */
+ if (arg->offset1[got_offset_size] + entry_size
+ > arg->offset2[got_offset_size])
+ {
+ /* Verify that this is the only switch to negative range for
+ got_offset_size. If this assertion fails, then we've miscalculated
+ range for got_offset_size entries in
+ elf_m68k_finalize_got_offsets. */
+ BFD_ASSERT (arg->offset2[got_offset_size]
+ != arg->offset2[-(int) got_offset_size - 1]);
+
+ /* Switch. */
+ arg->offset1[got_offset_size] = arg->offset1[-(int) got_offset_size - 1];
+ arg->offset2[got_offset_size] = arg->offset2[-(int) got_offset_size - 1];
+
+ /* Verify that now we have enough room for the entry. */
+ BFD_ASSERT (arg->offset1[got_offset_size] + entry_size
+ <= arg->offset2[got_offset_size]);
+ }
+
+ /* Assign offset to entry. */
+ entry->u.s2.offset = arg->offset1[got_offset_size];
+ arg->offset1[got_offset_size] += entry_size;
+
+ if (entry->key_.bfd == NULL)
+ /* Hook up this entry into the list of got_entries of H. */
+ {
+ struct elf_m68k_link_hash_entry *h;
+
+ h = arg->symndx2h[entry->key_.symndx];
+ if (h != NULL)
+ {
+ entry->u.s2.next = h->glist;
+ h->glist = entry;
+ }
+ else
+ /* This should be the entry for TLS_LDM relocation then. */
+ {
+ BFD_ASSERT ((elf_m68k_reloc_got_type (entry->key_.type)
+ == R_68K_TLS_LDM32)
+ && entry->key_.symndx == 0);
+
+ ++arg->n_ldm_entries;
+ }
+ }
+ else
+ /* This entry is for local symbol. */
+ entry->u.s2.next = NULL;
+
+ return 1;
+}
+
+/* Assign offsets within GOT. USE_NEG_GOT_OFFSETS_P indicates if we
+ should use negative offsets.
+ Build list of GOT entries for global symbols along the way.
+ SYMNDX2H is mapping from global symbol indices to actual
+ global symbols.
+ Return offset at which next GOT should start. */
+
+static void
+elf_m68k_finalize_got_offsets (struct elf_m68k_got *got,
+ bfd_boolean use_neg_got_offsets_p,
+ struct elf_m68k_link_hash_entry **symndx2h,
+ bfd_vma *final_offset, bfd_vma *n_ldm_entries)
+{
+ struct elf_m68k_finalize_got_offsets_arg arg_;
+ bfd_vma offset1_[2 * R_LAST];
+ bfd_vma offset2_[2 * R_LAST];
+ int i;
+ bfd_vma start_offset;
+
+ BFD_ASSERT (got->offset != (bfd_vma) -1);
+
+ /* We set entry offsets relative to the .got section (and not the
+ start of a particular GOT), so that we can use them in
+ finish_dynamic_symbol without needing to know the GOT which they come
+ from. */
+
+ /* Put offset1 in the middle of offset1_, same for offset2. */
+ arg_.offset1 = offset1_ + R_LAST;
+ arg_.offset2 = offset2_ + R_LAST;
+
+ start_offset = got->offset;
+
+ if (use_neg_got_offsets_p)
+ /* Setup both negative and positive ranges for R_8, R_16 and R_32. */
+ i = -(int) R_32 - 1;
+ else
+ /* Setup positives ranges for R_8, R_16 and R_32. */
+ i = (int) R_8;
+
+ for (; i <= (int) R_32; ++i)
+ {
+ int j;
+ size_t n;
+
+ /* Set beginning of the range of offsets I. */
+ arg_.offset1[i] = start_offset;
+
+ /* Calculate number of slots that require I offsets. */
+ j = (i >= 0) ? i : -i - 1;
+ n = (j >= 1) ? got->n_slots[j - 1] : 0;
+ n = got->n_slots[j] - n;
+
+ if (use_neg_got_offsets_p && n != 0)
+ {
+ if (i < 0)
+ /* We first fill the positive side of the range, so we might
+ end up with one empty slot at that side when we can't fit
+ whole 2-slot entry. Account for that at negative side of
+ the interval with one additional entry. */
+ n = n / 2 + 1;
+ else
+ /* When the number of slots is odd, make positive side of the
+ range one entry bigger. */
+ n = (n + 1) / 2;
+ }
+
+ /* N is the number of slots that require I offsets.
+ Calculate length of the range for I offsets. */
+ n = 4 * n;
+
+ /* Set end of the range. */
+ arg_.offset2[i] = start_offset + n;
+
+ start_offset = arg_.offset2[i];
+ }
+
+ if (!use_neg_got_offsets_p)
+ /* Make sure that if we try to switch to negative offsets in
+ elf_m68k_finalize_got_offsets_1, the assert therein will catch
+ the bug. */
+ for (i = R_8; i <= R_32; ++i)
+ arg_.offset2[-i - 1] = arg_.offset2[i];
+
+ /* Setup got->offset. offset1[R_8] is either in the middle or at the
+ beginning of GOT depending on use_neg_got_offsets_p. */
+ got->offset = arg_.offset1[R_8];
+
+ arg_.symndx2h = symndx2h;
+ arg_.n_ldm_entries = 0;
+
+ /* Assign offsets. */
+ htab_traverse (got->entries, elf_m68k_finalize_got_offsets_1, &arg_);
+
+ /* Check offset ranges we have actually assigned. */
+ for (i = (int) R_8; i <= (int) R_32; ++i)
+ BFD_ASSERT (arg_.offset2[i] - arg_.offset1[i] <= 4);
+
+ *final_offset = start_offset;
+ *n_ldm_entries = arg_.n_ldm_entries;
+}
+
+struct elf_m68k_partition_multi_got_arg
+{
+ /* The GOT we are adding entries to. Aka big got. */
+ struct elf_m68k_got *current_got;
+
+ /* Offset to assign the next CURRENT_GOT. */
+ bfd_vma offset;
+
+ /* Context where memory should be allocated. */
+ struct bfd_link_info *info;
+
+ /* Total number of slots in the .got section.
+ This is used to calculate size of the .got and .rela.got sections. */
+ bfd_vma n_slots;
+
+ /* Difference in numbers of allocated slots in the .got section
+ and necessary relocations in the .rela.got section.
+ This is used to calculate size of the .rela.got section. */
+ bfd_vma slots_relas_diff;
+
+ /* Error flag. */
+ bfd_boolean error_p;
+
+ /* Mapping from global symndx to global symbols.
+ This is used to build lists of got entries for global symbols. */
+ struct elf_m68k_link_hash_entry **symndx2h;
+};
+
+static void
+elf_m68k_partition_multi_got_2 (struct elf_m68k_partition_multi_got_arg *arg)
+{
+ bfd_vma n_ldm_entries;
+
+ elf_m68k_finalize_got_offsets (arg->current_got,
+ (elf_m68k_hash_table (arg->info)
+ ->use_neg_got_offsets_p),
+ arg->symndx2h,
+ &arg->offset, &n_ldm_entries);
+
+ arg->n_slots += arg->current_got->n_slots[R_32];
+
+ if (!arg->info->shared)
+ /* If we are generating a shared object, we need to
+ output a R_68K_RELATIVE reloc so that the dynamic
+ linker can adjust this GOT entry. Overwise we
+ don't need space in .rela.got for local symbols. */
+ arg->slots_relas_diff += arg->current_got->local_n_slots;
+
+ /* @LDM relocations require a 2-slot GOT entry, but only
+ one relocation. Account for that. */
+ arg->slots_relas_diff += n_ldm_entries;
+
+ BFD_ASSERT (arg->slots_relas_diff <= arg->n_slots);
+}
+
+
+/* Process a single BFD2GOT entry and either merge GOT to CURRENT_GOT
+ or start a new CURRENT_GOT. */
+
+static int
+elf_m68k_partition_multi_got_1 (void **_entry, void *_arg)
+{
+ struct elf_m68k_bfd2got_entry *entry;
+ struct elf_m68k_partition_multi_got_arg *arg;
+ struct elf_m68k_got *got;
+ struct elf_m68k_got diff_;
+ struct elf_m68k_got *diff;
+
+ entry = (struct elf_m68k_bfd2got_entry *) *_entry;
+ arg = (struct elf_m68k_partition_multi_got_arg *) _arg;
+
+ got = entry->got;
+ BFD_ASSERT (got != NULL);
+ BFD_ASSERT (got->offset == (bfd_vma) -1);
+
+ diff = NULL;
+
+ if (arg->current_got != NULL)
+ /* Construct diff. */
+ {
+ diff = &diff_;
+ elf_m68k_init_got (diff);
+
+ if (!elf_m68k_can_merge_gots (arg->current_got, got, arg->info, diff))
+ {
+ if (diff->offset == 0)
+ /* Offset set to 0 in the diff_ indicates an error. */
+ {
+ arg->error_p = TRUE;
+ goto final_return;
+ }
+
+ if (elf_m68k_hash_table (arg->info)->allow_multigot_p)
+ {
+ elf_m68k_clear_got (diff);
+ /* Schedule to finish up current_got and start new one. */
+ diff = NULL;
+ }
+ /* else
+ Merge GOTs no matter what. If big GOT overflows,
+ we'll fail in relocate_section due to truncated relocations.
+
+ ??? May be fail earlier? E.g., in can_merge_gots. */
+ }
+ }
+ else
+ /* Diff of got against empty current_got is got itself. */
+ {
+ /* Create empty current_got to put subsequent GOTs to. */
+ arg->current_got = elf_m68k_create_empty_got (arg->info);
+ if (arg->current_got == NULL)
+ {
+ arg->error_p = TRUE;
+ goto final_return;
+ }
+
+ arg->current_got->offset = arg->offset;
+
+ diff = got;
+ }
+
+ if (diff != NULL)
+ {
+ if (!elf_m68k_merge_gots (arg->current_got, diff, arg->info))
+ {
+ arg->error_p = TRUE;
+ goto final_return;
+ }
+
+ /* Now we can free GOT. */
+ elf_m68k_clear_got (got);
+
+ entry->got = arg->current_got;
+ }
+ else
+ {
+ /* Finish up current_got. */
+ elf_m68k_partition_multi_got_2 (arg);
+
+ /* Schedule to start a new current_got. */
+ arg->current_got = NULL;
+
+ /* Retry. */
+ if (!elf_m68k_partition_multi_got_1 (_entry, _arg))
+ {
+ BFD_ASSERT (arg->error_p);
+ goto final_return;
+ }
+ }
+
+ final_return:
+ if (diff != NULL)
+ elf_m68k_clear_got (diff);
+
+ return arg->error_p == FALSE ? 1 : 0;
+}
+
+/* Helper function to build symndx2h mapping. */
+
+static bfd_boolean
+elf_m68k_init_symndx2h_1 (struct elf_link_hash_entry *_h,
+ void *_arg)
+{
+ struct elf_m68k_link_hash_entry *h;
+
+ h = elf_m68k_hash_entry (_h);
+
+ if (h->got_entry_key != 0)
+ /* H has at least one entry in the GOT. */
+ {
+ struct elf_m68k_partition_multi_got_arg *arg;
+
+ arg = (struct elf_m68k_partition_multi_got_arg *) _arg;
+
+ BFD_ASSERT (arg->symndx2h[h->got_entry_key] == NULL);
+ arg->symndx2h[h->got_entry_key] = h;
+ }
+
+ return TRUE;
+}
+
+/* Merge GOTs of some BFDs, assign offsets to GOT entries and build
+ lists of GOT entries for global symbols.
+ Calculate sizes of .got and .rela.got sections. */
+
+static bfd_boolean
+elf_m68k_partition_multi_got (struct bfd_link_info *info)
+{
+ struct elf_m68k_multi_got *multi_got;
+ struct elf_m68k_partition_multi_got_arg arg_;
+
+ multi_got = elf_m68k_multi_got (info);
+
+ arg_.current_got = NULL;
+ arg_.offset = 0;
+ arg_.info = info;
+ arg_.n_slots = 0;
+ arg_.slots_relas_diff = 0;
+ arg_.error_p = FALSE;
+
+ if (multi_got->bfd2got != NULL)
+ {
+ /* Initialize symndx2h mapping. */
+ {
+ arg_.symndx2h = bfd_zmalloc (multi_got->global_symndx
+ * sizeof (*arg_.symndx2h));
+ if (arg_.symndx2h == NULL)
+ return FALSE;
+
+ elf_link_hash_traverse (elf_hash_table (info),
+ elf_m68k_init_symndx2h_1, &arg_);
+ }
+
+ /* Partition. */
+ htab_traverse (multi_got->bfd2got, elf_m68k_partition_multi_got_1,
+ &arg_);
+ if (arg_.error_p)
+ {
+ free (arg_.symndx2h);
+ arg_.symndx2h = NULL;
+
+ return FALSE;
+ }
+
+ /* Finish up last current_got. */
+ elf_m68k_partition_multi_got_2 (&arg_);
+
+ free (arg_.symndx2h);
+ }
+
+ if (elf_hash_table (info)->dynobj != NULL)
+ /* Set sizes of .got and .rela.got sections. */
+ {
+ asection *s;
+
+ s = bfd_get_section_by_name (elf_hash_table (info)->dynobj, ".got");
+ if (s != NULL)
+ s->size = arg_.offset;
+ else
+ BFD_ASSERT (arg_.offset == 0);
+
+ BFD_ASSERT (arg_.slots_relas_diff <= arg_.n_slots);
+ arg_.n_slots -= arg_.slots_relas_diff;
+
+ s = bfd_get_section_by_name (elf_hash_table (info)->dynobj, ".rela.got");
+ if (s != NULL)
+ s->size = arg_.n_slots * sizeof (Elf32_External_Rela);
+ else
+ BFD_ASSERT (arg_.n_slots == 0);
+ }
+ else
+ BFD_ASSERT (multi_got->bfd2got == NULL);
+
+ return TRUE;
+}
+
+/* Specialized version of elf_m68k_get_got_entry that returns pointer
+ to hashtable slot, thus allowing removal of entry via
+ elf_m68k_remove_got_entry. */
+
+static struct elf_m68k_got_entry **
+elf_m68k_find_got_entry_ptr (struct elf_m68k_got *got,
+ struct elf_m68k_got_entry_key *key)
+{
+ void **ptr;
+ struct elf_m68k_got_entry entry_;
+ struct elf_m68k_got_entry **entry_ptr;
+
+ entry_.key_ = *key;
+ ptr = htab_find_slot (got->entries, &entry_, NO_INSERT);
+ BFD_ASSERT (ptr != NULL);
+
+ entry_ptr = (struct elf_m68k_got_entry **) ptr;
+
+ return entry_ptr;
+}
+
+/* Remove entry pointed to by ENTRY_PTR from GOT. */
+
+static void
+elf_m68k_remove_got_entry (struct elf_m68k_got *got,
+ struct elf_m68k_got_entry **entry_ptr)
+{
+ struct elf_m68k_got_entry *entry;
+
+ entry = *entry_ptr;
+
+ /* Check that offsets have not been finalized yet. */
+ BFD_ASSERT (got->offset == (bfd_vma) -1);
+ /* Check that this entry is indeed unused. */
+ BFD_ASSERT (entry->u.s1.refcount == 0);
+
+ elf_m68k_remove_got_entry_type (got, entry->key_.type);
+
+ if (entry->key_.bfd != NULL)
+ got->local_n_slots -= elf_m68k_reloc_got_n_slots (entry->key_.type);
+
+ BFD_ASSERT (got->n_slots[R_32] >= got->local_n_slots);
+
+ htab_clear_slot (got->entries, (void **) entry_ptr);
+}
+
+/* Copy any information related to dynamic linking from a pre-existing
+ symbol to a newly created symbol. Also called to copy flags and
+ other back-end info to a weakdef, in which case the symbol is not
+ newly created and plt/got refcounts and dynamic indices should not
+ be copied. */
+
+static void
+elf_m68k_copy_indirect_symbol (struct bfd_link_info *info,
+ struct elf_link_hash_entry *_dir,
+ struct elf_link_hash_entry *_ind)
+{
+ struct elf_m68k_link_hash_entry *dir;
+ struct elf_m68k_link_hash_entry *ind;
+
+ _bfd_elf_link_hash_copy_indirect (info, _dir, _ind);
+
+ if (_ind->root.type != bfd_link_hash_indirect)
+ return;
+
+ dir = elf_m68k_hash_entry (_dir);
+ ind = elf_m68k_hash_entry (_ind);
+
+ /* Any absolute non-dynamic relocations against an indirect or weak
+ definition will be against the target symbol. */
+ _dir->non_got_ref |= _ind->non_got_ref;
+
+ /* We might have a direct symbol already having entries in the GOTs.
+ Update its key only in case indirect symbol has GOT entries and
+ assert that both indirect and direct symbols don't have GOT entries
+ at the same time. */
+ if (ind->got_entry_key != 0)
+ {
+ BFD_ASSERT (dir->got_entry_key == 0);
+ /* Assert that GOTs aren't partioned yet. */
+ BFD_ASSERT (ind->glist == NULL);
+
+ dir->got_entry_key = ind->got_entry_key;
+ ind->got_entry_key = 0;
+ }
+}
+
+/* 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
+elf_m68k_check_relocs (abfd, info, sec, relocs)
+ bfd *abfd;
+ struct bfd_link_info *info;
+ asection *sec;
+ const Elf_Internal_Rela *relocs;
+{
+ bfd *dynobj;
+ Elf_Internal_Shdr *symtab_hdr;