/* ELF linker support.
- Copyright 1995, 1996, 1997 Free Software Foundation, Inc.
+ Copyright 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
This file is part of BFD, the Binary File Descriptor library.
boolean newdyn, olddyn, olddef, newdef, newdyncommon, olddyncommon;
*override = false;
- *type_change_ok = false;
- *size_change_ok = false;
sec = *psec;
bind = ELF_ST_BIND (sym->st_info);
if (info->hash->creator->flavour == bfd_target_elf_flavour)
{
Elf_Internal_Versym iver;
- int vernum;
+ unsigned int vernum = 0;
boolean override;
if (ever != NULL)
if (vernum > elf_tdata (abfd)->dynverdef_hdr.sh_info)
{
(*_bfd_error_handler)
- ("%s: %s: invalid version %d (max %d)",
+ (_("%s: %s: invalid version %u (max %d)"),
abfd->filename, name, vernum,
elf_tdata (abfd)->dynverdef_hdr.sh_info);
bfd_set_error (bfd_error_bad_value);
if (verstr == NULL)
{
(*_bfd_error_handler)
- ("%s: %s: invalid needed version %d",
+ (_("%s: %s: invalid needed version %d"),
abfd->filename, name, vernum);
bfd_set_error (bfd_error_bad_value);
goto error_return;
{
if (h->size != 0 && h->size != sym.st_size && ! size_change_ok)
(*_bfd_error_handler)
- ("Warning: size of symbol `%s' changed from %lu to %lu in %s",
+ (_("Warning: size of symbol `%s' changed from %lu to %lu in %s"),
name, (unsigned long) h->size, (unsigned long) sym.st_size,
bfd_get_filename (abfd));
&& h->type != ELF_ST_TYPE (sym.st_info)
&& ! type_change_ok)
(*_bfd_error_handler)
- ("Warning: type of symbol `%s' changed from %d to %d in %s",
+ (_("Warning: type of symbol `%s' changed from %d to %d in %s"),
name, h->type, ELF_ST_TYPE (sym.st_info),
bfd_get_filename (abfd));
purposes of the merge, act as though we were
defining the symbol we just defined, although we
actually going to define an indirect symbol. */
+ type_change_ok = false;
+ size_change_ok = false;
if (! elf_merge_symbol (abfd, info, shortname, &sym, &sec,
&value, &hi, &override,
&type_change_ok, &size_change_ok))
& (ELF_LINK_HASH_REF_DYNAMIC
| ELF_LINK_HASH_REF_REGULAR));
- /* Copy over the global table offset entry.
- This may have been already set up by a
- check_relocs routine. */
- if (ht->got_offset == (bfd_vma) -1)
+ /* Copy over the global and procedure linkage table
+ offset entries. These may have been already set
+ up by a check_relocs routine. */
+ if (ht->got.offset == (bfd_vma) -1)
{
- ht->got_offset = hi->got_offset;
- hi->got_offset = (bfd_vma) -1;
+ ht->got.offset = hi->got.offset;
+ hi->got.offset = (bfd_vma) -1;
}
- BFD_ASSERT (hi->got_offset == (bfd_vma) -1);
+ BFD_ASSERT (hi->got.offset == (bfd_vma) -1);
+
+ if (ht->plt.offset == (bfd_vma) -1)
+ {
+ ht->plt.offset = hi->plt.offset;
+ hi->plt.offset = (bfd_vma) -1;
+ }
+ BFD_ASSERT (hi->plt.offset == (bfd_vma) -1);
if (ht->dynindx == -1)
{
strcpy (shortname + (p - name), p + 1);
/* Once again, merge with any existing symbol. */
+ type_change_ok = false;
+ size_change_ok = false;
if (! elf_merge_symbol (abfd, info, shortname, &sym, &sec,
&value, &hi, &override,
&type_change_ok, &size_change_ok))
don't expect to see the type of override we
do in the case above. */
(*_bfd_error_handler)
- ("%s: warning: unexpected redefinition of `%s'",
+ (_("%s: warning: unexpected redefinition of `%s'"),
bfd_get_filename (abfd), shortname);
}
else
& (ELF_LINK_HASH_REF_DYNAMIC
| ELF_LINK_HASH_REF_REGULAR));
- /* Copy over the global table offset entry.
- This may have been already set up by a
- check_relocs routine. */
- if (h->got_offset == (bfd_vma) -1)
+ /* Copy over the global and procedure linkage
+ table offset entries. These may have been
+ already set up by a check_relocs routine. */
+ if (h->got.offset == (bfd_vma) -1)
+ {
+ h->got.offset = hi->got.offset;
+ hi->got.offset = (bfd_vma) -1;
+ }
+ BFD_ASSERT (hi->got.offset == (bfd_vma) -1);
+
+ if (h->plt.offset == (bfd_vma) -1)
{
- h->got_offset = hi->got_offset;
- hi->got_offset = (bfd_vma) -1;
+ h->plt.offset = hi->plt.offset;
+ hi->plt.offset = (bfd_vma) -1;
}
- BFD_ASSERT (hi->got_offset == (bfd_vma) -1);
+ BFD_ASSERT (hi->got.offset == (bfd_vma) -1);
if (h->dynindx == -1)
{
*sinterpptr = NULL;
- soname_indx = -1;
+ soname_indx = (bfd_size_type) -1;
if (info->hash->creator->flavour != bfd_target_elf_flavour)
return true;
def.vd_next = (sizeof (Elf_External_Verdef)
+ sizeof (Elf_External_Verdaux));
- if (soname_indx != -1)
+ if (soname_indx != (bfd_size_type) -1)
{
def.vd_hash = bfd_elf_hash ((const unsigned char *) soname);
defaux.vda_name = soname_indx;
&& eif->info->shared
&& eif->info->symbolic
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) != 0)
- h->elf_link_hash_flags &=~ ELF_LINK_HASH_NEEDS_PLT;
+ {
+ h->elf_link_hash_flags &=~ ELF_LINK_HASH_NEEDS_PLT;
+ h->plt.offset = (bfd_vma) -1;
+ }
return true;
}
|| (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) == 0
|| ((h->elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR) == 0
&& (h->weakdef == NULL || h->weakdef->dynindx == -1))))
- return true;
+ {
+ h->plt.offset = (bfd_vma) -1;
+ return true;
+ }
/* If we've already adjusted this symbol, don't do it again. This
can happen via a recursive call. */
h->elf_link_hash_flags &=~
ELF_LINK_HASH_NEEDS_PLT;
h->dynindx = -1;
+ h->plt.offset = (bfd_vma) -1;
/* FIXME: The name of the symbol has
already been recorded in the dynamic
string table section. */
/* We could not find the version for a symbol when
generating a shared archive. Return an error. */
(*_bfd_error_handler)
- ("%s: undefined version name %s",
+ (_("%s: undefined versioned symbol name %s"),
bfd_get_filename (sinfo->output_bfd), h->root.root.string);
bfd_set_error (bfd_error_bad_value);
sinfo->failed = true;
h->elf_link_hash_flags |= ELF_LINK_FORCED_LOCAL;
h->elf_link_hash_flags &=~ ELF_LINK_HASH_NEEDS_PLT;
h->dynindx = -1;
+ h->plt.offset = (bfd_vma) -1;
/* FIXME: The name of the symbol has already
been recorded in the dynamic string table
section. */
h->elf_link_hash_flags |= ELF_LINK_FORCED_LOCAL;
h->elf_link_hash_flags &=~ ELF_LINK_HASH_NEEDS_PLT;
h->dynindx = -1;
+ h->plt.offset = (bfd_vma) -1;
/* FIXME: The name of the symbol has already been
recorded in the dynamic string table section. */
}
we could write the relocs out and then read them again; I don't
know how bad the memory loss will be. */
- for (sub = info->input_bfds; sub != NULL; sub = sub->next)
+ for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
sub->output_has_begun = false;
for (o = abfd->sections; o != NULL; o = o->next)
{
input_sec->output_section);
if (sym.st_shndx == (unsigned short) -1)
{
+ (*_bfd_error_handler)
+ (_("%s: could not find output section %s for input section %s"),
+ bfd_get_filename (finfo->output_bfd),
+ input_sec->output_section->name,
+ input_sec->name);
eoinfo->failed = true;
return false;
}
/* Write out the modified section contents. */
if (elf_section_data (o)->stab_info == NULL)
{
- if (! bfd_set_section_contents (output_bfd, o->output_section,
+ if (! (o->flags & SEC_EXCLUDE) &&
+ ! bfd_set_section_contents (output_bfd, o->output_section,
contents, o->output_offset,
(o->_cooked_size != 0
? o->_cooked_size
/* Allocate a table to hold the local symbols if first time */
if (!ptr)
{
- int num_symbols = elf_tdata (abfd)->symtab_hdr.sh_info;
+ unsigned int num_symbols = elf_tdata (abfd)->symtab_hdr.sh_info;
register unsigned int i;
ptr = (elf_linker_section_pointers_t **)
processing. */
return relocation - linker_section_ptr->addend;
}
+\f
+/* Garbage collect unused sections. */
+
+static boolean elf_gc_mark
+ PARAMS ((struct bfd_link_info *info, asection *sec,
+ asection * (*gc_mark_hook)
+ PARAMS ((bfd *, struct bfd_link_info *, Elf_Internal_Rela *,
+ struct elf_link_hash_entry *, Elf_Internal_Sym *))));
+
+static boolean elf_gc_sweep
+ PARAMS ((struct bfd_link_info *info,
+ boolean (*gc_sweep_hook)
+ PARAMS ((bfd *abfd, struct bfd_link_info *info, asection *o,
+ const Elf_Internal_Rela *relocs))));
+
+static boolean elf_gc_sweep_symbol
+ PARAMS ((struct elf_link_hash_entry *h, PTR idxptr));
+
+static boolean elf_gc_allocate_got_offsets
+ PARAMS ((struct elf_link_hash_entry *h, PTR offarg));
+
+static boolean elf_gc_propagate_vtable_entries_used
+ PARAMS ((struct elf_link_hash_entry *h, PTR dummy));
+
+static boolean elf_gc_smash_unused_vtentry_relocs
+ PARAMS ((struct elf_link_hash_entry *h, PTR dummy));
+
+/* The mark phase of garbage collection. For a given section, mark
+ it, and all the sections which define symbols to which it refers. */
+
+static boolean
+elf_gc_mark (info, sec, gc_mark_hook)
+ struct bfd_link_info *info;
+ asection *sec;
+ asection * (*gc_mark_hook)
+ PARAMS ((bfd *, struct bfd_link_info *, Elf_Internal_Rela *,
+ struct elf_link_hash_entry *, Elf_Internal_Sym *));
+{
+ boolean ret = true;
+
+ sec->gc_mark = 1;
+
+ /* Look through the section relocs. */
+
+ if ((sec->flags & SEC_RELOC) != 0 && sec->reloc_count > 0)
+ {
+ Elf_Internal_Rela *relstart, *rel, *relend;
+ Elf_Internal_Shdr *symtab_hdr;
+ struct elf_link_hash_entry **sym_hashes;
+ size_t nlocsyms;
+ size_t extsymoff;
+ Elf_External_Sym *locsyms, *freesyms = NULL;
+ bfd *input_bfd = sec->owner;
+
+ /* GCFIXME: how to arrange so that relocs and symbols are not
+ reread continually? */
+
+ symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+ sym_hashes = elf_sym_hashes (input_bfd);
+
+ /* Read the local symbols. */
+ if (elf_bad_symtab (input_bfd))
+ {
+ nlocsyms = symtab_hdr->sh_size / sizeof (Elf_External_Sym);
+ extsymoff = 0;
+ }
+ else
+ extsymoff = nlocsyms = symtab_hdr->sh_info;
+ if (symtab_hdr->contents)
+ locsyms = (Elf_External_Sym *) symtab_hdr->contents;
+ else if (nlocsyms == 0)
+ locsyms = NULL;
+ else
+ {
+ locsyms = freesyms =
+ bfd_malloc (nlocsyms * sizeof (Elf_External_Sym));
+ if (freesyms == NULL
+ || bfd_seek (input_bfd, symtab_hdr->sh_offset, SEEK_SET) != 0
+ || (bfd_read (locsyms, sizeof (Elf_External_Sym),
+ nlocsyms, input_bfd)
+ != nlocsyms * sizeof (Elf_External_Sym)))
+ {
+ ret = false;
+ goto out1;
+ }
+ }
+
+ /* Read the relocations. */
+ relstart = (NAME(_bfd_elf,link_read_relocs)
+ (sec->owner, sec, NULL, (Elf_Internal_Rela *) NULL,
+ info->keep_memory));
+ if (relstart == NULL)
+ {
+ ret = false;
+ goto out1;
+ }
+ relend = relstart + sec->reloc_count;
+
+ for (rel = relstart; rel < relend; rel++)
+ {
+ unsigned long r_symndx;
+ asection *rsec;
+ struct elf_link_hash_entry *h;
+ Elf_Internal_Sym s;
+
+ r_symndx = ELF_R_SYM (rel->r_info);
+ if (r_symndx == 0)
+ continue;
+
+ if (elf_bad_symtab (sec->owner))
+ {
+ elf_swap_symbol_in (input_bfd, &locsyms[r_symndx], &s);
+ if (ELF_ST_BIND (s.st_info) == STB_LOCAL)
+ rsec = (*gc_mark_hook)(sec->owner, info, rel, NULL, &s);
+ else
+ {
+ h = sym_hashes[r_symndx - extsymoff];
+ rsec = (*gc_mark_hook)(sec->owner, info, rel, h, NULL);
+ }
+ }
+ else if (r_symndx >= nlocsyms)
+ {
+ h = sym_hashes[r_symndx - extsymoff];
+ rsec = (*gc_mark_hook)(sec->owner, info, rel, h, NULL);
+ }
+ else
+ {
+ elf_swap_symbol_in (input_bfd, &locsyms[r_symndx], &s);
+ rsec = (*gc_mark_hook)(sec->owner, info, rel, NULL, &s);
+ }
+
+ if (rsec && !rsec->gc_mark)
+ if (!elf_gc_mark (info, rsec, gc_mark_hook))
+ {
+ ret = false;
+ goto out2;
+ }
+ }
+
+ out2:
+ if (!info->keep_memory)
+ free (relstart);
+ out1:
+ if (freesyms)
+ free (freesyms);
+ }
+
+ return ret;
+}
+
+/* The sweep phase of garbage collection. Remove all garbage sections. */
+
+static boolean
+elf_gc_sweep (info, gc_sweep_hook)
+ struct bfd_link_info *info;
+ boolean (*gc_sweep_hook)
+ PARAMS ((bfd *abfd, struct bfd_link_info *info, asection *o,
+ const Elf_Internal_Rela *relocs));
+{
+ bfd *sub;
+
+ for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
+ {
+ asection *o;
+
+ for (o = sub->sections; o != NULL; o = o->next)
+ {
+ /* Keep special sections. Keep .debug sections. */
+ if ((o->flags & SEC_LINKER_CREATED)
+ || (o->flags & SEC_DEBUGGING))
+ o->gc_mark = 1;
+
+ if (o->gc_mark)
+ continue;
+
+ /* Skip sweeping sections already excluded. */
+ if (o->flags & SEC_EXCLUDE)
+ continue;
+
+ /* Since this is early in the link process, it is simple
+ to remove a section from the output. */
+ o->flags |= SEC_EXCLUDE;
+
+ /* But we also have to update some of the relocation
+ info we collected before. */
+ if (gc_sweep_hook
+ && (o->flags & SEC_RELOC) && o->reloc_count > 0)
+ {
+ Elf_Internal_Rela *internal_relocs;
+ boolean r;
+
+ internal_relocs = (NAME(_bfd_elf,link_read_relocs)
+ (o->owner, o, NULL, NULL, info->keep_memory));
+ if (internal_relocs == NULL)
+ return false;
+
+ r = (*gc_sweep_hook)(o->owner, info, o, internal_relocs);
+
+ if (!info->keep_memory)
+ free (internal_relocs);
+
+ if (!r)
+ return false;
+ }
+ }
+ }
+
+ /* Remove the symbols that were in the swept sections from the dynamic
+ symbol table. GCFIXME: Anyone know how to get them out of the
+ static symbol table as well? */
+ {
+ int i = 0;
+
+ elf_link_hash_traverse (elf_hash_table (info),
+ elf_gc_sweep_symbol,
+ (PTR) &i);
+
+ elf_hash_table (info)->dynsymcount = i;
+ }
+
+ return true;
+}
+
+/* Sweep symbols in swept sections. Called via elf_link_hash_traverse. */
+
+static boolean
+elf_gc_sweep_symbol (h, idxptr)
+ struct elf_link_hash_entry *h;
+ PTR idxptr;
+{
+ int *idx = (int *) idxptr;
+
+ if (h->dynindx != -1
+ && ((h->root.type != bfd_link_hash_defined
+ && h->root.type != bfd_link_hash_defweak)
+ || h->root.u.def.section->gc_mark))
+ h->dynindx = (*idx)++;
+
+ return true;
+}
+
+/* Propogate collected vtable information. This is called through
+ elf_link_hash_traverse. */
+
+static boolean
+elf_gc_propagate_vtable_entries_used (h, okp)
+ struct elf_link_hash_entry *h;
+ PTR okp;
+{
+ /* Those that are not vtables. */
+ if (h->vtable_parent == NULL)
+ return true;
+
+ /* Those vtables that do not have parents, we cannot merge. */
+ if (h->vtable_parent == (struct elf_link_hash_entry *) -1)
+ return true;
+
+ /* If we've already been done, exit. */
+ if (h->vtable_entries_used && h->vtable_entries_used[-1])
+ return true;
+
+ /* Make sure the parent's table is up to date. */
+ elf_gc_propagate_vtable_entries_used (h->vtable_parent, okp);
+
+ if (h->vtable_entries_used == NULL)
+ {
+ /* None of this table's entries were referenced. Re-use the
+ parent's table. */
+ h->vtable_entries_used = h->vtable_parent->vtable_entries_used;
+ }
+ else
+ {
+ size_t n;
+ boolean *cu, *pu;
+
+ /* Or the parent's entries into ours. */
+ cu = h->vtable_entries_used;
+ cu[-1] = true;
+ pu = h->vtable_parent->vtable_entries_used;
+ if (pu != NULL)
+ {
+ n = h->vtable_parent->size / FILE_ALIGN;
+ while (--n != 0)
+ {
+ if (*pu) *cu = true;
+ pu++, cu++;
+ }
+ }
+ }
+
+ return true;
+}
+
+static boolean
+elf_gc_smash_unused_vtentry_relocs (h, okp)
+ struct elf_link_hash_entry *h;
+ PTR okp;
+{
+ asection *sec;
+ bfd_vma hstart, hend;
+ Elf_Internal_Rela *relstart, *relend, *rel;
+
+ /* Take care of both those symbols that do not describe vtables as
+ well as those that are not loaded. */
+ if (h->vtable_parent == NULL)
+ return true;
+
+ BFD_ASSERT (h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak);
+
+ sec = h->root.u.def.section;
+ hstart = h->root.u.def.value;
+ hend = hstart + h->size;
+
+ relstart = (NAME(_bfd_elf,link_read_relocs)
+ (sec->owner, sec, NULL, (Elf_Internal_Rela *) NULL, true));
+ if (!relstart)
+ return *(boolean *)okp = false;
+ relend = relstart + sec->reloc_count;
+
+ for (rel = relstart; rel < relend; ++rel)
+ if (rel->r_offset >= hstart && rel->r_offset < hend)
+ {
+ /* If the entry is in use, do nothing. */
+ if (h->vtable_entries_used)
+ {
+ bfd_vma entry = (rel->r_offset - hstart) / FILE_ALIGN;
+ if (h->vtable_entries_used[entry])
+ continue;
+ }
+ /* Otherwise, kill it. */
+ rel->r_offset = rel->r_info = rel->r_addend = 0;
+ }
+
+ return true;
+}
+
+/* Do mark and sweep of unused sections. */
+
+boolean
+elf_gc_sections (abfd, info)
+ bfd *abfd;
+ struct bfd_link_info *info;
+{
+ boolean ok = true;
+ bfd *sub;
+ asection * (*gc_mark_hook)
+ PARAMS ((bfd *abfd, struct bfd_link_info *, Elf_Internal_Rela *,
+ struct elf_link_hash_entry *h, Elf_Internal_Sym *));
+
+ if (!get_elf_backend_data (abfd)->can_gc_sections
+ || info->relocateable)
+ return true;
+
+ /* Apply transitive closure to the vtable entry usage info. */
+ elf_link_hash_traverse (elf_hash_table (info),
+ elf_gc_propagate_vtable_entries_used,
+ (PTR) &ok);
+ if (!ok)
+ return false;
+
+ /* Kill the vtable relocations that were not used. */
+ elf_link_hash_traverse (elf_hash_table (info),
+ elf_gc_smash_unused_vtentry_relocs,
+ (PTR) &ok);
+ if (!ok)
+ return false;
+
+ /* Grovel through relocs to find out who stays ... */
+
+ gc_mark_hook = get_elf_backend_data (abfd)->gc_mark_hook;
+ for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
+ {
+ asection *o;
+ for (o = sub->sections; o != NULL; o = o->next)
+ {
+ if (o->flags & SEC_KEEP)
+ if (!elf_gc_mark (info, o, gc_mark_hook))
+ return false;
+ }
+ }
+
+ /* ... and mark SEC_EXCLUDE for those that go. */
+ if (!elf_gc_sweep(info, get_elf_backend_data (abfd)->gc_sweep_hook))
+ return false;
+
+ return true;
+}
+\f
+/* Called from check_relocs to record the existance of a VTINHERIT reloc. */
+
+boolean
+elf_gc_record_vtinherit (abfd, sec, h, offset)
+ bfd *abfd;
+ asection *sec;
+ struct elf_link_hash_entry *h;
+ bfd_vma offset;
+{
+ struct elf_link_hash_entry **sym_hashes, **sym_hashes_end;
+ struct elf_link_hash_entry **search, *child;
+ bfd_size_type extsymcount;
+
+ /* The sh_info field of the symtab header tells us where the
+ external symbols start. We don't care about the local symbols at
+ this point. */
+ extsymcount = elf_tdata (abfd)->symtab_hdr.sh_size/sizeof (Elf_External_Sym);
+ if (!elf_bad_symtab (abfd))
+ extsymcount -= elf_tdata (abfd)->symtab_hdr.sh_info;
+
+ sym_hashes = elf_sym_hashes (abfd);
+ sym_hashes_end = sym_hashes + extsymcount;
+
+ /* Hunt down the child symbol, which is in this section at the same
+ offset as the relocation. */
+ for (search = sym_hashes; search != sym_hashes_end; ++search)
+ {
+ if ((child = *search) != NULL
+ && (child->root.type == bfd_link_hash_defined
+ || child->root.type == bfd_link_hash_defweak)
+ && child->root.u.def.section == sec
+ && child->root.u.def.value == offset)
+ goto win;
+ }
+
+ (*_bfd_error_handler) ("%s: %s+%lu: No symbol found for INHERIT",
+ bfd_get_filename (abfd), sec->name,
+ (unsigned long)offset);
+ bfd_set_error (bfd_error_invalid_operation);
+ return false;
+
+win:
+ if (!h)
+ {
+ /* This *should* only be the absolute section. It could potentially
+ be that someone has defined a non-global vtable though, which
+ would be bad. It isn't worth paging in the local symbols to be
+ sure though; that case should simply be handled by the assembler. */
+
+ child->vtable_parent = (struct elf_link_hash_entry *) -1;
+ }
+ else
+ child->vtable_parent = h;
+
+ return true;
+}
+
+/* Called from check_relocs to record the existance of a VTENTRY reloc. */
+
+boolean
+elf_gc_record_vtentry (abfd, sec, h, addend)
+ bfd *abfd;
+ asection *sec;
+ struct elf_link_hash_entry *h;
+ bfd_vma addend;
+{
+ if (h->vtable_entries_used == NULL)
+ {
+ /* Allocate one extra entry for use as a "done" flag for the
+ consolidation pass. */
+ size_t size = (h->size / FILE_ALIGN + 1) * sizeof(boolean);
+ h->vtable_entries_used = (boolean *) bfd_alloc (abfd, size);
+ if (h->vtable_entries_used == NULL)
+ return false;
+
+ /* And arrange for that done flag to be at index -1. */
+ memset (h->vtable_entries_used++, 0, size);
+ }
+ h->vtable_entries_used[addend / FILE_ALIGN] = true;
+
+ return true;
+}
+
+/* And an accompanying bit to work out final got entry offsets once
+ we're done. Should be called from final_link. */
+
+boolean
+elf_gc_common_finalize_got_offsets (abfd, info)
+ bfd *abfd;
+ struct bfd_link_info *info;
+{
+ bfd *i;
+ bfd_vma off[2], gotoff = 0;
+
+ /* Do the local .got entries first. */
+ for (i = info->input_bfds; i; i = i->link_next)
+ {
+ bfd_signed_vma *local_got = elf_local_got_refcounts (i);
+ bfd_size_type j, locsymcount;
+ Elf_Internal_Shdr *symtab_hdr;
+
+ if (!local_got)
+ continue;
+
+ symtab_hdr = &elf_tdata (i)->symtab_hdr;
+ if (elf_bad_symtab (i))
+ locsymcount = symtab_hdr->sh_size / sizeof (Elf_External_Sym);
+ else
+ locsymcount = symtab_hdr->sh_info;
+
+ for (j = 0; j < locsymcount; ++j)
+ {
+ if (local_got[j] > 0)
+ {
+ local_got[j] = gotoff;
+ gotoff += ARCH_SIZE / 8;
+ }
+ else
+ local_got[j] = (bfd_vma) -1;
+ }
+ }
+
+ /* Then the global .got and .plt entries. */
+ off[0] = gotoff;
+ off[1] = 0;
+ elf_link_hash_traverse (elf_hash_table (info),
+ elf_gc_allocate_got_offsets,
+ (PTR) off);
+ return true;
+}
+
+/* We need a special top-level link routine to convert got reference counts
+ to real got offsets. */
+
+static boolean
+elf_gc_allocate_got_offsets (h, offarg)
+ struct elf_link_hash_entry *h;
+ PTR offarg;
+{
+ bfd_vma *off = (bfd_vma *) offarg;
+
+ if (h->got.refcount > 0)
+ {
+ h->got.offset = off[0];
+ off[0] += ARCH_SIZE / 8;
+ }
+ else
+ h->got.offset = (bfd_vma) -1;
+
+ return true;
+}
+
+/* Many folk need no more in the way of final link than this, once
+ got entry reference counting is enabled. */
+
+boolean
+elf_gc_common_final_link (abfd, info)
+ bfd *abfd;
+ struct bfd_link_info *info;
+{
+ if (!elf_gc_common_finalize_got_offsets (abfd, info))
+ return false;
+
+ /* Invoke the regular ELF backend linker to do all the work. */
+ return elf_bfd_final_link (abfd, info);
+}