[PATCH 1/5] Lay out MIPS GOTs later in the link process

Richard Sandiford rdsandiford@googlemail.com
Sat Jun 28 16:47:00 GMT 2008


This patch is the first of a series aimed at improving the GOT allocation.
It addresses what I see as the most fundamental flaw of the current code:
that it allocates the GOT too early, before we even know how many dynamic
symbols there will be.

Specifically, the current code allocates the GOT in
elf_backend_always_size_sections rather than
elf_backend_size_dynamic_sections.  This has two main drawbacks:

  - We have an 80+ line function, _bfd_mips_elf_hide_symbol,
    whose sole purpose is to try to update the GOT allocation
    in response to symbol hiding between always_size_sections
    and size_dynamic_sections.  As the comment says, it isn't
    always possible to reclaim space:

      /* If this was a global symbol forced into the primary GOT, we
	 no longer need an entry for it.  We can't release the entry
	 at this point, but we must at least stop counting it as one
	 of the symbols that required a forced got entry.  */

    This then leads to further fixups in size_dynamic_sections itself:

      /* _bfd_mips_elf_always_size_sections() has already done
	 most of the work, but some symbols may have been mapped
	 to versions that we must now resolve in the got_entries
	 hash tables.  */

  - We don't know in always_size_sections how many stubs we need --
    that's determined by _bfd_mips_elf_adjust_dynamic_symbol --
    so we have to use a very conservative estimate when computing
    loadable_size:

      /* In the worst case, we'll get one stub per dynamic symbol, plus
	 one to account for the dummy entry at the end required by IRIX
	 rld.  */
      loadable_size += htab->function_stub_size * (i + 1);

    This means that we can have more page entries than we really need.

Before the patch, the code has the following steps:

  always_size_sections
  --------------------

  [A] Compute the amount of memory that can be addressed by GOT page entries.
  [B] Sort the symbol table, dividing it into non-GOT-mapped and
      GOT-mapped areas.
  [C] Pick a stub size.
  [D] Count the number of page and TLS GOT entries.
  [E] Replace GOT entries for indirect and warning symbols
      with GOT entries for the target symbol.

  Either call mips_elf_multi_got or:
    [F1] Assign indices for TLS GOT entries.

  mips_elf_multi_got (called from always_size_sections)
  ------------------

  [G] Create a separate GOT for each input BFD.
  [H] Merge per-BFD GOTs together where possible, choosing one as
      the primary GOT.
  [I] Mark symbols that need primary GOT entries and reorder the
      symbol table so that those symbols come before all others
      in the "master" (i.e. ABI-defined) GOT.
  [J] Prevent symbols referenced by secondary GOTs from having
      lazy-binding stubs.
  [F2] Assign indices for TLS GOT entries.

  adjust_dynamic_symbol
  ---------------------
  [K] Allocate .MIPS.stub entries to each function that needs a stub.

  mips_elf_record_global_got_symbol
  ---------------------------------
  [L1] Count the number of forced-local symbols with global GOT entries.

  hide_symbol
  -----------
  [L2] Adjust local/global-GOT counts for symbols that have already
       been allocated a GOT entry.

  size_dymamic_sections
  ---------------------
  [M] If we have multiple GOTs, reapply [E].
  [N] If we have multiple GOTs, assign each primary and secondary GOT
      a position in the master GOT.
  [O] Allocate dynamic relocations needed by the GOT.

The corresponding steps after the patch are:

  adjust_dynamic_symbol
  ---------------------
  [k1] Count the number of stubs needed.

  mips_elf_estimate_stub_size (called from size_dynamic_sections)
  ---------------------------
  [c] Pick a stub size and estimate the size of .MIPS.stubs.

  mips_lay_out_got (called from size_dynamic_sections)
  ----------------
  [e] Replace GOT entries for indirect and warning symbols
      with GOT entries for the target symbol.
  [l] Count the number forced-local symbols.
  [b] Sort the symbol table, dividing it into non-GOT-mapped and
      GOT-mapped areas.
  [a] Compute the amount of memory that can be addressed by GOT page entries.
  [d] Count the number of page and TLS GOT entries.

  Either call mips_elf_multi_got or:
    [f1] Assign indices for TLS GOT entries.
    [o1] Allocate dynamic relocations needed by the GOT.

  mips_elf_multi_got (called from mips_lay_out_got)
  ------------------
  [g] Create a separate GOT for each input BFD.
  [h] Merge per-BFD GOTs together where possible, choosing one as
      the primary GOT.
  [i] Mark symbols that need primary GOT entries and reorder the
      symbol table so that those symbols come before all others
      in the "master" (i.e. ABI-defined) GOT.
  [j] Prevent symbols referenced by secondary GOTs from having
      lazy-binding stubs.
  [f2] Assign indices for TLS GOT entries.
  [n] Assign each primary and secondary GOT a position in the master GOT.
  [o2] Allocate dynamic relocations needed by the GOT.

  mips_elf_lay_out_lazy_stubs (called from size_dynamic_sections)
  ---------------------------
  [k2] Allocate offsets to each .MIPS.stub entry.

  size_dynamic_sections
  ---------------------
  Call mips_elf_estimate_stub_size, mips_elf_lay_out_got and
  mips_elf_lay_out_lazy_stubs.

In other words, everything is now handled by adjust_dynamic_symbol and
size_dynamic_sections, although some of the work is done by subroutines.
There is no need for special handling in always_size_sections,
mips_elf_record_global_got_symbol or hide_symbol.

Notes:

  - [J] happened before [K], so setting no_fn_stub to TRUE was
    enough to prevent a stub being used.  [k1] now happens before [j],
    so [k1] conservatively assumes that we need a stub, then [j]
    removes it if necessary.  The patch uses a new "needs_lazy_stub"
    field to track whether we're creating a stub.

    This arrangement means that, if we need multiple GOTs, we still
    overestimate the size of .MIPS.stubs when deciding how many page
    entries are needed.  The new estimate is tighter than the old one
    though.  Note that the final size of .MIPS.stubs is computed by
    [k2] and is accurate.

    mips_elf_output_extsym used no_fn_stub to detect whether a stub
    had been created.  The patch uses needs_lazy_stub instead.

  - There was a comment querying whether [M] is really needed:

      /* NOTE 2005-02-03: How can this call, or the next, ever
	 find any indirect entries to resolve?  They were all
	 resolved in mips_elf_multi_got.  */

    It certainly isn't after the patch.  The comment refers to step [E]
    (actually in always_size_sections) and [e], like [M], now happens
    in size_dynamic_sections.

  - [E] was needlessly quadratic.  mips_elf_resolve_final_got_entries
    applied mips_elf_resolve_final_got_entry to each GOT entry,
    but stopped as soon as it found an entry that needed adjusting.
    (It isn't safe to continue a traversal after modifying the
    hashtable.)  mips_elf_resolve_final_got_entries would then
    start again from the beginning, restarting again if it found
    another entry that needed adjusting.

    [e] instead does one walk to see whether any change needs to
    be made.  If so, it uses a second traversal to creates a new GOT
    with the new information.

  - Symbols had a MIPS-specific "forced_local" field as well as the
    generic ELF "root.forced_local" field.  Only GOT code used the
    MIPS-specific version.

    I'm not sure if the old code really needed a separate field,
    but the new code doesn't.  I think it's confusing to have both.

  - The main size_dynamic_sections loop allocated GOT dynamic
    relocations ([O]), so the loop had to treat .rel.dyn specially,
    postponing the size calculation until after the loop.  Now that
    mips_lay_out_got does [o] as part of the main GOT lay-out code,
    this special treatment is no longer necessary.

Tested on mips64-linux-gnu and mips64el-linux-gnu.  OK to install?

Richard


bfd/
	* elf32-mips.c (elf_backend_hide_symbol): Delete.
	* elfn32-mips.c (elf_backend_hide_symbol): Likewise.
	* elf64-mips.c (elf_backend_hide_symbol): Likewise.
	* elfxx-mips.h (elf_backend_hide_symbol): Likewise.
	* elfxx-mips.c (mips_elf_link_hash_entry): Remove "forced_local"
	and add "needs_lazy_stub".
	(mips_elf_link_hash_newfunc): Update accordingly.
	(mips_elf_link_hash_table): Remove "computed_got_sizes" and
	add "lazy_stub_count".
	(_bfd_mips_elf_link_hash_table_create): Update accordingly.
	(mips_elf_output_extsym): Use hd->needs_lazy_stub to detect
	cases where a lazy stub is being used.
	(mips_elf_sort_hash_table_f): Use h->root.forced_local instead
	of h->forced_local.
	(mips_elf_record_global_got_symbol): Use _bfd_elf_link_hash_hide_symbol
	instead of _bfd_mips_elf_hide_symbol.  Do not increment local_gotno
	here.
	(mips_elf_allocate_dynamic_relocations): Move before new first use.
	(mips_elf_check_recreate_got, mips_elf_recreate_got): New functions.
	(mips_elf_resolve_final_got_entries): Move earlier in file.  Make at
	most two passes over the hash table.  Use mips_elf_check_recreate_got
	to see if there are any indirect or warning entries and
	mips_elf_recreate_got to create a new GOT without them.
	Return a boolean success value.
	(mips_elf_count_forced_local_got_entries): New function.
	(mips_elf_make_got_per_bfd): Check h->root.forced_local instead of
	h->forced_local.
	(mips_elf_set_global_got_offset): Likewise.
	(mips_elf_set_no_stub): Replace with...
	(mips_elf_forbid_lazy_stubs): ...this new function.
	(mips_elf_resolve_final_got_entry): Delete.
	(mips_elf_multi_got): Fix formatting.  Use mips_elf_forbid_lazy_stubs
	instead of mips_elf_set_no_stub.  Move the code that sets
	global offsets and allocates dynamic relocations from the main
	_bfd_mips_elf_size_dynamic_sections loop to here.
	(_bfd_mips_elf_adjust_dynamic_symbol): Do not allocate room in
	.MIPS.stubs here; just set hmips->needs_lazy_stub and increment
	htab->lazy_stub_count.
	(_bfd_mips_elf_always_size_sections): Move the stub-estimation
	code to mips_elf_estimate_stub_size and the GOT-sizing code to
	mips_elf_lay_out_got.  Do not call these functions here.
	(mips_elf_estimate_stub_size): New function, split
	out from _bfd_mips_elf_always_size_sections.  Call
	mips_elf_resolve_final_got_entries earlier.  Count the number
	of forced-local entries.  Do not add stub sizes to loadable_size;
	after this patch, the stub sizes are already included in the main
	estimate.  Allocate dynamic relocations here rather than in the
	main _bfd_mips_elf_size_dynamic_sections loop.
	(mips_elf_estimate_stub_size): New function, split out from
	_bfd_mips_elf_always_size_sections.
	(mips_elf_allocate_lazy_stub): New function.
	(mips_elf_lay_out_lazy_stubs): Likewise.
	(_bfd_mips_elf_size_dynamic_sections): Call mips_elf_estimate_stub_size,
	mips_elf_lay_out_got and mips_elf_lay_out_lazy_stubs.  Do not handle
	the allocation of sreldyn specially.
	(_bfd_mips_elf_hide_symbol): Delete.

ld/testsuite/
	* ld-mips-elf/tlsdyn-o32-2.got, ld-mips-elf/tlsdyn-o32-3.got,
	ld-mips-elf/tlsdyn-o32-2.d, ld-mips-elf/tlsdyn-o32-3.d: Change the
	GOT layout as follows:

	         BEFORE                  AFTER
	   +0x08 %call16(__tls_get_addr) %call16(__tls_get_addr)
	   +0x0c %tlsldm(tlsbin_ld)      %gottprel(tlsvar_ie)
	   +0x10   "  "                  %tlsgd(tlsvar_gd)
	   +0x14 %tlsgd(tlsvar_gd)          "  "
	   +0x18   "  "                  %tlsgd(tlsbin_gd)
	   +0x1c %gottprel(tlsvar_ie)       "  "
	   +0x20 %tlsgd(tlsbin_gd)       %tlsldm(tlsbin_ld)
	   +0x24   "  "                     "  "       
	   +0x28 %gottprel(tlsbin_ie)    %gottprel(tlsbin_ie)

Index: bfd/elf32-mips.c
===================================================================
--- bfd/elf32-mips.c	2008-06-28 17:14:17.000000000 +0100
+++ bfd/elf32-mips.c	2008-06-28 17:14:36.000000000 +0100
@@ -1606,7 +1606,6 @@ #define elf_backend_gc_mark_hook	_bfd_mi
 #define elf_backend_gc_sweep_hook	_bfd_mips_elf_gc_sweep_hook
 #define elf_backend_copy_indirect_symbol \
 					_bfd_mips_elf_copy_indirect_symbol
-#define elf_backend_hide_symbol		_bfd_mips_elf_hide_symbol
 #define elf_backend_grok_prstatus	elf32_mips_grok_prstatus
 #define elf_backend_grok_psinfo		elf32_mips_grok_psinfo
 #define elf_backend_ecoff_debug_swap	&mips_elf32_ecoff_debug_swap
Index: bfd/elfn32-mips.c
===================================================================
--- bfd/elfn32-mips.c	2008-06-28 17:14:17.000000000 +0100
+++ bfd/elfn32-mips.c	2008-06-28 17:14:36.000000000 +0100
@@ -2433,7 +2433,6 @@ #define elf_backend_gc_mark_hook	_bfd_mi
 #define elf_backend_gc_sweep_hook	_bfd_mips_elf_gc_sweep_hook
 #define elf_backend_copy_indirect_symbol \
 					_bfd_mips_elf_copy_indirect_symbol
-#define elf_backend_hide_symbol		_bfd_mips_elf_hide_symbol
 #define elf_backend_grok_prstatus	elf32_mips_grok_prstatus
 #define elf_backend_grok_psinfo		elf32_mips_grok_psinfo
 #define elf_backend_ecoff_debug_swap	&mips_elf32_ecoff_debug_swap
Index: bfd/elf64-mips.c
===================================================================
--- bfd/elf64-mips.c	2008-06-28 17:14:17.000000000 +0100
+++ bfd/elf64-mips.c	2008-06-28 17:14:36.000000000 +0100
@@ -3179,7 +3179,6 @@ #define elf_backend_gc_mark_hook	_bfd_mi
 #define elf_backend_gc_sweep_hook	_bfd_mips_elf_gc_sweep_hook
 #define elf_backend_copy_indirect_symbol \
 					_bfd_mips_elf_copy_indirect_symbol
-#define elf_backend_hide_symbol		_bfd_mips_elf_hide_symbol
 #define elf_backend_ignore_discarded_relocs \
 					_bfd_mips_elf_ignore_discarded_relocs
 #define elf_backend_mips_irix_compat	elf64_mips_irix_compat
Index: bfd/elfxx-mips.h
===================================================================
--- bfd/elfxx-mips.h	2008-06-28 17:09:14.000000000 +0100
+++ bfd/elfxx-mips.h	2008-06-28 17:14:36.000000000 +0100
@@ -81,8 +81,6 @@
 extern void _bfd_mips_elf_copy_indirect_symbol
   (struct bfd_link_info *, struct elf_link_hash_entry *,
    struct elf_link_hash_entry *);
-extern void _bfd_mips_elf_hide_symbol
-  (struct bfd_link_info *, struct elf_link_hash_entry *, bfd_boolean);
 extern bfd_boolean _bfd_mips_elf_ignore_discarded_relocs
   (asection *);
 extern bfd_boolean _bfd_mips_elf_find_nearest_line
Index: bfd/elfxx-mips.c
===================================================================
--- bfd/elfxx-mips.c	2008-06-28 17:14:35.000000000 +0100
+++ bfd/elfxx-mips.c	2008-06-28 17:14:36.000000000 +0100
@@ -317,15 +317,15 @@ #define GOT_TLS_DONE    0x80
      in any relocs other than a 16 bit call.  */
   unsigned int need_fn_stub : 1;
 
-  /* Are we forced local?  This will only be set if we have converted
-     the initial global GOT entry to a local GOT entry.  */
-  unsigned int forced_local : 1;
-
   /* Are we referenced by some kind of relocation?  */
   unsigned int is_relocation_target : 1;
 
   /* Are we referenced by branch relocations?  */
   unsigned int is_branch_target : 1;
+
+  /* Does this symbol need a traditional MIPS lazy-binding stub
+     (as opposed to a PLT entry)?  */
+  unsigned int needs_lazy_stub : 1;
 };
 
 /* MIPS ELF linker hash table.  */
@@ -349,8 +349,6 @@ struct mips_elf_link_hash_table
   bfd_vma rld_value;
   /* This is set if we see any mips16 stub sections.  */
   bfd_boolean mips16_stubs_seen;
-  /* True if we've computed the size of the GOT.  */
-  bfd_boolean computed_got_sizes;
   /* True if we're generating code for VxWorks.  */
   bfd_boolean is_vxworks;
   /* True if we already reported the small-data section overflow.  */
@@ -371,6 +369,8 @@ struct mips_elf_link_hash_table
   bfd_vma plt_header_size;
   /* The size of a PLT entry in bytes (VxWorks only).  */
   bfd_vma plt_entry_size;
+  /* The number of functions that need a lazy-binding stub.  */
+  bfd_vma lazy_stub_count;
   /* The size of a function stub entry in bytes.  */
   bfd_vma function_stub_size;
 };
@@ -871,9 +871,9 @@ mips_elf_link_hash_newfunc (struct bfd_h
       ret->readonly_reloc = FALSE;
       ret->no_fn_stub = FALSE;
       ret->need_fn_stub = FALSE;
-      ret->forced_local = FALSE;
       ret->is_relocation_target = FALSE;
       ret->is_branch_target = FALSE;
+      ret->needs_lazy_stub = FALSE;
     }
 
   return (struct bfd_hash_entry *) ret;
@@ -2066,18 +2066,14 @@ mips_elf_output_extsym (struct mips_elf_
       else
 	h->esym.asym.value = 0;
     }
-  else if (h->root.needs_plt)
+  else
     {
       struct mips_elf_link_hash_entry *hd = h;
-      bfd_boolean no_fn_stub = h->no_fn_stub;
 
       while (hd->root.root.type == bfd_link_hash_indirect)
-	{
-	  hd = (struct mips_elf_link_hash_entry *)h->root.root.u.i.link;
-	  no_fn_stub = no_fn_stub || hd->no_fn_stub;
-	}
+	hd = (struct mips_elf_link_hash_entry *)h->root.root.u.i.link;
 
-      if (!no_fn_stub)
+      if (hd->needs_lazy_stub)
 	{
 	  /* Set type and value for a symbol with a function stub.  */
 	  h->esym.asym.st = stProc;
@@ -2965,7 +2961,7 @@ mips_elf_sort_hash_table_f (struct mips_
 	hsd->low = (struct elf_link_hash_entry *) h;
       h->root.dynindx = hsd->max_unref_got_dynindx++;
     }
-  else if (h->root.got.offset != 1 || h->forced_local)
+  else if (h->root.got.offset != 1 || h->root.forced_local)
     h->root.dynindx = hsd->max_non_got_dynindx++;
   else
     {
@@ -3001,7 +2997,7 @@ mips_elf_record_global_got_symbol (struc
 	{
 	case STV_INTERNAL:
 	case STV_HIDDEN:
-	  _bfd_mips_elf_hide_symbol (info, h, TRUE);
+	  _bfd_elf_link_hash_hide_symbol (info, h, TRUE);
 	  break;
 	}
       if (!bfd_elf_link_record_dynamic_symbol (info, h))
@@ -3042,14 +3038,10 @@ mips_elf_record_global_got_symbol (struc
     return TRUE;
 
   if (tls_flag == 0)
-    {
-      /* By setting this to a value other than -1, we are indicating that
-	 there needs to be a GOT entry for H.  Avoid using zero, as the
-	 generic ELF copy_indirect_symbol tests for <= 0.  */
-      h->got.offset = 1;
-      if (h->forced_local)
-	g->local_gotno++;
-    }
+    /* By setting this to a value other than -1, we are indicating that
+       there needs to be a GOT entry for H.  Avoid using zero, as the
+       generic ELF copy_indirect_symbol tests for <= 0.  */
+    h->got.offset = 1;
 
   return TRUE;
 }
@@ -3229,6 +3221,148 @@ mips_elf_record_got_page_entry (struct b
 
   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);
+  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);
+    }
+}
+
+/* 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)
+	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;
+}
+
+/* An elf_link_hash_traverse callback for which DATA points to a mips_got_info.
+   Add each forced-local GOT symbol to DATA's local_gotno field.  */
+
+static int
+mips_elf_count_forced_local_got_symbols (struct elf_link_hash_entry *h,
+					 void *data)
+{
+  struct mips_got_info *g;
+
+  g = (struct mips_got_info *) data;
+  if (h->got.offset != MINUS_ONE
+      && (h->forced_local || h->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->got.offset == 2)
+	h->got.offset = MINUS_ONE;
+      else
+	g->local_gotno++;
+    }
+  return 1;
+}
 
 /* Compute the hash value of the bfd in a bfd2got hash entry.  */
 
@@ -3360,7 +3494,7 @@ mips_elf_make_got_per_bfd (void **entryp
       if (entry->tls_type & GOT_TLS_IE)
 	g->tls_gotno += 1;
     }
-  else if (entry->symndx >= 0 || entry->d.h->forced_local)
+  else if (entry->symndx >= 0 || entry->d.h->root.forced_local)
     ++g->local_gotno;
   else
     ++g->global_gotno;
@@ -3593,7 +3727,7 @@ mips_elf_set_global_got_offset (void **e
 
   if (entry->abfd != NULL && entry->symndx == -1
       && entry->d.h->root.dynindx != -1
-      && !entry->d.h->forced_local
+      && !entry->d.h->root.forced_local
       && entry->d.h->tls_type == GOT_NORMAL)
     {
       if (g)
@@ -3614,85 +3748,31 @@ mips_elf_set_global_got_offset (void **e
   return 1;
 }
 
-/* Mark any global symbols referenced in the GOT we are iterating over
-   as inelligible for lazy resolution stubs.  */
+/* A htab_traverse callback for GOT entries for which DATA is the
+   bfd_link_info.  Forbid any global symbols from having traditional
+   lazy-binding stubs.  */
+
 static int
-mips_elf_set_no_stub (void **entryp, void *p ATTRIBUTE_UNUSED)
+mips_elf_forbid_lazy_stubs (void **entryp, void *data)
 {
-  struct mips_got_entry *entry = (struct mips_got_entry *)*entryp;
+  struct bfd_link_info *info;
+  struct mips_elf_link_hash_table *htab;
+  struct mips_got_entry *entry;
 
+  entry = (struct mips_got_entry *) *entryp;
+  info = (struct bfd_link_info *) data;
+  htab = mips_elf_hash_table (info);
   if (entry->abfd != NULL
       && entry->symndx == -1
-      && entry->d.h->root.dynindx != -1)
-    entry->d.h->no_fn_stub = TRUE;
-
-  return 1;
-}
-
-/* Follow indirect and warning hash entries so that each got entry
-   points to the final symbol definition.  P must point to a pointer
-   to the hash table we're traversing.  Since this traversal may
-   modify the hash table, we set this pointer to NULL to indicate
-   we've made a potentially-destructive change to the hash table, so
-   the traversal must be restarted.  */
-static int
-mips_elf_resolve_final_got_entry (void **entryp, void *p)
-{
-  struct mips_got_entry *entry = (struct mips_got_entry *)*entryp;
-  htab_t got_entries = *(htab_t *)p;
-
-  if (entry->abfd != NULL && entry->symndx == -1)
+      && entry->d.h->needs_lazy_stub)
     {
-      struct mips_elf_link_hash_entry *h = entry->d.h;
-
-      while (h->root.root.type == bfd_link_hash_indirect
- 	     || h->root.root.type == bfd_link_hash_warning)
-	h = (struct mips_elf_link_hash_entry *) h->root.root.u.i.link;
-
-      if (entry->d.h == h)
-	return 1;
-
-      entry->d.h = h;
-
-      /* If we can't find this entry with the new bfd hash, re-insert
-	 it, and get the traversal restarted.  */
-      if (! htab_find (got_entries, entry))
-	{
-	  htab_clear_slot (got_entries, entryp);
-	  entryp = htab_find_slot (got_entries, entry, INSERT);
-	  if (! *entryp)
-	    *entryp = entry;
-	  /* Abort the traversal, since the whole table may have
-	     moved, and leave it up to the parent to restart the
-	     process.  */
-	  *(htab_t *)p = NULL;
-	  return 0;
-	}
-      /* We might want to decrement the global_gotno count, but it's
-	 either too early or too late for that at this point.  */
+      entry->d.h->needs_lazy_stub = FALSE;
+      htab->lazy_stub_count--;
     }
 
   return 1;
 }
 
-/* Turn indirect got entries in a got_entries table into their final
-   locations.  */
-static void
-mips_elf_resolve_final_got_entries (struct mips_got_info *g)
-{
-  htab_t got_entries;
-
-  do
-    {
-      got_entries = g->got_entries;
-
-      htab_traverse (got_entries,
-		     mips_elf_resolve_final_got_entry,
-		     &got_entries);
-    }
-  while (got_entries == NULL);
-}
-
 /* Return the offset of an input bfd IBFD's GOT from the beginning of
    the primary GOT.  */
 static bfd_vma
@@ -3724,8 +3804,10 @@ mips_elf_multi_got (bfd *abfd, struct bf
   struct mips_elf_got_per_bfd_arg got_per_bfd_arg;
   struct mips_elf_set_global_got_offset_arg set_got_offset_arg;
   struct mips_got_info *g, *gg;
-  unsigned int assign;
+  unsigned int assign, needed_relocs;
+  bfd *dynobj;
 
+  dynobj = elf_hash_table (info)->dynobj;
   htab = mips_elf_hash_table (info);
   g = htab->got_info;
   g->bfd2got = htab_try_create (1, mips_elf_bfd2got_entry_hash,
@@ -3916,16 +3998,49 @@ mips_elf_multi_got (bfd *abfd, struct bf
       /* Move onto the next GOT.  It will be a secondary GOT if nonull.  */
       g = gn;
 
-      /* Mark global symbols in every non-primary GOT as ineligible for
-	 stubs.  */
+      /* Forbid global symbols in every non-primary GOT from having
+	 lazy-binding stubs.  */
       if (g)
-	htab_traverse (g->got_entries, mips_elf_set_no_stub, NULL);
+	htab_traverse (g->got_entries, mips_elf_forbid_lazy_stubs, info);
     }
   while (g);
 
   got->size = (gg->next->local_gotno
-		    + gg->next->global_gotno
-		    + gg->next->tls_gotno) * MIPS_ELF_GOT_SIZE (abfd);
+	       + gg->next->global_gotno
+	       + gg->next->tls_gotno) * MIPS_ELF_GOT_SIZE (abfd);
+
+  needed_relocs = 0;
+  set_got_offset_arg.value = MIPS_ELF_GOT_SIZE (abfd);
+  set_got_offset_arg.info = info;
+  for (g = gg->next; g && g->next != gg; g = g->next)
+    {
+      unsigned int save_assign;
+
+      /* Assign offsets to global GOT entries.  */
+      save_assign = g->assigned_gotno;
+      g->assigned_gotno = g->local_gotno;
+      set_got_offset_arg.g = g;
+      set_got_offset_arg.needed_relocs = 0;
+      htab_traverse (g->got_entries,
+		     mips_elf_set_global_got_offset,
+		     &set_got_offset_arg);
+      needed_relocs += set_got_offset_arg.needed_relocs;
+      BFD_ASSERT (g->assigned_gotno - g->local_gotno <= g->global_gotno);
+
+      g->assigned_gotno = save_assign;
+      if (info->shared)
+	{
+	  needed_relocs += g->local_gotno - g->assigned_gotno;
+	  BFD_ASSERT (g->assigned_gotno == g->next->local_gotno
+		      + g->next->global_gotno
+		      + g->next->tls_gotno
+		      + MIPS_RESERVED_GOTNO (info));
+	}
+    }
+
+  if (needed_relocs)
+    mips_elf_allocate_dynamic_relocations (dynobj, info,
+					   needed_relocs);
 
   return TRUE;
 }
@@ -5056,33 +5171,6 @@ mips_elf_perform_relocation (struct bfd_
   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);
-  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);
-    }
-}
-
 /* Create a rel.dyn relocation for the dynamic linker to resolve.  REL
    is the original relocation, which is now being transformed into a
    dynamic relocation.  The ADDENDP is adjusted if necessary; the
@@ -7491,18 +7579,8 @@ _bfd_mips_elf_adjust_dynamic_symbol (str
 	 executable and the shared library.  */
       if (!h->def_regular)
 	{
-	  /* We need .stub section.  */
-	  h->root.u.def.section = htab->sstubs;
-	  h->root.u.def.value = htab->sstubs->size;
-
-	  /* XXX Write this stub address somewhere.  */
-	  h->plt.offset = htab->sstubs->size;
-
-	  /* Make room for this stub code.  */
-	  htab->sstubs->size += htab->function_stub_size;
-
-	  /* The last half word of the stub will be filled with the index
-	     of this symbol in .dynsym section.  */
+	  hmips->needs_lazy_stub = TRUE;
+	  htab->lazy_stub_count++;
 	  return TRUE;
 	}
     }
@@ -7701,15 +7779,6 @@ _bfd_mips_elf_always_size_sections (bfd 
 				    struct bfd_link_info *info)
 {
   asection *ri;
-
-  asection *s;
-  struct mips_got_info *g;
-  int i;
-  bfd_size_type loadable_size = 0;
-  bfd_size_type page_gotno;
-  bfd_size_type dynsymcount;
-  bfd *sub;
-  struct mips_elf_count_tls_arg count_tls_arg;
   struct mips_elf_link_hash_table *htab;
 
   htab = mips_elf_hash_table (info);
@@ -7724,29 +7793,40 @@ _bfd_mips_elf_always_size_sections (bfd 
     mips_elf_link_hash_traverse (mips_elf_hash_table (info),
 				 mips_elf_check_mips16_stubs, info);
 
+  return TRUE;
+}
+
+/* If the link uses a GOT, lay it out and work out its size.  */
+
+static bfd_boolean
+mips_elf_lay_out_got (bfd *output_bfd, struct bfd_link_info *info)
+{
+  bfd *dynobj;
+  asection *s;
+  struct mips_got_info *g;
+  int i;
+  bfd_size_type loadable_size = 0;
+  bfd_size_type page_gotno;
+  bfd *sub;
+  struct mips_elf_count_tls_arg count_tls_arg;
+  struct mips_elf_link_hash_table *htab;
+
+  htab = mips_elf_hash_table (info);
   s = htab->sgot;
   if (s == NULL)
     return TRUE;
 
+  dynobj = elf_hash_table (info)->dynobj;
   g = htab->got_info;
 
-  /* Calculate the total loadable size of the output.  That
-     will give us the maximum number of GOT_PAGE entries
-     required.  */
-  for (sub = info->input_bfds; sub; sub = sub->link_next)
-    {
-      asection *subsection;
+  /* Replace entries for indirect and warning symbols with entries for
+     the target symbol.  */
+  if (!mips_elf_resolve_final_got_entries (g))
+    return FALSE;
 
-      for (subsection = sub->sections;
-	   subsection;
-	   subsection = subsection->next)
-	{
-	  if ((subsection->flags & SEC_ALLOC) == 0)
-	    continue;
-	  loadable_size += ((subsection->size + 0xf)
-			    &~ (bfd_size_type) 0xf);
-	}
-    }
+  /* Count the number of forced-local entries.  */
+  elf_link_hash_traverse (elf_hash_table (info),
+			  mips_elf_count_forced_local_got_symbols, g);
 
   /* There has to be a global GOT entry for every symbol with
      a dynamic symbol table index of DT_MIPS_GOTSYM or
@@ -7763,22 +7843,23 @@ _bfd_mips_elf_always_size_sections (bfd 
        relocations, then GLOBAL_GOTSYM will be NULL.  */
     i = 0;
 
-  /* Get a worst-case estimate of the number of dynamic symbols needed.
-     At this point, dynsymcount does not account for section symbols
-     and count_section_dynsyms may overestimate the number that will
-     be needed.  */
-  dynsymcount = (elf_hash_table (info)->dynsymcount
-		 + count_section_dynsyms (output_bfd, info));
-
-  /* Determine the size of one stub entry.  */
-  htab->function_stub_size = (dynsymcount > 0x10000
-			      ? MIPS_FUNCTION_STUB_BIG_SIZE
-			      : MIPS_FUNCTION_STUB_NORMAL_SIZE);
+  /* Calculate the total loadable size of the output.  That
+     will give us the maximum number of GOT_PAGE entries
+     required.  */
+  for (sub = info->input_bfds; sub; sub = sub->link_next)
+    {
+      asection *subsection;
 
-  /* In the worst case, we'll get one stub per dynamic symbol, plus
-     one to account for the dummy entry at the end required by IRIX
-     rld.  */
-  loadable_size += htab->function_stub_size * (i + 1);
+      for (subsection = sub->sections;
+	   subsection;
+	   subsection = subsection->next)
+	{
+	  if ((subsection->flags & SEC_ALLOC) == 0)
+	    continue;
+	  loadable_size += ((subsection->size + 0xf)
+			    &~ (bfd_size_type) 0xf);
+	}
+    }
 
   if (htab->is_vxworks)
     /* There's no need to allocate page entries for VxWorks; R_MIPS*_GOT16
@@ -7812,27 +7893,120 @@ _bfd_mips_elf_always_size_sections (bfd 
   g->tls_gotno += count_tls_arg.needed;
   s->size += g->tls_gotno * MIPS_ELF_GOT_SIZE (output_bfd);
 
-  mips_elf_resolve_final_got_entries (g);
-
   /* VxWorks does not support multiple GOTs.  It initializes $gp to
      __GOTT_BASE__[__GOTT_INDEX__], the value of which is set by the
      dynamic loader.  */
-  if (!htab->is_vxworks && s->size > MIPS_ELF_GOT_MAX_SIZE (info))
+  if (htab->is_vxworks)
+    {
+      /* VxWorks executables do not need a GOT.  */
+      if (info->shared)
+	{
+	  /* Each VxWorks GOT entry needs an explicit relocation.  */
+	  unsigned int count;
+
+	  count = g->global_gotno + g->local_gotno - MIPS_RESERVED_GOTNO (info);
+	  if (count)
+	    mips_elf_allocate_dynamic_relocations (dynobj, info, count);
+	}
+    }
+  else if (s->size > MIPS_ELF_GOT_MAX_SIZE (info))
     {
       if (!mips_elf_multi_got (output_bfd, info, s, page_gotno))
 	return FALSE;
     }
   else
     {
-      /* Set up TLS entries for the first GOT.  */
+      struct mips_elf_count_tls_arg arg;
+
+      /* Set up TLS entries.  */
       g->tls_assigned_gotno = g->global_gotno + g->local_gotno;
       htab_traverse (g->got_entries, mips_elf_initialize_tls_index, g);
+
+      /* Allocate room for the TLS relocations.  */
+      arg.info = info;
+      arg.needed = 0;
+      htab_traverse (g->got_entries, mips_elf_count_local_tls_relocs, &arg);
+      elf_link_hash_traverse (elf_hash_table (info),
+			      mips_elf_count_global_tls_relocs,
+			      &arg);
+      if (arg.needed)
+	mips_elf_allocate_dynamic_relocations (dynobj, info, arg.needed);
     }
-  htab->computed_got_sizes = TRUE;
 
   return TRUE;
 }
 
+/* Estimate the size of the .MIPS.stubs section.  */
+
+static void
+mips_elf_estimate_stub_size (bfd *output_bfd, struct bfd_link_info *info)
+{
+  struct mips_elf_link_hash_table *htab;
+  bfd_size_type dynsymcount;
+
+  htab = mips_elf_hash_table (info);
+  if (htab->lazy_stub_count == 0)
+    return;
+
+  /* IRIX rld assumes that a function stub isn't at the end of the .text
+     section, so add a dummy entry to the end.  */
+  htab->lazy_stub_count++;
+
+  /* Get a worst-case estimate of the number of dynamic symbols needed.
+     At this point, dynsymcount does not account for section symbols
+     and count_section_dynsyms may overestimate the number that will
+     be needed.  */
+  dynsymcount = (elf_hash_table (info)->dynsymcount
+		 + count_section_dynsyms (output_bfd, info));
+
+  /* Determine the size of one stub entry.  */
+  htab->function_stub_size = (dynsymcount > 0x10000
+			      ? MIPS_FUNCTION_STUB_BIG_SIZE
+			      : MIPS_FUNCTION_STUB_NORMAL_SIZE);
+
+  htab->sstubs->size = htab->lazy_stub_count * htab->function_stub_size;
+}
+
+/* A mips_elf_link_hash_traverse callback for which DATA points to the
+   MIPS hash table.  If H needs a traditional MIPS lazy-binding stub,
+   allocate an entry in the stubs section.  */
+
+static bfd_boolean
+mips_elf_allocate_lazy_stub (struct mips_elf_link_hash_entry *h, void **data)
+{
+  struct mips_elf_link_hash_table *htab;
+
+  htab = (struct mips_elf_link_hash_table *) data;
+  if (h->needs_lazy_stub)
+    {
+      h->root.root.u.def.section = htab->sstubs;
+      h->root.root.u.def.value = htab->sstubs->size;
+      h->root.plt.offset = htab->sstubs->size;
+      htab->sstubs->size += htab->function_stub_size;
+    }
+  return TRUE;
+}
+
+/* Allocate offsets in the stubs section to each symbol that needs one.
+   Set the final size of the .MIPS.stub section.  */
+
+static void
+mips_elf_lay_out_lazy_stubs (struct bfd_link_info *info)
+{
+  struct mips_elf_link_hash_table *htab;
+
+  htab = mips_elf_hash_table (info);
+  if (htab->lazy_stub_count == 0)
+    return;
+
+  htab->sstubs->size = 0;
+  mips_elf_link_hash_traverse (mips_elf_hash_table (info),
+			       mips_elf_allocate_lazy_stub, htab);
+  htab->sstubs->size += htab->function_stub_size;
+  BFD_ASSERT (htab->sstubs->size
+	      == htab->lazy_stub_count * htab->function_stub_size);
+}
+
 /* Set the sizes of the dynamic sections.  */
 
 bfd_boolean
@@ -7840,7 +8014,7 @@ _bfd_mips_elf_size_dynamic_sections (bfd
 				     struct bfd_link_info *info)
 {
   bfd *dynobj;
-  asection *s, *sreldyn;
+  asection *s;
   bfd_boolean reltext;
   struct mips_elf_link_hash_table *htab;
 
@@ -7862,16 +8036,17 @@ _bfd_mips_elf_size_dynamic_sections (bfd
 	}
       }
 
-  /* IRIX rld assumes that the function stub isn't at the end
-     of the .text section, so add a dummy entry to the end.  */
-  if (htab->sstubs && htab->sstubs->size > 0)
-    htab->sstubs->size += htab->function_stub_size;
+  mips_elf_estimate_stub_size (output_bfd, info);
+
+  if (!mips_elf_lay_out_got (output_bfd, info))
+    return FALSE;
+
+  mips_elf_lay_out_lazy_stubs (info);
 
   /* The check_relocs and adjust_dynamic_symbol entry points have
      determined the sizes of the various dynamic sections.  Allocate
      memory for them.  */
   reltext = FALSE;
-  sreldyn = NULL;
   for (s = dynobj->sections; s != NULL; s = s->next)
     {
       const char *name;
@@ -7919,88 +8094,6 @@ _bfd_mips_elf_size_dynamic_sections (bfd
 	      info->combreloc = 0;
 	    }
 	}
-      else if (htab->is_vxworks && strcmp (name, ".got") == 0)
-	{
-	  /* Executables do not need a GOT.  */
-	  if (info->shared)
-	    {
-	      /* Allocate relocations for all but the reserved entries.  */
-	      unsigned int count;
-
-	      count = (htab->got_info->global_gotno
-		       + htab->got_info->local_gotno
-		       - MIPS_RESERVED_GOTNO (info));
-	      mips_elf_allocate_dynamic_relocations (dynobj, info, count);
-	    }
-	}
-      else if (!htab->is_vxworks && CONST_STRNEQ (name, ".got"))
-	{
-	  /* _bfd_mips_elf_always_size_sections() has already done
-	     most of the work, but some symbols may have been mapped
-	     to versions that we must now resolve in the got_entries
-	     hash tables.  */
-	  struct mips_got_info *gg = htab->got_info;
-	  struct mips_got_info *g = gg;
-	  struct mips_elf_set_global_got_offset_arg set_got_offset_arg;
-	  unsigned int needed_relocs = 0;
-
-	  if (gg->next)
-	    {
-	      set_got_offset_arg.value = MIPS_ELF_GOT_SIZE (output_bfd);
-	      set_got_offset_arg.info = info;
-
-	      /* NOTE 2005-02-03: How can this call, or the next, ever
-		 find any indirect entries to resolve?  They were all
-		 resolved in mips_elf_multi_got.  */
-	      mips_elf_resolve_final_got_entries (gg);
-	      for (g = gg->next; g && g->next != gg; g = g->next)
-		{
-		  unsigned int save_assign;
-
-		  mips_elf_resolve_final_got_entries (g);
-
-		  /* Assign offsets to global GOT entries.  */
-		  save_assign = g->assigned_gotno;
-		  g->assigned_gotno = g->local_gotno;
-		  set_got_offset_arg.g = g;
-		  set_got_offset_arg.needed_relocs = 0;
-		  htab_traverse (g->got_entries,
-				 mips_elf_set_global_got_offset,
-				 &set_got_offset_arg);
-		  needed_relocs += set_got_offset_arg.needed_relocs;
-		  BFD_ASSERT (g->assigned_gotno - g->local_gotno
-			      <= g->global_gotno);
-
-		  g->assigned_gotno = save_assign;
-		  if (info->shared)
-		    {
-		      needed_relocs += g->local_gotno - g->assigned_gotno;
-		      BFD_ASSERT (g->assigned_gotno == g->next->local_gotno
-				  + g->next->global_gotno
-				  + g->next->tls_gotno
-				  + MIPS_RESERVED_GOTNO (info));
-		    }
-		}
-	    }
-	  else
-	    {
-	      struct mips_elf_count_tls_arg arg;
-	      arg.info = info;
-	      arg.needed = 0;
-
-	      htab_traverse (gg->got_entries, mips_elf_count_local_tls_relocs,
-			     &arg);
-	      elf_link_hash_traverse (elf_hash_table (info),
-				      mips_elf_count_global_tls_relocs,
-				      &arg);
-
-	      needed_relocs += arg.needed;
-	    }
-
-	  if (needed_relocs)
-	    mips_elf_allocate_dynamic_relocations (dynobj, info,
-						   needed_relocs);
-	}
       else if (! info->shared
 	       && ! mips_elf_hash_table (info)->use_rld_obj_head
 	       && CONST_STRNEQ (name, ".rld_map"))
@@ -8013,6 +8106,7 @@ _bfd_mips_elf_size_dynamic_sections (bfd
 	       && CONST_STRNEQ (name, ".compact_rel"))
 	s->size += mips_elf_hash_table (info)->compact_rel_size;
       else if (! CONST_STRNEQ (name, ".init")
+	       && s != htab->sgot
 	       && s != htab->sgotplt
 	       && s != htab->splt
 	       && s != htab->sstubs)
@@ -8030,14 +8124,6 @@ _bfd_mips_elf_size_dynamic_sections (bfd
       if ((s->flags & SEC_HAS_CONTENTS) == 0)
 	continue;
 
-      /* Allocate memory for this section last, since we may increase its
-	 size above.  */
-      if (strcmp (name, MIPS_ELF_REL_DYN_NAME (info)) == 0)
-	{
-	  sreldyn = s;
-	  continue;
-	}
-
       /* Allocate memory for the section contents.  */
       s->contents = bfd_zalloc (dynobj, s->size);
       if (s->contents == NULL)
@@ -8047,17 +8133,6 @@ _bfd_mips_elf_size_dynamic_sections (bfd
 	}
     }
 
-  /* Allocate memory for the .rel(a).dyn section.  */
-  if (sreldyn != NULL)
-    {
-      sreldyn->contents = bfd_zalloc (dynobj, sreldyn->size);
-      if (sreldyn->contents == NULL)
-	{
-	  bfd_set_error (bfd_error_no_memory);
-	  return FALSE;
-	}
-    }
-
   if (elf_hash_table (info)->dynamic_sections_created)
     {
       /* Add some entries to the .dynamic section.  We fill in the
@@ -10136,91 +10211,6 @@ _bfd_mips_elf_copy_indirect_symbol (stru
   if (dirmips->tls_type == 0)
     dirmips->tls_type = indmips->tls_type;
 }
-
-void
-_bfd_mips_elf_hide_symbol (struct bfd_link_info *info,
-			   struct elf_link_hash_entry *entry,
-			   bfd_boolean force_local)
-{
-  bfd *dynobj;
-  struct mips_got_info *g;
-  struct mips_elf_link_hash_entry *h;
-  struct mips_elf_link_hash_table *htab;
-
-  h = (struct mips_elf_link_hash_entry *) entry;
-  if (h->forced_local)
-    return;
-  h->forced_local = force_local;
-
-  dynobj = elf_hash_table (info)->dynobj;
-  htab = mips_elf_hash_table (info);
-  if (dynobj != NULL
-      && force_local
-      && h->root.type != STT_TLS
-      && htab->got_info != NULL)
-    {
-      g = htab->got_info;
-      if (g->next)
-	{
-	  struct mips_got_entry e;
-	  struct mips_got_info *gg = g;
-
-	  /* Since we're turning what used to be a global symbol into a
-	     local one, bump up the number of local entries of each GOT
-	     that had an entry for it.  This will automatically decrease
-	     the number of global entries, since global_gotno is actually
-	     the upper limit of global entries.  */
-	  e.abfd = dynobj;
-	  e.symndx = -1;
-	  e.d.h = h;
-	  e.tls_type = 0;
-
-	  for (g = g->next; g != gg; g = g->next)
-	    if (htab_find (g->got_entries, &e))
-	      {
-		BFD_ASSERT (g->global_gotno > 0);
-		g->local_gotno++;
-		g->global_gotno--;
-	      }
-
-	  /* If this was a global symbol forced into the primary GOT, we
-	     no longer need an entry for it.  We can't release the entry
-	     at this point, but we must at least stop counting it as one
-	     of the symbols that required a forced got entry.  */
-	  if (h->root.got.offset == 2)
-	    {
-	      BFD_ASSERT (gg->assigned_gotno > 0);
-	      gg->assigned_gotno--;
-	    }
-	}
-      else if (h->root.got.offset == 1)
-	{
-	  /* check_relocs didn't know that this symbol would be
-	     forced-local, so add an extra local got entry.  */
-	  g->local_gotno++;
-	  if (htab->computed_got_sizes)
-	    {
-	      /* We'll have treated this symbol as global rather
-		 than local.  */
-	      BFD_ASSERT (g->global_gotno > 0);
-	      g->global_gotno--;
-	    }
-	}
-      else if (htab->is_vxworks && h->root.needs_plt)
-	{
-	  /* check_relocs didn't know that this symbol would be
-	     forced-local, so add an extra local got entry.  */
-	  g->local_gotno++;
-	  if (htab->computed_got_sizes)
-	    /* The symbol is only used in call relocations, so we'll
-	       have assumed it only needs a .got.plt entry.  Increase
-	       the size of .got accordingly.  */
-	    htab->sgot->size += MIPS_ELF_GOT_SIZE (dynobj);
-        }
-    }
-
-  _bfd_elf_link_hash_hide_symbol (info, &h->root, force_local);
-}
 
 #define PDR_SIZE 32
 
@@ -10677,7 +10667,6 @@ _bfd_mips_elf_link_hash_table_create (bf
   ret->use_rld_obj_head = FALSE;
   ret->rld_value = 0;
   ret->mips16_stubs_seen = FALSE;
-  ret->computed_got_sizes = FALSE;
   ret->is_vxworks = FALSE;
   ret->small_data_overflow_reported = FALSE;
   ret->srelbss = NULL;
@@ -10691,6 +10680,7 @@ _bfd_mips_elf_link_hash_table_create (bf
   ret->got_info = NULL;
   ret->plt_header_size = 0;
   ret->plt_entry_size = 0;
+  ret->lazy_stub_count = 0;
   ret->function_stub_size = 0;
 
   return &ret->root.root;
Index: ld/testsuite/ld-mips-elf/tlsdyn-o32-2.got
===================================================================
--- ld/testsuite/ld-mips-elf/tlsdyn-o32-2.got	2008-06-28 17:09:14.000000000 +0100
+++ ld/testsuite/ld-mips-elf/tlsdyn-o32-2.got	2008-06-28 17:14:36.000000000 +0100
@@ -4,16 +4,16 @@
 DYNAMIC RELOCATION RECORDS
 OFFSET   TYPE              VALUE 
 00000000 R_MIPS_NONE       \*ABS\*
-10000040 R_MIPS_TLS_DTPMOD32  tlsbin_gd
-10000044 R_MIPS_TLS_DTPREL32  tlsbin_gd
-10000034 R_MIPS_TLS_DTPMOD32  tlsvar_gd
-10000038 R_MIPS_TLS_DTPREL32  tlsvar_gd
-1000003c R_MIPS_TLS_TPREL32  tlsvar_ie
+10000038 R_MIPS_TLS_DTPMOD32  tlsbin_gd
+1000003c R_MIPS_TLS_DTPREL32  tlsbin_gd
+10000030 R_MIPS_TLS_DTPMOD32  tlsvar_gd
+10000034 R_MIPS_TLS_DTPREL32  tlsvar_gd
+1000002c R_MIPS_TLS_TPREL32  tlsvar_ie
 10000048 R_MIPS_TLS_TPREL32  tlsbin_ie
 
 
 Contents of section .got:
- 10000020 00000000 80000000 0040053c 00000001  .*
+ 10000020 00000000 80000000 0040053c 00000000  .*
  10000030 00000000 00000000 00000000 00000000  .*
- 10000040 00000000 00000000 00000000 00000000  .*
+ 10000040 00000001 00000000 00000000 00000000  .*
  10000050 00000000 00000000                    .*
Index: ld/testsuite/ld-mips-elf/tlsdyn-o32-3.got
===================================================================
--- ld/testsuite/ld-mips-elf/tlsdyn-o32-3.got	2008-06-28 17:09:14.000000000 +0100
+++ ld/testsuite/ld-mips-elf/tlsdyn-o32-3.got	2008-06-28 17:14:36.000000000 +0100
@@ -4,16 +4,16 @@
 DYNAMIC RELOCATION RECORDS
 OFFSET   TYPE              VALUE 
 00000000 R_MIPS_NONE       \*ABS\*
-10000040 R_MIPS_TLS_DTPMOD32  tlsbin_gd
-10000044 R_MIPS_TLS_DTPREL32  tlsbin_gd
-10000034 R_MIPS_TLS_DTPMOD32  tlsvar_gd
-10000038 R_MIPS_TLS_DTPREL32  tlsvar_gd
-1000003c R_MIPS_TLS_TPREL32  tlsvar_ie
+10000038 R_MIPS_TLS_DTPMOD32  tlsbin_gd
+1000003c R_MIPS_TLS_DTPREL32  tlsbin_gd
+10000030 R_MIPS_TLS_DTPMOD32  tlsvar_gd
+10000034 R_MIPS_TLS_DTPREL32  tlsvar_gd
+1000002c R_MIPS_TLS_TPREL32  tlsvar_ie
 10000048 R_MIPS_TLS_TPREL32  tlsbin_ie
 
 
 Contents of section .got:
- 10000020 00000000 80000000 004005ec 00000001  .*
+ 10000020 00000000 80000000 004005ec 00000000  .*
  10000030 00000000 00000000 00000000 00000000  .*
- 10000040 00000000 00000000 00000000 00000000  .*
+ 10000040 00000001 00000000 00000000 00000000  .*
  10000050 00000000 00000000                    .*
Index: ld/testsuite/ld-mips-elf/tlsdyn-o32-2.d
===================================================================
--- ld/testsuite/ld-mips-elf/tlsdyn-o32-2.d	2008-06-28 17:14:30.000000000 +0100
+++ ld/testsuite/ld-mips-elf/tlsdyn-o32-2.d	2008-06-28 17:14:36.000000000 +0100
@@ -12,19 +12,19 @@ Disassembly of section .text:
   .*:	03a0f021 	move	s8,sp
   .*:	afbc0000 	sw	gp,0\(sp\)
   .*:	8f998018 	lw	t9,-32744\(gp\)
-  .*:	27848030 	addiu	a0,gp,-32720
+  .*:	27848028 	addiu	a0,gp,-32728
   .*:	0320f809 	jalr	t9
   .*:	00000000 	nop
   .*:	8fdc0000 	lw	gp,0\(s8\)
   .*:	00000000 	nop
   .*:	8f998018 	lw	t9,-32744\(gp\)
-  .*:	27848024 	addiu	a0,gp,-32732
+  .*:	27848020 	addiu	a0,gp,-32736
   .*:	0320f809 	jalr	t9
   .*:	00000000 	nop
   .*:	8fdc0000 	lw	gp,0\(s8\)
   .*:	00000000 	nop
   .*:	8f998018 	lw	t9,-32744\(gp\)
-  .*:	2784801c 	addiu	a0,gp,-32740
+  .*:	27848030 	addiu	a0,gp,-32720
   .*:	0320f809 	jalr	t9
   .*:	00000000 	nop
   .*:	8fdc0000 	lw	gp,0\(s8\)
@@ -36,7 +36,7 @@ Disassembly of section .text:
   .*:	8f838038 	lw	v1,-32712\(gp\)
   .*:	00000000 	nop
   .*:	00621821 	addu	v1,v1,v0
-  .*:	8f83802c 	lw	v1,-32724\(gp\)
+  .*:	8f83801c 	lw	v1,-32740\(gp\)
   .*:	00000000 	nop
   .*:	00621821 	addu	v1,v1,v0
   .*:	7c02283b 	rdhwr	v0,\$5
@@ -62,19 +62,19 @@ Disassembly of section .text:
   .*:	03a0f021 	move	s8,sp
   .*:	afbc0000 	sw	gp,0\(sp\)
   .*:	8f998018 	lw	t9,-32744\(gp\)
-  .*:	27848030 	addiu	a0,gp,-32720
+  .*:	27848028 	addiu	a0,gp,-32728
   .*:	0320f809 	jalr	t9
   .*:	00000000 	nop
   .*:	8fdc0000 	lw	gp,0\(s8\)
   .*:	00000000 	nop
   .*:	8f998018 	lw	t9,-32744\(gp\)
-  .*:	27848024 	addiu	a0,gp,-32732
+  .*:	27848020 	addiu	a0,gp,-32736
   .*:	0320f809 	jalr	t9
   .*:	00000000 	nop
   .*:	8fdc0000 	lw	gp,0\(s8\)
   .*:	00000000 	nop
   .*:	8f998018 	lw	t9,-32744\(gp\)
-  .*:	2784801c 	addiu	a0,gp,-32740
+  .*:	27848030 	addiu	a0,gp,-32720
   .*:	0320f809 	jalr	t9
   .*:	00000000 	nop
   .*:	8fdc0000 	lw	gp,0\(s8\)
@@ -86,7 +86,7 @@ Disassembly of section .text:
   .*:	8f838038 	lw	v1,-32712\(gp\)
   .*:	00000000 	nop
   .*:	00621821 	addu	v1,v1,v0
-  .*:	8f83802c 	lw	v1,-32724\(gp\)
+  .*:	8f83801c 	lw	v1,-32740\(gp\)
   .*:	00000000 	nop
   .*:	00621821 	addu	v1,v1,v0
   .*:	7c02283b 	rdhwr	v0,\$5
Index: ld/testsuite/ld-mips-elf/tlsdyn-o32-3.d
===================================================================
--- ld/testsuite/ld-mips-elf/tlsdyn-o32-3.d	2008-06-28 17:14:30.000000000 +0100
+++ ld/testsuite/ld-mips-elf/tlsdyn-o32-3.d	2008-06-28 17:14:36.000000000 +0100
@@ -12,19 +12,19 @@ Disassembly of section .text:
   .*:	03a0f021 	move	s8,sp
   .*:	afbc0000 	sw	gp,0\(sp\)
   .*:	8f998018 	lw	t9,-32744\(gp\)
-  .*:	27848030 	addiu	a0,gp,-32720
+  .*:	27848028 	addiu	a0,gp,-32728
   .*:	0320f809 	jalr	t9
   .*:	00000000 	nop
   .*:	8fdc0000 	lw	gp,0\(s8\)
   .*:	00000000 	nop
   .*:	8f998018 	lw	t9,-32744\(gp\)
-  .*:	27848024 	addiu	a0,gp,-32732
+  .*:	27848020 	addiu	a0,gp,-32736
   .*:	0320f809 	jalr	t9
   .*:	00000000 	nop
   .*:	8fdc0000 	lw	gp,0\(s8\)
   .*:	00000000 	nop
   .*:	8f998018 	lw	t9,-32744\(gp\)
-  .*:	2784801c 	addiu	a0,gp,-32740
+  .*:	27848030 	addiu	a0,gp,-32720
   .*:	0320f809 	jalr	t9
   .*:	00000000 	nop
   .*:	8fdc0000 	lw	gp,0\(s8\)
@@ -36,7 +36,7 @@ Disassembly of section .text:
   .*:	8f838038 	lw	v1,-32712\(gp\)
   .*:	00000000 	nop
   .*:	00621821 	addu	v1,v1,v0
-  .*:	8f83802c 	lw	v1,-32724\(gp\)
+  .*:	8f83801c 	lw	v1,-32740\(gp\)
   .*:	00000000 	nop
   .*:	00621821 	addu	v1,v1,v0
   .*:	7c02283b 	rdhwr	v0,\$5
@@ -58,19 +58,19 @@ Disassembly of section .text:
   .*:	03a0f021 	move	s8,sp
   .*:	afbc0000 	sw	gp,0\(sp\)
   .*:	8f998018 	lw	t9,-32744\(gp\)
-  .*:	27848030 	addiu	a0,gp,-32720
+  .*:	27848028 	addiu	a0,gp,-32728
   .*:	0320f809 	jalr	t9
   .*:	00000000 	nop
   .*:	8fdc0000 	lw	gp,0\(s8\)
   .*:	00000000 	nop
   .*:	8f998018 	lw	t9,-32744\(gp\)
-  .*:	27848024 	addiu	a0,gp,-32732
+  .*:	27848020 	addiu	a0,gp,-32736
   .*:	0320f809 	jalr	t9
   .*:	00000000 	nop
   .*:	8fdc0000 	lw	gp,0\(s8\)
   .*:	00000000 	nop
   .*:	8f998018 	lw	t9,-32744\(gp\)
-  .*:	2784801c 	addiu	a0,gp,-32740
+  .*:	27848030 	addiu	a0,gp,-32720
   .*:	0320f809 	jalr	t9
   .*:	00000000 	nop
   .*:	8fdc0000 	lw	gp,0\(s8\)
@@ -82,7 +82,7 @@ Disassembly of section .text:
   .*:	8f838038 	lw	v1,-32712\(gp\)
   .*:	00000000 	nop
   .*:	00621821 	addu	v1,v1,v0
-  .*:	8f83802c 	lw	v1,-32724\(gp\)
+  .*:	8f83801c 	lw	v1,-32740\(gp\)
   .*:	00000000 	nop
   .*:	00621821 	addu	v1,v1,v0
   .*:	7c02283b 	rdhwr	v0,\$5



More information about the Binutils mailing list