bfd/
authorRichard Sandiford <rdsandiford@googlemail.com>
Thu, 7 Aug 2008 19:58:38 +0000 (19:58 +0000)
committerRichard Sandiford <rdsandiford@googlemail.com>
Thu, 7 Aug 2008 19:58:38 +0000 (19:58 +0000)
* elfxx-mips.c (GGA_NORMAL, GGA_RELOC_ONLY, GGA_NONE): New macros.
(mips_elf_link_hash_entry): Add a "global_got_area" field.
(mips_elf_link_hash_newfunc): Initialize it.
(mips_elf_sort_hash_table_f): Use h->global_got_area instead of
h->root.got.offset.  Do not handle forced_local symbols specially.
(mips_elf_record_global_got_symbol): Set h->global_got_area
instead of h->root.got.offset.
(mips_elf_recreate_got): Assert that h->global_got_area == GGA_NONE
for indirect and warning symbols.
(mips_elf_count_forced_local_got_symbols): Change the argument
from a "elf_link_hash_entry" to "mips_elf_link_hash_entry".
Use and set h->global_got_area instead of h->root.got.offset.
Set it to GGA_NONE for all forced-local symbols.
(mips_elf_set_global_got_offset): Set h->global_got_area
instead of h->root.got.offset.  Use g->global_got_area instead
of a combination of dynindx, forced_local and tls_type.
(mips_elf_multi_got): Remove disabled code.  Pass GGA_* values to
mips_elf_set_global_got_offset.
(mips_elf_lay_out_got): Use mips_elf_link_hash_traverse instead
of elf_link_hash_traverse.
(_bfd_mips_elf_copy_indirect_symbol): Copy the indirect symbol's
global_got_area to the direct symbol if the latter's value is higher.
Set the indirect symbol's area to GGA_NONE.

ld/testsuite/
* ld-mips-elf/got-vers-1a.s, ld-mips-elf/got-vers-1b.s,
ld-mips-elf/got-vers-1.ver, ld-mips-elf/got-vers-1.dd,
ld-mips-elf/got-vers-1.sd, ld-mips-elf/got-vers-1.rd: New tests.
* ld-mips-elf/mips-elf.exp: Run them.

bfd/ChangeLog
bfd/elfxx-mips.c
ld/testsuite/ChangeLog
ld/testsuite/ld-mips-elf/got-vers-1.dd [new file with mode: 0644]
ld/testsuite/ld-mips-elf/got-vers-1.rd [new file with mode: 0644]
ld/testsuite/ld-mips-elf/got-vers-1.sd [new file with mode: 0644]
ld/testsuite/ld-mips-elf/got-vers-1.ver [new file with mode: 0644]
ld/testsuite/ld-mips-elf/got-vers-1a.s [new file with mode: 0644]
ld/testsuite/ld-mips-elf/got-vers-1b.s [new file with mode: 0644]
ld/testsuite/ld-mips-elf/mips-elf.exp

index ee151a737bd1da28b5b789a0983bc685b5e4692d..ac84f48088bd5dadcac823d432531f6eff58cef1 100644 (file)
@@ -1,3 +1,29 @@
+2008-08-07  Richard Sandiford  <rdsandiford@googlemail.com>
+
+       * elfxx-mips.c (GGA_NORMAL, GGA_RELOC_ONLY, GGA_NONE): New macros.
+       (mips_elf_link_hash_entry): Add a "global_got_area" field.
+       (mips_elf_link_hash_newfunc): Initialize it.
+       (mips_elf_sort_hash_table_f): Use h->global_got_area instead of
+       h->root.got.offset.  Do not handle forced_local symbols specially.
+       (mips_elf_record_global_got_symbol): Set h->global_got_area
+       instead of h->root.got.offset.
+       (mips_elf_recreate_got): Assert that h->global_got_area == GGA_NONE
+       for indirect and warning symbols.
+       (mips_elf_count_forced_local_got_symbols): Change the argument
+       from a "elf_link_hash_entry" to "mips_elf_link_hash_entry".
+       Use and set h->global_got_area instead of h->root.got.offset.
+       Set it to GGA_NONE for all forced-local symbols.
+       (mips_elf_set_global_got_offset): Set h->global_got_area
+       instead of h->root.got.offset.  Use g->global_got_area instead
+       of a combination of dynindx, forced_local and tls_type.
+       (mips_elf_multi_got): Remove disabled code.  Pass GGA_* values to
+       mips_elf_set_global_got_offset.
+       (mips_elf_lay_out_got): Use mips_elf_link_hash_traverse instead
+       of elf_link_hash_traverse.
+       (_bfd_mips_elf_copy_indirect_symbol): Copy the indirect symbol's
+       global_got_area to the direct symbol if the latter's value is higher.
+       Set the indirect symbol's area to GGA_NONE.
+
 2008-08-07  Richard Sandiford  <rdsandiford@googlemail.com>
 
        * elf32-mips.c (elf_backend_hide_symbol): Delete.
index e3e07fe512ac2b6c1c9b6543e4a76601344bd301..8fc8b7bd6fae07b0cd197085300b8993b6ac46ef 100644 (file)
@@ -241,6 +241,27 @@ struct _mips_elf_section_data
 #define mips_elf_section_data(sec) \
   ((struct _mips_elf_section_data *) elf_section_data (sec))
 
+/* The ABI says that every symbol used by dynamic relocations must have
+   a global GOT entry.  Among other things, this provides the dynamic
+   linker with a free, directly-indexed cache.  The GOT can therefore
+   contain symbols that are not referenced by GOT relocations themselves
+   (in other words, it may have symbols that are not referenced by things
+   like R_MIPS_GOT16 and R_MIPS_GOT_PAGE).
+
+   GOT relocations are less likely to overflow if we put the associated
+   GOT entries towards the beginning.  We therefore divide the global
+   GOT entries into two areas: "normal" and "reloc-only".  Entries in
+   the first area can be used for both dynamic relocations and GP-relative
+   accesses, while those in the "reloc-only" area are for dynamic
+   relocations only.
+
+   These GGA_* ("Global GOT Area") values are organised so that lower
+   values are more general than higher values.  Also, non-GGA_NONE
+   values are ordered by the position of the area in the GOT.  */
+#define GGA_NORMAL 0
+#define GGA_RELOC_ONLY 1
+#define GGA_NONE 2
+
 /* This structure is passed to mips_elf_sort_hash_table_f when sorting
    the dynamic symbols.  */
 
@@ -303,6 +324,9 @@ struct mips_elf_link_hash_entry
      overloaded already.  */
   bfd_vma tls_got_offset;
 
+  /* The highest GGA_* value that satisfies all references to this symbol.  */
+  unsigned int global_got_area : 2;
+
   /* True if one of the relocations described by possibly_dynamic_relocs
      is against a readonly section.  */
   unsigned int readonly_reloc : 1;
@@ -868,6 +892,7 @@ mips_elf_link_hash_newfunc (struct bfd_hash_entry *entry,
       ret->call_stub = NULL;
       ret->call_fp_stub = NULL;
       ret->tls_type = GOT_NORMAL;
+      ret->global_got_area = GGA_NONE;
       ret->readonly_reloc = FALSE;
       ret->no_fn_stub = FALSE;
       ret->need_fn_stub = FALSE;
@@ -2948,27 +2973,26 @@ mips_elf_sort_hash_table_f (struct mips_elf_link_hash_entry *h, void *data)
   if (h->root.dynindx == -1)
     return TRUE;
 
-  /* 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
-     -1.  Forced local symbols may also be marked with got offset 1,
-     but are never given global GOT entries.  */
-  if (h->root.got.offset == 2)
+  switch (h->global_got_area)
     {
-      BFD_ASSERT (h->tls_type == GOT_NORMAL);
+    case GGA_NONE:
+      h->root.dynindx = hsd->max_non_got_dynindx++;
+      break;
 
-      if (hsd->max_unref_got_dynindx == hsd->min_got_dynindx)
-       hsd->low = (struct elf_link_hash_entry *) h;
-      h->root.dynindx = hsd->max_unref_got_dynindx++;
-    }
-  else if (h->root.got.offset != 1 || h->root.forced_local)
-    h->root.dynindx = hsd->max_non_got_dynindx++;
-  else
-    {
+    case GGA_NORMAL:
       BFD_ASSERT (h->tls_type == GOT_NORMAL);
 
       h->root.dynindx = --hsd->min_got_dynindx;
       hsd->low = (struct elf_link_hash_entry *) h;
+      break;
+
+    case GGA_RELOC_ONLY:
+      BFD_ASSERT (h->tls_type == GOT_NORMAL);
+
+      if (hsd->max_unref_got_dynindx == hsd->min_got_dynindx)
+       hsd->low = (struct elf_link_hash_entry *) h;
+      h->root.dynindx = hsd->max_unref_got_dynindx++;
+      break;
     }
 
   return TRUE;
@@ -2984,10 +3008,12 @@ mips_elf_record_global_got_symbol (struct elf_link_hash_entry *h,
                                   unsigned char tls_flag)
 {
   struct mips_elf_link_hash_table *htab;
+  struct mips_elf_link_hash_entry *hmips;
   struct mips_got_entry entry, **loc;
   struct mips_got_info *g;
 
   htab = mips_elf_hash_table (info);
+  hmips = (struct mips_elf_link_hash_entry *) h;
 
   /* A global symbol in the GOT must also be in the dynamic symbol
      table.  */
@@ -3034,14 +3060,8 @@ mips_elf_record_global_got_symbol (struct elf_link_hash_entry *h,
 
   memcpy (*loc, &entry, sizeof entry);
 
-  if (h->got.offset != MINUS_ONE)
-    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;
+    hmips->global_got_area = GGA_NORMAL;
 
   return TRUE;
 }
@@ -3296,7 +3316,10 @@ mips_elf_recreate_got (void **entryp, void *data)
       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;
+       {
+         BFD_ASSERT (h->global_got_area == GGA_NONE);
+         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);
@@ -3340,26 +3363,26 @@ mips_elf_resolve_final_got_entries (struct mips_got_info *g)
   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.  */
+/* A mips_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,
+mips_elf_count_forced_local_got_symbols (struct mips_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))
+  if (h->global_got_area != GGA_NONE
+      && (h->root.forced_local || h->root.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
+      if (h->global_got_area != GGA_RELOC_ONLY)
        g->local_gotno++;
+      h->global_got_area = GGA_NONE;
     }
   return 1;
 }
@@ -3725,10 +3748,9 @@ mips_elf_set_global_got_offset (void **entryp, void *p)
       mips_tls_got_relocs (arg->info, entry->tls_type,
                           entry->symndx == -1 ? &entry->d.h->root : NULL);
 
-  if (entry->abfd != NULL && entry->symndx == -1
-      && entry->d.h->root.dynindx != -1
-      && !entry->d.h->root.forced_local
-      && entry->d.h->tls_type == GOT_NORMAL)
+  if (entry->abfd != NULL
+      && entry->symndx == -1
+      && entry->d.h->global_got_area != GGA_NONE)
     {
       if (g)
        {
@@ -3742,7 +3764,7 @@ mips_elf_set_global_got_offset (void **entryp, void *p)
            ++arg->needed_relocs;
        }
       else
-       entry->d.h->root.got.offset = arg->value;
+       entry->d.h->global_got_area = arg->value;
     }
 
   return 1;
@@ -3908,47 +3930,17 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info,
     *bfdgotp = bfdgot;
   }
 
-  /* The IRIX dynamic linker requires every symbol that is referenced
-     in a dynamic relocation to be present in the primary GOT, so
-     arrange for them to appear after those that are actually
-     referenced.
-
-     GNU/Linux could very well do without it, but it would slow down
-     the dynamic linker, since it would have to resolve every dynamic
-     symbol referenced in other GOTs more than once, without help from
-     the cache.  Also, knowing that every external symbol has a GOT
-     helps speed up the resolution of local symbols too, so GNU/Linux
-     follows IRIX's practice.
-
-     The number 2 is used by mips_elf_sort_hash_table_f to count
-     global GOT symbols that are unreferenced in the primary GOT, with
-     an initial dynamic index computed from gg->assigned_gotno, where
-     the number of unreferenced global entries in the primary GOT is
-     preserved.  */
-  if (1)
-    {
-      gg->assigned_gotno = gg->global_gotno - g->global_gotno;
-      g->global_gotno = gg->global_gotno;
-      set_got_offset_arg.value = 2;
-    }
-  else
-    {
-      /* This could be used for dynamic linkers that don't optimize
-        symbol resolution while applying relocations so as to use
-        primary GOT entries or assuming the symbol is locally-defined.
-        With this code, we assign lower dynamic indices to global
-        symbols that are not referenced in the primary GOT, so that
-        their entries can be omitted.  */
-      gg->assigned_gotno = 0;
-      set_got_offset_arg.value = -1;
-    }
+  /* Every symbol that is referenced in a dynamic relocation must be
+     present in the primary GOT, so arrange for them to appear after
+     those that are actually referenced.  */
+  gg->assigned_gotno = gg->global_gotno - g->global_gotno;
+  g->global_gotno = gg->global_gotno;
 
-  /* Reorder dynamic symbols as described above (which behavior
-     depends on the setting of VALUE).  */
   set_got_offset_arg.g = NULL;
+  set_got_offset_arg.value = GGA_RELOC_ONLY;
   htab_traverse (gg->got_entries, mips_elf_set_global_got_offset,
                 &set_got_offset_arg);
-  set_got_offset_arg.value = 1;
+  set_got_offset_arg.value = GGA_NORMAL;
   htab_traverse (g->got_entries, mips_elf_set_global_got_offset,
                 &set_got_offset_arg);
   if (! mips_elf_sort_hash_table (info, 1))
@@ -7881,8 +7873,8 @@ mips_elf_lay_out_got (bfd *output_bfd, struct bfd_link_info *info)
     return FALSE;
 
   /* Count the number of forced-local entries.  */
-  elf_link_hash_traverse (elf_hash_table (info),
-                         mips_elf_count_forced_local_got_symbols, g);
+  mips_elf_link_hash_traverse (htab,
+                              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
@@ -10264,6 +10256,10 @@ _bfd_mips_elf_copy_indirect_symbol (struct bfd_link_info *info,
     dirmips->readonly_reloc = TRUE;
   if (indmips->no_fn_stub)
     dirmips->no_fn_stub = TRUE;
+  if (indmips->global_got_area < dirmips->global_got_area)
+    dirmips->global_got_area = indmips->global_got_area;
+  if (indmips->global_got_area < GGA_NONE)
+    indmips->global_got_area = GGA_NONE;
 
   if (dirmips->tls_type == 0)
     dirmips->tls_type = indmips->tls_type;
index 529643b840b18574258eeb6f1d6a82e83a0c42da..d40ec92da1bd39f57b4742dd792685bcd37a1065 100644 (file)
@@ -1,3 +1,10 @@
+2008-08-07  Richard Sandiford  <rdsandiford@googlemail.com>
+
+       * ld-mips-elf/got-vers-1a.s, ld-mips-elf/got-vers-1b.s,
+       ld-mips-elf/got-vers-1.ver, ld-mips-elf/got-vers-1.dd,
+       ld-mips-elf/got-vers-1.sd, ld-mips-elf/got-vers-1.rd: New tests.
+       * ld-mips-elf/mips-elf.exp: Run them.
+
 2008-08-07  Richard Sandiford  <rdsandiford@googlemail.com>
 
        * ld-mips-elf/tlsdyn-o32-2.got, ld-mips-elf/tlsdyn-o32-3.got,
diff --git a/ld/testsuite/ld-mips-elf/got-vers-1.dd b/ld/testsuite/ld-mips-elf/got-vers-1.dd
new file mode 100644 (file)
index 0000000..98cda95
--- /dev/null
@@ -0,0 +1,6 @@
+# There must be one global GOT symbol.  Its index doesn't matter.
+#...
+ 0x70000011 \(MIPS_SYMTABNO\) * 4
+#...
+ 0x70000013 \(MIPS_GOTSYM\) * 0x3
+#pass
diff --git a/ld/testsuite/ld-mips-elf/got-vers-1.rd b/ld/testsuite/ld-mips-elf/got-vers-1.rd
new file mode 100644 (file)
index 0000000..d99ead1
--- /dev/null
@@ -0,0 +1,6 @@
+
+Relocation section '\.rel\.dyn' at offset .* contains 2 entries:
+ *Offset * Info * Type * Sym\.Value * Sym\. Name
+00000000 * 00000000 * R_MIPS_NONE *
+# This index must be the same as DT_MIPS_GOTsYM.
+[^ ]+ * 00000303 * R_MIPS_REL32 * [^ ]+ * foo
diff --git a/ld/testsuite/ld-mips-elf/got-vers-1.sd b/ld/testsuite/ld-mips-elf/got-vers-1.sd
new file mode 100644 (file)
index 0000000..9c3a8c0
--- /dev/null
@@ -0,0 +1,6 @@
+# foo@@V2 must have index DT_MIPS_GOTSYM
+#...
+ *3: .* 4 * OBJECT * GLOBAL * DEFAULT * [0-9]+ * foo@@V2
+
+Symbol table '\.symtab' contains .*:
+#pass
diff --git a/ld/testsuite/ld-mips-elf/got-vers-1.ver b/ld/testsuite/ld-mips-elf/got-vers-1.ver
new file mode 100644 (file)
index 0000000..defa8e9
--- /dev/null
@@ -0,0 +1 @@
+V2 { global: foo; local: *; };
diff --git a/ld/testsuite/ld-mips-elf/got-vers-1a.s b/ld/testsuite/ld-mips-elf/got-vers-1a.s
new file mode 100644 (file)
index 0000000..b9959ff
--- /dev/null
@@ -0,0 +1,2 @@
+       .abicalls
+       .word   foo
diff --git a/ld/testsuite/ld-mips-elf/got-vers-1b.s b/ld/testsuite/ld-mips-elf/got-vers-1b.s
new file mode 100644 (file)
index 0000000..dd308b4
--- /dev/null
@@ -0,0 +1,7 @@
+       .abicalls
+       .symver foo2,foo@@V2
+       .global foo2
+       .data
+       .type   foo2,%object
+       .size   foo2,4
+foo2:  .word   0
index b4da7489e1314b6ec1354f0e5a3919c97897f9ba..368335a39b052775788525a994ed8796c966eafa 100644 (file)
@@ -363,3 +363,15 @@ run_dump_test "attr-gnu-4-43"
 run_dump_test "attr-gnu-4-44"
 run_dump_test "attr-gnu-4-45"
 run_dump_test "attr-gnu-4-51"
+
+if { $linux_gnu } {
+    run_ld_link_tests {
+       {"GOT and versioning 1"
+        "-shared -melf32btsmip --version-script got-vers-1.ver"
+        "-EB -mips2 -32" {got-vers-1a.s got-vers-1b.s}
+        {{readelf -d got-vers-1.dd}
+         {readelf --symbols got-vers-1.sd}
+         {readelf --relocs got-vers-1.rd}}
+        "got-vers-1.so"}
+    }
+}