Fix GOT overflow on MIPS with page references to mergeable sections

Richard Sandiford rdsandiford@googlemail.com
Wed Feb 13 14:09:00 GMT 2013


It took longer than it should have done, but here's a patch to fix the
bug that Robert found: the counting of GOT page entries didn't work in
cases where a bfd has page references to multiple pieces of mergeable data,
some of which might end up resolving to input sections in other bfds
that are far away from the original.

The idea is to collect references during check_relocs, and only convert
them to page entries during size_dynamic_sections.  This also has the
benefit of handling hidden global symbols.

The patch applies on top of the recent GOT rewrite patches, so it's only
suitable for mainline.  Robert: could you check whether mainline CVS
now works with your original testcase?

Tested in the same way as the GOT rewrite patches.  Applied.

Richard


bfd/
	* elfxx-mips.c (mips_got_page_ref): New structure.
	(mips_got_page_entry): Use a section rather than a (bfd, symndx)
	pair to represent the anchor point.
	(mips_got_info): Add a got_page_refs field.
	(mips_elf_link_hash_table): Add a sym_cache field.
	(mips_got_page_ref_hash, mips_got_page_ref_eq): New functions.
	(mips_got_page_entry_hash, mips_got_page_entry_eq): Update for
	new anchor representation.
	(mips_elf_create_got_info): Create got_page_refs rather than
	got_page_entries.
	(mips_elf_record_got_page_ref): New function.
	(mips_elf_pages_for_range): Move further down file.
	(mips_elf_record_got_page_entry): Likewise.  Take a got as argument.
	Use a section rather than a (bfd, symndx) pair to represent the
	anchor point.
	(mips_elf_resolve_got_page_ref): New function.
	(mips_elf_resolve_final_got_entries): Use it to populate
	got_page_entries.
	(_bfd_mips_elf_check_relocs): Call mips_elf_record_got_page_ref
	rather than mips_elf_record_got_page_entry.  Only nullify h
	afterwards.
	(mips_elf_lay_out_got): Call mips_elf_resolve_final_got_entries
	earlier.

ld/testsuite/
	* ld-mips-elf/mips16-pic-2.dd,
	ld-mips-elf/mips16-pic-2.gd: Remove 3 unused local GOT entries.
	* ld-mips-elf/got-page-4a.s, ld-mips-elf/got-page-4b.s,
	ld-mips-elf/got-page-4a.d, ld-mips-elf/got-page-4a.got,
	ld-mips-elf/got-page-4b.d, ld-mips-elf/got-page-4b.got,
	ld-mips-elf/got-page-5.s, ld-mips-elf/got-page-5.d,
	ld-mips-elf/got-page-5.got, ld-mips-elf/got-page-6.s,
	ld-mips-elf/got-page-6.d, ld-mips-elf/got-page-6.got,
	ld-mips-elf/got-page-7a.s, ld-mips-elf/got-page-7b.s,
	ld-mips-elf/got-page-7c.s, ld-mips-elf/got-page-7d.s,
	ld-mips-elf/got-page-7e.s, ld-mips-elf/got-page-7.d,
	ld-mips-elf/got-page-7.got: New tests.
	* ld-mips-elf/mips-elf.exp: Run them.

Index: bfd/elfxx-mips.c
===================================================================
--- bfd/elfxx-mips.c	2013-02-13 13:43:51.961189241 +0000
+++ bfd/elfxx-mips.c	2013-02-13 13:43:56.072215253 +0000
@@ -108,6 +108,27 @@ struct mips_got_entry
   long gotidx;
 };
 
+/* This structure represents a GOT page reference from an input bfd.
+   Each instance represents a symbol + ADDEND, where the representation
+   of the symbol depends on whether it is local to the input bfd.
+   If it is, then SYMNDX >= 0, and the symbol has index SYMNDX in U.ABFD.
+   Otherwise, SYMNDX < 0 and U.H points to the symbol's hash table entry.
+
+   Page references with SYMNDX >= 0 always become page references
+   in the output.  Page references with SYMNDX < 0 only become page
+   references if the symbol binds locally; in other cases, the page
+   reference decays to a global GOT reference.  */
+struct mips_got_page_ref
+{
+  long symndx;
+  union
+  {
+    struct mips_elf_link_hash_entry *h;
+    bfd *abfd;
+  } u;
+  bfd_vma addend;
+};
+
 /* 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.  */
@@ -119,13 +140,11 @@ struct mips_got_page_range
 };
 
 /* This structure describes the range of addends that are applied to page
-   relocations against a given symbol.  */
+   relocations against a given section.  */
 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 section that these entries are based on.  */
+  asection *sec;
   /* The ranges for this page entry.  */
   struct mips_got_page_range *ranges;
   /* The maximum number of page entries needed for RANGES.  */
@@ -155,6 +174,8 @@ struct mips_got_info
   unsigned int assigned_gotno;
   /* A hash table holding members of the got.  */
   struct htab *got_entries;
+  /* A hash table holding mips_got_page_ref structures.  */
+  struct htab *got_page_refs;
   /* A hash table of mips_got_page_entry structures.  */
   struct htab *got_page_entries;
   /* In multi-got links, a pointer to the next got (err, rather, most
@@ -444,6 +465,9 @@ struct mips_elf_link_hash_table
      The function returns the new section on success, otherwise it
      returns null.  */
   asection *(*add_stub_section) (const char *, asection *, asection *);
+
+  /* Small local sym cache.  */
+  struct sym_cache sym_cache;
 };
 
 /* Get the MIPS ELF linker hash table from a link_info structure.  */
@@ -2771,12 +2795,38 @@ mips_elf_got_entry_eq (const void *entry
 }
 
 static hashval_t
+mips_got_page_ref_hash (const void *ref_)
+{
+  const struct mips_got_page_ref *ref;
+
+  ref = (const struct mips_got_page_ref *) ref_;
+  return ((ref->symndx >= 0
+	   ? (hashval_t) (ref->u.abfd->id + ref->symndx)
+	   : ref->u.h->root.root.root.hash)
+	  + mips_elf_hash_bfd_vma (ref->addend));
+}
+
+static int
+mips_got_page_ref_eq (const void *ref1_, const void *ref2_)
+{
+  const struct mips_got_page_ref *ref1, *ref2;
+
+  ref1 = (const struct mips_got_page_ref *) ref1_;
+  ref2 = (const struct mips_got_page_ref *) ref2_;
+  return (ref1->symndx == ref2->symndx
+	  && (ref1->symndx < 0
+	      ? ref1->u.h == ref2->u.h
+	      : ref1->u.abfd == ref2->u.abfd)
+	  && ref1->addend == ref2->addend);
+}
+
+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;
+  return entry->sec->id;
 }
 
 static int
@@ -2786,7 +2836,7 @@ mips_got_page_entry_eq (const void *entr
 
   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 entry1->sec == entry2->sec;
 }
 
 /* Create and return a new mips_got_info structure.  */
@@ -2805,9 +2855,9 @@ mips_elf_create_got_info (bfd *abfd)
   if (g->got_entries == NULL)
     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)
+  g->got_page_refs = htab_try_create (1, mips_got_page_ref_hash,
+				      mips_got_page_ref_eq, NULL);
+  if (g->got_page_refs == NULL)
     return NULL;
 
   return g;
@@ -2844,7 +2894,9 @@ mips_elf_replace_bfd_got (bfd *abfd, str
       /* The GOT structure itself and the hash table entries are
 	 allocated to a bfd, but the hash tables aren't.  */
       htab_delete (tdata->got->got_entries);
-      htab_delete (tdata->got->got_page_entries);
+      htab_delete (tdata->got->got_page_refs);
+      if (tdata->got->got_page_entries)
+	htab_delete (tdata->got->got_page_entries);
     }
   tdata->got = g;
 }
@@ -3691,30 +3743,18 @@ mips_elf_record_local_got_symbol (bfd *a
   return mips_elf_record_got_entry (info, abfd, &entry);
 }
 
-/* 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.
-
-   This function creates an upper bound on the number of GOT slots
-   required; no attempt is made to combine references to non-overridable
-   global symbols across multiple input files.  */
+/* Record that ABFD has a page relocation against SYMNDX + ADDEND.
+   H is the symbol's hash table entry, or null if SYMNDX is local
+   to ABFD.  */
 
 static bfd_boolean
-mips_elf_record_got_page_entry (struct bfd_link_info *info, bfd *abfd,
-				long symndx, bfd_signed_vma addend)
+mips_elf_record_got_page_ref (struct bfd_link_info *info, bfd *abfd,
+			      long symndx, struct elf_link_hash_entry *h,
+			      bfd_signed_vma addend)
 {
   struct mips_elf_link_hash_table *htab;
   struct mips_got_info *g1, *g2;
-  struct mips_got_page_entry lookup, *entry;
-  struct mips_got_page_range **range_ptr, *range;
-  bfd_vma old_pages, new_pages;
+  struct mips_got_page_ref lookup, *entry;
   void **loc, **bfd_loc;
 
   htab = mips_elf_hash_table (info);
@@ -3723,26 +3763,29 @@ mips_elf_record_got_page_entry (struct b
   g1 = htab->got_info;
   BFD_ASSERT (g1 != NULL);
 
-  /* Find the mips_got_page_entry hash table entry for this symbol.  */
-  lookup.abfd = abfd;
-  lookup.symndx = symndx;
-  loc = htab_find_slot (g1->got_page_entries, &lookup, INSERT);
+  if (h)
+    {
+      lookup.symndx = -1;
+      lookup.u.h = (struct mips_elf_link_hash_entry *) h;
+    }
+  else
+    {
+      lookup.symndx = symndx;
+      lookup.u.abfd = abfd;
+    }
+  lookup.addend = addend;
+  loc = htab_find_slot (g1->got_page_refs, &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;
+  entry = (struct mips_got_page_ref *) *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;
+      *entry = lookup;
       *loc = entry;
     }
 
@@ -3751,67 +3794,13 @@ mips_elf_record_got_page_entry (struct b
   if (!g2)
     return FALSE;
 
-  bfd_loc = htab_find_slot (g2->got_page_entries, &lookup, INSERT);
+  bfd_loc = htab_find_slot (g2->got_page_refs, &lookup, INSERT);
   if (!bfd_loc)
     return FALSE;
 
   if (!*bfd_loc)
     *bfd_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++;
-      g1->page_gotno++;
-      g2->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;
-      g1->page_gotno += new_pages - old_pages;
-      g2->page_gotno += new_pages - old_pages;
-    }
-
   return TRUE;
 }
 
@@ -3930,8 +3919,188 @@ mips_elf_recreate_got (void **entryp, vo
   return 1;
 }
 
+/* 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 G requires a page entry that can reach SEC + ADDEND.  */
+
+static bfd_boolean
+mips_elf_record_got_page_entry (struct mips_got_info *g,
+				asection *sec, bfd_signed_vma addend)
+{
+  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 section.  */
+  lookup.sec = sec;
+  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 section.  */
+  entry = (struct mips_got_page_entry *) *loc;
+  if (!entry)
+    {
+      entry = bfd_zalloc (sec->owner, sizeof (*entry));
+      if (!entry)
+	return FALSE;
+
+      entry->sec = sec;
+      *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_zalloc (sec->owner, 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;
+}
+
+/* A htab_traverse callback for which *REFP points to a mips_got_page_ref
+   and for which DATA points to a mips_elf_traverse_got_arg.  Work out
+   whether the page reference described by *REFP needs a GOT page entry,
+   and record that entry in DATA->g if so.  Set DATA->g to null on failure.  */
+
+static bfd_boolean
+mips_elf_resolve_got_page_ref (void **refp, void *data)
+{
+  struct mips_got_page_ref *ref;
+  struct mips_elf_traverse_got_arg *arg;
+  struct mips_elf_link_hash_table *htab;
+  asection *sec;
+  bfd_vma addend;
+
+  ref = (struct mips_got_page_ref *) *refp;
+  arg = (struct mips_elf_traverse_got_arg *) data;
+  htab = mips_elf_hash_table (arg->info);
+
+  if (ref->symndx < 0)
+    {
+      struct mips_elf_link_hash_entry *h;
+
+      /* Global GOT_PAGEs decay to GOT_DISP and so don't need page entries.  */
+      h = ref->u.h;
+      if (!SYMBOL_REFERENCES_LOCAL (arg->info, &h->root))
+	return 1;
+
+      /* Ignore undefined symbols; we'll issue an error later if
+	 appropriate.  */
+      if (!((h->root.root.type == bfd_link_hash_defined
+	     || h->root.root.type == bfd_link_hash_defweak)
+	    && h->root.root.u.def.section))
+	return 1;
+
+      sec = h->root.root.u.def.section;
+      addend = h->root.root.u.def.value + ref->addend;
+    }
+  else
+    {
+      Elf_Internal_Sym *isym;
+
+      /* Read in the symbol.  */
+      isym = bfd_sym_from_r_symndx (&htab->sym_cache, ref->u.abfd,
+				    ref->symndx);
+      if (isym == NULL)
+	{
+	  arg->g = NULL;
+	  return 0;
+	}
+
+      /* Get the associated input section.  */
+      sec = bfd_section_from_elf_index (ref->u.abfd, isym->st_shndx);
+      if (sec == NULL)
+	{
+	  arg->g = NULL;
+	  return 0;
+	}
+
+      /* If this is a mergable section, work out the section and offset
+	 of the merged data.  For section symbols, the addend specifies
+	 of the offset _of_ the first byte in the data, otherwise it
+	 specifies the offset _from_ the first byte.  */
+      if (sec->flags & SEC_MERGE)
+	{
+	  void *secinfo;
+
+	  secinfo = elf_section_data (sec)->sec_info;
+	  if (ELF_ST_TYPE (isym->st_info) == STT_SECTION)
+	    addend = _bfd_merged_section_offset (ref->u.abfd, &sec, secinfo,
+						 isym->st_value + ref->addend);
+	  else
+	    addend = _bfd_merged_section_offset (ref->u.abfd, &sec, secinfo,
+						 isym->st_value) + ref->addend;
+	}
+      else
+	addend = isym->st_value + ref->addend;
+    }
+  if (!mips_elf_record_got_page_entry (arg->g, sec, addend))
+    {
+      arg->g = NULL;
+      return 0;
+    }
+  return 1;
+}
+
 /* If any entries in G->got_entries are for indirect or warning symbols,
-   replace them with entries for the target symbol.  */
+   replace them with entries for the target symbol.  Convert g->got_page_refs
+   into got_page_entry structures and estimate the number of page entries
+   that they require.  */
 
 static bfd_boolean
 mips_elf_resolve_final_got_entries (struct bfd_link_info *info,
@@ -3961,6 +4130,16 @@ mips_elf_resolve_final_got_entries (stru
 
       htab_delete (oldg.got_entries);
     }
+
+  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;
+
+  tga.info = info;
+  tga.g = g;
+  htab_traverse (g->got_page_refs, mips_elf_resolve_got_page_ref, &tga);
+
   return TRUE;
 }
 
@@ -7823,21 +8002,6 @@ _bfd_mips_elf_check_relocs (bfd *abfd, s
 
 	case R_MIPS_GOT_PAGE:
 	case R_MICROMIPS_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)
-	    {
-	      struct mips_elf_link_hash_entry *hmips =
-		(struct mips_elf_link_hash_entry *) h;
-
-	      /* This symbol is definitely not overridable.  */
-	      if (hmips->root.def_regular
-		  && ! (info->shared && ! info->symbolic
-			&& ! hmips->root.forced_local))
-		h = NULL;
-	    }
-	  /* Fall through.  */
-
 	case R_MIPS16_GOT16:
 	case R_MIPS_GOT16:
 	case R_MIPS_GOT_HI16:
@@ -7866,10 +8030,24 @@ _bfd_mips_elf_check_relocs (bfd *abfd, s
 		}
 	      else
 		addend = rel->r_addend;
-	      if (!mips_elf_record_got_page_entry (info, abfd, r_symndx,
-						   addend))
+	      if (!mips_elf_record_got_page_ref (info, abfd, r_symndx,
+						 h, addend))
 		return FALSE;
+
+	      if (h)
+		{
+		  struct mips_elf_link_hash_entry *hmips =
+		    (struct mips_elf_link_hash_entry *) h;
+
+		  /* This symbol is definitely not overridable.  */
+		  if (hmips->root.def_regular
+		      && ! (info->shared && ! info->symbolic
+			    && ! hmips->root.forced_local))
+		    h = NULL;
+		}
 	    }
+	  /* If this is a global, overridable symbol, GOT_PAGE will
+	     decay to GOT_DISP, so we'll need a GOT entry for it.  */
 	  /* Fall through.  */
 
 	case R_MIPS_GOT_DISP:
@@ -8602,6 +8780,9 @@ mips_elf_lay_out_got (bfd *output_bfd, s
      count the number of reloc-only GOT symbols.  */
   mips_elf_link_hash_traverse (htab, mips_elf_count_got_symbols, info);
 
+  if (!mips_elf_resolve_final_got_entries (info, g))
+    return FALSE;
+
   /* Calculate the total loadable size of the output.  That
      will give us the maximum number of GOT_PAGE entries
      required.  */
@@ -8630,18 +8811,13 @@ mips_elf_lay_out_got (bfd *output_bfd, s
        sections.  Is 5 enough?  */
     page_gotno = (loadable_size >> 16) + 5;
 
-  /* Choose the smaller of the two estimates; both are intended to be
+  /* Choose the smaller of the two page estimates; both are intended to be
      conservative.  */
   if (page_gotno > g->page_gotno)
     page_gotno = g->page_gotno;
 
   g->local_gotno += page_gotno;
 
-  /* Replace entries for indirect and warning symbols with entries for
-     the target symbol.  Count the number of GOT entries and TLS relocs.  */
-  if (!mips_elf_resolve_final_got_entries (info, g))
-    return FALSE;
-
   s->size += g->local_gotno * MIPS_ELF_GOT_SIZE (output_bfd);
   s->size += g->global_gotno * MIPS_ELF_GOT_SIZE (output_bfd);
   s->size += g->tls_gotno * MIPS_ELF_GOT_SIZE (output_bfd);
Index: ld/testsuite/ld-mips-elf/mips16-pic-2.dd
===================================================================
--- ld/testsuite/ld-mips-elf/mips16-pic-2.dd	2013-02-13 13:43:51.960189235 +0000
+++ ld/testsuite/ld-mips-elf/mips16-pic-2.dd	2013-02-13 13:43:56.073215259 +0000
@@ -77,7 +77,7 @@ Disassembly of section \.text:
 .*:	[^\t]*	move	t9,v0
 .*:	[^\t]*	lw	v0,16\(sp\)
 .*:	[^\t]*	move	gp,v0
-.*:	[^\t]*	lw	v0,-32716\(v0\)
+.*:	[^\t]*	lw	v0,-32728\(v0\)
 .*:	[^\t]*	jalr	v0
 .*:	[^\t]*	move	t9,v0
 .*:	[^\t]*	lw	v0,16\(sp\)
@@ -101,7 +101,7 @@ Disassembly of section \.text:
 .*:	[^\t]*	move	t9,v0
 .*:	[^\t]*	lw	v0,16\(sp\)
 .*:	[^\t]*	move	gp,v0
-.*:	[^\t]*	lw	v0,-32712\(v0\)
+.*:	[^\t]*	lw	v0,-32724\(v0\)
 .*:	[^\t]*	jalr	v0
 .*:	[^\t]*	move	t9,v0
 .*:	[^\t]*	lw	v0,16\(sp\)
Index: ld/testsuite/ld-mips-elf/mips16-pic-2.gd
===================================================================
--- ld/testsuite/ld-mips-elf/mips16-pic-2.gd	2013-02-13 13:43:51.864188625 +0000
+++ ld/testsuite/ld-mips-elf/mips16-pic-2.gd	2013-02-13 13:43:56.074215266 +0000
@@ -13,14 +13,11 @@ Primary GOT:
   0005000c -32740\(gp\) 00040409
   00050010 -32736\(gp\) 0004040d
   00050014 -32732\(gp\) 00000000
-  00050018 -32728\(gp\) 00000000
-  0005001c -32724\(gp\) 00000000
-  00050020 -32720\(gp\) 00000000
 
  Global entries:
    Address     Access  Initial Sym\.Val\. Type    Ndx Name
-  00050024 -32716\(gp\) 00040574 00040574 FUNC      6 used6
-  00050028 -32712\(gp\) 00040598 00040598 FUNC      6 used7
-  0005002c -32708\(gp\) 00040550 00040550 FUNC      6 used5
-  00050030 -32704\(gp\) 0004052c 0004052c FUNC      6 used4
+  00050018 -32728\(gp\) 00040574 00040574 FUNC      6 used6
+  0005001c -32724\(gp\) 00040598 00040598 FUNC      6 used7
+  00050020 -32720\(gp\) 00040550 00040550 FUNC      6 used5
+  00050024 -32716\(gp\) 0004052c 0004052c FUNC      6 used4
 
Index: ld/testsuite/ld-mips-elf/got-page-4a.s
===================================================================
--- /dev/null	2013-02-10 23:11:23.371797561 +0000
+++ ld/testsuite/ld-mips-elf/got-page-4a.s	2013-02-13 13:43:56.075215273 +0000
@@ -0,0 +1,14 @@
+	.section .rodata.cst4,"aM",@progbits,4
+	.set	x,0x01000000
+	.set	y,0x02000000
+	# Add the 16 values that the next input file wants, but in such
+	# a way that each one lives on a separate page.
+	.rept	15
+	.word	y
+	.set	y,y+1
+	.rept	0x4000
+	.word	x
+	.set	x,x+1
+	.endr
+	.endr
+	.word	y
Index: ld/testsuite/ld-mips-elf/got-page-4b.s
===================================================================
--- /dev/null	2013-02-10 23:11:23.371797561 +0000
+++ ld/testsuite/ld-mips-elf/got-page-4b.s	2013-02-13 14:04:36.805756786 +0000
@@ -0,0 +1,21 @@
+	.globl	foo
+	.ent	foo
+foo:
+	# Create page references to 16 values.  The layout of the values
+	# in this input file requires at most 2 page entries.
+	.set	y,0x02000000
+	.rept	16
+	lw	$4,%got_page(1f)($gp)
+	addiu	$4,$4,%got_ofst(1f)
+	.section .rodata.cst4,"aM",@progbits,4
+1:	.word	y
+	.set	y,y+1
+	.text
+	.endr
+	.end	foo
+
+	# Make sure the loadable size of the library is large.
+	.section .bss
+	.globl	g
+g:
+	.space	0x800000
Index: ld/testsuite/ld-mips-elf/got-page-4a.d
===================================================================
--- /dev/null	2013-02-10 23:11:23.371797561 +0000
+++ ld/testsuite/ld-mips-elf/got-page-4a.d	2013-02-13 13:43:56.075215273 +0000
@@ -0,0 +1,35 @@
+#...
+.* <foo>:
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,0
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,4
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,8
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,12
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,16
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,20
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,24
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,28
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,32
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,36
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,40
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,44
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,48
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,52
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,56
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,60
+#pass
Index: ld/testsuite/ld-mips-elf/got-page-4a.got
===================================================================
--- /dev/null	2013-02-10 23:11:23.371797561 +0000
+++ ld/testsuite/ld-mips-elf/got-page-4a.got	2013-02-13 13:43:56.075215273 +0000
@@ -0,0 +1,7 @@
+#...
+ Local entries:
+   Address     Access  Initial
+  00090008 -32744\(gp\) 00080000
+  0009000c -32740\(gp\) 00000000
+
+#pass
Index: ld/testsuite/ld-mips-elf/got-page-4b.d
===================================================================
--- /dev/null	2013-02-10 23:11:23.371797561 +0000
+++ ld/testsuite/ld-mips-elf/got-page-4b.d	2013-02-13 13:43:56.076215279 +0000
@@ -0,0 +1,36 @@
+#...
+.* <foo>:
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,0
+.*	lw	a0,-32740\(gp\)
+.*	addiu	a0,a0,4
+.*	lw	a0,-32736\(gp\)
+.*	addiu	a0,a0,8
+.*	lw	a0,-32732\(gp\)
+.*	addiu	a0,a0,12
+.*	lw	a0,-32728\(gp\)
+.*	addiu	a0,a0,16
+.*	lw	a0,-32724\(gp\)
+.*	addiu	a0,a0,20
+.*	lw	a0,-32720\(gp\)
+.*	addiu	a0,a0,24
+.*	lw	a0,-32716\(gp\)
+.*	addiu	a0,a0,28
+.*	lw	a0,-32712\(gp\)
+.*	addiu	a0,a0,32
+.*	lw	a0,-32708\(gp\)
+.*	addiu	a0,a0,36
+.*	lw	a0,-32704\(gp\)
+.*	addiu	a0,a0,40
+.*	lw	a0,-32700\(gp\)
+.*	addiu	a0,a0,44
+.*	lw	a0,-32696\(gp\)
+.*	addiu	a0,a0,48
+.*	lw	a0,-32692\(gp\)
+.*	addiu	a0,a0,52
+.*	lw	a0,-32688\(gp\)
+.*	addiu	a0,a0,56
+.*	lw	a0,-32684\(gp\)
+.*	addiu	a0,a0,60
+
+#pass
Index: ld/testsuite/ld-mips-elf/got-page-4b.got
===================================================================
--- /dev/null	2013-02-10 23:11:23.371797561 +0000
+++ ld/testsuite/ld-mips-elf/got-page-4b.got	2013-02-13 13:43:56.076215279 +0000
@@ -0,0 +1,21 @@
+#...
+ Local entries:
+   Address     Access  Initial
+  00180008 -32744\(gp\) 00080000
+  0018000c -32740\(gp\) 00090000
+  00180010 -32736\(gp\) 000a0000
+  00180014 -32732\(gp\) 000b0000
+  00180018 -32728\(gp\) 000c0000
+  0018001c -32724\(gp\) 000d0000
+  00180020 -32720\(gp\) 000e0000
+  00180024 -32716\(gp\) 000f0000
+  00180028 -32712\(gp\) 00100000
+  0018002c -32708\(gp\) 00110000
+  00180030 -32704\(gp\) 00120000
+  00180034 -32700\(gp\) 00130000
+  00180038 -32696\(gp\) 00140000
+  0018003c -32692\(gp\) 00150000
+  00180040 -32688\(gp\) 00160000
+  00180044 -32684\(gp\) 00170000
+
+#pass
Index: ld/testsuite/ld-mips-elf/got-page-5.s
===================================================================
--- /dev/null	2013-02-10 23:11:23.371797561 +0000
+++ ld/testsuite/ld-mips-elf/got-page-5.s	2013-02-13 13:43:56.078215292 +0000
@@ -0,0 +1,31 @@
+	# Create a mergeable section full of a single value,
+	# and page references relative to one entry called "data".
+	#
+	# The mergeable entries collapse to one, but the offsets
+	# from "data" must still be retained, and need 3 page entries.
+	#
+	# Technically this isn't valid, because it creates out-of-section
+	# page references.  It's still a useful way of making sure that
+	# offsets in mergeable sections are handled correctly.
+	.globl	foo
+	.ent	foo
+foo:
+	.set	y,0
+	.rept	4
+	lw	$4,%got_page(data + y)($gp)
+	addiu	$4,$4,%got_ofst(data + y)
+	.set	y,y+0x8000
+	.endr
+	.end	foo
+
+	.section .rodata.cst4,"aM",@progbits,4
+data:
+	.rept	0x8000*4
+	.word	123456
+	.endr
+
+	# Make sure the loadable size of the library is large.
+	.section .bss
+	.globl	g
+g:
+	.space	0x800000
Index: ld/testsuite/ld-mips-elf/got-page-5.d
===================================================================
--- /dev/null	2013-02-10 23:11:23.371797561 +0000
+++ ld/testsuite/ld-mips-elf/got-page-5.d	2013-02-13 13:43:56.077215285 +0000
@@ -0,0 +1,10 @@
+#...
+.* <foo>:
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,0
+.*	lw	a0,-32740\(gp\)
+.*	addiu	a0,a0,-32768
+.*	lw	a0,-32740\(gp\)
+.*	addiu	a0,a0,0
+.*	lw	a0,-32736\(gp\)
+.*	addiu	a0,a0,-32768
Index: ld/testsuite/ld-mips-elf/got-page-5.got
===================================================================
--- /dev/null	2013-02-10 23:11:23.371797561 +0000
+++ ld/testsuite/ld-mips-elf/got-page-5.got	2013-02-13 13:43:56.078215292 +0000
@@ -0,0 +1,8 @@
+#...
+ Local entries:
+   Address     Access  Initial
+  00090008 -32744\(gp\) 00080000
+  0009000c -32740\(gp\) 00090000
+  00090010 -32736\(gp\) 000a0000
+
+#pass
Index: ld/testsuite/ld-mips-elf/got-page-6.s
===================================================================
--- /dev/null	2013-02-10 23:11:23.371797561 +0000
+++ ld/testsuite/ld-mips-elf/got-page-6.s	2013-02-13 13:43:56.079215299 +0000
@@ -0,0 +1,27 @@
+	# Create a mergeable section full of a single value.
+	# Create page references relative to instances of the value
+	# that are large distances apart.
+	#
+	# The mergeable entries collapse to one, so even with the
+	# large distances in the original file, we should end
+	# up with a single page entry.
+	.globl	foo
+	.ent	foo
+foo:
+	.rept	4
+	lw	$4,%got_page(1f)($gp)
+	addiu	$4,$4,%got_ofst(1f)
+	.section .rodata.cst4,"aM",@progbits,4
+1:
+	.rept	0x8000
+	.word	123456
+	.endr
+	.text
+	.endr
+	.end	foo
+
+	# Make sure the loadable size of the library is large.
+	.section .bss
+	.globl	g
+g:
+	.space	0x800000
Index: ld/testsuite/ld-mips-elf/got-page-6.d
===================================================================
--- /dev/null	2013-02-10 23:11:23.371797561 +0000
+++ ld/testsuite/ld-mips-elf/got-page-6.d	2013-02-13 13:43:56.078215292 +0000
@@ -0,0 +1,10 @@
+#...
+.* <foo>:
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,0
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,0
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,0
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,0
Index: ld/testsuite/ld-mips-elf/got-page-6.got
===================================================================
--- /dev/null	2013-02-10 23:11:23.371797561 +0000
+++ ld/testsuite/ld-mips-elf/got-page-6.got	2013-02-13 13:43:56.079215299 +0000
@@ -0,0 +1,6 @@
+#...
+ Local entries:
+   Address     Access  Initial
+  00090008 -32744\(gp\) 00080000
+
+#pass
Index: ld/testsuite/ld-mips-elf/got-page-7a.s
===================================================================
--- /dev/null	2013-02-10 23:11:23.371797561 +0000
+++ ld/testsuite/ld-mips-elf/got-page-7a.s	2013-02-13 13:43:56.080215306 +0000
@@ -0,0 +1,6 @@
+	.globl	f1
+	.ent	f1
+f1:
+	lw	$4,%got_page(g)($gp)
+	addiu	$4,$4,%got_ofst(g)
+	.end	f1
Index: ld/testsuite/ld-mips-elf/got-page-7b.s
===================================================================
--- /dev/null	2013-02-10 23:11:23.371797561 +0000
+++ ld/testsuite/ld-mips-elf/got-page-7b.s	2013-02-13 13:43:56.080215306 +0000
@@ -0,0 +1,6 @@
+	.globl	f2
+	.ent	f2
+f2:
+	lw	$4,%got_page(g + 0x4000)($gp)
+	addiu	$4,$4,%got_ofst(g + 0x4000)
+	.end	f2
Index: ld/testsuite/ld-mips-elf/got-page-7c.s
===================================================================
--- /dev/null	2013-02-10 23:11:23.371797561 +0000
+++ ld/testsuite/ld-mips-elf/got-page-7c.s	2013-02-13 13:43:56.080215306 +0000
@@ -0,0 +1,6 @@
+	.globl	f3
+	.ent	f3
+f3:
+	lw	$4,%got_page(g + 0x8000)($gp)
+	addiu	$4,$4,%got_ofst(g + 0x8000)
+	.end	f3
Index: ld/testsuite/ld-mips-elf/got-page-7d.s
===================================================================
--- /dev/null	2013-02-10 23:11:23.371797561 +0000
+++ ld/testsuite/ld-mips-elf/got-page-7d.s	2013-02-13 13:43:56.081215313 +0000
@@ -0,0 +1,6 @@
+	.globl	f4
+	.ent	f4
+f4:
+	lw	$4,%got_page(g + 0x10000)($gp)
+	addiu	$4,$4,%got_ofst(g + 0x10000)
+	.end	f4
Index: ld/testsuite/ld-mips-elf/got-page-7e.s
===================================================================
--- /dev/null	2013-02-10 23:11:23.371797561 +0000
+++ ld/testsuite/ld-mips-elf/got-page-7e.s	2013-02-13 13:43:56.081215313 +0000
@@ -0,0 +1,6 @@
+	# Make sure the loadable size of the library is large.
+	.section .bss
+	.globl	g
+	.hidden	g
+g:
+	.space	0x800000
Index: ld/testsuite/ld-mips-elf/got-page-7.d
===================================================================
--- /dev/null	2013-02-10 23:11:23.371797561 +0000
+++ ld/testsuite/ld-mips-elf/got-page-7.d	2013-02-13 13:43:56.079215299 +0000
@@ -0,0 +1,17 @@
+#...
+.* <f1>:
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,1024
+#...
+.* <f2>:
+.*	lw	a0,-32744\(gp\)
+.*	addiu	a0,a0,17408
+#...
+.* <f3>:
+.*	lw	a0,-32740\(gp\)
+.*	addiu	a0,a0,-31744
+#...
+.* <f4>:
+.*	lw	a0,-32740\(gp\)
+.*	addiu	a0,a0,1024
+#pass
Index: ld/testsuite/ld-mips-elf/got-page-7.got
===================================================================
--- /dev/null	2013-02-10 23:11:23.371797561 +0000
+++ ld/testsuite/ld-mips-elf/got-page-7.got	2013-02-13 13:43:56.080215306 +0000
@@ -0,0 +1,7 @@
+#...
+ Local entries:
+   Address     Access  Initial
+  00090008 -32744\(gp\) 00090000
+  0009000c -32740\(gp\) 000a0000
+
+#pass
Index: ld/testsuite/ld-mips-elf/mips-elf.exp
===================================================================
--- ld/testsuite/ld-mips-elf/mips-elf.exp	2013-02-13 13:43:51.864188625 +0000
+++ ld/testsuite/ld-mips-elf/mips-elf.exp	2013-02-13 13:48:32.777929917 +0000
@@ -453,6 +453,46 @@ if { $linux_gnu } {
 	run_dump_test "dyn-sec64"
     }
     run_dump_test "got-page-3"
+    run_ld_link_tests [subst {
+	{"GOT page 4 (one file)" "-shared $abi_ldflags(o32) -T got-page-1.ld"
+	 "$abi_asflags(o32) -mips2" {got-page-4b.s}
+	 {{objdump -dr got-page-4a.d}
+	  {readelf -A got-page-4a.got}}
+	 "got-page-4a.so"}
+	{"GOT page 4 (two files)" "-shared $abi_ldflags(o32) -T got-page-1.ld"
+	 "$abi_asflags(o32) -mips2" {got-page-4a.s got-page-4b.s}
+	 {{objdump -dr got-page-4b.d}
+	  {readelf -A got-page-4b.got}}
+	 "got-page-4b.so"}
+    }]
+    if $has_newabi {
+        run_ld_link_tests [subst {
+	    {"GOT page 5" "-shared $abi_ldflags(n32) -T got-page-1.ld"
+	     "$abi_asflags(n32)" {got-page-5.s}
+	     {{objdump -dr got-page-5.d}
+	      {readelf -A got-page-5.got}}
+	     "got-page-5.so"}
+	    {"GOT page 6" "-shared $abi_ldflags(n32) -T got-page-1.ld"
+	     "$abi_asflags(n32)" {got-page-6.s}
+	     {{objdump -dr got-page-6.d}
+	      {readelf -A got-page-6.got}}
+	     "got-page-6.so"}
+	    {"GOT page 7 (order 1)" "-shared $abi_ldflags(n32) -T got-page-1.ld"
+	     "$abi_asflags(n32)"
+	     {got-page-7a.s got-page-7b.s got-page-7c.s got-page-7d.s
+	      got-page-7e.s}
+	     {{objdump -dr got-page-7.d}
+	      {readelf -A got-page-7.got}}
+	     "got-page-7a.so"}
+	    {"GOT page 7 (order 2)" "-shared $abi_ldflags(n32) -T got-page-1.ld"
+	     "$abi_asflags(n32)"
+	     {got-page-7e.s got-page-7a.s got-page-7b.s got-page-7c.s
+	      got-page-7d.s}
+	     {{objdump -dr got-page-7.d}
+	      {readelf -A got-page-7.got}}
+	     "got-page-7b.so"}
+	}]
+    }
     run_dump_test "got-dump-1"
     if $has_newabi {
 	run_dump_test "got-dump-2"



More information about the Binutils mailing list