diff options
Diffstat (limited to 'bfd')
-rw-r--r-- | bfd/ChangeLog | 59 | ||||
-rw-r--r-- | bfd/elf32-mips.c | 1 | ||||
-rw-r--r-- | bfd/elf64-mips.c | 1 | ||||
-rw-r--r-- | bfd/elfn32-mips.c | 1 | ||||
-rw-r--r-- | bfd/elfxx-mips.c | 746 | ||||
-rw-r--r-- | bfd/elfxx-mips.h | 2 |
6 files changed, 427 insertions, 383 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 40a5085..ee151a7 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,5 +1,64 @@ 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. + * 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. + +2008-08-07 Richard Sandiford <rdsandiford@googlemail.com> + * elfxx-mips.c (allocate_dynrelocs): Ignore indirect and warning symbols. diff --git a/bfd/elf32-mips.c b/bfd/elf32-mips.c index 8169991..526c222 100644 --- a/bfd/elf32-mips.c +++ b/bfd/elf32-mips.c @@ -1606,7 +1606,6 @@ static const struct ecoff_debug_swap mips_elf32_ecoff_debug_swap = { #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 diff --git a/bfd/elf64-mips.c b/bfd/elf64-mips.c index 520fafe..f79d889 100644 --- a/bfd/elf64-mips.c +++ b/bfd/elf64-mips.c @@ -3179,7 +3179,6 @@ const struct elf_size_info mips_elf64_size_info = #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 diff --git a/bfd/elfn32-mips.c b/bfd/elfn32-mips.c index 2a2d59e..63734f8 100644 --- a/bfd/elfn32-mips.c +++ b/bfd/elfn32-mips.c @@ -2433,7 +2433,6 @@ static const struct ecoff_debug_swap mips_elf32_ecoff_debug_swap = { #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 diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c index f3536fd..e3e07fe 100644 --- a/bfd/elfxx-mips.c +++ b/bfd/elfxx-mips.c @@ -317,15 +317,15 @@ struct mips_elf_link_hash_entry 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_hash_entry *entry, 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_link_hash_entry *h, void *data) 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_elf_link_hash_entry *h, void *data) 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 (struct elf_link_hash_entry *h, { 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 (struct elf_link_hash_entry *h, 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 bfd_link_info *info, bfd *abfd, 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, void *p) 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 **entryp, void *p) 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 **entryp, void *p) 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 bfd_link_info *info, 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 bfd_link_info *info, /* 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; } @@ -5059,33 +5174,6 @@ mips_elf_perform_relocation (struct bfd_link_info *info, 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 @@ -7547,18 +7635,8 @@ _bfd_mips_elf_adjust_dynamic_symbol (struct bfd_link_info *info, 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; } } @@ -7757,15 +7835,6 @@ _bfd_mips_elf_always_size_sections (bfd *output_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); @@ -7780,29 +7849,40 @@ _bfd_mips_elf_always_size_sections (bfd *output_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 @@ -7819,22 +7899,23 @@ _bfd_mips_elf_always_size_sections (bfd *output_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 @@ -7868,27 +7949,120 @@ _bfd_mips_elf_always_size_sections (bfd *output_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 @@ -7896,7 +8070,7 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info) { bfd *dynobj; - asection *s, *sreldyn; + asection *s; bfd_boolean reltext; struct mips_elf_link_hash_table *htab; @@ -7918,19 +8092,20 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_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; - /* Allocate space for global sym dynamic relocs. */ elf_link_hash_traverse (&htab->root, allocate_dynrelocs, (PTR) info); + 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; @@ -7978,88 +8153,6 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_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")) @@ -8072,6 +8165,7 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_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) @@ -8089,14 +8183,6 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_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) @@ -8106,17 +8192,6 @@ _bfd_mips_elf_size_dynamic_sections (bfd *output_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 @@ -10193,91 +10268,6 @@ _bfd_mips_elf_copy_indirect_symbol (struct bfd_link_info *info, 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 @@ -10734,7 +10724,6 @@ _bfd_mips_elf_link_hash_table_create (bfd *abfd) 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; @@ -10748,6 +10737,7 @@ _bfd_mips_elf_link_hash_table_create (bfd *abfd) 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; diff --git a/bfd/elfxx-mips.h b/bfd/elfxx-mips.h index 79d234a..659c418 100644 --- a/bfd/elfxx-mips.h +++ b/bfd/elfxx-mips.h @@ -81,8 +81,6 @@ extern bfd_boolean _bfd_mips_elf_gc_sweep_hook 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 |