From 634835aefb84bf887ac88dc7da998c1f992266bd Mon Sep 17 00:00:00 2001 From: Richard Sandiford Date: Thu, 7 Aug 2008 19:58:38 +0000 Subject: [PATCH] bfd/ * 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 | 26 +++++ bfd/elfxx-mips.c | 146 ++++++++++++------------ ld/testsuite/ChangeLog | 7 ++ ld/testsuite/ld-mips-elf/got-vers-1.dd | 6 + ld/testsuite/ld-mips-elf/got-vers-1.rd | 6 + ld/testsuite/ld-mips-elf/got-vers-1.sd | 6 + ld/testsuite/ld-mips-elf/got-vers-1.ver | 1 + ld/testsuite/ld-mips-elf/got-vers-1a.s | 2 + ld/testsuite/ld-mips-elf/got-vers-1b.s | 7 ++ ld/testsuite/ld-mips-elf/mips-elf.exp | 12 ++ 10 files changed, 144 insertions(+), 75 deletions(-) create mode 100644 ld/testsuite/ld-mips-elf/got-vers-1.dd create mode 100644 ld/testsuite/ld-mips-elf/got-vers-1.rd create mode 100644 ld/testsuite/ld-mips-elf/got-vers-1.sd create mode 100644 ld/testsuite/ld-mips-elf/got-vers-1.ver create mode 100644 ld/testsuite/ld-mips-elf/got-vers-1a.s create mode 100644 ld/testsuite/ld-mips-elf/got-vers-1b.s diff --git a/bfd/ChangeLog b/bfd/ChangeLog index ee151a737bd..ac84f48088b 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,29 @@ +2008-08-07 Richard Sandiford + + * 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 * elf32-mips.c (elf_backend_hide_symbol): Delete. diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c index e3e07fe512a..8fc8b7bd6fa 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 529643b840b..d40ec92da1b 100644 --- a/ld/testsuite/ChangeLog +++ b/ld/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2008-08-07 Richard Sandiford + + * 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 * 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 index 00000000000..98cda9502cd --- /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 00000000000..d99ead12508 --- /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 00000000000..9c3a8c03c26 --- /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 00000000000..defa8e90300 --- /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 00000000000..b9959ff4a07 --- /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 00000000000..dd308b43715 --- /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 b4da7489e13..368335a39b0 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"} + } +} -- 2.30.2