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,SPU] Generate .fixup section to support PIC


Hi,

The SPU ABI requires PIC to support plugins.  If DLL's or PIE's were
supported, they would also require PIC.

Currently, the SPU ABI does not generate correct PIC if the code
contains SPU_ADDR32 relocations.  SPU_ADDR32 relocations correspond to
things like the addresses stored in global variables.  The values
computed for these relocations must be updated at run-time to get the
correct run-time value.

To implement this we add a new section called .fixup.  The address
corresponding to the r_offset of each SPU_ADDR32 relocation is encoded
into the .fixup section at link time.  The value of the relocation is
stored at that address in the output file.  At run time, code will
process the .fixup section, updating the values at the addresses.

A .fixup section consists of N records.  Each record is 4 bytes.  The
first record is addressed with the symbol __fixup_start.  The last
record is a sentinal with the value 0.  The other records are formatted
thusly:
  most significant 28 bits
  - contain the most significant 28 bits of a quadword address.  In
    other words (record & 0xfffffff0) results in a quadword address.
  least significant 4 bits
  - a bit mask specifying which words in the quadword should be updated.
    This allows some compression for arrays of pointers, like vtables.
    The bits match the words as if they were expanded with the 'fsm'
    instruction.

The following code can be used at run time to process the .fixup
section:


  static void
  __init_fixups (unsigned int pic_base)
  {
    unsigned int * fixup = __fixup_start;
    __vector unsigned int *addr;
    for ( ; *fixup; fixup++)
      {
        addr = (__vector unsigned int *)((pic_base + *fixup) & -16);
        *addr += (spu_splats (pic_base) & spu_maskw (*fixup));
      }
  }

The attached patch has been bootstrapped and tested on x86 and SPU with
no new regressions.

Ok to check in?

Trevor


2009-08-04  Trevor Smigiel  <Trevor_Smigiel@playstation.sony.com>

bfd/
	* elf32-spu.h (spu_elf_params): Add member emit_fixups.
        (spu_elf_size_sections): Declare prototype.
	* elf32-spu.c (spu_link_hash_table): Add member sfixup.
        (FIXUP_RECORD_SIZE, FIXUP_GET, FIXUP_PUT): New macros.
	(spu_elf_emit_fixup): New function.
	(spu_elf_relocate_section): Emit fixup for each SPU_ADDR32.
	(spu_elf_size_sections): New function.
ld/
        * emulparams/elf32_spu.sh (OTHER_READONLY_SECTIONS): Add .fixup
        section and __fixup_start symbol.
        * emultempl/spuelf.em (params): Initialize emit_fixups member.
        (spu_before_allocation): Call spu_elf_size_sections.
        (OPTION_SPU_EMIT_FIXUPS): Define.
        (PARSE_AND_LIST_LONGOPTS): Add --emit-fixups.
	(PARSE_AND_LIST_ARGS_CASES): Handle --emit-fixups.
        * ld.texinfo (--emit-fixups): Document.
ld/testsuite/
	* ld-spu/fixup.d: New.
	* ld-spu/fixup.s: New.

diff -rNup src.orig/bfd/elf32-spu.c src/bfd/elf32-spu.c
--- src.orig/bfd/elf32-spu.c	2009-07-10 07:00:38.000000000 -0700
+++ src/bfd/elf32-spu.c	2009-07-28 13:08:08.000000000 -0700
@@ -339,6 +339,9 @@ struct spu_link_hash_table
   /* Count of overlay stubs needed in non-overlay area.  */
   unsigned int non_ovly_stub;
 
+  /* Pointer to the fixup section */
+  asection *sfixup;
+
   /* Set on error.  */
   unsigned int stub_err : 1;
 };
@@ -553,6 +556,7 @@ get_sym_h (struct elf_link_hash_entry **
 bfd_boolean
 spu_elf_create_sections (struct bfd_link_info *info)
 {
+  struct spu_link_hash_table *htab = spu_hash_table (info);
   bfd *ibfd;
 
   for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
@@ -595,6 +599,24 @@ spu_elf_create_sections (struct bfd_link
       s->contents = data;
     }
 
+  if (htab->params->emit_fixups)
+    {
+      asection *s;
+      flagword flags;
+      ibfd = info->input_bfds;
+      flags = SEC_LOAD | SEC_ALLOC | SEC_READONLY | SEC_HAS_CONTENTS
+	| SEC_IN_MEMORY;
+      s = bfd_make_section_anyway_with_flags (ibfd, ".fixup", flags);
+      if (s == NULL || !bfd_set_section_alignment (ibfd, s, 2))
+	return FALSE;
+      /* Set a minimum size of 4 for the sentinel.  The real size will
+	 be set later. */
+      if (!bfd_set_section_size (ibfd, s, 4))
+	return FALSE;
+      s->contents = bfd_zalloc (ibfd, 4);
+      htab->sfixup = s;
+    }
+
   return TRUE;
 }
 
@@ -4713,6 +4735,48 @@ spu_elf_count_relocs (struct bfd_link_in
   return count;
 }
 
+/* Functions for adding fixup records to .fixup */
+
+#define FIXUP_RECORD_SIZE 4
+
+#define FIXUP_PUT(output_bfd,htab,index,addr) \
+	  bfd_put_32 (output_bfd, addr, \
+		      htab->sfixup->contents + FIXUP_RECORD_SIZE * (index))
+#define FIXUP_GET(output_bfd,htab,index) \
+	  bfd_get_32 (output_bfd, \
+		      htab->sfixup->contents + FIXUP_RECORD_SIZE * (index))
+
+/* Store OFFSET in .fixup.  This assumes it will be called with an
+   increasing OFFSET.  When this OFFSET fits with the last base offset,
+   it just sets a bit, otherwise it adds a new fixup record.  */
+static void
+spu_elf_emit_fixup (bfd * output_bfd, struct bfd_link_info *info,
+		    bfd_vma offset)
+{
+  struct spu_link_hash_table *htab = spu_hash_table (info);
+  asection *sfixup = htab->sfixup;
+  bfd_vma qaddr = offset & ~(bfd_vma) 15;
+  bfd_vma bit = ((bfd_vma) 8) >> ((offset & 15) >> 2);
+  if (sfixup->reloc_count == 0)
+    {
+      FIXUP_PUT (output_bfd, htab, 0, qaddr | bit);
+      sfixup->reloc_count++;
+    }
+  else
+    {
+      bfd_vma base = FIXUP_GET (output_bfd, htab, sfixup->reloc_count - 1);
+      if (qaddr != (base & ~(bfd_vma) 15))
+	{
+	  if ((sfixup->reloc_count + 1) * FIXUP_RECORD_SIZE > sfixup->size)
+	    (*_bfd_error_handler) (_("fatal error while creating .fixup"));
+	  FIXUP_PUT (output_bfd, htab, sfixup->reloc_count, qaddr | bit);
+	  sfixup->reloc_count++;
+	}
+      else
+	FIXUP_PUT (output_bfd, htab, sfixup->reloc_count - 1, base | bit);
+    }
+}
+
 /* Apply RELOCS to CONTENTS of INPUT_SECTION from INPUT_BFD.  */
 
 static int
@@ -4895,6 +4959,16 @@ spu_elf_relocate_section (bfd *output_bf
 	    }
 	}
 
+      if (htab->params->emit_fixups && !info->relocatable
+	  && (input_section->flags & SEC_ALLOC) != 0
+	  && r_type == R_SPU_ADDR32)
+	{
+	  bfd_vma offset;
+	  offset = rel->r_offset + input_section->output_section->vma
+		   + input_section->output_offset;
+	  spu_elf_emit_fixup (output_bfd, info, offset);
+	}
+
       if (unresolved_reloc)
 	;
       else if (r_type == R_SPU_PPU32 || r_type == R_SPU_PPU64)
@@ -5290,6 +5364,72 @@ spu_elf_modify_program_headers (bfd *abf
   return TRUE;
 }
 
+bfd_boolean
+spu_elf_size_sections (bfd * output_bfd, struct bfd_link_info *info)
+{
+  struct spu_link_hash_table *htab = spu_hash_table (info);
+  if (htab->params->emit_fixups)
+    {
+      asection *sfixup = htab->sfixup;
+      int fixup_count = 0;
+      bfd *ibfd;
+      size_t size;
+
+      for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+	{
+	  asection *isec;
+
+	  if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour)
+	    continue;
+
+	  /* Walk over each section attached to the input bfd.  */
+	  for (isec = ibfd->sections; isec != NULL; isec = isec->next)
+	    {
+	      Elf_Internal_Rela *internal_relocs, *irelaend, *irela;
+	      bfd_vma base_end;
+
+	      /* If there aren't any relocs, then there's nothing more
+	         to do.  */
+	      if ((isec->flags & SEC_RELOC) == 0
+		  || isec->reloc_count == 0)
+		continue;
+
+	      /* Get the relocs.  */
+	      internal_relocs =
+		_bfd_elf_link_read_relocs (ibfd, isec, NULL, NULL,
+					   info->keep_memory);
+	      if (internal_relocs == NULL)
+		return FALSE;
+
+	      /* 1 quadword can contain up to 4 R_SPU_ADDR32
+	       * relocations.  They are stored in a single word by
+	       * saving the upper 28 bits of the address and setting the
+	       * lower 4 bits to a bit mask of the words that have the
+	       * relocation.  BASE_END keeps track of the next quadword. */
+	      irela = internal_relocs;
+	      irelaend = irela + isec->reloc_count;
+	      base_end = 0;
+	      for (; irela < irelaend; irela++)
+		if (ELF32_R_TYPE (irela->r_info) == R_SPU_ADDR32
+		    && irela->r_offset >= base_end)
+		  {
+		    base_end = (irela->r_offset & ~(bfd_vma) 15) + 16;
+		    fixup_count++;
+		  }
+	    }
+	}
+
+      /* We always have a NULL fixup as a sentinel */
+      size = (fixup_count + 1) * FIXUP_RECORD_SIZE;
+      if (!bfd_set_section_size (output_bfd, sfixup, size))
+	return FALSE;
+      sfixup->contents = (bfd_byte *) bfd_zalloc (info->input_bfds, size);
+      if (sfixup->contents == NULL)
+	return FALSE;
+    }
+  return TRUE;
+}
+
 #define TARGET_BIG_SYM		bfd_elf32_spu_vec
 #define TARGET_BIG_NAME		"elf32-spu"
 #define ELF_ARCH		bfd_arch_spu
diff -rNup src.orig/bfd/elf32-spu.h src/bfd/elf32-spu.h
--- src.orig/bfd/elf32-spu.h	2009-05-13 21:30:01.000000000 -0700
+++ src/bfd/elf32-spu.h	2009-07-15 15:02:28.000000000 -0700
@@ -57,6 +57,9 @@ struct spu_elf_params
   /* Set if non-icache code should be allowed in icache lines.  */
   unsigned int non_ia_text : 1;
 
+  /* Set if the .fixups section should be generated. */
+  unsigned int emit_fixups : 1;
+
   /* Range of valid addresses for loadable sections.  */
   bfd_vma local_store_lo;
   bfd_vma local_store_hi;
@@ -114,6 +117,7 @@ extern void spu_elf_plugin (int);
 extern bfd_boolean spu_elf_open_builtin_lib (bfd **,
 					     const struct _ovl_stream *);
 extern bfd_boolean spu_elf_create_sections (struct bfd_link_info *);
+extern bfd_boolean spu_elf_size_sections (bfd *, struct bfd_link_info *);
 extern int spu_elf_find_overlays (struct bfd_link_info *);
 extern int spu_elf_size_stubs (struct bfd_link_info *);
 extern void spu_elf_place_overlay_data (struct bfd_link_info *);
diff -rNup src.orig/ld/emulparams/elf32_spu.sh src/ld/emulparams/elf32_spu.sh
--- src.orig/ld/emulparams/elf32_spu.sh	2008-10-21 22:20:44.000000000 -0700
+++ src/ld/emulparams/elf32_spu.sh	2009-07-20 14:14:36.000000000 -0700
@@ -20,3 +20,8 @@ DATA_ADDR="ALIGN(${MAXPAGESIZE})"
 OTHER_BSS_SECTIONS=".toe ALIGN(128) : { *(.toe) } = 0"
 OTHER_SECTIONS=".note.spu_name 0 : { KEEP(*(.note.spu_name)) }
   ._ea 0 : { KEEP(*(._ea)) KEEP(*(._ea.*)) }"
+OTHER_READONLY_SECTIONS="
+  .fixup ${RELOCATING-0} : {
+    PROVIDE (__fixup_start = .);
+    *(.fixup)
+  }"
diff -rNup src.orig/ld/emultempl/spuelf.em src/ld/emultempl/spuelf.em
--- src.orig/ld/emultempl/spuelf.em	2009-05-14 09:56:08.000000000 -0700
+++ src/ld/emultempl/spuelf.em	2009-07-27 13:36:21.000000000 -0700
@@ -37,7 +37,7 @@ static struct spu_elf_params params =
   &spu_elf_load_ovl_mgr,
   &spu_elf_open_overlay_script,
   &spu_elf_relink,
-  0, ovly_normal, 0, 0, 0, 0, 0, 0, 0,
+  0, ovly_normal, 0, 0, 0, 0, 0, 0, 0, 0,
   0, 0x3ffff,
   1, 0, 16, 0, 0, 2000
 };
@@ -316,6 +316,10 @@ spu_before_allocation (void)
       lang_reset_memory_regions ();
     }
 
+  if (is_spu_target ()
+      && !link_info.relocatable)
+    spu_elf_size_sections (link_info.output_bfd, &link_info);
+
   gld${EMULATION_NAME}_before_allocation ();
 }
 
@@ -600,6 +604,7 @@ PARSE_AND_LIST_PROLOGUE='
 #define OPTION_SPU_RESERVED_SPACE	(OPTION_SPU_FIXED_SPACE + 1)
 #define OPTION_SPU_EXTRA_STACK		(OPTION_SPU_RESERVED_SPACE + 1)
 #define OPTION_SPU_NO_AUTO_OVERLAY	(OPTION_SPU_EXTRA_STACK + 1)
+#define OPTION_SPU_EMIT_FIXUPS		(OPTION_SPU_NO_AUTO_OVERLAY + 1)
 '
 
 PARSE_AND_LIST_LONGOPTS='
@@ -625,6 +630,7 @@ PARSE_AND_LIST_LONGOPTS='
   { "reserved-space", required_argument, NULL, OPTION_SPU_RESERVED_SPACE },
   { "extra-stack-space", required_argument, NULL, OPTION_SPU_EXTRA_STACK },
   { "no-auto-overlay", optional_argument, NULL, OPTION_SPU_NO_AUTO_OVERLAY },
+  { "emit-fixups", optional_argument, NULL, OPTION_SPU_EMIT_FIXUPS },
 '
 
 PARSE_AND_LIST_OPTIONS='
@@ -812,6 +818,10 @@ PARSE_AND_LIST_ARGS_CASES='
 	  break;
 	}
       break;
+
+    case OPTION_SPU_EMIT_FIXUPS:
+      params.emit_fixups = 1;
+      break;
 '
 
 LDEMUL_AFTER_OPEN=spu_after_open
diff -rNup src.orig/ld/testsuite/ld-spu/fixup.d src/ld/testsuite/ld-spu/fixup.d
--- src.orig/ld/testsuite/ld-spu/fixup.d	1969-12-31 16:00:00.000000000 -0800
+++ src/ld/testsuite/ld-spu/fixup.d	2009-07-20 14:08:40.000000000 -0700
@@ -0,0 +1,20 @@
+#source: fixup.s
+#ld: --emit-fixups
+#objdump: -s 
+
+.*elf32-spu
+
+Contents of section .text:
+ 0000 00000000                             ....            
+Contents of section .fixup:
+ 0004 0000008b 00000091 000000c1 00000000  ................
+Contents of section .data:
+ 0080 000000d0 00000000 00000000 000000c0  ................
+ 0090 00000000 00000000 00000000 000000b0  ................
+ 00a0 00000001 00000000 00000000 00000000  ................
+ 00b0 00000002 00000000 00000000 00000000  ................
+ 00c0 00000000 00000000 00000000 00000080  ................
+Contents of section .note.spu_name:
+.*
+.*
+#pass
diff -rNup src.orig/ld/testsuite/ld-spu/fixup.s src/ld/testsuite/ld-spu/fixup.s
--- src.orig/ld/testsuite/ld-spu/fixup.s	1969-12-31 16:00:00.000000000 -0800
+++ src/ld/testsuite/ld-spu/fixup.s	2009-07-20 14:02:38.000000000 -0700
@@ -0,0 +1,24 @@
+ .global _end
+ .global _start
+ .global glob
+ .global after
+ .global before
+ .weak undef
+
+ .section .text,"ax"
+_start:
+ stop
+
+
+ .data
+ .p2align 4
+before:
+ .long _end, 0, _start, after
+ .long 0, 0, 0, glob
+loc:
+ .long 1,0,0,0
+glob:
+ .long 2,0,0,0
+after:
+ .long 0, 0, 0, before
+

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