aboutsummaryrefslogtreecommitdiff
path: root/bfd/elfxx-mips.c
diff options
context:
space:
mode:
authorRichard Sandiford <rdsandiford@googlemail.com>2007-11-14 20:46:15 +0000
committerRichard Sandiford <rdsandiford@googlemail.com>2007-11-14 20:46:15 +0000
commitc224138d88196fe7cabce4fe554c10a1520f5a4f (patch)
treed70ed2d76daa6b13ed8bd8eb10e2e23e1b60eeda /bfd/elfxx-mips.c
parent51a0dd312ea996c87f576fea04ca5d0db5826411 (diff)
downloadgdb-c224138d88196fe7cabce4fe554c10a1520f5a4f.zip
gdb-c224138d88196fe7cabce4fe554c10a1520f5a4f.tar.gz
gdb-c224138d88196fe7cabce4fe554c10a1520f5a4f.tar.bz2
bfd/
2007-09-18 Richard Sandiford <richard@codesourcery.com> * elfxx-mips.c (mips_got_page_range): New structure. (mips_got_page_entry): Likewise. (mips_got_info): Add page_gotno and got_page_entries fields. (mips_elf_got_per_bfd_arg): Add max_pages field. Delete primary_count and current_count fields. (mips_got_page_entry_hash, mips_got_page_entry_eq): New functions. (mips_elf_pages_for_range, mips_elf_record_got_page_entry): Likewise. (mips_elf_get_got_for_bfd): New function, split out from mips_elf_make_got_per_bfd. Initialize the page_gotno and got_page_entries fields when creating a new GOT structure. (mips_elf_make_got_pages_per_bfd): New function. (mips_elf_merge_got_with): New function, split out from mips_elf_make_got_per_bfd. Merge page entries as well as non-page entries. Use the minimum of max_pages and the sum of the page_gotnos to estimate the number of page entries. (mips_elf_merge_gots): Use the minimum of max_pages and the bfd's page_gotno to estimate the number of page entries. Use the above functions. (mips_elf_multi_got): Add page entries as well as non-page entries to the individual per-bfd GOTs. Initialize got_per_bfd_arg.max_pages. Initialize the page_gotno and got_page_entries fields when creating a new primary GOT. Use the minimum of pages and page_gotno when adding the number of pages entries to local_gotno. (mips_elf_create_got_section): Initialize the page_gotno and got_page_entries fields of the GOT structure. (mips_elf_rel_relocation_p, mips_elf_read_rel_addend) (mips_elf_add_lo16_rel_addend, mips_elf_get_section_contents): New functions, split out from... (_bfd_mips_elf_relocate_section): ...here. (_bfd_mips_elf_check_relocs): Record GOT page entries too. (_bfd_mips_relax_section): Use mips_elf_get_section_contents. (_bfd_mips_elf_always_size_sections): Use the smaller of the loadable_size- and page_gotno-derived estimates. ld/testsuite/ 2007-09-18 Richard Sandiford <richard@codesourcery.com> Joseph Myers <joseph@codesourcery.com> * ld-mips-elf/got-page-1.d, ld-mips-elf/got-page-1.s, * ld-mips-elf/got-page-2.d, ld-mips-elf/got-page-2.s, * ld-mips-elf/got-page-3.d, ld-mips-elf/got-page-3a.s, * ld-mips-elf/got-page-3b.s, ld-mips-elf/got-page-3c.s, * ld-mips-elf/got-page-1.ld: New tests. * ld-mips-elf/mips-elf.exp: Run them. * ld-mips-elf/multi-got-1.d, ld-mips-elf/multi-got-no-shared.d, * ld-mips-elf/tls-hidden2-got.d, ld-mips-elf/tls-hidden2.d, * ld-mips-elf/tls-hidden3.d, ld-mips-elf/tls-hidden3.got, * ld-mips-elf/tls-hidden3.r, ld-mips-elf/tls-hidden4.got, * ld-mips-elf/tls-hidden4.r, ld-mips-elf/tls-multi-got-1.d, * ld-mips-elf/tls-multi-got-1.got, ld-mips-elf/tls-multi-got-1.r, * ld-mips-elf/tlsbin-o32.d, ld-mips-elf/tlsbin-o32.got, * ld-mips-elf/tlsdyn-o32-1.d, ld-mips-elf/tlsdyn-o32-1.got, * ld-mips-elf/tlsdyn-o32-2.d, ld-mips-elf/tlsdyn-o32-2.got, * ld-mips-elf/tlsdyn-o32-3.d, ld-mips-elf/tlsdyn-o32-3.got, * ld-mips-elf/tlsdyn-o32.d, ld-mips-elf/tlsdyn-o32.got, * ld-mips-elf/tlslib-o32-hidden.got, ld-mips-elf/tlslib-o32-ver.got, * ld-mips-elf/tlslib-o32.d, ld-mips-elf/tlslib-o32.got: Update for GOT allocation changes.
Diffstat (limited to 'bfd/elfxx-mips.c')
-rw-r--r--bfd/elfxx-mips.c728
1 files changed, 499 insertions, 229 deletions
diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
index 73959f1..470bed9 100644
--- a/bfd/elfxx-mips.c
+++ b/bfd/elfxx-mips.c
@@ -112,6 +112,30 @@ struct mips_got_entry
long gotidx;
};
+/* This structure describes a range of addends: [MIN_ADDEND, MAX_ADDEND].
+ The structures form a non-overlapping list that is sorted by increasing
+ MIN_ADDEND. */
+struct mips_got_page_range
+{
+ struct mips_got_page_range *next;
+ bfd_signed_vma min_addend;
+ bfd_signed_vma max_addend;
+};
+
+/* This structure describes the range of addends that are applied to page
+ relocations against a given symbol. */
+struct mips_got_page_entry
+{
+ /* The input bfd in which the symbol is defined. */
+ bfd *abfd;
+ /* The index of the symbol, as stored in the relocation r_info. */
+ long symndx;
+ /* The ranges for this page entry. */
+ struct mips_got_page_range *ranges;
+ /* The maximum number of page entries needed for RANGES. */
+ bfd_vma num_pages;
+};
+
/* This structure is used to hold .got information when linking. */
struct mips_got_info
@@ -126,12 +150,16 @@ struct mips_got_info
/* The first unused TLS .got entry. Used only during
mips_elf_initialize_tls_index. */
unsigned int tls_assigned_gotno;
- /* The number of local .got entries. */
+ /* The number of local .got entries, eventually including page entries. */
unsigned int local_gotno;
+ /* The maximum number of page entries needed. */
+ unsigned int page_gotno;
/* The number of local .got entries we have used. */
unsigned int assigned_gotno;
/* A hash table holding members of the got. */
struct htab *got_entries;
+ /* A hash table of mips_got_page_entry structures. */
+ struct htab *got_page_entries;
/* A hash table mapping input bfds to other mips_got_info. NULL
unless multi-got was necessary. */
struct htab *bfd2got;
@@ -173,10 +201,8 @@ struct mips_elf_got_per_bfd_arg
/* The maximum number of got entries that can be addressed with a
16-bit offset. */
unsigned int max_count;
- /* The number of local and global entries in the primary got. */
- unsigned int primary_count;
- /* The number of local and global entries in the current got. */
- unsigned int current_count;
+ /* The maximum number of page entries needed by each got. */
+ unsigned int max_pages;
/* The total number of global entries which will live in the
primary got and be automatically relocated. This includes
those not referenced by the primary GOT but included in
@@ -2039,6 +2065,25 @@ mips_elf_multi_got_entry_eq (const void *entry1, const void *entry2)
? e1->abfd == e2->abfd && e1->d.address == e2->d.address
: e1->d.h == e2->d.h);
}
+
+static hashval_t
+mips_got_page_entry_hash (const void *entry_)
+{
+ const struct mips_got_page_entry *entry;
+
+ entry = (const struct mips_got_page_entry *) entry_;
+ return entry->abfd->id + entry->symndx;
+}
+
+static int
+mips_got_page_entry_eq (const void *entry1_, const void *entry2_)
+{
+ const struct mips_got_page_entry *entry1, *entry2;
+
+ entry1 = (const struct mips_got_page_entry *) entry1_;
+ entry2 = (const struct mips_got_page_entry *) entry2_;
+ return entry1->abfd == entry2->abfd && entry1->symndx == entry2->symndx;
+}
/* Return the dynamic relocation section. If it doesn't exist, try to
create a new it if CREATE_P, otherwise return NULL. Also return NULL
@@ -2953,6 +2998,104 @@ mips_elf_record_local_got_symbol (bfd *abfd, long symndx, bfd_vma addend,
return TRUE;
}
+
+/* Return the maximum number of GOT page entries required for RANGE. */
+
+static bfd_vma
+mips_elf_pages_for_range (const struct mips_got_page_range *range)
+{
+ return (range->max_addend - range->min_addend + 0x1ffff) >> 16;
+}
+
+/* Record that ABFD has a page relocation against symbol SYMNDX and that
+ ADDEND is the addend for that relocation. G is the GOT information. */
+
+static bfd_boolean
+mips_elf_record_got_page_entry (bfd *abfd, long symndx, bfd_signed_vma addend,
+ struct mips_got_info *g)
+{
+ struct mips_got_page_entry lookup, *entry;
+ struct mips_got_page_range **range_ptr, *range;
+ bfd_vma old_pages, new_pages;
+ void **loc;
+
+ /* Find the mips_got_page_entry hash table entry for this symbol. */
+ lookup.abfd = abfd;
+ lookup.symndx = symndx;
+ loc = htab_find_slot (g->got_page_entries, &lookup, INSERT);
+ if (loc == NULL)
+ return FALSE;
+
+ /* Create a mips_got_page_entry if this is the first time we've
+ seen the symbol. */
+ entry = (struct mips_got_page_entry *) *loc;
+ if (!entry)
+ {
+ entry = bfd_alloc (abfd, sizeof (*entry));
+ if (!entry)
+ return FALSE;
+
+ entry->abfd = abfd;
+ entry->symndx = symndx;
+ entry->ranges = NULL;
+ entry->num_pages = 0;
+ *loc = entry;
+ }
+
+ /* Skip over ranges whose maximum extent cannot share a page entry
+ with ADDEND. */
+ range_ptr = &entry->ranges;
+ while (*range_ptr && addend > (*range_ptr)->max_addend + 0xffff)
+ range_ptr = &(*range_ptr)->next;
+
+ /* If we scanned to the end of the list, or found a range whose
+ minimum extent cannot share a page entry with ADDEND, create
+ a new singleton range. */
+ range = *range_ptr;
+ if (!range || addend < range->min_addend - 0xffff)
+ {
+ range = bfd_alloc (abfd, sizeof (*range));
+ if (!range)
+ return FALSE;
+
+ range->next = *range_ptr;
+ range->min_addend = addend;
+ range->max_addend = addend;
+
+ *range_ptr = range;
+ entry->num_pages++;
+ g->page_gotno++;
+ return TRUE;
+ }
+
+ /* Remember how many pages the old range contributed. */
+ old_pages = mips_elf_pages_for_range (range);
+
+ /* Update the ranges. */
+ if (addend < range->min_addend)
+ range->min_addend = addend;
+ else if (addend > range->max_addend)
+ {
+ if (range->next && addend >= range->next->min_addend - 0xffff)
+ {
+ old_pages += mips_elf_pages_for_range (range->next);
+ range->max_addend = range->next->max_addend;
+ range->next = range->next->next;
+ }
+ else
+ range->max_addend = addend;
+ }
+
+ /* Record any change in the total estimate. */
+ new_pages = mips_elf_pages_for_range (range);
+ if (old_pages != new_pages)
+ {
+ entry->num_pages += new_pages - old_pages;
+ g->page_gotno += new_pages - old_pages;
+ }
+
+ return TRUE;
+}
/* Compute the hash value of the bfd in a bfd2got hash entry. */
@@ -2994,53 +3137,42 @@ mips_elf_got_for_ibfd (struct mips_got_info *g, bfd *ibfd)
return p ? p->g : NULL;
}
-/* Create one separate got for each bfd that has entries in the global
- got, such that we can tell how many local and global entries each
- bfd requires. */
+/* Use BFD2GOT to find ABFD's got entry, creating one if none exists.
+ Return NULL if an error occured. */
-static int
-mips_elf_make_got_per_bfd (void **entryp, void *p)
+static struct mips_got_info *
+mips_elf_get_got_for_bfd (struct htab *bfd2got, bfd *output_bfd,
+ bfd *input_bfd)
{
- struct mips_got_entry *entry = (struct mips_got_entry *)*entryp;
- struct mips_elf_got_per_bfd_arg *arg = (struct mips_elf_got_per_bfd_arg *)p;
- htab_t bfd2got = arg->bfd2got;
- struct mips_got_info *g;
struct mips_elf_bfd2got_hash bfdgot_entry, *bfdgot;
+ struct mips_got_info *g;
void **bfdgotp;
- /* Find the got_info for this GOT entry's input bfd. Create one if
- none exists. */
- bfdgot_entry.bfd = entry->abfd;
+ bfdgot_entry.bfd = input_bfd;
bfdgotp = htab_find_slot (bfd2got, &bfdgot_entry, INSERT);
- bfdgot = (struct mips_elf_bfd2got_hash *)*bfdgotp;
+ bfdgot = (struct mips_elf_bfd2got_hash *) *bfdgotp;
- if (bfdgot != NULL)
- g = bfdgot->g;
- else
+ if (bfdgot == NULL)
{
- bfdgot = (struct mips_elf_bfd2got_hash *)bfd_alloc
- (arg->obfd, sizeof (struct mips_elf_bfd2got_hash));
-
+ bfdgot = ((struct mips_elf_bfd2got_hash *)
+ bfd_alloc (output_bfd, sizeof (struct mips_elf_bfd2got_hash)));
if (bfdgot == NULL)
- {
- arg->obfd = 0;
- return 0;
- }
+ return NULL;
*bfdgotp = bfdgot;
- bfdgot->bfd = entry->abfd;
- bfdgot->g = g = (struct mips_got_info *)
- bfd_alloc (arg->obfd, sizeof (struct mips_got_info));
+ g = ((struct mips_got_info *)
+ bfd_alloc (output_bfd, sizeof (struct mips_got_info)));
if (g == NULL)
- {
- arg->obfd = 0;
- return 0;
- }
+ return NULL;
+
+ bfdgot->bfd = input_bfd;
+ bfdgot->g = g;
g->global_gotsym = NULL;
g->global_gotno = 0;
g->local_gotno = 0;
+ g->page_gotno = 0;
g->assigned_gotno = -1;
g->tls_gotno = 0;
g->tls_assigned_gotno = 0;
@@ -3048,15 +3180,39 @@ mips_elf_make_got_per_bfd (void **entryp, void *p)
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)
- {
- arg->obfd = 0;
- return 0;
- }
+ return NULL;
+
+ g->got_page_entries = htab_try_create (1, mips_got_page_entry_hash,
+ mips_got_page_entry_eq, NULL);
+ if (g->got_page_entries == NULL)
+ return NULL;
g->bfd2got = NULL;
g->next = NULL;
}
+ return bfdgot->g;
+}
+
+/* A htab_traverse callback for the entries in the master got.
+ Create one separate got for each bfd that has entries in the global
+ got, such that we can tell how many local and global entries each
+ bfd requires. */
+
+static int
+mips_elf_make_got_per_bfd (void **entryp, void *p)
+{
+ struct mips_got_entry *entry = (struct mips_got_entry *)*entryp;
+ struct mips_elf_got_per_bfd_arg *arg = (struct mips_elf_got_per_bfd_arg *)p;
+ struct mips_got_info *g;
+
+ g = mips_elf_get_got_for_bfd (arg->bfd2got, arg->obfd, entry->abfd);
+ if (g == NULL)
+ {
+ arg->obfd = NULL;
+ return 0;
+ }
+
/* Insert the GOT entry in the bfd's got entry hash table. */
entryp = htab_find_slot (g->got_entries, entry, INSERT);
if (*entryp != NULL)
@@ -3079,6 +3235,85 @@ mips_elf_make_got_per_bfd (void **entryp, void *p)
return 1;
}
+/* A htab_traverse callback for the page entries in the master got.
+ Associate each page entry with the bfd's got. */
+
+static int
+mips_elf_make_got_pages_per_bfd (void **entryp, void *p)
+{
+ struct mips_got_page_entry *entry = (struct mips_got_page_entry *) *entryp;
+ struct mips_elf_got_per_bfd_arg *arg = (struct mips_elf_got_per_bfd_arg *) p;
+ struct mips_got_info *g;
+
+ g = mips_elf_get_got_for_bfd (arg->bfd2got, arg->obfd, entry->abfd);
+ if (g == NULL)
+ {
+ arg->obfd = NULL;
+ return 0;
+ }
+
+ /* Insert the GOT entry in the bfd's got entry hash table. */
+ entryp = htab_find_slot (g->got_page_entries, entry, INSERT);
+ if (*entryp != NULL)
+ return 1;
+
+ *entryp = entry;
+ g->page_gotno += entry->num_pages;
+ return 1;
+}
+
+/* Consider merging the got described by BFD2GOT with TO, using the
+ information given by ARG. Return -1 if this would lead to overflow,
+ 1 if they were merged successfully, and 0 if a merge failed due to
+ lack of memory. (These values are chosen so that nonnegative return
+ values can be returned by a htab_traverse callback.) */
+
+static int
+mips_elf_merge_got_with (struct mips_elf_bfd2got_hash *bfd2got,
+ struct mips_got_info *to,
+ struct mips_elf_got_per_bfd_arg *arg)
+{
+ struct mips_got_info *from = bfd2got->g;
+ unsigned int estimate;
+
+ /* Work out how many page entries we would need for the combined GOT. */
+ estimate = arg->max_pages;
+ if (estimate >= from->page_gotno + to->page_gotno)
+ estimate = from->page_gotno + to->page_gotno;
+
+ /* And conservatively estimate how many local, global and TLS entries
+ would be needed. */
+ estimate += (from->local_gotno
+ + from->global_gotno
+ + from->tls_gotno
+ + to->local_gotno
+ + to->global_gotno
+ + to->tls_gotno);
+
+ /* Bail out if the combined GOT might be too big. */
+ if (estimate > arg->max_count)
+ return -1;
+
+ /* Commit to the merge. Record that TO is now the bfd for this got. */
+ bfd2got->g = to;
+
+ /* Transfer the bfd's got information from FROM to TO. */
+ htab_traverse (from->got_entries, mips_elf_make_got_per_bfd, arg);
+ if (arg->obfd == NULL)
+ return 0;
+
+ htab_traverse (from->got_page_entries, mips_elf_make_got_pages_per_bfd, arg);
+ if (arg->obfd == NULL)
+ return 0;
+
+ /* We don't have to worry about releasing memory of the actual
+ got entries, since they're all in the master got_entries hash
+ table anyway. */
+ htab_delete (from->got_entries);
+ htab_delete (from->got_page_entries);
+ return 1;
+}
+
/* Attempt to merge gots of different input bfds. Try to use as much
as possible of the primary got, since it doesn't require explicit
dynamic relocations, but don't use bfds that would reference global
@@ -3092,98 +3327,53 @@ mips_elf_merge_gots (void **bfd2got_, void *p)
struct mips_elf_bfd2got_hash *bfd2got
= (struct mips_elf_bfd2got_hash *)*bfd2got_;
struct mips_elf_got_per_bfd_arg *arg = (struct mips_elf_got_per_bfd_arg *)p;
- unsigned int lcount = bfd2got->g->local_gotno;
- unsigned int gcount = bfd2got->g->global_gotno;
- unsigned int tcount = bfd2got->g->tls_gotno;
- unsigned int maxcnt = arg->max_count;
- bfd_boolean too_many_for_tls = FALSE;
+ struct mips_got_info *g;
+ unsigned int estimate;
+ int result;
+
+ g = bfd2got->g;
+
+ /* Work out the number of page, local and TLS entries. */
+ estimate = arg->max_pages;
+ if (estimate > g->page_gotno)
+ estimate = g->page_gotno;
+ estimate += g->local_gotno + g->tls_gotno;
/* We place TLS GOT entries after both locals and globals. The globals
for the primary GOT may overflow the normal GOT size limit, so be
sure not to merge a GOT which requires TLS with the primary GOT in that
case. This doesn't affect non-primary GOTs. */
- if (tcount > 0)
- {
- unsigned int primary_total = lcount + tcount + arg->global_count;
- if (primary_total > maxcnt)
- too_many_for_tls = TRUE;
- }
+ estimate += (g->tls_gotno > 0 ? arg->global_count : g->global_gotno);
- /* If we don't have a primary GOT and this is not too big, use it as
- a starting point for the primary GOT. */
- if (! arg->primary && lcount + gcount + tcount <= maxcnt
- && ! too_many_for_tls)
- {
- arg->primary = bfd2got->g;
- arg->primary_count = lcount + gcount;
- }
- /* If it looks like we can merge this bfd's entries with those of
- the primary, merge them. The heuristics is conservative, but we
- don't have to squeeze it too hard. */
- else if (arg->primary && ! too_many_for_tls
- && (arg->primary_count + lcount + gcount + tcount) <= maxcnt)
+ if (estimate <= arg->max_count)
{
- struct mips_got_info *g = bfd2got->g;
- int old_lcount = arg->primary->local_gotno;
- int old_gcount = arg->primary->global_gotno;
- int old_tcount = arg->primary->tls_gotno;
-
- bfd2got->g = arg->primary;
-
- htab_traverse (g->got_entries,
- mips_elf_make_got_per_bfd,
- arg);
- if (arg->obfd == NULL)
- return 0;
-
- htab_delete (g->got_entries);
- /* We don't have to worry about releasing memory of the actual
- got entries, since they're all in the master got_entries hash
- table anyway. */
-
- BFD_ASSERT (old_lcount + lcount >= arg->primary->local_gotno);
- BFD_ASSERT (old_gcount + gcount >= arg->primary->global_gotno);
- BFD_ASSERT (old_tcount + tcount >= arg->primary->tls_gotno);
+ /* If we don't have a primary GOT, use it as
+ a starting point for the primary GOT. */
+ if (!arg->primary)
+ {
+ arg->primary = bfd2got->g;
+ return 1;
+ }
- arg->primary_count = arg->primary->local_gotno
- + arg->primary->global_gotno + arg->primary->tls_gotno;
+ /* Try merging with the primary GOT. */
+ result = mips_elf_merge_got_with (bfd2got, arg->primary, arg);
+ if (result >= 0)
+ return result;
}
+
/* If we can merge with the last-created got, do it. */
- else if (arg->current
- && arg->current_count + lcount + gcount + tcount <= maxcnt)
+ if (arg->current)
{
- struct mips_got_info *g = bfd2got->g;
- int old_lcount = arg->current->local_gotno;
- int old_gcount = arg->current->global_gotno;
- int old_tcount = arg->current->tls_gotno;
-
- bfd2got->g = arg->current;
-
- htab_traverse (g->got_entries,
- mips_elf_make_got_per_bfd,
- arg);
- if (arg->obfd == NULL)
- return 0;
-
- htab_delete (g->got_entries);
-
- BFD_ASSERT (old_lcount + lcount >= arg->current->local_gotno);
- BFD_ASSERT (old_gcount + gcount >= arg->current->global_gotno);
- BFD_ASSERT (old_tcount + tcount >= arg->current->tls_gotno);
-
- arg->current_count = arg->current->local_gotno
- + arg->current->global_gotno + arg->current->tls_gotno;
+ result = mips_elf_merge_got_with (bfd2got, arg->current, arg);
+ if (result >= 0)
+ return result;
}
+
/* Well, we couldn't merge, so create a new GOT. Don't check if it
fits; if it turns out that it doesn't, we'll get relocation
overflows anyway. */
- else
- {
- bfd2got->g->next = arg->current;
- arg->current = bfd2got->g;
-
- arg->current_count = lcount + gcount + 2 * tcount;
- }
+ g->next = arg->current;
+ arg->current = g;
return 1;
}
@@ -3418,14 +3608,18 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info,
if (got_per_bfd_arg.obfd == NULL)
return FALSE;
+ /* Also count how many page entries each input bfd requires. */
+ htab_traverse (g->got_page_entries, mips_elf_make_got_pages_per_bfd,
+ &got_per_bfd_arg);
+ if (got_per_bfd_arg.obfd == NULL)
+ return FALSE;
+
got_per_bfd_arg.current = NULL;
got_per_bfd_arg.primary = NULL;
- /* Taking out PAGES entries is a worst-case estimate. We could
- compute the maximum number of pages that each separate input bfd
- uses, but it's probably not worth it. */
got_per_bfd_arg.max_count = ((MIPS_ELF_GOT_MAX_SIZE (info)
/ MIPS_ELF_GOT_SIZE (abfd))
- - MIPS_RESERVED_GOTNO (info) - pages);
+ - MIPS_RESERVED_GOTNO (info));
+ got_per_bfd_arg.max_pages = pages;
/* The number of globals that will be included in the primary GOT.
See the calls to mips_elf_set_global_got_offset below for more
information. */
@@ -3449,6 +3643,7 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info,
g->next->global_gotsym = NULL;
g->next->global_gotno = 0;
g->next->local_gotno = 0;
+ g->next->page_gotno = 0;
g->next->tls_gotno = 0;
g->next->assigned_gotno = 0;
g->next->tls_assigned_gotno = 0;
@@ -3458,6 +3653,11 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info,
NULL);
if (g->next->got_entries == NULL)
return FALSE;
+ g->next->got_page_entries = htab_try_create (1, mips_got_page_entry_hash,
+ mips_got_page_entry_eq,
+ NULL);
+ if (g->next->got_page_entries == NULL)
+ return FALSE;
g->next->bfd2got = NULL;
}
else
@@ -3562,7 +3762,8 @@ mips_elf_multi_got (bfd *abfd, struct bfd_link_info *info,
assign += MIPS_RESERVED_GOTNO (info);
g->assigned_gotno = assign;
- g->local_gotno += assign + pages;
+ g->local_gotno += assign;
+ g->local_gotno += (pages < g->page_gotno ? pages : g->page_gotno);
assign = g->local_gotno + g->global_gotno + g->tls_gotno;
/* Take g out of the direct list, and push it onto the reversed
@@ -3814,6 +4015,7 @@ mips_elf_create_got_section (bfd *abfd, struct bfd_link_info *info,
g->global_gotno = 0;
g->tls_gotno = 0;
g->local_gotno = MIPS_RESERVED_GOTNO (info);
+ g->page_gotno = 0;
g->assigned_gotno = MIPS_RESERVED_GOTNO (info);
g->bfd2got = NULL;
g->next = NULL;
@@ -3822,6 +4024,10 @@ mips_elf_create_got_section (bfd *abfd, struct bfd_link_info *info,
mips_elf_got_entry_eq, NULL);
if (g->got_entries == NULL)
return FALSE;
+ g->got_page_entries = htab_try_create (1, mips_got_page_entry_hash,
+ mips_got_page_entry_eq, NULL);
+ if (g->got_page_entries == NULL)
+ return FALSE;
mips_elf_section_data (s)->u.got_info = g;
mips_elf_section_data (s)->elf.this_hdr.sh_flags
|= SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL;
@@ -6121,6 +6327,127 @@ _bfd_mips_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
return TRUE;
}
+/* Return true if relocation REL against section SEC is a REL rather than
+ RELA relocation. RELOCS is the first relocation in the section and
+ ABFD is the bfd that contains SEC. */
+
+static bfd_boolean
+mips_elf_rel_relocation_p (bfd *abfd, asection *sec,
+ const Elf_Internal_Rela *relocs,
+ const Elf_Internal_Rela *rel)
+{
+ Elf_Internal_Shdr *rel_hdr;
+ const struct elf_backend_data *bed;
+
+ /* To determine which flavor or relocation this is, we depend on the
+ fact that the INPUT_SECTION's REL_HDR is read before its REL_HDR2. */
+ rel_hdr = &elf_section_data (sec)->rel_hdr;
+ bed = get_elf_backend_data (abfd);
+ if ((size_t) (rel - relocs)
+ >= (NUM_SHDR_ENTRIES (rel_hdr) * bed->s->int_rels_per_ext_rel))
+ rel_hdr = elf_section_data (sec)->rel_hdr2;
+ return rel_hdr->sh_entsize == MIPS_ELF_REL_SIZE (abfd);
+}
+
+/* Read the addend for REL relocation REL, which belongs to bfd ABFD.
+ HOWTO is the relocation's howto and CONTENTS points to the contents
+ of the section that REL is against. */
+
+static bfd_vma
+mips_elf_read_rel_addend (bfd *abfd, const Elf_Internal_Rela *rel,
+ reloc_howto_type *howto, bfd_byte *contents)
+{
+ bfd_byte *location;
+ unsigned int r_type;
+ bfd_vma addend;
+
+ r_type = ELF_R_TYPE (abfd, rel->r_info);
+ location = contents + rel->r_offset;
+
+ /* Get the addend, which is stored in the input file. */
+ _bfd_mips16_elf_reloc_unshuffle (abfd, r_type, FALSE, location);
+ addend = mips_elf_obtain_contents (howto, rel, abfd, contents);
+ _bfd_mips16_elf_reloc_shuffle (abfd, r_type, FALSE, location);
+
+ return addend & howto->src_mask;
+}
+
+/* REL is a relocation in ABFD that needs a partnering LO16 relocation
+ and *ADDEND is the addend for REL itself. Look for the LO16 relocation
+ and update *ADDEND with the final addend. Return true on success
+ or false if the LO16 could not be found. RELEND is the exclusive
+ upper bound on the relocations for REL's section. */
+
+static bfd_boolean
+mips_elf_add_lo16_rel_addend (bfd *abfd,
+ const Elf_Internal_Rela *rel,
+ const Elf_Internal_Rela *relend,
+ bfd_byte *contents, bfd_vma *addend)
+{
+ unsigned int r_type, lo16_type;
+ const Elf_Internal_Rela *lo16_relocation;
+ reloc_howto_type *lo16_howto;
+ bfd_vma l;
+
+ r_type = ELF_R_TYPE (abfd, rel->r_info);
+ if (r_type == R_MIPS16_HI16)
+ lo16_type = R_MIPS16_LO16;
+ else
+ lo16_type = R_MIPS_LO16;
+
+ /* The combined value is the sum of the HI16 addend, left-shifted by
+ sixteen bits, and the LO16 addend, sign extended. (Usually, the
+ code does a `lui' of the HI16 value, and then an `addiu' of the
+ LO16 value.)
+
+ Scan ahead to find a matching LO16 relocation.
+
+ According to the MIPS ELF ABI, the R_MIPS_LO16 relocation must
+ be immediately following. However, for the IRIX6 ABI, the next
+ relocation may be a composed relocation consisting of several
+ relocations for the same address. In that case, the R_MIPS_LO16
+ relocation may occur as one of these. We permit a similar
+ extension in general, as that is useful for GCC.
+
+ In some cases GCC dead code elimination removes the LO16 but keeps
+ the corresponding HI16. This is strictly speaking a violation of
+ the ABI but not immediately harmful. */
+ lo16_relocation = mips_elf_next_relocation (abfd, lo16_type, rel, relend);
+ if (lo16_relocation == NULL)
+ return FALSE;
+
+ /* Obtain the addend kept there. */
+ lo16_howto = MIPS_ELF_RTYPE_TO_HOWTO (abfd, lo16_type, FALSE);
+ l = mips_elf_read_rel_addend (abfd, lo16_relocation, lo16_howto, contents);
+
+ l <<= lo16_howto->rightshift;
+ l = _bfd_mips_elf_sign_extend (l, 16);
+
+ *addend <<= 16;
+ *addend += l;
+ return TRUE;
+}
+
+/* Try to read the contents of section SEC in bfd ABFD. Return true and
+ store the contents in *CONTENTS on success. Assume that *CONTENTS
+ already holds the contents if it is nonull on entry. */
+
+static bfd_boolean
+mips_elf_get_section_contents (bfd *abfd, asection *sec, bfd_byte **contents)
+{
+ if (*contents)
+ return TRUE;
+
+ /* Get cached copy if it exists. */
+ if (elf_section_data (sec)->this_hdr.contents != NULL)
+ {
+ *contents = elf_section_data (sec)->this_hdr.contents;
+ return TRUE;
+ }
+
+ return bfd_malloc_and_get_section (abfd, sec, contents);
+}
+
/* Look through the relocs for a section during the first phase, and
allocate space in the global offset table. */
@@ -6140,6 +6467,9 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
asection *sreloc;
const struct elf_backend_data *bed;
struct mips_elf_link_hash_table *htab;
+ bfd_byte *contents;
+ bfd_vma addend;
+ reloc_howto_type *howto;
if (info->relocatable)
return TRUE;
@@ -6404,6 +6734,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
sreloc = NULL;
bed = get_elf_backend_data (abfd);
rel_end = relocs + sec->reloc_count * bed->s->int_rels_per_ext_rel;
+ contents = NULL;
for (rel = relocs; rel < rel_end; ++rel)
{
unsigned long r_symndx;
@@ -6559,9 +6890,7 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
case R_MIPS_GOT_PAGE:
/* If this is a global, overridable symbol, GOT_PAGE will
decay to GOT_DISP, so we'll need a GOT entry for it. */
- if (h == NULL)
- break;
- else
+ if (h)
{
struct mips_elf_link_hash_entry *hmips =
(struct mips_elf_link_hash_entry *) h;
@@ -6574,13 +6903,37 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
if (hmips->root.def_regular
&& ! (info->shared && ! info->symbolic
&& ! hmips->root.forced_local))
- break;
+ h = NULL;
}
/* Fall through. */
case R_MIPS_GOT16:
case R_MIPS_GOT_HI16:
case R_MIPS_GOT_LO16:
+ if (!h)
+ {
+ /* This relocation needs a page entry in the GOT. */
+ if (mips_elf_rel_relocation_p (abfd, sec, relocs, rel))
+ {
+ if (!mips_elf_get_section_contents (abfd, sec, &contents))
+ return FALSE;
+ howto = MIPS_ELF_RTYPE_TO_HOWTO (abfd, r_type, FALSE);
+ addend = mips_elf_read_rel_addend (abfd, rel,
+ howto, contents);
+ if (r_type == R_MIPS_GOT16)
+ mips_elf_add_lo16_rel_addend (abfd, rel, rel_end,
+ contents, &addend);
+ else
+ addend <<= howto->rightshift;
+ }
+ else
+ addend = rel->r_addend;
+ if (!mips_elf_record_got_page_entry (abfd, r_symndx, addend, g))
+ return FALSE;
+ break;
+ }
+ /* Fall through. */
+
case R_MIPS_GOT_DISP:
if (h && ! mips_elf_record_global_got_symbol (h, abfd, info, g, 0))
return FALSE;
@@ -6885,17 +7238,8 @@ _bfd_mips_relax_section (bfd *abfd, asection *sec,
continue;
/* Get the section contents if we haven't done so already. */
- if (contents == NULL)
- {
- /* Get cached copy if it exists. */
- if (elf_section_data (sec)->this_hdr.contents != NULL)
- contents = elf_section_data (sec)->this_hdr.contents;
- else
- {
- if (!bfd_malloc_and_get_section (abfd, sec, &contents))
- goto relax_return;
- }
- }
+ if (!mips_elf_get_section_contents (abfd, sec, &contents))
+ goto relax_return;
instruction = bfd_get_32 (abfd, contents + irel->r_offset);
@@ -7210,7 +7554,7 @@ _bfd_mips_elf_always_size_sections (bfd *output_bfd,
struct mips_got_info *g;
int i;
bfd_size_type loadable_size = 0;
- bfd_size_type local_gotno;
+ bfd_size_type page_gotno;
bfd_size_type dynsymcount;
bfd *sub;
struct mips_elf_count_tls_arg count_tls_arg;
@@ -7291,13 +7635,18 @@ _bfd_mips_elf_always_size_sections (bfd *output_bfd,
/* There's no need to allocate page entries for VxWorks; R_MIPS_GOT16
relocations against local symbols evaluate to "G", and the EABI does
not include R_MIPS_GOT_PAGE. */
- local_gotno = 0;
+ page_gotno = 0;
else
/* Assume there are two loadable segments consisting of contiguous
sections. Is 5 enough? */
- local_gotno = (loadable_size >> 16) + 5;
+ page_gotno = (loadable_size >> 16) + 5;
+
+ /* Choose the smaller of the two estimates; both are intended to be
+ conservative. */
+ if (page_gotno > g->page_gotno)
+ page_gotno = g->page_gotno;
- g->local_gotno += local_gotno;
+ g->local_gotno += page_gotno;
s->size += g->local_gotno * MIPS_ELF_GOT_SIZE (output_bfd);
g->global_gotno = i;
@@ -7321,7 +7670,7 @@ _bfd_mips_elf_always_size_sections (bfd *output_bfd,
dynamic loader. */
if (!htab->is_vxworks && s->size > MIPS_ELF_GOT_MAX_SIZE (info))
{
- if (! mips_elf_multi_got (output_bfd, info, g, s, local_gotno))
+ if (! mips_elf_multi_got (output_bfd, info, g, s, page_gotno))
return FALSE;
}
else
@@ -7818,77 +8167,24 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
if (!use_saved_addend_p)
{
- Elf_Internal_Shdr *rel_hdr;
-
/* If these relocations were originally of the REL variety,
we must pull the addend out of the field that will be
relocated. Otherwise, we simply use the contents of the
- RELA relocation. To determine which flavor or relocation
- this is, we depend on the fact that the INPUT_SECTION's
- REL_HDR is read before its REL_HDR2. */
- rel_hdr = &elf_section_data (input_section)->rel_hdr;
- if ((size_t) (rel - relocs)
- >= (NUM_SHDR_ENTRIES (rel_hdr) * bed->s->int_rels_per_ext_rel))
- rel_hdr = elf_section_data (input_section)->rel_hdr2;
- if (rel_hdr->sh_entsize == MIPS_ELF_REL_SIZE (input_bfd))
+ RELA relocation. */
+ if (mips_elf_rel_relocation_p (input_bfd, input_section,
+ relocs, rel))
{
- bfd_byte *location = contents + rel->r_offset;
-
- /* Note that this is a REL relocation. */
rela_relocation_p = FALSE;
-
- /* Get the addend, which is stored in the input file. */
- _bfd_mips16_elf_reloc_unshuffle (input_bfd, r_type, FALSE,
- location);
- addend = mips_elf_obtain_contents (howto, rel, input_bfd,
- contents);
- _bfd_mips16_elf_reloc_shuffle(input_bfd, r_type, FALSE,
- location);
-
- addend &= howto->src_mask;
-
- /* For some kinds of relocations, the ADDEND is a
- combination of the addend stored in two different
- relocations. */
- if (r_type == R_MIPS_HI16 || r_type == R_MIPS16_HI16
+ addend = mips_elf_read_rel_addend (input_bfd, rel,
+ howto, contents);
+ if (r_type == R_MIPS_HI16
+ || r_type == R_MIPS16_HI16
|| (r_type == R_MIPS_GOT16
&& mips_elf_local_relocation_p (input_bfd, rel,
local_sections, FALSE)))
{
- const Elf_Internal_Rela *lo16_relocation;
- reloc_howto_type *lo16_howto;
- int lo16_type;
-
- if (r_type == R_MIPS16_HI16)
- lo16_type = R_MIPS16_LO16;
- else
- lo16_type = R_MIPS_LO16;
-
- /* The combined value is the sum of the HI16 addend,
- left-shifted by sixteen bits, and the LO16
- addend, sign extended. (Usually, the code does
- a `lui' of the HI16 value, and then an `addiu' of
- the LO16 value.)
-
- Scan ahead to find a matching LO16 relocation.
-
- According to the MIPS ELF ABI, the R_MIPS_LO16
- relocation must be immediately following.
- However, for the IRIX6 ABI, the next relocation
- may be a composed relocation consisting of
- several relocations for the same address. In
- that case, the R_MIPS_LO16 relocation may occur
- as one of these. We permit a similar extension
- in general, as that is useful for GCC.
-
- In some cases GCC dead code elimination removes
- the LO16 but keeps the corresponding HI16. This
- is strictly speaking a violation of the ABI but
- not immediately harmful. */
- lo16_relocation = mips_elf_next_relocation (input_bfd,
- lo16_type,
- rel, relend);
- if (lo16_relocation == NULL)
+ if (!mips_elf_add_lo16_rel_addend (input_bfd, rel, relend,
+ contents, &addend))
{
const char *name;
@@ -7903,32 +8199,6 @@ _bfd_mips_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
input_bfd, input_section, name, howto->name,
rel->r_offset);
}
- else
- {
- bfd_byte *lo16_location;
- bfd_vma l;
-
- lo16_location = contents + lo16_relocation->r_offset;
-
- /* Obtain the addend kept there. */
- lo16_howto = MIPS_ELF_RTYPE_TO_HOWTO (input_bfd,
- lo16_type, FALSE);
- _bfd_mips16_elf_reloc_unshuffle (input_bfd, lo16_type,
- FALSE, lo16_location);
- l = mips_elf_obtain_contents (lo16_howto,
- lo16_relocation,
- input_bfd, contents);
- _bfd_mips16_elf_reloc_shuffle (input_bfd, lo16_type,
- FALSE, lo16_location);
- l &= lo16_howto->src_mask;
- l <<= lo16_howto->rightshift;
- l = _bfd_mips_elf_sign_extend (l, 16);
-
- addend <<= 16;
-
- /* Compute the combined addend. */
- addend += l;
- }
}
else
addend <<= howto->rightshift;