Patch for MIPS multi-got bug with forced-local symbols

Joseph S. Myers joseph@codesourcery.com
Wed Sep 19 12:12:00 GMT 2007


This patch fixes a MIPS multi-GOT bug (assertion failures followed by
other link errors) shown by the testcase added.

The symbols __init_array_start and __init_array_end are first
processed as undefined hidden references, then they are defined by a
PROVIDE_HIDDEN in the linker script.

Inconsistencies arise in how these symbols are counted for multi-GOT
allocation.  mips_elf_sort_hash_table starts with

  hsd.max_unref_got_dynindx =
  hsd.min_got_dynindx = elf_hash_table (info)->dynsymcount
    /* In the multi-got case, assigned_gotno of the master got_info
       indicate the number of entries that aren't referenced in the
       primary GOT, but that must have entries because there are
       dynamic relocations that reference it.  Since they aren't
       referenced, we move them to the end of the GOT, so that they
       don't prevent other entries that are referenced from getting
       too large offsets.  */
    - (g->next ? g->assigned_gotno : 0);

and assigned_gotno was calculated in mips_elf_multi_got,

      gg->assigned_gotno = gg->global_gotno - g->global_gotno;

but this is subtracting figures calculated in two different ways.
gg->global_gotno, the figure for the master GOT, is calculated in
_bfd_mips_elf_always_size_sections:

  if (g->global_gotsym != NULL)
    i = elf_hash_table (info)->dynsymcount - g->global_gotsym->dynindx;
...
  g->global_gotno = i;

(and includes __init_array_*: they were assigned dynindx values when
first processed because they were undefined at that point) while
g->global_gotno, the figure for the primary GOT, is calculated more
directly by a series of increments in mips_elf_make_got_per_bfd, where
the conditionals

  else if (entry->symndx >= 0 || entry->d.h->forced_local)
    ++g->local_gotno;
  else
    ++g->global_gotno;

ensure that the forced-local __init_array_* are not counted as global.
Now the code in mips_elf_multi_got

      set_got_offset_arg.value = 2;
...
  /* Reorder dynamic symbols as described above (which behavior
     depends on the setting of VALUE).  */
  set_got_offset_arg.g = NULL;
  htab_traverse (gg->got_entries, mips_elf_set_global_got_offset,
                 &set_got_offset_arg);
  set_got_offset_arg.value = 1;
  htab_traverse (g->got_entries, mips_elf_set_global_got_offset,
                 &set_got_offset_arg);
  if (! mips_elf_sort_hash_table (info, 1))
    return FALSE;

sets the offsets for __init_array_* to 2 and then to 1, so
mips_elf_sort_hash_table_f treats them as global GOT entries,
explicitly referenced, which would only be correct for entries *not*
included in that assigned_gotno figure.  (These offsets were
previously set to 1 in mips_elf_record_global_got_symbol so stopping
mips_elf_set_global_got_offset from touching them doesn't make any
difference.)

The patch deals with the problem by counting those forced-local
symbols that have been assigned dynindx values and adjusting the
calculations accordingly.  Tested with cross to mips-linux-gnu,
binutils testsuites; also made sure it will build a working glibc for
O32, N32 and N64.  OK to commit?

bfd:
2007-09-19  Joseph Myers  <joseph@codesourcery.com>

	* elfxx-mips.c (struct mips_got_info): Add forced_local_count.
	(struct mips_elf_hash_sort_data): Add forced_local and
	prev_forced_local.
	(mips_elf_sort_hash_table): Subtract g->forced_local_count in
	computing hsd.min_got_dynindx.  Initialize hsd.forced_local and
	hsd.prev_forced_local.  Set g->forced_local_count after sorting.
	(mips_elf_sort_hash_table_f): Count forced-local symbols.  Handle
	them as unreferenced where allowed for in calculation of
	min_got_dynindx.
	(mips_elf_make_got_per_bfd, mips_elf_multi_got,
	mips_elf_create_got_section): Initialize forced_local_count.
	(_bfd_mips_elf_always_size_sections): Subtract forced_local_count
	in calculating global_gotno.
	(_bfd_mips_elf_final_link): Subtract forced_local_count in
	assertion.

ld/testsuite:
2007-09-19  Joseph Myers  <joseph@codesourcery.com>

	* ld-mips-elf/multi-got-hidden.d, ld-mips-elf/multi-got-hidden.s:
	New.
	* ld-mips-elf/mips-elf.exp: Run multi-got-hidden test.

Index: bfd/elfxx-mips.c
===================================================================
RCS file: /cvs/src/src/bfd/elfxx-mips.c,v
retrieving revision 1.213
diff -u -r1.213 elfxx-mips.c
--- bfd/elfxx-mips.c	13 Aug 2007 21:16:38 -0000	1.213
+++ bfd/elfxx-mips.c	19 Sep 2007 11:53:47 -0000
@@ -143,6 +143,9 @@
      because a single-GOT link may have multiple hash table entries
      for the LDM.  It does not get initialized in multi-GOT mode.  */
   bfd_vma tls_ldm_offset;
+  /* The number of forced-local entries being counted as global found
+     during sorting.  */
+  unsigned int forced_local_count;
 };
 
 /* Map an input bfd to a got in a multi-got link.  */
@@ -234,6 +237,10 @@
   /* The greatest dynamic symbol table index not corresponding to a
      symbol without a GOT entry.  */
   long max_non_got_dynindx;
+  /* The number of forced-local entries being counted as global.  */
+  long forced_local;
+  /* The number of such entries found previously.  */
+  long prev_forced_local;
 };
 
 /* The MIPS ELF linker needs additional information for each symbol in
@@ -2757,8 +2764,10 @@
        referenced, we move them to the end of the GOT, so that they
        don't prevent other entries that are referenced from getting
        too large offsets.  */
-    - (g->next ? g->assigned_gotno : 0);
+    - (g->next ? g->assigned_gotno : 0) - g->forced_local_count;
   hsd.max_non_got_dynindx = max_local;
+  hsd.forced_local = 0;
+  hsd.prev_forced_local = g->forced_local_count;
   mips_elf_link_hash_traverse (((struct mips_elf_link_hash_table *)
 				elf_hash_table (info)),
 			       mips_elf_sort_hash_table_f,
@@ -2774,6 +2783,8 @@
      table index in the GOT.  */
   g->global_gotsym = hsd.low;
 
+  g->forced_local_count = hsd.forced_local;
+
   return TRUE;
 }
 
@@ -2794,6 +2805,9 @@
   if (h->root.dynindx == -1)
     return TRUE;
 
+  if (h->forced_local)
+    hsd->forced_local++;
+
   /* Global symbols that need GOT entries that are not explicitly
      referenced are marked with got offset 2.  Those that are
      referenced get a 1, and those that don't need GOT entries get
@@ -2812,8 +2826,13 @@
     {
       BFD_ASSERT (h->tls_type == GOT_NORMAL);
 
-      h->root.dynindx = --hsd->min_got_dynindx;
-      hsd->low = (struct elf_link_hash_entry *) h;
+      if (h->forced_local && hsd->forced_local <= hsd->prev_forced_local)
+	h->root.dynindx = hsd->max_unref_got_dynindx++;
+      else
+	{
+	  h->root.dynindx = --hsd->min_got_dynindx;
+	  hsd->low = (struct elf_link_hash_entry *) h;
+	}
     }
 
   return TRUE;
@@ -3044,6 +3063,7 @@
       g->tls_gotno = 0;
       g->tls_assigned_gotno = 0;
       g->tls_ldm_offset = MINUS_ONE;
+      g->forced_local_count = 0;
       g->got_entries = htab_try_create (1, mips_elf_multi_got_entry_hash,
 					mips_elf_multi_got_entry_eq, NULL);
       if (g->got_entries == NULL)
@@ -3451,6 +3471,7 @@
       g->next->assigned_gotno = 0;
       g->next->tls_assigned_gotno = 0;
       g->next->tls_ldm_offset = MINUS_ONE;
+      g->next->forced_local_count = 0;
       g->next->got_entries = htab_try_create (1, mips_elf_multi_got_entry_hash,
 					      mips_elf_multi_got_entry_eq,
 					      NULL);
@@ -3816,6 +3837,7 @@
   g->bfd2got = NULL;
   g->next = NULL;
   g->tls_ldm_offset = MINUS_ONE;
+  g->forced_local_count = 0;
   g->got_entries = htab_try_create (1, mips_elf_got_entry_hash,
 				    mips_elf_got_entry_eq, NULL);
   if (g->got_entries == NULL)
@@ -7252,7 +7274,8 @@
     return FALSE;
 
   if (g->global_gotsym != NULL)
-    i = elf_hash_table (info)->dynsymcount - g->global_gotsym->dynindx;
+    i = elf_hash_table (info)->dynsymcount - g->global_gotsym->dynindx
+	- g->forced_local_count;
   else
     /* If there are no global symbols, or none requiring
        relocations, then GLOBAL_GOTSYM will be NULL.  */
@@ -10306,7 +10329,8 @@
 
       if (g->global_gotsym != NULL)
 	BFD_ASSERT ((elf_hash_table (info)->dynsymcount
-		     - g->global_gotsym->dynindx)
+		     - g->global_gotsym->dynindx
+		     - g->forced_local_count)
 		    <= g->global_gotno);
     }
 
Index: ld/testsuite/ld-mips-elf/mips-elf.exp
===================================================================
RCS file: /cvs/src/src/ld/testsuite/ld-mips-elf/mips-elf.exp,v
retrieving revision 1.47
diff -u -r1.47 mips-elf.exp
--- ld/testsuite/ld-mips-elf/mips-elf.exp	13 Aug 2007 21:16:39 -0000	1.47
+++ ld/testsuite/ld-mips-elf/mips-elf.exp	19 Sep 2007 11:53:48 -0000
@@ -68,6 +68,7 @@
 if { $linux_gnu } {
     run_dump_test "multi-got-1"
     run_dump_test "multi-got-no-shared"
+    run_dump_test "multi-got-hidden"
 }
 
 if $has_newabi {
Index: ld/testsuite/ld-mips-elf/multi-got-hidden.d
===================================================================
RCS file: ld/testsuite/ld-mips-elf/multi-got-hidden.d
diff -N ld/testsuite/ld-mips-elf/multi-got-hidden.d
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-mips-elf/multi-got-hidden.d	19 Sep 2007 11:53:48 -0000
@@ -0,0 +1,8 @@
+#name: MIPS multi-got-hidden
+#as: -EB -32 -KPIC
+#source: multi-got-1-1.s
+#source: multi-got-1-2.s
+#source: multi-got-hidden.s
+#ld: -melf32btsmip -e 0
+#objdump: -dr
+#pass
Index: ld/testsuite/ld-mips-elf/multi-got-hidden.s
===================================================================
RCS file: ld/testsuite/ld-mips-elf/multi-got-hidden.s
diff -N ld/testsuite/ld-mips-elf/multi-got-hidden.s
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ ld/testsuite/ld-mips-elf/multi-got-hidden.s	19 Sep 2007 11:53:48 -0000
@@ -0,0 +1,5 @@
+.hidden __init_array_end
+.hidden __init_array_start
+sym_3_1:
+la $2, __init_array_start
+la $2, __init_array_end

-- 
Joseph S. Myers
joseph@codesourcery.com



More information about the Binutils mailing list