This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[patch, committed] Nios II GOT pointer initialization


I've checked in this patch to fix a Nios II ABI conformance issue. Actually, there are two problems:

(1) The ABI document says: "The linker-defined symbol _gp_got points to the base address used for GOT-relative relocations" and "The GOT pointer is loaded using a PC-relative offset to the _gp_got symbol". However, the linker doesn't define _gp_got, and GCC is initializing the GOT pointer from _GLOBAL_OFFSET_TABLE_ instead.

(2) The ABI document refers to a 64K limitation on the size of the GOT, but because we're using a signed 16-bit offset from the base of the GOT, the current implementation is actually limited to 32K.

This patch fixes the problem in a backwards-compatible way. _GLOBAL_OFFSET_TABLE_ continues to point to the base of the GOT, so that it's link-compatible with existing compiled code. The linker defines _gp_got also to point to the base of the GOT if the size of the GOT section is less than 32K, otherwise it's biased by 32K.

I have a corresponding patch to change GCC to initialize the GOT pointer from the correct symbol, which I'll commit shortly.

-Sandra

2014-02-02  Sandra Loosemore  <sandra@codesourcery.com>

	bfd/
	* elf32-nios2.c (struct elf32_nios2_link_hash_table): Add
	h_gp_got field.
	(nios2_elf32_relocate_section): Use got_base to adjust
	GOT-pointer-relative relocations relative to _gp_got.
	(create_got_section): Create _gp_got symbol.
	(nios2_elf32_finish_dynamic_symbol): Make _gp_got absolute.
	(nios2_elf32_size_dynamic_sections): Set _gp_got offset.
diff --git a/bfd/elf32-nios2.c b/bfd/elf32-nios2.c
index ba76898..dbe5289 100644
--- a/bfd/elf32-nios2.c
+++ b/bfd/elf32-nios2.c
@@ -892,6 +892,9 @@ struct elf32_nios2_link_hash_table
     asection *srelbss;
     asection *sbss;
 
+    /* GOT pointer symbol _gp_got.  */
+    struct elf_link_hash_entry *h_gp_got;
+
     union {
       bfd_signed_vma refcount;
       bfd_vma offset;
@@ -2671,6 +2674,7 @@ nios2_elf32_relocate_section (bfd *output_bfd,
   asection *splt;
   asection *sreloc = NULL;
   bfd_vma *local_got_offsets;
+  bfd_vma got_base;
 
   symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (input_bfd);
@@ -2681,6 +2685,11 @@ nios2_elf32_relocate_section (bfd *output_bfd,
   splt = htab->root.splt;
   local_got_offsets = elf_local_got_offsets (input_bfd);
 
+  if (elf32_nios2_hash_table (info)->h_gp_got == NULL)
+    got_base = 0;
+  else
+    got_base = elf32_nios2_hash_table (info)->h_gp_got->root.u.def.value;
+
   for (rel = relocs; rel < relend; rel++)
     {
       reloc_howto_type *howto;
@@ -3026,10 +3035,11 @@ nios2_elf32_relocate_section (bfd *output_bfd,
 	      if (use_plt && info->shared)
 		{
 		  off = ((h->plt.offset - 24) / 12 + 3) * 4;
-		  relocation = htab->root.sgotplt->output_offset + off;
+		  relocation = (htab->root.sgotplt->output_offset + off
+				- got_base);
 		}
 	      else
-		relocation = sgot->output_offset + off;
+		relocation = sgot->output_offset + off - got_base;
 
 	      /* This relocation does not use the addend.  */
 	      rel->r_addend = 0;
@@ -3042,8 +3052,7 @@ nios2_elf32_relocate_section (bfd *output_bfd,
 	    case R_NIOS2_GOTOFF_LO:
 	    case R_NIOS2_GOTOFF_HA:
 	    case R_NIOS2_GOTOFF:
-	      /* Relocation is relative to the start of the
-		 global offset table.  */
+	      /* Relocation is relative to the global offset table pointer.  */
 
 	      BFD_ASSERT (sgot != NULL);
 	      if (sgot == NULL)
@@ -3052,12 +3061,10 @@ nios2_elf32_relocate_section (bfd *output_bfd,
 		  break;
 		}
 
-	      /* Note that sgot->output_offset is not involved in this
-		 calculation.  We always want the start of .got.  If we
-		 define _GLOBAL_OFFSET_TABLE in a different way, as is
-		 permitted by the ABI, we might have to change this
-		 calculation.  */
-	      relocation -= sgot->output_section->vma;
+	      /* Adjust the relocation to be relative to the GOT pointer.  */
+	      relocation -= (sgot->output_section->vma
+			     + sgot->output_offset - got_base);
+
 	      switch (howto->type)
 		{
 		case R_NIOS2_GOTOFF_LO:
@@ -3127,7 +3134,7 @@ nios2_elf32_relocate_section (bfd *output_bfd,
 		  htab->tls_ldm_got.offset |= 1;
 		}
 
-	      relocation = (htab->root.sgot->output_offset + off);
+	      relocation = htab->root.sgot->output_offset + off - got_base;
 
 	      r = _bfd_final_link_relocate (howto, input_bfd, input_section,
 					    contents, rel->r_offset,
@@ -3284,7 +3291,7 @@ nios2_elf32_relocate_section (bfd *output_bfd,
 
 		if ((tls_type & GOT_TLS_GD) && r_type != R_NIOS2_TLS_GD16)
 		  off += 8;
-		relocation = (htab->root.sgot->output_offset + off);
+		relocation = htab->root.sgot->output_offset + off - got_base;
 
 		r = _bfd_final_link_relocate (howto, input_bfd, input_section,
 					      contents, rel->r_offset,
@@ -3489,6 +3496,7 @@ static bfd_boolean
 create_got_section (bfd *dynobj, struct bfd_link_info *info)
 {
   struct elf32_nios2_link_hash_table *htab;
+  struct elf_link_hash_entry *h;
 
   htab = elf32_nios2_hash_table (info);
 
@@ -3500,6 +3508,16 @@ create_got_section (bfd *dynobj, struct bfd_link_info *info)
   if (!bfd_set_section_alignment (dynobj, htab->root.sgotplt, 4))
     return FALSE;
 
+  /* The Nios II ABI specifies that GOT-relative relocations are relative
+     to the linker-created symbol _gp_got, rather than using
+     _GLOBAL_OFFSET_TABLE_ directly.  In particular, the latter always
+     points to the base of the GOT while _gp_got may include a bias.  */
+  h = _bfd_elf_define_linkage_sym (dynobj, info, htab->root.sgotplt,
+				   "_gp_got");
+  elf32_nios2_hash_table (info)->h_gp_got = h;
+  if (h == NULL)
+    return FALSE;
+
   return TRUE;
 }
 
@@ -4178,9 +4196,10 @@ nios2_elf32_finish_dynamic_symbol (bfd *output_bfd,
       bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
     }
 
-  /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute.  */
+  /* Mark _DYNAMIC, _GLOBAL_OFFSET_TABLE_, and _gp_got as absolute.  */
   if (strcmp (h->root.root.string, "_DYNAMIC") == 0
-      || h == elf_hash_table (info)->hgot)
+      || h == elf_hash_table (info)->hgot
+      || h == elf32_nios2_hash_table (info)->h_gp_got)
     sym->st_shndx = SHN_ABS;
 
   return TRUE;
@@ -4846,6 +4865,16 @@ nios2_elf32_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
      sym dynamic relocs.  */
   elf_link_hash_traverse (& htab->root, allocate_dynrelocs, info);
 
+  if (elf_hash_table (info)->dynamic_sections_created)
+    {
+      /* If the .got section is more than 0x8000 bytes, we add
+	 0x8000 to the value of _gp_got, so that 16-bit relocations
+	 have a greater chance of working. */
+      if (htab->root.sgot->size >= 0x8000
+	  && elf32_nios2_hash_table (info)->h_gp_got->root.u.def.value == 0)
+	elf32_nios2_hash_table (info)->h_gp_got->root.u.def.value = 0x8000;
+    }
+
   /* The check_relocs and adjust_dynamic_symbol entry points have
      determined the sizes of the various dynamic sections.  Allocate
      memory for them.  */

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]