aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bfd/ChangeLog26
-rw-r--r--bfd/elfxx-mips.c146
-rw-r--r--ld/testsuite/ChangeLog7
-rw-r--r--ld/testsuite/ld-mips-elf/got-vers-1.dd6
-rw-r--r--ld/testsuite/ld-mips-elf/got-vers-1.rd6
-rw-r--r--ld/testsuite/ld-mips-elf/got-vers-1.sd6
-rw-r--r--ld/testsuite/ld-mips-elf/got-vers-1.ver1
-rw-r--r--ld/testsuite/ld-mips-elf/got-vers-1a.s2
-rw-r--r--ld/testsuite/ld-mips-elf/got-vers-1b.s7
-rw-r--r--ld/testsuite/ld-mips-elf/mips-elf.exp12
10 files changed, 144 insertions, 75 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index ee151a7..ac84f48 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,5 +1,31 @@
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.
* elfn32-mips.c (elf_backend_hide_symbol): Likewise.
* elf64-mips.c (elf_backend_hide_symbol): Likewise.
diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
index e3e07fe..8fc8b7b 100644
--- a/bfd/elfxx-mips.c
+++ b/bfd/elfxx-mips.c
@@ -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;
diff --git a/ld/testsuite/ChangeLog b/ld/testsuite/ChangeLog
index 529643b..d40ec92 100644
--- a/ld/testsuite/ChangeLog
+++ b/ld/testsuite/ChangeLog
@@ -1,5 +1,12 @@
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,
ld-mips-elf/tlsdyn-o32-2.d, ld-mips-elf/tlsdyn-o32-3.d: Change the
GOT layout as follows:
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
index 0000000..98cda95
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/got-vers-1.dd
@@ -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
index 0000000..d99ead1
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/got-vers-1.rd
@@ -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
index 0000000..9c3a8c0
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/got-vers-1.sd
@@ -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
index 0000000..defa8e9
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/got-vers-1.ver
@@ -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
index 0000000..b9959ff
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/got-vers-1a.s
@@ -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
index 0000000..dd308b4
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/got-vers-1b.s
@@ -0,0 +1,7 @@
+ .abicalls
+ .symver foo2,foo@@V2
+ .global foo2
+ .data
+ .type foo2,%object
+ .size foo2,4
+foo2: .word 0
diff --git a/ld/testsuite/ld-mips-elf/mips-elf.exp b/ld/testsuite/ld-mips-elf/mips-elf.exp
index b4da748..368335a 100644
--- a/ld/testsuite/ld-mips-elf/mips-elf.exp
+++ b/ld/testsuite/ld-mips-elf/mips-elf.exp
@@ -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"}
+ }
+}