This is the mail archive of the binutils@sources.redhat.com 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]

Re: [PATCH] PIC support for SH


On Aug 31, 2000, Alexandre Oliva <aoliva@redhat.com> wrote:

> On Aug 29, 2000, kaz Kojima <kkojima@rr.iij4u.or.jp> wrote:
>> Here's a patch to support -fpic option for Hitach SH processors.  The
>> patch consists of two parts, SH specific changes and emit-rtl.c
>> change.

> Here's a patch that brings from the Red Hat internal sources all the
> changes for -fPIC support on sh-elf.  I guess I could just go ahead
> and install them, since Jeff Law had already approved them all, but
> I'll wait for explicit approval for the GCC tree.  Ok to install?

And here are the corresponding changes for binutils.  Ok to install?

Index: include/elf/ChangeLog
from  Alexandre Oliva  <aoliva@redhat.com>

	* sh.h (R_SH_GOT32, R_SH_PLT32, R_SH_COPY, R_SH_GLOB_DAT,
	R_SH_JMP_SLOT, R_SH_RELATIVE, R_SH_GOTOFF, R_SH_GOTPC): New relocs.
	(R_SH_FIRST_INVALID_RELOC): Adjust.

Index: include/elf/sh.h
===================================================================
RCS file: /cvs/src/src/include/elf/sh.h,v
retrieving revision 1.4
diff -u -p -r1.4 sh.h
--- include/elf/sh.h	2000/06/07 03:43:33	1.4
+++ include/elf/sh.h	2000/08/31 14:14:39
@@ -63,7 +63,15 @@ START_RELOC_NUMBERS (elf_sh_reloc_type)
   RELOC_NUMBER (R_SH_DIR8BP, 7)
   RELOC_NUMBER (R_SH_DIR8W, 8)
   RELOC_NUMBER (R_SH_DIR8L, 9)
-  FAKE_RELOC (R_SH_FIRST_INVALID_RELOC, 10)
+  RELOC_NUMBER (R_SH_GOT32, 10)
+  RELOC_NUMBER (R_SH_PLT32, 11)
+  RELOC_NUMBER (R_SH_COPY, 12)
+  RELOC_NUMBER (R_SH_GLOB_DAT, 13)
+  RELOC_NUMBER (R_SH_JMP_SLOT, 14)
+  RELOC_NUMBER (R_SH_RELATIVE, 15)
+  RELOC_NUMBER (R_SH_GOTOFF, 16)
+  RELOC_NUMBER (R_SH_GOTPC, 17)
+  FAKE_RELOC (R_SH_FIRST_INVALID_RELOC, 18)
   FAKE_RELOC (R_SH_LAST_INVALID_RELOC, 24)
   RELOC_NUMBER (R_SH_SWITCH16, 25)
   RELOC_NUMBER (R_SH_SWITCH32, 26)
Index: bfd/ChangeLog
from  Niibe Yutaka  <gniibe@m17n.org>, Kaz Kojima  <kkojima@rr.iij4u.or.jp>, Alexandre Oliva  <aoliva@redhat.com>

	* elf32-sh.c (R_SH_GOT32, R_SH_PLT32, R_SH_COPY, R_SH_GLOB_DAT,
	R_SH_JMP_SLOT, R_SH_RELATIVE, R_SH_GOTOFF, R_SH_GOTPC): New.
	(sh_reloc_map): Add new relocs.
	(sh_elf_check_relocs, sh_elf_link_hash_newfunc,
	sh_elf_link_hash_table_create, sh_elf_adjust_dynamic_symbol,
	sh_elf_size_dynamic_sections, sh_elf_finish_dynamic_symbol,
	sh_elf_finishe_dynamic_sections, sh_elf_discard_copies): New
	functions.
	(ELF_DYNAMIC_INTERPRETER, PLT_ENTRY_SIZE): Define.
	(elf_sh_plt0_entry_be, elf_sh_plt0_entry_le,
	elf_sh_plt_entry_be, elf_sh_plt_entry_le,
	elf_sh_pic_plt_entry_be, elf_sh_pic_plt_entry_le):
	New array constants.
	(elf_sh_plt0_entry, elf_sh_plt_entry, elf_sh_pic_plt_entry):
	New variables.
	(elf_sh_sizeof_plt, elf_sh_plt_plt0_offset,
	elf_sh_plt0_linker_offset, elf_sh_plt0_gotid_offset,
	elf_sh_plt_temp_offset, elf_sh_plt_symbol_offset,
	elf_sh_plt_reloc_offset): Define.
	(elf_sh_pcrel_relocs_copied, elf_sh_link_hash_entry,
	elf_sh_link_hash_table): New structs.
	(sh_elf_link_hash_traverse, sh_elf_hash_table): New macros.
	(sh_elf_relocate_section, sh_elf_check_relocs): Handle new
	relocation types.
	(elf_backend_create_dynamic_sections,
	bfd_elf32_bfd_link_hash_table_create,
	elf_backend_adjust_dynamic_symbol,
	elf_backend_size_dynamic_sections,
	elf_backend_finish_dynamic_symbol,
	elf_backend_finish_dynamic_sections, elf_backend_want_got_plt,
	elf_backend_plt_readonly, elf_backend_want_plt_sym,
	elf_backend_got_header_size, elf_backend_plt_header_size):
	Define.
	* reloc.c (BFD_RELOC_SH_COPY, BFD_RELOC_SH_GLOB_DAT,
	BFD_RELOC_SH_JMP_SLOT, BFD_RELOC_SH_RELATIVE, BFD_RELOC_SH_GOTPC):
	New relocs.
	* bfd-in2.h, libbfd.h: Rebuilt.

Index: bfd/elf32-sh.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-sh.c,v
retrieving revision 1.15
diff -u -p -r1.15 elf32-sh.c
--- bfd/elf32-sh.c	2000/08/22 04:58:25	1.15
+++ bfd/elf32-sh.c	2000/08/31 14:19:39
@@ -55,7 +55,28 @@ static boolean sh_elf_relocate_section
 static bfd_byte *sh_elf_get_relocated_section_contents
   PARAMS ((bfd *, struct bfd_link_info *, struct bfd_link_order *,
 	   bfd_byte *, boolean, asymbol **));
+static boolean sh_elf_check_relocs
+  PARAMS ((bfd *, struct bfd_link_info *, asection *,
+	   const Elf_Internal_Rela *));
+static struct bfd_hash_entry *sh_elf_link_hash_newfunc
+  PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
+static struct bfd_link_hash_table *sh_elf_link_hash_table_create
+  PARAMS ((bfd *));
+static boolean sh_elf_adjust_dynamic_symbol
+  PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *));
+static boolean sh_elf_size_dynamic_sections
+  PARAMS ((bfd *, struct bfd_link_info *));
+static boolean sh_elf_finish_dynamic_symbol
+  PARAMS ((bfd *, struct bfd_link_info *, struct elf_link_hash_entry *,
+	   Elf_Internal_Sym *));
+static boolean sh_elf_finish_dynamic_sections
+  PARAMS ((bfd *, struct bfd_link_info *));
 
+/* The name of the dynamic interpreter.  This is put in the .interp
+   section.  */
+
+#define ELF_DYNAMIC_INTERPRETER "/usr/lib/libc.so.1"
+
 static reloc_howto_type sh_elf_howto_table[] =
 {
   /* No relocation.  */
@@ -214,15 +235,119 @@ static reloc_howto_type sh_elf_howto_tab
 	 0,			/* src_mask */
 	 0xff,			/* dst_mask */
 	 true),			/* pcrel_offset */
+
+  HOWTO (R_SH_GOT32,		/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 false,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_bitfield, /* complain_on_overflow */
+	 bfd_elf_generic_reloc, /* */
+	 "R_SH_GOT32",		/* name */
+	 true,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 false),		/* pcrel_offset */
+
+  HOWTO (R_SH_PLT32,		/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_bitfield, /* complain_on_overflow */
+	 bfd_elf_generic_reloc, /* */
+	 "R_SH_PLT32",		/* name */
+	 true,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_SH_COPY,		/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 false,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_bitfield, /* complain_on_overflow */
+	 bfd_elf_generic_reloc, /* */
+	 "R_SH_COPY",		/* name */
+	 true,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 false),		/* pcrel_offset */
+
+  HOWTO (R_SH_GLOB_DAT,		/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 false,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_bitfield, /* complain_on_overflow */
+	 bfd_elf_generic_reloc, /* */
+	 "R_SH_GLOB_DAT",	/* name */
+	 true,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 false),		/* pcrel_offset */
+
+  HOWTO (R_SH_JMP_SLOT,		/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 false,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_bitfield, /* complain_on_overflow */
+	 bfd_elf_generic_reloc, /* */
+	 "R_SH_JMP_SLOT",	/* name */
+	 true,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 false),		/* pcrel_offset */
+
+  HOWTO (R_SH_RELATIVE,		/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 false,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_bitfield, /* complain_on_overflow */
+	 bfd_elf_generic_reloc, /* */
+	 "R_SH_RELATIVE",	/* name */
+	 true,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 false),		/* pcrel_offset */
+
+  HOWTO (R_SH_GOTOFF,		/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 false,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_bitfield, /* complain_on_overflow */
+	 bfd_elf_generic_reloc, /* */
+	 "R_SH_GOTOFF",		/* name */
+	 true,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 false),		/* pcrel_offset */
+
+  HOWTO (R_SH_GOTPC,		/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_bitfield, /* complain_on_overflow */
+	 bfd_elf_generic_reloc, /* */
+	 "R_SH_GOTPC",		/* name */
+	 true,			/* partial_inplace */
+	 0xffffffff,		/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
 
-  EMPTY_HOWTO (10),
-  EMPTY_HOWTO (11),
-  EMPTY_HOWTO (12),
-  EMPTY_HOWTO (13),
-  EMPTY_HOWTO (14),
-  EMPTY_HOWTO (15),
-  EMPTY_HOWTO (16),
-  EMPTY_HOWTO (17),
   EMPTY_HOWTO (18),
   EMPTY_HOWTO (19),
   EMPTY_HOWTO (20),
@@ -687,6 +812,14 @@ static const struct elf_reloc_map sh_rel
   { BFD_RELOC_VTABLE_ENTRY, R_SH_GNU_VTENTRY },
   { BFD_RELOC_SH_LOOP_START, R_SH_LOOP_START },
   { BFD_RELOC_SH_LOOP_END, R_SH_LOOP_END },
+  { BFD_RELOC_32_GOT_PCREL, R_SH_GOT32 },
+  { BFD_RELOC_32_PLT_PCREL, R_SH_PLT32 },
+  { BFD_RELOC_SH_COPY, R_SH_COPY },
+  { BFD_RELOC_SH_GLOB_DAT, R_SH_GLOB_DAT },
+  { BFD_RELOC_SH_JMP_SLOT, R_SH_JMP_SLOT },
+  { BFD_RELOC_SH_RELATIVE, R_SH_RELATIVE },
+  { BFD_RELOC_32_GOTOFF, R_SH_GOTOFF },
+  { BFD_RELOC_SH_GOTPC, R_SH_GOTPC },
 };
 
 /* Given a BFD reloc code, return the howto structure for the
@@ -1819,182 +1952,1265 @@ sh_elf_swap_insns (abfd, sec, relocs, co
   return true;
 }
 
-/* Relocate an SH ELF section.  */
+/* The size in bytes of an entry in the procedure linkage table.  */
 
-static boolean
-sh_elf_relocate_section (output_bfd, info, input_bfd, input_section,
-			 contents, relocs, local_syms, local_sections)
-     bfd *output_bfd ATTRIBUTE_UNUSED;
-     struct bfd_link_info *info;
-     bfd *input_bfd;
-     asection *input_section;
-     bfd_byte *contents;
-     Elf_Internal_Rela *relocs;
-     Elf_Internal_Sym *local_syms;
-     asection **local_sections;
+#define PLT_ENTRY_SIZE 28
+
+/* First entry in an absolute procedure linkage table look like this.  */
+
+static const bfd_byte elf_sh_plt0_entry_be[PLT_ENTRY_SIZE] =
 {
-  Elf_Internal_Shdr *symtab_hdr;
-  struct elf_link_hash_entry **sym_hashes;
-  Elf_Internal_Rela *rel, *relend;
+  0xd0, 0x04,	/* mov.l 1f,r0 */
+  0xd2, 0x05,	/* mov.l 2f,r2 */
+  0x60, 0x02,	/* mov.l @r0,r0 */
+  0x62, 0x22,	/* mov.l @r2,r2 */
+  0x40, 0x2b,	/* jmp @r0 */
+  0xe0, 0x00,	/*  mov #0,r0 */
+  0x00, 0x09,	/* nop */
+  0x00, 0x09,	/* nop */
+  0x00, 0x09,	/* nop */
+  0x00, 0x09,	/* nop */
+  0, 0, 0, 0,	/* 1: replaced with address of .got.plt + 8.  */
+  0, 0, 0, 0,	/* 2: replaced with address of .got.plt + 4.  */
+};
 
-  symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
-  sym_hashes = elf_sym_hashes (input_bfd);
+static const bfd_byte elf_sh_plt0_entry_le[PLT_ENTRY_SIZE] =
+{
+  0x04, 0xd0,	/* mov.l 1f,r0 */
+  0x05, 0xd2,	/* mov.l 2f,r2 */
+  0x02, 0x60,	/* mov.l @r0,r0 */
+  0x22, 0x62,	/* mov.l @r2,r2 */
+  0x2b, 0x40,	/* jmp @r0 */
+  0x00, 0xe0,	/*  mov #0,r0 */
+  0x09, 0x00,	/* nop */
+  0x09, 0x00,	/* nop */
+  0x09, 0x00,	/* nop */
+  0x09, 0x00,	/* nop */
+  0, 0, 0, 0,	/* 1: replaced with address of .got.plt + 8.  */
+  0, 0, 0, 0,	/* 2: replaced with address of .got.plt + 4.  */
+};
 
-  rel = relocs;
-  relend = relocs + input_section->reloc_count;
-  for (; rel < relend; rel++)
-    {
-      int r_type;
-      reloc_howto_type *howto;
-      unsigned long r_symndx;
-      Elf_Internal_Sym *sym;
-      asection *sec;
-      struct elf_link_hash_entry *h;
-      bfd_vma relocation;
-      bfd_vma addend = (bfd_vma)0;
-      bfd_reloc_status_type r;
+/* Sebsequent entries in an absolute procedure linkage table look like
+   this.  */
 
-      r_symndx = ELF32_R_SYM (rel->r_info);
+static const bfd_byte elf_sh_plt_entry_be[PLT_ENTRY_SIZE] =
+{
+  0xd0, 0x04,	/* mov.l 1f,r0 */
+  0x60, 0x02,	/* mov.l @r0,r0 */
+  0xd2, 0x02,	/* mov.l 0f,r2 */
+  0x40, 0x2b,   /* jmp @r0 */
+  0x60, 0x23,	/*  mov r2,r0 */
+  0xd1, 0x03,	/* mov.l 2f,r1 */
+  0x40, 0x2b,	/* jmp @r0 */
+  0x00, 0x09,	/* nop */
+  0, 0, 0, 0,	/* 0: replaced with address of .PLT0.  */
+  0, 0, 0, 0,	/* 1: replaced with address of this symbol in .got.  */
+  0, 0, 0, 0,	/* 2: replaced with offset into relocation table.  */
+};
 
-      if (info->relocateable)
-	{
-	  /* This is a relocateable link.  We don't have to change
-             anything, unless the reloc is against a section symbol,
-             in which case we have to adjust according to where the
-             section symbol winds up in the output section.  */
-	  if (r_symndx < symtab_hdr->sh_info)
-	    {
-	      sym = local_syms + r_symndx;
-	      if (ELF_ST_TYPE (sym->st_info) == STT_SECTION)
-		{
-		  sec = local_sections[r_symndx];
-		  rel->r_addend += sec->output_offset + sym->st_value;
-		}
-	    }
+static const bfd_byte elf_sh_plt_entry_le[PLT_ENTRY_SIZE] =
+{
+  0x04, 0xd0,	/* mov.l 1f,r0 */
+  0x02, 0x60,	/* mov.l @r0,r0 */
+  0x02, 0xd2,	/* mov.l 0f,r2 */
+  0x2b, 0x40,   /* jmp @r0 */
+  0x23, 0x60,	/*  mov r2,r0 */
+  0x03, 0xd1,	/* mov.l 2f,r1 */
+  0x2b, 0x40,	/* jmp @r0 */
+  0x09, 0x00,	/*  nop */
+  0, 0, 0, 0,	/* 0: replaced with address of .PLT.  */
+  0, 0, 0, 0,	/* 1: replaced with address of this symbol in .got.  */
+  0, 0, 0, 0,	/* 2: replaced with offset into relocation table.  */
+};
 
-	  continue;
-	}
+/* Entries in a PIC procedure linkage table look like this.  */
 
-      r_type = ELF32_R_TYPE (rel->r_info);
+static const bfd_byte elf_sh_pic_plt_entry_be[PLT_ENTRY_SIZE] =
+{
+  0xd0, 0x04,	/* mov.l 1f,r0 */
+  0x00, 0xce,	/* mov.l @(r0,r12),r0 */
+  0x40, 0x2b,	/* jmp @r0 */
+  0x00, 0x09,	/*  nop */
+  0x50, 0xc2,	/* 0: mov.l @(8,r12),r0 */
+  0x52, 0xc1,	/* 1: mov.l @(4,r12),r2 */
+  0xd1, 0x02,	/* mov.l 2f,r1 */
+  0x40, 0x2b,	/* jmp @r0 */
+  0xe0, 0x00,	/*  mov #0,r0 ! shows the type of PLT.  */
+  0x00, 0x09,	/* nop */
+  0, 0, 0, 0,	/* 1: replaced with address of this symbol in .got.  */
+  0, 0, 0, 0    /* 2: replaced with offset into relocation table.  */
+};
 
-      /* Many of the relocs are only used for relaxing, and are
-         handled entirely by the relaxation code.  */
-      if (r_type > (int) R_SH_LAST_INVALID_RELOC
-	  && r_type < (int) R_SH_LOOP_START)
-	continue;
+static const bfd_byte elf_sh_pic_plt_entry_le[PLT_ENTRY_SIZE] =
+{
+  0x04, 0xd0,	/* mov.l 1f,r0 */
+  0xce, 0x00,	/* mov.l @(r0,r12),r0 */
+  0x2b, 0x40,	/* jmp @r0 */
+  0x09, 0x00,	/*  nop */
+  0xc2, 0x50,	/* 0: mov.l @(8,r12),r0 */
+  0xc1, 0x52,	/* 1: mov.l @(4,r12),r2 */
+  0x02, 0xd1,	/* mov.l 2f,r1 */
+  0x2b, 0x40,	/* jmp @r0 */
+  0x00, 0xe0,	/*  mov #0,r0 ! shows the type of PLT.  */
+  0x09, 0x00,	/* nop */
+  0, 0, 0, 0,	/* 1: replaced with address of this symbol in .got.  */
+  0, 0, 0, 0    /* 2: replaced with offset into relocation table.  */
+};
 
-      if (r_type < 0
-	  || r_type >= R_SH_max
-	  || (r_type >= (int) R_SH_FIRST_INVALID_RELOC
-	      && r_type <= (int) R_SH_LAST_INVALID_RELOC))
-	{
-	  bfd_set_error (bfd_error_bad_value);
-	  return false;
-	}
+static const bfd_byte *elf_sh_plt0_entry;
+static const bfd_byte *elf_sh_plt_entry;
+static const bfd_byte *elf_sh_pic_plt_entry;
 
-      howto = sh_elf_howto_table + r_type;
+/* Return size of a PLT entry.  */
+#define elf_sh_sizeof_plt(info) PLT_ENTRY_SIZE
 
-      /* This is a final link.  */
-      h = NULL;
-      sym = NULL;
-      sec = NULL;
-      if (r_symndx < symtab_hdr->sh_info)
-	{
-	  sym = local_syms + r_symndx;
-	  sec = local_sections[r_symndx];
-	  relocation = (sec->output_section->vma
-			+ sec->output_offset
-			+ sym->st_value);
-	}
-      else
-	{
-	  h = sym_hashes[r_symndx - symtab_hdr->sh_info];
-	  while (h->root.type == bfd_link_hash_indirect
-		 || h->root.type == bfd_link_hash_warning)
-	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
-	  if (h->root.type == bfd_link_hash_defined
-	      || h->root.type == bfd_link_hash_defweak)
-	    {
-	      sec = h->root.u.def.section;
-	      relocation = (h->root.u.def.value
-			    + sec->output_section->vma
-			    + sec->output_offset);
-	    }
-	  else if (h->root.type == bfd_link_hash_undefweak)
-	    relocation = 0;
-	  else
-	    {
-	      if (! ((*info->callbacks->undefined_symbol)
-		     (info, h->root.root.string, input_bfd,
-		      input_section, rel->r_offset, true)))
-		return false;
-	      relocation = 0;
-	    }
-	}
+/* Return offset of the PLT0 address in an absolute PLT entry.  */
+#define elf_sh_plt_plt0_offset(info) 16
 
-      switch ((int)r_type)
-	{
-	final_link_relocate:
-	  /* COFF relocs don't use the addend. The addend is used for
-	     R_SH_DIR32 to be compatible with other compilers. */
-	  r = _bfd_final_link_relocate (howto, input_bfd, input_section,
-					contents, rel->r_offset,
-					relocation, addend);
-	  break;
+/* Return offset of the linker in PLT0 entry.  */
+#define elf_sh_plt0_linker_offset(info) 20
 
-	case R_SH_IND12W:
-	case R_SH_DIR8WPN:
-	case R_SH_DIR8WPZ:
-	case R_SH_DIR8WPL:
-	  /* These should normally be handled by the assembler, but at
-	     least IND12W is generated by ourselves, so we must deal
-	     with it.  */
-	  relocation -= 4;
-	  goto final_link_relocate;
+/* Return offset of the GOT id in PLT0 entry.  */
+#define elf_sh_plt0_gotid_offset(info) 24
 
-	default:
-	  bfd_set_error (bfd_error_bad_value);
-	  return false;
+/* Return offset of the tempoline in PLT entry */
+#define elf_sh_plt_temp_offset(info) 8
 
-	case R_SH_DIR32:
-	  addend = rel->r_addend;
-	  goto final_link_relocate;
+/* Return offset of the symbol in PLT entry.  */
+#define elf_sh_plt_symbol_offset(info) 20
 
-	case R_SH_LOOP_START:
-	  {
-	    static bfd_vma start, end;
+/* Return offset of the relocation in PLT entry.  */
+#define elf_sh_plt_reloc_offset(info) 24
 
-	    start = (relocation + rel->r_addend
-		     - (sec->output_section->vma + sec->output_offset));
-	    r = sh_elf_reloc_loop (r_type, input_bfd, input_section, contents,
-				   rel->r_offset, sec, start, end);
-	    break;
+/* The sh linker needs to keep track of the number of relocs that it
+   decides to copy in check_relocs for each symbol.  This is so that
+   it can discard PC relative relocs if it doesn't need them when
+   linking with -Bsymbolic.  We store the information in a field
+   extending the regular ELF linker hash table.  */
 
-	case R_SH_LOOP_END:
-	    end = (relocation + rel->r_addend
-		   - (sec->output_section->vma + sec->output_offset));
-	    r = sh_elf_reloc_loop (r_type, input_bfd, input_section, contents,
-				   rel->r_offset, sec, start, end);
-	    break;
-	  }
-	}
+/* This structure keeps track of the number of PC relative relocs we
+   have copied for a given symbol.  */
 
-      if (r != bfd_reloc_ok)
-	{
-	  switch (r)
-	    {
-	    default:
-	    case bfd_reloc_outofrange:
-	      abort ();
-	    case bfd_reloc_overflow:
-	      {
-		const char *name;
+struct elf_sh_pcrel_relocs_copied
+{
+  /* Next section.  */
+  struct elf_sh_pcrel_relocs_copied *next;
+  /* A section in dynobj.  */
+  asection *section;
+  /* Number of relocs copied in this section.  */
+  bfd_size_type count;
+};
 
-		if (h != NULL)
-		  name = h->root.root.string;
-		else
-		  {
-		    name = (bfd_elf_string_from_elf_section
+/* sh ELF linker hash entry.  */
+
+struct elf_sh_link_hash_entry
+{
+  struct elf_link_hash_entry root;
+
+  /* Number of PC relative relocs copied for this symbol.  */
+  struct elf_sh_pcrel_relocs_copied *pcrel_relocs_copied;
+};
+
+/* sh ELF linker hash table.  */
+
+struct elf_sh_link_hash_table
+{
+  struct elf_link_hash_table root;
+};
+
+/* Declare this now that the above structures are defined.  */
+
+static boolean sh_elf_discard_copies
+  PARAMS ((struct elf_sh_link_hash_entry *, PTR));
+
+/* Traverse an sh ELF linker hash table.  */
+
+#define sh_elf_link_hash_traverse(table, func, info)			\
+  (elf_link_hash_traverse						\
+   (&(table)->root,							\
+    (boolean (*) PARAMS ((struct elf_link_hash_entry *, PTR))) (func),	\
+    (info)))
+
+/* Get the sh ELF linker hash table from a link_info structure.  */
+
+#define sh_elf_hash_table(p) \
+  ((struct elf_sh_link_hash_table *) ((p)->hash))
+
+/* Create an entry in an sh ELF linker hash table.  */
+
+static struct bfd_hash_entry *
+sh_elf_link_hash_newfunc (entry, table, string)
+     struct bfd_hash_entry *entry;
+     struct bfd_hash_table *table;
+     const char *string;
+{
+  struct elf_sh_link_hash_entry *ret =
+    (struct elf_sh_link_hash_entry *) entry;
+
+  /* Allocate the structure if it has not already been allocated by a
+     subclass.  */
+  if (ret == (struct elf_sh_link_hash_entry *) NULL)
+    ret = ((struct elf_sh_link_hash_entry *)
+	   bfd_hash_allocate (table,
+			      sizeof (struct elf_sh_link_hash_entry)));
+  if (ret == (struct elf_sh_link_hash_entry *) NULL)
+    return (struct bfd_hash_entry *) ret;
+
+  /* Call the allocation method of the superclass.  */
+  ret = ((struct elf_sh_link_hash_entry *)
+	 _bfd_elf_link_hash_newfunc ((struct bfd_hash_entry *) ret,
+				     table, string));
+  if (ret != (struct elf_sh_link_hash_entry *) NULL)
+    {
+      ret->pcrel_relocs_copied = NULL;
+    }
+
+  return (struct bfd_hash_entry *) ret;
+}
+
+/* Create an sh ELF linker hash table.  */
+
+static struct bfd_link_hash_table *
+sh_elf_link_hash_table_create (abfd)
+     bfd *abfd;
+{
+  struct elf_sh_link_hash_table *ret;
+
+  ret = ((struct elf_sh_link_hash_table *)
+	 bfd_alloc (abfd, sizeof (struct elf_sh_link_hash_table)));
+  if (ret == (struct elf_sh_link_hash_table *) NULL)
+    return NULL;
+
+  if (! _bfd_elf_link_hash_table_init (&ret->root, abfd,
+				       sh_elf_link_hash_newfunc))
+    {
+      bfd_release (abfd, ret);
+      return NULL;
+    }
+
+  return &ret->root.root;
+}
+
+/* Create dynamic sections when linking against a dynamic object.  */
+
+static boolean
+sh_elf_create_dynamic_sections (abfd, info)
+     bfd *abfd;
+     struct bfd_link_info *info;
+{
+  flagword flags, pltflags;
+  register asection *s;
+  struct elf_backend_data *bed = get_elf_backend_data (abfd);
+  int ptralign = 0;
+
+  switch (bed->s->arch_size)
+    {
+    case 32:
+      ptralign = 2;
+      break;
+
+    case 64:
+      ptralign = 3;
+      break;
+
+    default:
+      bfd_set_error (bfd_error_bad_value);
+      return false;
+    }
+
+  /* We need to create .plt, .rel[a].plt, .got, .got.plt, .dynbss, and
+     .rel[a].bss sections.  */
+
+  flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
+	   | SEC_LINKER_CREATED);
+
+  pltflags = flags;
+  pltflags |= SEC_CODE;
+  if (bed->plt_not_loaded)
+    pltflags &= ~ (SEC_LOAD | SEC_HAS_CONTENTS);
+  if (bed->plt_readonly)
+    pltflags |= SEC_READONLY;
+
+  s = bfd_make_section (abfd, ".plt");
+  if (s == NULL
+      || ! bfd_set_section_flags (abfd, s, pltflags)
+      || ! bfd_set_section_alignment (abfd, s, bed->plt_alignment))
+    return false;
+
+  if (bed->want_plt_sym)
+    {
+      /* Define the symbol _PROCEDURE_LINKAGE_TABLE_ at the start of the
+	 .plt section.  */
+      struct elf_link_hash_entry *h = NULL;
+      if (! (_bfd_generic_link_add_one_symbol
+	     (info, abfd, "_PROCEDURE_LINKAGE_TABLE_", BSF_GLOBAL, s,
+	      (bfd_vma) 0, (const char *) NULL, false,
+	      get_elf_backend_data (abfd)->collect,
+	      (struct bfd_link_hash_entry **) &h)))
+	return false;
+      h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
+      h->type = STT_OBJECT;
+
+      if (info->shared
+	  && ! _bfd_elf_link_record_dynamic_symbol (info, h))
+	return false;
+    }
+
+  s = bfd_make_section (abfd, 
+			bed->default_use_rela_p ? ".rela.plt" : ".rel.plt");
+  if (s == NULL
+      || ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)
+      || ! bfd_set_section_alignment (abfd, s, ptralign))
+    return false;
+
+  if (! _bfd_elf_create_got_section (abfd, info))
+    return false;
+
+  {
+    const char *secname;
+    char *relname;
+    flagword secflags;
+    asection *sec;
+
+    for (sec = abfd->sections; sec; sec = sec->next)
+      {
+	secflags = bfd_get_section_flags (abfd, sec);
+	if ((secflags & (SEC_DATA | SEC_LINKER_CREATED))
+	    || ((secflags & SEC_HAS_CONTENTS) != SEC_HAS_CONTENTS))
+	  continue;
+	secname = bfd_get_section_name (abfd, sec);
+	relname = (char *) bfd_malloc (strlen (secname) + 6);
+	strcpy (relname, ".rela");
+	strcat (relname, secname);
+	s = bfd_make_section (abfd, relname);
+	if (s == NULL
+	    || ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)
+	    || ! bfd_set_section_alignment (abfd, s, ptralign))
+	  return false;
+      }
+  }
+
+  if (bed->want_dynbss)
+    {
+      /* The .dynbss section is a place to put symbols which are defined
+	 by dynamic objects, are referenced by regular objects, and are
+	 not functions.  We must allocate space for them in the process
+	 image and use a R_*_COPY reloc to tell the dynamic linker to
+	 initialize them at run time.  The linker script puts the .dynbss
+	 section into the .bss section of the final image.  */
+      s = bfd_make_section (abfd, ".dynbss");
+      if (s == NULL
+	  || ! bfd_set_section_flags (abfd, s, SEC_ALLOC))
+	return false;
+
+      /* The .rel[a].bss section holds copy relocs.  This section is not
+	 normally needed.  We need to create it here, though, so that the
+	 linker will map it to an output section.  We can't just create it
+	 only if we need it, because we will not know whether we need it
+	 until we have seen all the input files, and the first time the
+	 main linker code calls BFD after examining all the input files
+	 (size_dynamic_sections) the input sections have already been
+	 mapped to the output sections.  If the section turns out not to
+	 be needed, we can discard it later.  We will never need this
+	 section when generating a shared object, since they do not use
+	 copy relocs.  */
+      if (! info->shared)
+	{
+	  s = bfd_make_section (abfd, 
+				(bed->default_use_rela_p 
+				 ? ".rela.bss" : ".rel.bss")); 
+	  if (s == NULL
+	      || ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)
+	      || ! bfd_set_section_alignment (abfd, s, ptralign))
+	    return false;
+	}
+    }
+
+  return true;
+}
+
+
+/* Adjust a symbol defined by a dynamic object and referenced by a
+   regular object.  The current definition is in some section of the
+   dynamic object, but we're not including those sections.  We have to
+   change the definition to something the rest of the link can
+   understand.  */
+
+static boolean
+sh_elf_adjust_dynamic_symbol (info, h)
+     struct bfd_link_info *info;
+     struct elf_link_hash_entry *h;
+{
+  bfd *dynobj;
+  asection *s;
+  unsigned int power_of_two;
+
+  dynobj = elf_hash_table (info)->dynobj;
+
+  /* Make sure we know what is going on here.  */
+  BFD_ASSERT (dynobj != NULL
+	      && ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT)
+		  || h->weakdef != NULL
+		  || ((h->elf_link_hash_flags
+		       & ELF_LINK_HASH_DEF_DYNAMIC) != 0
+		      && (h->elf_link_hash_flags
+			  & ELF_LINK_HASH_REF_REGULAR) != 0
+		      && (h->elf_link_hash_flags
+			  & ELF_LINK_HASH_DEF_REGULAR) == 0)));
+
+  /* If this is a function, put it in the procedure linkage table.  We
+     will fill in the contents of the procedure linkage table later,
+     when we know the address of the .got section.  */
+  if (h->type == STT_FUNC
+      || (h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0)
+    {
+      if (! info->shared
+	  && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) == 0
+	  && (h->elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) == 0)
+	{
+	  /* This case can occur if we saw a PLT reloc in an input
+	     file, but the symbol was never referred to by a dynamic
+	     object.  In such a case, we don't actually need to build
+	     a procedure linkage table, and we can just do a REL32
+	     reloc instead.  */
+	  BFD_ASSERT ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0);
+	  return true;
+	}
+
+      /* Make sure this symbol is output as a dynamic symbol.  */
+      if (h->dynindx == -1)
+	{
+	  if (! bfd_elf32_link_record_dynamic_symbol (info, h))
+	    return false;
+	}
+
+      s = bfd_get_section_by_name (dynobj, ".plt");
+      BFD_ASSERT (s != NULL);
+
+      /* If this is the first .plt entry, make room for the special
+	 first entry.  */
+      if (s->_raw_size == 0)
+	s->_raw_size += PLT_ENTRY_SIZE;
+
+      /* If this symbol is not defined in a regular file, and we are
+	 not generating a shared library, then set the symbol to this
+	 location in the .plt.  This is required to make function
+	 pointers compare as equal between the normal executable and
+	 the shared library.  */
+      if (! info->shared
+	  && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
+	{
+	  h->root.u.def.section = s;
+	  h->root.u.def.value = s->_raw_size;
+	}
+
+      h->plt.offset = s->_raw_size;
+
+      /* Make room for this entry.  */
+      s->_raw_size += elf_sh_sizeof_plt (info);
+
+      /* We also need to make an entry in the .got.plt section, which
+	 will be placed in the .got section by the linker script.  */
+
+      s = bfd_get_section_by_name (dynobj, ".got.plt");
+      BFD_ASSERT (s != NULL);
+      s->_raw_size += 4;
+
+      /* We also need to make an entry in the .rela.plt section.  */
+
+      s = bfd_get_section_by_name (dynobj, ".rela.plt");
+      BFD_ASSERT (s != NULL);
+      s->_raw_size += sizeof (Elf32_External_Rela);
+
+      return true;
+    }
+
+  /* If this is a weak symbol, and there is a real definition, the
+     processor independent code will have arranged for us to see the
+     real definition first, and we can just use the same value.  */
+  if (h->weakdef != NULL)
+    {
+      BFD_ASSERT (h->weakdef->root.type == bfd_link_hash_defined
+		  || h->weakdef->root.type == bfd_link_hash_defweak);
+      h->root.u.def.section = h->weakdef->root.u.def.section;
+      h->root.u.def.value = h->weakdef->root.u.def.value;
+      return true;
+    }
+
+  /* This is a reference to a symbol defined by a dynamic object which
+     is not a function.  */
+
+  /* If we are creating a shared library, we must presume that the
+     only references to the symbol are via the global offset table.
+     For such cases we need not do anything here; the relocations will
+     be handled correctly by relocate_section.  */
+  if (info->shared)
+    return true;
+
+  /* If there are no references to this symbol that do not use the
+     GOT, we don't need to generate a copy reloc.  */
+  if ((h->elf_link_hash_flags & ELF_LINK_NON_GOT_REF) == 0)
+    return true;
+
+  /* We must allocate the symbol in our .dynbss section, which will
+     become part of the .bss section of the executable.  There will be
+     an entry for this symbol in the .dynsym section.  The dynamic
+     object will contain position independent code, so all references
+     from the dynamic object to this symbol will go through the global
+     offset table.  The dynamic linker will use the .dynsym entry to
+     determine the address it must put in the global offset table, so
+     both the dynamic object and the regular object will refer to the
+     same memory location for the variable.  */
+
+  s = bfd_get_section_by_name (dynobj, ".dynbss");
+  BFD_ASSERT (s != NULL);
+
+  /* We must generate a R_SH_COPY reloc to tell the dynamic linker to
+     copy the initial value out of the dynamic object and into the
+     runtime process image.  We need to remember the offset into the
+     .rela.bss section we are going to use.  */
+  if ((h->root.u.def.section->flags & SEC_ALLOC) != 0)
+    {
+      asection *srel;
+
+      srel = bfd_get_section_by_name (dynobj, ".rela.bss");
+      BFD_ASSERT (srel != NULL);
+      srel->_raw_size += sizeof (Elf32_External_Rela);
+      h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_COPY;
+    }
+
+  /* We need to figure out the alignment required for this symbol.  I
+     have no idea how ELF linkers handle this.  */
+  power_of_two = bfd_log2 (h->size);
+  if (power_of_two > 3)
+    power_of_two = 3;
+
+  /* Apply the required alignment.  */
+  s->_raw_size = BFD_ALIGN (s->_raw_size,
+			    (bfd_size_type) (1 << power_of_two));
+  if (power_of_two > bfd_get_section_alignment (dynobj, s))
+    {
+      if (! bfd_set_section_alignment (dynobj, s, power_of_two))
+	return false;
+    }
+
+  /* Define the symbol as being at this point in the section.  */
+  h->root.u.def.section = s;
+  h->root.u.def.value = s->_raw_size;
+
+  /* Increment the section size to make room for the symbol.  */
+  s->_raw_size += h->size;
+
+  return true;
+}
+
+/* Set the sizes of the dynamic sections.  */
+
+static boolean
+sh_elf_size_dynamic_sections (output_bfd, info)
+     bfd *output_bfd;
+     struct bfd_link_info *info;
+{
+  bfd *dynobj;
+  asection *s;
+  boolean plt;
+  boolean relocs;
+  boolean reltext;
+
+  dynobj = elf_hash_table (info)->dynobj;
+  BFD_ASSERT (dynobj != NULL);
+
+  if (elf_hash_table (info)->dynamic_sections_created)
+    {
+      /* Set the contents of the .interp section to the interpreter.  */
+      if (! info->shared)
+	{
+	  s = bfd_get_section_by_name (dynobj, ".interp");
+	  BFD_ASSERT (s != NULL);
+	  s->_raw_size = sizeof ELF_DYNAMIC_INTERPRETER;
+	  s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER;
+	}
+    }
+  else
+    {
+      /* We may have created entries in the .rela.got section.
+	 However, if we are not creating the dynamic sections, we will
+	 not actually use these entries.  Reset the size of .rela.got,
+	 which will cause it to get stripped from the output file
+	 below.  */
+      s = bfd_get_section_by_name (dynobj, ".rela.got");
+      if (s != NULL)
+	s->_raw_size = 0;
+    }
+
+  /* If this is a -Bsymbolic shared link, then we need to discard all
+     PC relative relocs against symbols defined in a regular object.
+     We allocated space for them in the check_relocs routine, but we
+     will not fill them in in the relocate_section routine.  */
+  if (info->shared && info->symbolic)
+    sh_elf_link_hash_traverse (sh_elf_hash_table (info),
+				 sh_elf_discard_copies,
+				 (PTR) NULL);
+
+  /* The check_relocs and adjust_dynamic_symbol entry points have
+     determined the sizes of the various dynamic sections.  Allocate
+     memory for them.  */
+  plt = false;
+  relocs = false;
+  reltext = false;
+  for (s = dynobj->sections; s != NULL; s = s->next)
+    {
+      const char *name;
+      boolean strip;
+
+      if ((s->flags & SEC_LINKER_CREATED) == 0)
+	continue;
+
+      /* It's OK to base decisions on the section name, because none
+	 of the dynobj section names depend upon the input files.  */
+      name = bfd_get_section_name (dynobj, s);
+
+      strip = false;
+
+      if (strcmp (name, ".plt") == 0)
+	{
+	  if (s->_raw_size == 0)
+	    {
+	      /* Strip this section if we don't need it; see the
+		 comment below.  */
+	      strip = true;
+	    }
+	  else
+	    {
+	      /* Remember whether there is a PLT.  */
+	      plt = true;
+	    }
+	}
+      else if (strncmp (name, ".rela", 5) == 0)
+	{
+	  if (s->_raw_size == 0)
+	    {
+	      /* If we don't need this section, strip it from the
+		 output file.  This is mostly to handle .rela.bss and
+		 .rela.plt.  We must create both sections in
+		 create_dynamic_sections, because they must be created
+		 before the linker maps input sections to output
+		 sections.  The linker does that before
+		 adjust_dynamic_symbol is called, and it is that
+		 function which decides whether anything needs to go
+		 into these sections.  */
+	      strip = true;
+	    }
+	  else
+	    {
+	      asection *target;
+
+	      /* Remember whether there are any reloc sections other
+		 than .rela.plt.  */
+	      if (strcmp (name, ".rela.plt") != 0)
+		{
+		  const char *outname;
+
+		  relocs = true;
+
+		  /* If this relocation section applies to a read only
+		     section, then we probably need a DT_TEXTREL
+		     entry.  The entries in the .rela.plt section
+		     really apply to the .got section, which we
+		     created ourselves and so know is not readonly.  */
+		  outname = bfd_get_section_name (output_bfd,
+						  s->output_section);
+		  target = bfd_get_section_by_name (output_bfd, outname + 5);
+		  if (target != NULL
+		      && (target->flags & SEC_READONLY) != 0
+		      && (target->flags & SEC_ALLOC) != 0)
+		    reltext = true;
+		}
+
+	      /* We use the reloc_count field as a counter if we need
+		 to copy relocs into the output file.  */
+	      s->reloc_count = 0;
+	    }
+	}
+      else if (strncmp (name, ".got", 4) != 0)
+	{
+	  /* It's not one of our sections, so don't allocate space.  */
+	  continue;
+	}
+
+      if (strip)
+	{
+	  _bfd_strip_section_from_output (info, s);
+	  continue;
+	}
+
+      /* Allocate memory for the section contents.  */
+      s->contents = (bfd_byte *) bfd_alloc (dynobj, s->_raw_size);
+      if (s->contents == NULL && s->_raw_size != 0)
+	return false;
+    }
+
+  if (elf_hash_table (info)->dynamic_sections_created)
+    {
+      /* Add some entries to the .dynamic section.  We fill in the
+	 values later, in sh_elf_finish_dynamic_sections, but we
+	 must add the entries now so that we get the correct size for
+	 the .dynamic section.  The DT_DEBUG entry is filled in by the
+	 dynamic linker and used by the debugger.  */
+      if (! info->shared)
+	{
+	  if (! bfd_elf32_add_dynamic_entry (info, DT_DEBUG, 0))
+	    return false;
+	}
+
+      if (plt)
+	{
+	  if (! bfd_elf32_add_dynamic_entry (info, DT_PLTGOT, 0)
+	      || ! bfd_elf32_add_dynamic_entry (info, DT_PLTRELSZ, 0)
+	      || ! bfd_elf32_add_dynamic_entry (info, DT_PLTREL, DT_RELA)
+	      || ! bfd_elf32_add_dynamic_entry (info, DT_JMPREL, 0))
+	    return false;
+	}
+
+      if (relocs)
+	{
+	  if (! bfd_elf32_add_dynamic_entry (info, DT_RELA, 0)
+	      || ! bfd_elf32_add_dynamic_entry (info, DT_RELASZ, 0)
+	      || ! bfd_elf32_add_dynamic_entry (info, DT_RELAENT,
+						sizeof (Elf32_External_Rela)))
+	    return false;
+	}
+
+      if (reltext)
+	{
+	  if (! bfd_elf32_add_dynamic_entry (info, DT_TEXTREL, 0))
+	    return false;
+	}
+    }
+
+  return true;
+}
+
+/* This function is called via sh_elf_link_hash_traverse if we are
+   creating a shared object with -Bsymbolic.  It discards the space
+   allocated to copy PC relative relocs against symbols which are
+   defined in regular objects.  We allocated space for them in the
+   check_relocs routine, but we won't fill them in in the
+   relocate_section routine.  */
+
+/*ARGSUSED*/
+static boolean
+sh_elf_discard_copies (h, ignore)
+     struct elf_sh_link_hash_entry *h;
+     PTR ignore ATTRIBUTE_UNUSED;
+{
+  struct elf_sh_pcrel_relocs_copied *s;
+
+  /* We only discard relocs for symbols defined in a regular object.  */
+  if ((h->root.elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
+    return true;
+
+  for (s = h->pcrel_relocs_copied; s != NULL; s = s->next)
+    s->section->_raw_size -= s->count * sizeof (Elf32_External_Rela);
+
+  return true;
+}
+
+
+/* Relocate an SH ELF section.  */
+
+static boolean
+sh_elf_relocate_section (output_bfd, info, input_bfd, input_section,
+			 contents, relocs, local_syms, local_sections)
+     bfd *output_bfd ATTRIBUTE_UNUSED;
+     struct bfd_link_info *info;
+     bfd *input_bfd;
+     asection *input_section;
+     bfd_byte *contents;
+     Elf_Internal_Rela *relocs;
+     Elf_Internal_Sym *local_syms;
+     asection **local_sections;
+{
+  Elf_Internal_Shdr *symtab_hdr;
+  struct elf_link_hash_entry **sym_hashes;
+  Elf_Internal_Rela *rel, *relend;
+  bfd *dynobj;
+  bfd_vma *local_got_offsets;
+  asection *sgot;
+  asection *splt;
+  asection *sreloc;
+
+  symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+  sym_hashes = elf_sym_hashes (input_bfd);
+  dynobj = elf_hash_table (info)->dynobj;
+  local_got_offsets = elf_local_got_offsets (input_bfd);
+
+  sgot = NULL;
+  splt = NULL;
+  sreloc = NULL;
+
+  rel = relocs;
+  relend = relocs + input_section->reloc_count;
+  for (; rel < relend; rel++)
+    {
+      int r_type;
+      reloc_howto_type *howto;
+      unsigned long r_symndx;
+      Elf_Internal_Sym *sym;
+      asection *sec;
+      struct elf_link_hash_entry *h;
+      bfd_vma relocation;
+      bfd_vma addend = (bfd_vma)0;
+      bfd_reloc_status_type r;
+
+      r_symndx = ELF32_R_SYM (rel->r_info);
+
+      if (info->relocateable)
+	{
+	  /* This is a relocateable link.  We don't have to change
+             anything, unless the reloc is against a section symbol,
+             in which case we have to adjust according to where the
+             section symbol winds up in the output section.  */
+	  if (r_symndx < symtab_hdr->sh_info)
+	    {
+	      sym = local_syms + r_symndx;
+	      if (ELF_ST_TYPE (sym->st_info) == STT_SECTION)
+		{
+		  sec = local_sections[r_symndx];
+		  rel->r_addend += sec->output_offset + sym->st_value;
+		}
+	    }
+
+	  continue;
+	}
+
+      r_type = ELF32_R_TYPE (rel->r_info);
+
+      /* Many of the relocs are only used for relaxing, and are
+         handled entirely by the relaxation code.  */
+      if (r_type > (int) R_SH_LAST_INVALID_RELOC
+	  && r_type < (int) R_SH_LOOP_START)
+	continue;
+
+      if (r_type < 0
+	  || r_type >= R_SH_max
+	  || (r_type >= (int) R_SH_FIRST_INVALID_RELOC
+	      && r_type <= (int) R_SH_LAST_INVALID_RELOC))
+	{
+	  bfd_set_error (bfd_error_bad_value);
+	  return false;
+	}
+
+      howto = sh_elf_howto_table + r_type;
+
+      /* This is a final link.  */
+      h = NULL;
+      sym = NULL;
+      sec = NULL;
+      if (r_symndx < symtab_hdr->sh_info)
+	{
+	  sym = local_syms + r_symndx;
+	  sec = local_sections[r_symndx];
+	  relocation = (sec->output_section->vma
+			+ sec->output_offset
+			+ sym->st_value);
+	}
+      else
+	{
+	  h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+	  while (h->root.type == bfd_link_hash_indirect
+		 || h->root.type == bfd_link_hash_warning)
+	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
+	  if (h->root.type == bfd_link_hash_defined
+	      || h->root.type == bfd_link_hash_defweak)
+	    {
+	      sec = h->root.u.def.section;
+	      /* In these cases, we don't need the relocation value.
+		 We check specially because in some obscure cases
+		 sec->output_section will be NULL. */
+	      if (r_type == R_SH_GOTPC
+		  || (r_type == R_SH_PLT32
+		      && h->plt.offset != (bfd_vma) -1)
+		  || (r_type == R_SH_GOT32
+		      && elf_hash_table (info)->dynamic_sections_created
+		      && (! info->shared
+			  || (! info->symbolic && h->dynindx != -1)
+			  || (h->elf_link_hash_flags
+			      & ELF_LINK_HASH_DEF_REGULAR) == 0))
+		  /* The cases above are those in which relocation is
+		     overwritten in the switch block below.  The cases
+		     below are those in which we must defer relocation
+		     to run-time, because we can't resolve absolute
+		     addresses when creating a shared library.  */
+		  || (info->shared
+		      && ((! info->symbolic && h->dynindx != -1)
+			  || (h->elf_link_hash_flags
+			      & ELF_LINK_HASH_DEF_REGULAR) == 0)
+		      && ((r_type == R_SH_DIR32
+			   && !(ELF_ST_VISIBILITY (h->other) == STV_INTERNAL
+				|| ELF_ST_VISIBILITY (h->other) == STV_HIDDEN))
+			  || r_type == R_SH_REL32)
+		      && ((input_section->flags & SEC_ALLOC) != 0
+			  /* DWARF will emit R_SH_DIR32 relocations in its
+			     sections against symbols defined externally
+			     in shared libraries.  We can't do anything
+			     with them here.  */
+			  || (input_section->flags & SEC_DEBUGGING) != 0)))
+		relocation = 0;
+	      else if (sec->output_section == NULL)
+		{
+		  (*_bfd_error_handler)
+		    (_("%s: warning: unresolvable relocation against symbol `%s' from %s section"),
+		     bfd_get_filename (input_bfd), h->root.root.string,
+		     bfd_get_section_name (input_bfd, input_section));
+		  relocation = 0;
+		}
+	      else
+		relocation = (h->root.u.def.value
+			      + sec->output_section->vma
+			      + sec->output_offset);
+	    }
+	  else if (h->root.type == bfd_link_hash_undefweak)
+	    relocation = 0;
+	  else if (info->shared && !info->symbolic && !info->no_undefined)
+	    relocation = 0;
+	  else
+	    {
+	      if (! ((*info->callbacks->undefined_symbol)
+		     (info, h->root.root.string, input_bfd,
+		      input_section, rel->r_offset, true)))
+		return false;
+	      relocation = 0;
+	    }
+	}
+
+      switch ((int)r_type)
+	{
+	final_link_relocate:
+	  /* COFF relocs don't use the addend. The addend is used for
+	     R_SH_DIR32 to be compatible with other compilers. */
+	  r = _bfd_final_link_relocate (howto, input_bfd, input_section,
+					contents, rel->r_offset,
+					relocation, addend);
+	  break;
+
+	case R_SH_IND12W:
+	case R_SH_DIR8WPN:
+	case R_SH_DIR8WPZ:
+	case R_SH_DIR8WPL:
+	  /* These should normally be handled by the assembler, but at
+	     least IND12W is generated by ourselves, so we must deal
+	     with it.  */
+	  relocation -= 4;
+	  goto final_link_relocate;
+
+	default:
+	  bfd_set_error (bfd_error_bad_value);
+	  return false;
+
+	case R_SH_DIR32:
+	case R_SH_REL32:
+	  if (info->shared
+	      && (input_section->flags & SEC_ALLOC) != 0
+	      && (r_type != R_SH_REL32
+		  || (h != NULL
+		      && h->dynindx != -1
+		      && (! info->symbolic
+			  || (h->elf_link_hash_flags
+			      & ELF_LINK_HASH_DEF_REGULAR) == 0))))
+	    {
+	      Elf_Internal_Rela outrel;
+	      boolean skip, relocate;
+
+	      /* When generating a shared object, these relocations
+		 are copied into the output file to be resolved at run
+		 time.  */
+
+	      if (sreloc == NULL)
+		{
+		  const char *name;
+
+		  name = (bfd_elf_string_from_elf_section
+			  (input_bfd,
+			   elf_elfheader (input_bfd)->e_shstrndx,
+			   elf_section_data (input_section)->rel_hdr.sh_name));
+		  if (name == NULL)
+		    return false;
+
+		  BFD_ASSERT (strncmp (name, ".rela", 5) == 0
+			      && strcmp (bfd_get_section_name (input_bfd,
+							       input_section),
+					 name + 5) == 0);
+
+		  sreloc = bfd_get_section_by_name (dynobj, name);
+		  BFD_ASSERT (sreloc != NULL);
+		}
+
+	      skip = false;
+
+	      if (elf_section_data (input_section)->stab_info == NULL)
+		outrel.r_offset = rel->r_offset;
+	      else
+		{
+		  bfd_vma off;
+
+		  off = (_bfd_stab_section_offset
+			 (output_bfd, &elf_hash_table (info)->stab_info,
+			  input_section,
+			  &elf_section_data (input_section)->stab_info,
+			  rel->r_offset));
+		  if (off == (bfd_vma) -1)
+		    skip = true;
+		  outrel.r_offset = off;
+		}
+
+	      outrel.r_offset += (input_section->output_section->vma
+				  + input_section->output_offset);
+
+	      if (skip)
+		{
+		  memset (&outrel, 0, sizeof outrel);
+		  relocate = false;
+		}
+	      else if (r_type == R_SH_REL32)
+		{
+		  BFD_ASSERT (h != NULL && h->dynindx != -1);
+		  relocate = false;
+		  outrel.r_info = ELF32_R_INFO (h->dynindx, R_SH_REL32);
+		  outrel.r_addend = rel->r_addend;
+		}
+	      else
+		{
+		  /* h->dynindx may be -1 if this symbol was marked to
+		     become local.  */
+		  if (h == NULL
+		      || ((info->symbolic || h->dynindx == -1)
+			  && (h->elf_link_hash_flags
+			      & ELF_LINK_HASH_DEF_REGULAR) != 0))
+		    {
+		      relocate = true;
+		      outrel.r_info = ELF32_R_INFO (0, R_SH_RELATIVE);
+		      outrel.r_addend = relocation + rel->r_addend;
+		    }
+		  else
+		    {
+		      BFD_ASSERT (h->dynindx != -1);
+		      relocate = false;
+		      outrel.r_info = ELF32_R_INFO (h->dynindx, R_SH_DIR32);
+		      outrel.r_addend = relocation + rel->r_addend;
+		    }
+		}
+
+	      bfd_elf32_swap_reloca_out (output_bfd, &outrel,
+					(((Elf32_External_Rela *)
+					  sreloc->contents)
+					 + sreloc->reloc_count));
+	      ++sreloc->reloc_count;
+
+	      /* If this reloc is against an external symbol, we do
+		 not want to fiddle with the addend.  Otherwise, we
+		 need to include the symbol value so that it becomes
+		 an addend for the dynamic reloc.  */
+	      if (! relocate)
+		continue;
+	    }
+	  else if (r_type == R_SH_DIR32)
+	    addend = rel->r_addend;
+	  goto final_link_relocate;
+
+	case R_SH_GOT32:
+	  /* Relocation is to the entry for this symbol in the global
+	     offset table.  */
+	  if (sgot == NULL)
+	    {
+	      sgot = bfd_get_section_by_name (dynobj, ".got");
+	      BFD_ASSERT (sgot != NULL);
+	    }
+
+	  if (h != NULL)
+	    {
+	      bfd_vma off;
+
+	      off = h->got.offset;
+	      BFD_ASSERT (off != (bfd_vma) -1);
+
+	      if (! elf_hash_table (info)->dynamic_sections_created
+		  || (info->shared
+		      && (info->symbolic || h->dynindx == -1
+			  || ELF_ST_VISIBILITY (h->other) == STV_INTERNAL
+			  || ELF_ST_VISIBILITY (h->other) == STV_HIDDEN)
+		      && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR)))
+		{
+		  /* This is actually a static link, or it is a
+		     -Bsymbolic link and the symbol is defined
+		     locally, or the symbol was forced to be local
+		     because of a version file.  We must initialize
+		     this entry in the global offset table.  Since the
+		     offset must always be a multiple of 4, we use the
+		     least significant bit to record whether we have
+		     initialized it already.
+
+		     When doing a dynamic link, we create a .rela.got
+		     relocation entry to initialize the value.  This
+		     is done in the finish_dynamic_symbol routine.  */
+		  if ((off & 1) != 0)
+		    off &= ~1;
+		  else
+		    {
+		      bfd_put_32 (output_bfd, relocation,
+				  sgot->contents + off);
+		      h->got.offset |= 1;
+		    }
+		}
+
+	      relocation = sgot->output_offset + off;
+	    }
+	  else
+	    {
+	      bfd_vma off;
+
+	      BFD_ASSERT (local_got_offsets != NULL
+			  && local_got_offsets[r_symndx] != (bfd_vma) -1);
+
+	      off = local_got_offsets[r_symndx];
+
+	      /* The offset must always be a multiple of 4.  We use
+		 the least significant bit to record whether we have
+		 already generated the necessary reloc.  */
+	      if ((off & 1) != 0)
+		off &= ~1;
+	      else
+		{
+		  bfd_put_32 (output_bfd, relocation, sgot->contents + off);
+
+		  if (info->shared)
+		    {
+		      asection *srelgot;
+		      Elf_Internal_Rela outrel;
+
+		      srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
+		      BFD_ASSERT (srelgot != NULL);
+
+		      outrel.r_offset = (sgot->output_section->vma
+					 + sgot->output_offset
+					 + off);
+		      outrel.r_info = ELF32_R_INFO (0, R_SH_RELATIVE);
+		      outrel.r_addend = relocation;
+		      bfd_elf32_swap_reloca_out (output_bfd, &outrel,
+						(((Elf32_External_Rela *)
+						  srelgot->contents)
+						 + srelgot->reloc_count));
+		      ++srelgot->reloc_count;
+		    }
+
+		  local_got_offsets[r_symndx] |= 1;
+		}
+
+	      relocation = sgot->output_offset + off;
+	    }
+
+	  goto final_link_relocate;
+
+	case R_SH_GOTOFF:
+	  /* Relocation is relative to the start of the global offset
+	     table.  */
+
+	  if (sgot == NULL)
+	    {
+	      sgot = bfd_get_section_by_name (dynobj, ".got");
+	      BFD_ASSERT (sgot != NULL);
+	    }
+
+	  /* Note that sgot->output_offset is not involved in this
+	     calculation.  We always want the start of .got.  If we
+	     defined _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;
+
+	  goto final_link_relocate;
+
+	case R_SH_GOTPC:
+	  /* Use global offset table as symbol value.  */
+
+	  if (sgot == NULL)
+	    {
+	      sgot = bfd_get_section_by_name (dynobj, ".got");
+	      BFD_ASSERT (sgot != NULL);
+	    }
+
+	  relocation = sgot->output_section->vma;
+
+	  goto final_link_relocate;
+
+	case R_SH_PLT32:
+	  /* Relocation is to the entry for this symbol in the
+	     procedure linkage table.  */
+
+	  /* Resolve a PLT reloc against a local symbol directly,
+	     without using the procedure linkage table.  */
+	  if (h == NULL)
+	    goto final_link_relocate;
+
+	  if (ELF_ST_VISIBILITY (h->other) == STV_INTERNAL
+	      || ELF_ST_VISIBILITY (h->other) == STV_HIDDEN)
+	    goto final_link_relocate;
+
+	  if (h->plt.offset == (bfd_vma) -1)
+	    {
+	      /* We didn't make a PLT entry for this symbol.  This
+		 happens when statically linking PIC code, or when
+		 using -Bsymbolic.  */
+	      goto final_link_relocate;
+	    }
+
+	  if (splt == NULL)
+	    {
+	      splt = bfd_get_section_by_name (dynobj, ".plt");
+	      BFD_ASSERT (splt != NULL);
+	    }
+
+	  relocation = (splt->output_section->vma
+			+ splt->output_offset
+			+ h->plt.offset);
+
+	  goto final_link_relocate;
+
+	case R_SH_LOOP_START:
+	  {
+	    static bfd_vma start, end;
+
+	    start = (relocation + rel->r_addend
+		     - (sec->output_section->vma + sec->output_offset));
+	    r = sh_elf_reloc_loop (r_type, input_bfd, input_section, contents,
+				   rel->r_offset, sec, start, end);
+	    break;
+
+	case R_SH_LOOP_END:
+	    end = (relocation + rel->r_addend
+		   - (sec->output_section->vma + sec->output_offset));
+	    r = sh_elf_reloc_loop (r_type, input_bfd, input_section, contents,
+				   rel->r_offset, sec, start, end);
+	    break;
+	  }
+	}
+
+      if (r != bfd_reloc_ok)
+	{
+	  switch (r)
+	    {
+	    default:
+	    case bfd_reloc_outofrange:
+	      abort ();
+	    case bfd_reloc_overflow:
+	      {
+		const char *name;
+
+		if (h != NULL)
+		  name = h->root.root.string;
+		else
+		  {
+		    name = (bfd_elf_string_from_elf_section
 			    (input_bfd, symtab_hdr->sh_link, sym->st_name));
 		    if (name == NULL)
 		      return false;
@@ -2193,6 +3409,8 @@ sh_elf_gc_mark_hook (abfd, info, rel, h,
   return NULL;
 }
 
+/* Update the got entry reference counts for the section being removed.  */
+
 static boolean
 sh_elf_gc_sweep_hook (abfd, info, sec, relocs)
      bfd *abfd ATTRIBUTE_UNUSED;
@@ -2201,6 +3419,9 @@ sh_elf_gc_sweep_hook (abfd, info, sec, r
      const Elf_Internal_Rela *relocs ATTRIBUTE_UNUSED;
 {
   /* we don't use got and plt entries for sh. */
+  /* Now we do.  But it would seem that the existing SH code does no
+     sort of reference counting or whatnot on its GOT and PLT entries,
+     so it is not possible to garbage collect them at this time.  */
   return true;
 }
 
@@ -2219,6 +3440,16 @@ sh_elf_check_relocs (abfd, info, sec, re
   struct elf_link_hash_entry **sym_hashes, **sym_hashes_end;
   const Elf_Internal_Rela *rel;
   const Elf_Internal_Rela *rel_end;
+  bfd *dynobj;
+  bfd_vma *local_got_offsets;
+  asection *sgot;
+  asection *srelgot;
+  asection *sreloc;
+ 
+  sgot = NULL;
+  srelgot = NULL;
+  sreloc = NULL;
+
  
   if (info->relocateable)
     return true;
@@ -2229,6 +3460,9 @@ sh_elf_check_relocs (abfd, info, sec, re
   if (!elf_bad_symtab (abfd))
     sym_hashes_end -= symtab_hdr->sh_info;
  
+  dynobj = elf_hash_table (info)->dynobj;
+  local_got_offsets = elf_local_got_offsets (abfd);
+
   rel_end = relocs + sec->reloc_count;
   for (rel = relocs; rel < rel_end; rel++)
     {
@@ -2241,6 +3475,24 @@ sh_elf_check_relocs (abfd, info, sec, re
       else
         h = sym_hashes[r_symndx - symtab_hdr->sh_info];
  
+      /* Some relocs require a global offset table.  */
+      if (dynobj == NULL)
+	{
+	  switch (ELF32_R_TYPE (rel->r_info))
+	    {
+	    case R_SH_GOT32:
+	    case R_SH_GOTOFF:
+	    case R_SH_GOTPC:
+	      elf_hash_table (info)->dynobj = dynobj = abfd;
+	      if (! _bfd_elf_create_got_section (dynobj, info))
+		return false;
+	      break;
+
+	    default:
+	      break;
+	    }
+	}
+
       switch (ELF32_R_TYPE (rel->r_info))
         {
         /* This relocation describes the C++ object vtable hierarchy.
@@ -2256,6 +3508,211 @@ sh_elf_check_relocs (abfd, info, sec, re
           if (!_bfd_elf32_gc_record_vtentry (abfd, sec, h, rel->r_addend))
             return false;
           break;
+
+	case R_SH_GOT32:
+	  /* This symbol requires a global offset table entry.  */
+
+	  if (sgot == NULL)
+	    {
+	      sgot = bfd_get_section_by_name (dynobj, ".got");
+	      BFD_ASSERT (sgot != NULL);
+	    }
+
+	  if (srelgot == NULL
+	      && (h != NULL || info->shared))
+	    {
+	      srelgot = bfd_get_section_by_name (dynobj, ".rela.got");
+	      if (srelgot == NULL)
+		{
+		  srelgot = bfd_make_section (dynobj, ".rela.got");
+		  if (srelgot == NULL
+		      || ! bfd_set_section_flags (dynobj, srelgot,
+						  (SEC_ALLOC
+						   | SEC_LOAD
+						   | SEC_HAS_CONTENTS
+						   | SEC_IN_MEMORY
+						   | SEC_LINKER_CREATED
+						   | SEC_READONLY))
+		      || ! bfd_set_section_alignment (dynobj, srelgot, 2))
+		    return false;
+		}
+	    }
+
+	  if (h != NULL)
+	    {
+	      if (h->got.offset != (bfd_vma) -1)
+		{
+		  /* We have already allocated space in the .got.  */
+		  break;
+		}
+	      h->got.offset = sgot->_raw_size;
+
+	      /* Make sure this symbol is output as a dynamic symbol.  */
+	      if (h->dynindx == -1)
+		{
+		  if (! bfd_elf32_link_record_dynamic_symbol (info, h))
+		    return false;
+		}
+
+	      srelgot->_raw_size += sizeof (Elf32_External_Rela);
+	    }
+	  else
+	    {
+     	      /* This is a global offset table entry for a local
+		 symbol.  */
+	      if (local_got_offsets == NULL)
+		{
+		  size_t size;
+		  register unsigned int i;
+
+		  size = symtab_hdr->sh_info * sizeof (bfd_vma);
+		  local_got_offsets = (bfd_vma *) bfd_alloc (abfd, size);
+		  if (local_got_offsets == NULL)
+		    return false;
+		  elf_local_got_offsets (abfd) = local_got_offsets;
+		  for (i = 0; i < symtab_hdr->sh_info; i++)
+		    local_got_offsets[i] = (bfd_vma) -1;
+		}
+	      if (local_got_offsets[r_symndx] != (bfd_vma) -1)
+		{
+		  /* We have already allocated space in the .got.  */
+		  break;
+		}
+	      local_got_offsets[r_symndx] = sgot->_raw_size;
+
+	      if (info->shared)
+		{
+		  /* If we are generating a shared object, we need to
+		     output a R_SH_RELATIVE reloc so that the dynamic
+		     linker can adjust this GOT entry.  */
+		  srelgot->_raw_size += sizeof (Elf32_External_Rela);
+		}
+	    }
+
+	  sgot->_raw_size += 4;
+
+	  break;
+
+	case R_SH_PLT32:
+	  /* This symbol requires a procedure linkage table entry.  We
+	     actually build the entry in adjust_dynamic_symbol,
+	     because this might be a case of linking PIC code which is
+	     never referenced by a dynamic object, in which case we
+	     don't need to generate a procedure linkage table entry
+	     after all.  */
+
+	  /* If this is a local symbol, we resolve it directly without
+	     creating a procedure linkage table entry.  */
+	  if (h == NULL)
+	    continue;
+
+	  if (ELF_ST_VISIBILITY (h->other) == STV_INTERNAL
+	      || ELF_ST_VISIBILITY (h->other) == STV_HIDDEN)
+	    break;
+
+	  h->elf_link_hash_flags |= ELF_LINK_HASH_NEEDS_PLT;
+
+	  break;
+
+	case R_SH_DIR32:
+	case R_SH_REL32:
+	  if (h != NULL)
+	    h->elf_link_hash_flags |= ELF_LINK_NON_GOT_REF;
+
+	  /* If we are creating a shared library, and this is a reloc
+	     against a global symbol, or a non PC relative reloc
+	     against a local symbol, then we need to copy the reloc
+	     into the shared library.  However, if we are linking with
+	     -Bsymbolic, we do not need to copy a reloc against a
+	     global symbol which is defined in an object we are
+	     including in the link (i.e., DEF_REGULAR is set).  At
+	     this point we have not seen all the input files, so it is
+	     possible that DEF_REGULAR is not set now but will be set
+	     later (it is never cleared).  We account for that
+	     possibility below by storing information in the
+	     pcrel_relocs_copied field of the hash table entry.  */
+	  if (info->shared
+	      && (sec->flags & SEC_ALLOC) != 0
+	      && (ELF32_R_TYPE (rel->r_info) != R_SH_REL32
+		  || (h != NULL
+		      && (! info->symbolic
+			  || (h->elf_link_hash_flags
+			      & ELF_LINK_HASH_DEF_REGULAR) == 0))))
+	    {
+	      /* When creating a shared object, we must copy these
+		 reloc types into the output file.  We create a reloc
+		 section in dynobj and make room for this reloc.  */
+	      if (sreloc == NULL)
+		{
+		  const char *name;
+
+		  name = (bfd_elf_string_from_elf_section
+			  (abfd,
+			   elf_elfheader (abfd)->e_shstrndx,
+			   elf_section_data (sec)->rel_hdr.sh_name));
+		  if (name == NULL)
+		    return false;
+
+		  BFD_ASSERT (strncmp (name, ".rela", 5) == 0
+			      && strcmp (bfd_get_section_name (abfd, sec),
+					 name + 5) == 0);
+
+		  sreloc = bfd_get_section_by_name (dynobj, name);
+		  if (sreloc == NULL)
+		    {
+		      flagword flags;
+
+		      sreloc = bfd_make_section (dynobj, name);
+		      flags = (SEC_HAS_CONTENTS | SEC_READONLY
+			       | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+		      if ((sec->flags & SEC_ALLOC) != 0)
+			flags |= SEC_ALLOC | SEC_LOAD;
+		      if (sreloc == NULL
+			  || ! bfd_set_section_flags (dynobj, sreloc, flags)
+			  || ! bfd_set_section_alignment (dynobj, sreloc, 2))
+			return false;
+		    }
+		}
+
+	      sreloc->_raw_size += sizeof (Elf32_External_Rela);
+
+	      /* If we are linking with -Bsymbolic, and this is a
+		 global symbol, we count the number of PC relative
+		 relocations we have entered for this symbol, so that
+		 we can discard them again if the symbol is later
+		 defined by a regular object.  Note that this function
+		 is only called if we are using an elf_sh linker
+		 hash table, which means that h is really a pointer to
+		 an elf_sh_link_hash_entry.  */
+	      if (h != NULL && info->symbolic
+		  && ELF32_R_TYPE (rel->r_info) == R_SH_REL32)
+		{
+		  struct elf_sh_link_hash_entry *eh;
+		  struct elf_sh_pcrel_relocs_copied *p;
+
+		  eh = (struct elf_sh_link_hash_entry *) h;
+
+		  for (p = eh->pcrel_relocs_copied; p != NULL; p = p->next)
+		    if (p->section == sreloc)
+		      break;
+
+		  if (p == NULL)
+		    {
+		      p = ((struct elf_sh_pcrel_relocs_copied *)
+			   bfd_alloc (dynobj, sizeof *p));
+		      if (p == NULL)
+			return false;
+		      p->next = eh->pcrel_relocs_copied;
+		      eh->pcrel_relocs_copied = p;
+		      p->section = sreloc;
+		      p->count = 0;
+		    }
+
+		  ++p->count;
+		}
+	    }
+
+	  break;
         }
     }
  
@@ -2365,6 +3822,337 @@ sh_elf_merge_private_data (ibfd, obfd)
   return sh_elf_set_mach_from_flags (obfd);
 }
 
+/* Finish up dynamic symbol handling.  We set the contents of various
+   dynamic sections here.  */
+
+static boolean
+sh_elf_finish_dynamic_symbol (output_bfd, info, h, sym)
+     bfd *output_bfd;
+     struct bfd_link_info *info;
+     struct elf_link_hash_entry *h;
+     Elf_Internal_Sym *sym;
+{
+  bfd *dynobj;
+
+  dynobj = elf_hash_table (info)->dynobj;
+
+  if (h->plt.offset != (bfd_vma) -1)
+    {
+      asection *splt;
+      asection *sgot;
+      asection *srel;
+
+      bfd_vma plt_index;
+      bfd_vma got_offset;
+      Elf_Internal_Rela rel;
+
+      /* This symbol has an entry in the procedure linkage table.  Set
+	 it up.  */
+
+      BFD_ASSERT (h->dynindx != -1);
+
+      splt = bfd_get_section_by_name (dynobj, ".plt");
+      sgot = bfd_get_section_by_name (dynobj, ".got.plt");
+      srel = bfd_get_section_by_name (dynobj, ".rela.plt");
+      BFD_ASSERT (splt != NULL && sgot != NULL && srel != NULL);
+
+      /* Get the index in the procedure linkage table which
+	 corresponds to this symbol.  This is the index of this symbol
+	 in all the symbols for which we are making plt entries.  The
+	 first entry in the procedure linkage table is reserved.  */
+      plt_index = h->plt.offset / elf_sh_sizeof_plt (info) - 1;
+
+      /* Get the offset into the .got table of the entry that
+	 corresponds to this function.  Each .got entry is 4 bytes.
+	 The first three are reserved.  */
+      got_offset = (plt_index + 3) * 4;
+
+      /* Fill in the entry in the procedure linkage table.  */
+      if (! info->shared)
+	{
+	  if (elf_sh_plt_entry == NULL)
+	    {
+	      elf_sh_plt_entry = (bfd_big_endian (output_bfd)?
+				  elf_sh_plt_entry_be : elf_sh_plt_entry_le);
+	    }
+	  memcpy (splt->contents + h->plt.offset, elf_sh_plt_entry,
+		  elf_sh_sizeof_plt (info));
+	  bfd_put_32 (output_bfd,
+		      (sgot->output_section->vma
+		       + sgot->output_offset
+		       + got_offset),
+		      (splt->contents + h->plt.offset
+		       + elf_sh_plt_symbol_offset (info)));
+
+	  bfd_put_32 (output_bfd,
+		      (splt->output_section->vma + splt->output_offset),
+		      (splt->contents + h->plt.offset
+		       + elf_sh_plt_plt0_offset (info)));
+	}
+      else
+	{
+	  if (elf_sh_pic_plt_entry == NULL)
+	    {
+	      elf_sh_pic_plt_entry = (bfd_big_endian (output_bfd)?
+				      elf_sh_pic_plt_entry_be :
+				      elf_sh_pic_plt_entry_le);
+	    }
+	  memcpy (splt->contents + h->plt.offset, elf_sh_pic_plt_entry,
+		  elf_sh_sizeof_plt (info));
+	  bfd_put_32 (output_bfd, got_offset,
+		      (splt->contents + h->plt.offset
+		       + elf_sh_plt_symbol_offset (info)));
+	}
+
+      bfd_put_32 (output_bfd, plt_index * sizeof (Elf32_External_Rela),
+		  (splt->contents + h->plt.offset
+		   + elf_sh_plt_reloc_offset (info)));
+
+      /* Fill in the entry in the global offset table.  */
+      bfd_put_32 (output_bfd,
+		  (splt->output_section->vma
+		   + splt->output_offset
+		   + h->plt.offset
+		   + elf_sh_plt_temp_offset (info)),
+		  sgot->contents + got_offset);
+
+      /* Fill in the entry in the .rela.plt section.  */
+      rel.r_offset = (sgot->output_section->vma
+		      + sgot->output_offset
+		      + got_offset);
+      rel.r_info = ELF32_R_INFO (h->dynindx, R_SH_JMP_SLOT);
+      rel.r_addend = 0;
+      bfd_elf32_swap_reloca_out (output_bfd, &rel,
+				((Elf32_External_Rela *) srel->contents
+				 + plt_index));
+
+      if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
+	{
+	  /* Mark the symbol as undefined, rather than as defined in
+	     the .plt section.  Leave the value alone.  */
+	  sym->st_shndx = SHN_UNDEF;
+	}
+    }
+
+  if (h->got.offset != (bfd_vma) -1)
+    {
+      asection *sgot;
+      asection *srel;
+      Elf_Internal_Rela rel;
+
+      /* This symbol has an entry in the global offset table.  Set it
+	 up.  */
+
+      sgot = bfd_get_section_by_name (dynobj, ".got");
+      srel = bfd_get_section_by_name (dynobj, ".rela.got");
+      BFD_ASSERT (sgot != NULL && srel != NULL);
+
+      rel.r_offset = (sgot->output_section->vma
+		      + sgot->output_offset
+		      + (h->got.offset &~ 1));
+
+      /* If this is a -Bsymbolic link, and the symbol is defined
+	 locally, we just want to emit a RELATIVE reloc.  Likewise if
+	 the symbol was forced to be local because of a version file.
+	 The entry in the global offset table will already have been
+	 initialized in the relocate_section function.  */
+      if (info->shared
+	  && (info->symbolic || h->dynindx == -1)
+	  && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR))
+	{
+	  rel.r_info = ELF32_R_INFO (0, R_SH_RELATIVE);
+	  rel.r_addend = (h->root.u.def.value
+			  + h->root.u.def.section->output_section->vma
+			  + h->root.u.def.section->output_offset);
+	}
+      else
+	{
+	  bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + h->got.offset);
+	  rel.r_info = ELF32_R_INFO (h->dynindx, R_SH_GLOB_DAT);
+	  rel.r_addend = 0;
+	}
+
+      bfd_elf32_swap_reloca_out (output_bfd, &rel,
+				((Elf32_External_Rela *) srel->contents
+				 + srel->reloc_count));
+      ++srel->reloc_count;
+    }
+
+  if ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_COPY) != 0)
+    {
+      asection *s;
+      Elf_Internal_Rela rel;
+
+      /* This symbol needs a copy reloc.  Set it up.  */
+
+      BFD_ASSERT (h->dynindx != -1
+		  && (h->root.type == bfd_link_hash_defined
+		      || h->root.type == bfd_link_hash_defweak));
+
+      s = bfd_get_section_by_name (h->root.u.def.section->owner,
+				   ".rela.bss");
+      BFD_ASSERT (s != NULL);
+
+      rel.r_offset = (h->root.u.def.value
+		      + h->root.u.def.section->output_section->vma
+		      + h->root.u.def.section->output_offset);
+      rel.r_info = ELF32_R_INFO (h->dynindx, R_SH_COPY);
+      rel.r_addend = 0;
+      bfd_elf32_swap_reloca_out (output_bfd, &rel,
+				((Elf32_External_Rela *) s->contents
+				 + s->reloc_count));
+      ++s->reloc_count;
+    }
+
+  /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute.  */
+  if (strcmp (h->root.root.string, "_DYNAMIC") == 0
+      || strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0)
+    sym->st_shndx = SHN_ABS;
+
+  return true;
+}
+
+/* Finish up the dynamic sections.  */
+
+static boolean
+sh_elf_finish_dynamic_sections (output_bfd, info)
+     bfd *output_bfd;
+     struct bfd_link_info *info;
+{
+  bfd *dynobj;
+  asection *sgot;
+  asection *sdyn;
+
+  dynobj = elf_hash_table (info)->dynobj;
+
+  sgot = bfd_get_section_by_name (dynobj, ".got.plt");
+  BFD_ASSERT (sgot != NULL);
+  sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
+
+  if (elf_hash_table (info)->dynamic_sections_created)
+    {
+      asection *splt;
+      Elf32_External_Dyn *dyncon, *dynconend;
+
+      BFD_ASSERT (sdyn != NULL);
+
+      dyncon = (Elf32_External_Dyn *) sdyn->contents;
+      dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->_raw_size);
+      for (; dyncon < dynconend; dyncon++)
+	{
+	  Elf_Internal_Dyn dyn;
+	  const char *name;
+	  asection *s;
+
+	  bfd_elf32_swap_dyn_in (dynobj, dyncon, &dyn);
+
+	  switch (dyn.d_tag)
+	    {
+	    default:
+	      break;
+
+	    case DT_PLTGOT:
+	      name = ".got";
+	      goto get_vma;
+
+	    case DT_JMPREL:
+	      name = ".rela.plt";
+	    get_vma:
+	      s = bfd_get_section_by_name (output_bfd, name);
+	      BFD_ASSERT (s != NULL);
+	      dyn.d_un.d_ptr = s->vma;
+	      bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
+	      break;
+
+	    case DT_PLTRELSZ:
+	      s = bfd_get_section_by_name (output_bfd, ".rela.plt");
+	      BFD_ASSERT (s != NULL);
+	      if (s->_cooked_size != 0)
+		dyn.d_un.d_val = s->_cooked_size;
+	      else
+		dyn.d_un.d_val = s->_raw_size;
+	      bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
+	      break;
+
+	    case DT_RELASZ:
+	      /* My reading of the SVR4 ABI indicates that the
+		 procedure linkage table relocs (DT_JMPREL) should be
+		 included in the overall relocs (DT_RELA).  This is
+		 what Solaris does.  However, UnixWare can not handle
+		 that case.  Therefore, we override the DT_RELASZ entry
+		 here to make it not include the JMPREL relocs.  Since
+		 the linker script arranges for .rela.plt to follow all
+		 other relocation sections, we don't have to worry
+		 about changing the DT_RELA entry.  */
+	      s = bfd_get_section_by_name (output_bfd, ".rela.plt");
+	      if (s != NULL)
+		{
+		  if (s->_cooked_size != 0)
+		    dyn.d_un.d_val -= s->_cooked_size;
+		  else
+		    dyn.d_un.d_val -= s->_raw_size;
+		}
+	      bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
+	      break;
+	    }
+	}
+
+      /* Fill in the first entry in the procedure linkage table.  */
+      splt = bfd_get_section_by_name (dynobj, ".plt");
+      if (splt && splt->_raw_size > 0)
+	{
+	  if (info->shared)
+	    {
+	      if (elf_sh_pic_plt_entry == NULL)
+		{
+		  elf_sh_pic_plt_entry = (bfd_big_endian (output_bfd)?
+					  elf_sh_pic_plt_entry_be :
+					  elf_sh_pic_plt_entry_le);
+		}
+	      memcpy (splt->contents, elf_sh_pic_plt_entry,
+		      elf_sh_sizeof_plt (info));
+	    }
+	  else
+	    {
+	      if (elf_sh_plt0_entry == NULL)
+		{
+		  elf_sh_plt0_entry = (bfd_big_endian (output_bfd)?
+				       elf_sh_plt0_entry_be :
+				       elf_sh_plt0_entry_le);
+		}
+	      memcpy (splt->contents, elf_sh_plt0_entry, PLT_ENTRY_SIZE);
+	      bfd_put_32 (output_bfd,
+			  sgot->output_section->vma + sgot->output_offset + 4,
+			  splt->contents + elf_sh_plt0_gotid_offset (info));
+	      bfd_put_32 (output_bfd,
+			  sgot->output_section->vma + sgot->output_offset + 8,
+			  splt->contents + elf_sh_plt0_linker_offset (info));
+	    }
+
+	  /* UnixWare sets the entsize of .plt to 4, although that doesn't
+	     really seem like the right value.  */
+	  elf_section_data (splt->output_section)->this_hdr.sh_entsize = 4;
+	}
+    }
+
+  /* Fill in the first three entries in the global offset table.  */
+  if (sgot->_raw_size > 0)
+    {
+      if (sdyn == NULL)
+	bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents);
+      else
+	bfd_put_32 (output_bfd,
+		    sdyn->output_section->vma + sdyn->output_offset,
+		    sgot->contents);
+      bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + 4);
+      bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + 8);
+    }
+
+  elf_section_data (sgot->output_section)->this_hdr.sh_entsize = 4;
+
+  return true;
+}
+
 #define TARGET_BIG_SYM		bfd_elf32_sh_vec
 #define TARGET_BIG_NAME		"elf32-sh"
 #define TARGET_LITTLE_SYM	bfd_elf32_shl_vec
@@ -2394,4 +4182,22 @@ sh_elf_merge_private_data (ibfd, obfd)
 #define elf_backend_check_relocs        sh_elf_check_relocs
 
 #define elf_backend_can_gc_sections 1
+#define elf_backend_create_dynamic_sections \
+					sh_elf_create_dynamic_sections
+#define bfd_elf32_bfd_link_hash_table_create \
+					sh_elf_link_hash_table_create
+#define elf_backend_adjust_dynamic_symbol \
+					sh_elf_adjust_dynamic_symbol
+#define elf_backend_size_dynamic_sections \
+					sh_elf_size_dynamic_sections
+#define elf_backend_finish_dynamic_symbol \
+					sh_elf_finish_dynamic_symbol
+#define elf_backend_finish_dynamic_sections \
+					sh_elf_finish_dynamic_sections
+
+#define elf_backend_want_got_plt	1
+#define elf_backend_plt_readonly	1
+#define elf_backend_want_plt_sym	0
+#define elf_backend_got_header_size	12
+#define elf_backend_plt_header_size	PLT_ENTRY_SIZE
 #include "elf32-target.h"
Index: bfd/reloc.c
===================================================================
RCS file: /cvs/src/src/bfd/reloc.c,v
retrieving revision 1.28
diff -u -p -r1.28 reloc.c
--- bfd/reloc.c	2000/08/01 01:45:29	1.28
+++ bfd/reloc.c	2000/08/31 14:19:52
@@ -2053,6 +2053,17 @@ ENUMX
 ENUMX
   BFD_RELOC_MIPS_GOT_DISP
 COMMENT
+ENUMX
+  BFD_RELOC_SH_COPY
+ENUMX
+  BFD_RELOC_SH_GLOB_DAT
+ENUMX
+  BFD_RELOC_SH_JMP_SLOT
+ENUMX
+  BFD_RELOC_SH_RELATIVE
+ENUMX
+  BFD_RELOC_SH_GOTPC
+COMMENT
 ENUMDOC
   MIPS ELF relocations.
 
Index: bfd/libbfd.h
===================================================================
RCS file: /cvs/src/src/bfd/libbfd.h,v
retrieving revision 1.24
diff -u -p -r1.24 libbfd.h
--- bfd/libbfd.h	2000/08/01 01:45:29	1.24
+++ bfd/libbfd.h	2000/08/31 14:19:56
@@ -813,6 +813,11 @@ static const char *const bfd_reloc_code_
   "BFD_RELOC_SH_LABEL",
   "BFD_RELOC_SH_LOOP_START",
   "BFD_RELOC_SH_LOOP_END",
+  "BFD_RELOC_SH_COPY",
+  "BFD_RELOC_SH_GLOB_DAT",
+  "BFD_RELOC_SH_JMP_SLOT",
+  "BFD_RELOC_SH_RELATIVE",
+  "BFD_RELOC_SH_GOTPC",
   "BFD_RELOC_THUMB_PCREL_BRANCH9",
   "BFD_RELOC_THUMB_PCREL_BRANCH12",
   "BFD_RELOC_THUMB_PCREL_BRANCH23",
Index: bfd/bfd-in2.h
===================================================================
RCS file: /cvs/src/src/bfd/bfd-in2.h,v
retrieving revision 1.61
diff -u -p -r1.61 bfd-in2.h
--- bfd/bfd-in2.h	2000/08/22 19:33:16	1.61
+++ bfd/bfd-in2.h	2000/08/31 14:20:07
@@ -2162,6 +2162,11 @@ field in the instruction. */
   BFD_RELOC_SH_LABEL,
   BFD_RELOC_SH_LOOP_START,
   BFD_RELOC_SH_LOOP_END,
+  BFD_RELOC_SH_COPY,
+  BFD_RELOC_SH_GLOB_DAT,
+  BFD_RELOC_SH_JMP_SLOT,
+  BFD_RELOC_SH_RELATIVE,
+  BFD_RELOC_SH_GOTPC,
 
 /* Thumb 23-, 12- and 9-bit pc-relative branches.  The lowest bit must
 be zero and is not stored in the instruction. */
Index: gas/ChangeLog
from  Alexandre Oliva  <aoliva@redhat.com>

	* config/tc-sh.h [OBJ_ELF] (TC_FIX_ADJUSTABLE): Define.
	* config/tc-sh.c (md_apply_fix): Map 32-bit relocations that
	become PC-relative to BFD_RELOC_32_PCREL.  Reject 16- or 8-bit
	similar relocs.
	(sh_obj_adjustable): Return 1 for PC-relative offsets used in
	branches.

Index: gas/ChangeLog
from  Niibe Yutaka  <gniibe@m17n.org>, Kaz Kojima  <kkojima@rr.iij4u.or.jp>, Alexandre Oliva  <aoliva@redhat.com>

	* config/tc-sh.h (DIFF_EXPR_OK, GLOBAL_OFFSET_TABLE_NAME,
	TC_RELOC_GLOBAL_OFFSET_TABLE, TC_RELOC_RTSYM_LOC_FIXUP): Define.
	* config/tc-sh.c (sh_elf_cons, sh_elf_suffix): New functions.
	[OBJ_ELF] (md_pseudo_table) <long, int, word, short>: Use them.
	(GOT_symbol): New variable.
	(md_undefined_symbol): Set it.

Index: gas/config/tc-sh.c
===================================================================
RCS file: /cvs/src/src/gas/config/tc-sh.c,v
retrieving revision 1.21
diff -u -p -r1.21 tc-sh.c
--- gas/config/tc-sh.c	2000/08/15 20:47:19	1.21
+++ gas/config/tc-sh.c	2000/08/31 14:17:47
@@ -51,6 +51,12 @@ static void s_uacons PARAMS ((int));
 static sh_opcode_info *find_cooked_opcode PARAMS ((char **));
 static unsigned int assemble_ppi PARAMS ((char *, sh_opcode_info *));
 
+#ifdef OBJ_ELF
+static void sh_elf_cons PARAMS ((int));
+
+symbolS *GOT_symbol;		/* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
+#endif
+
 int shl = 0;
 
 static void
@@ -69,8 +75,15 @@ little (ignore)
 
 const pseudo_typeS md_pseudo_table[] =
 {
+#ifdef OBJ_ELF
+  {"long", sh_elf_cons, 4},
+  {"int", sh_elf_cons, 4},
+  {"word", sh_elf_cons, 2},
+  {"short", sh_elf_cons, 2},
+#else
   {"int", cons, 4},
   {"word", cons, 2},
+#endif /* OBJ_ELF */
   {"form", listing_psize, 0},
   {"little", little, 0},
   {"heading", listing_title, 0},
@@ -200,6 +213,191 @@ const relax_typeS md_relax_table[C (END,
 
 static struct hash_control *opcode_hash_control;	/* Opcode mnemonics */
 
+
+#ifdef OBJ_ELF
+/* Parse @got, etc. and return the desired relocation. 
+   If we have additional arithmetic expression, then we fill in new_exp_p.  */
+static bfd_reloc_code_real_type
+sh_elf_suffix (str_p, exp_p, new_exp_p)
+     char **str_p;
+     expressionS *exp_p, *new_exp_p;
+{
+  struct map_bfd {
+    char *string;
+    int length;
+    bfd_reloc_code_real_type reloc;
+  };
+
+  char ident[20];
+  char *str = *str_p;
+  char *str2;
+  int ch;
+  int len;
+  struct map_bfd *ptr;
+
+#define MAP(str,reloc) { str, sizeof(str)-1, reloc }
+
+  static struct map_bfd mapping[] = {
+    MAP ("got",		BFD_RELOC_32_GOT_PCREL),
+    MAP ("plt",		BFD_RELOC_32_PLT_PCREL),
+    MAP ("gotoff",	BFD_RELOC_32_GOTOFF),
+    { (char *)0,	0,	BFD_RELOC_UNUSED }
+  };
+
+  if (*str++ != '@')
+    return BFD_RELOC_UNUSED;
+
+  for (ch = *str, str2 = ident;
+       (str2 < ident + sizeof (ident) - 1
+	&& (isalnum (ch) || ch == '@'));
+       ch = *++str)
+    {
+      *str2++ = (islower (ch)) ? ch : tolower (ch);
+    }
+
+  *str2 = '\0';
+  len = str2 - ident;
+
+  ch = ident[0];
+  for (ptr = &mapping[0]; ptr->length > 0; ptr++)
+    if (ch == ptr->string[0]
+	&& len == ptr->length
+	&& memcmp (ident, ptr->string, ptr->length) == 0)
+      {
+	/* Now check for identifier@suffix+constant */
+	if (*str == '-' || *str == '+')
+	  {
+	    char *orig_line = input_line_pointer;
+
+	    input_line_pointer = str;
+	    expression (new_exp_p);
+	    if (new_exp_p->X_op == O_constant)
+	      {
+		exp_p->X_add_number += new_exp_p->X_add_number;
+		str = input_line_pointer;
+	      }
+	    if (new_exp_p->X_op == O_subtract)
+	      str = input_line_pointer;
+
+	    if (&input_line_pointer != str_p)
+	      input_line_pointer = orig_line;
+	  }
+
+	*str_p = str;
+	return ptr->reloc;
+      }
+
+  return BFD_RELOC_UNUSED;
+}
+
+/* The regular cons() function, that reads constants, doesn't support
+   suffixes such as @GOT, @GOTOFF and @PLT, that generate
+   machine-specific relocation types.  So we must define it here.  */
+/* Clobbers input_line_pointer, checks end-of-line.  */
+static void
+sh_elf_cons (nbytes)
+     register int nbytes;	/* 1=.byte, 2=.word, 4=.long */
+{
+  expressionS exp, new_exp;
+  bfd_reloc_code_real_type reloc;
+  const char *name;
+
+  if (is_it_end_of_statement ())
+    {
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  do
+    {
+      expression (&exp);
+      new_exp.X_op = O_absent;
+      new_exp.X_add_symbol = new_exp.X_op_symbol = NULL;
+      /* If the _GLOBAL_OFFSET_TABLE_ symbol hasn't been found yet,
+	 use the name of the symbol to tell whether it's the
+	 _GLOBAL_OFFSET_TABLE_.  If it has, comparing the symbols is
+	 sufficient.  */
+      if (! GOT_symbol && exp.X_add_symbol)
+	name = S_GET_NAME (exp.X_add_symbol);
+      else
+	name = NULL;
+      /* Check whether this expression involves the
+	 _GLOBAL_OFFSET_TABLE_ symbol, by itself or added to a
+	 difference of two other symbols.  */
+      if (((GOT_symbol && GOT_symbol == exp.X_add_symbol)
+	   || (! GOT_symbol && name
+	       && strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0))
+	  && (exp.X_op == O_symbol
+	      || (exp.X_op == O_add
+		  && ((symbol_get_value_expression (exp.X_op_symbol)->X_op)
+		      == O_subtract))))
+	{
+	  reloc_howto_type *reloc_howto = bfd_reloc_type_lookup (stdoutput,
+								 BFD_RELOC_32);
+	  int size = bfd_get_reloc_size (reloc_howto);
+
+	  if (GOT_symbol == NULL)
+	    GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
+
+	  if (size > nbytes)
+	    as_bad (_("%s relocations do not fit in %d bytes\n"),
+		    reloc_howto->name, nbytes);
+	  else
+	    {
+	      register char *p = frag_more ((int) nbytes);
+	      int offset = nbytes - size;
+
+	      fix_new_exp (frag_now, p - frag_now->fr_literal + offset,
+			   size, &exp, 0, TC_RELOC_GLOBAL_OFFSET_TABLE);
+	    }
+	}
+      /* Check if this symbol involves one of the magic suffixes, such
+	 as @GOT, @GOTOFF or @PLT, and determine which relocation type
+	 to use.  */
+      else if ((exp.X_op == O_symbol || (exp.X_op == O_add && exp.X_op_symbol))
+	  && *input_line_pointer == '@'
+	  && ((reloc = sh_elf_suffix (&input_line_pointer, &exp, &new_exp))
+	      != BFD_RELOC_UNUSED))
+	{
+	  reloc_howto_type *reloc_howto = bfd_reloc_type_lookup (stdoutput,
+								 reloc);
+	  int size = bfd_get_reloc_size (reloc_howto);
+
+	  /* Force a GOT to be generated.  */
+	  if (GOT_symbol == NULL)
+	    GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
+
+	  if (size > nbytes)
+	    as_bad (_("%s relocations do not fit in %d bytes\n"),
+		    reloc_howto->name, nbytes);
+	  else
+	    {
+	      register char *p = frag_more ((int) nbytes);
+	      int offset = nbytes - size;
+
+	      fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size,
+			   &exp, 0, reloc);
+	      if (new_exp.X_op != O_absent) 
+		fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size,
+			     &new_exp, 0, BFD_RELOC_32);
+	    }
+	}
+      else
+	emit_expr (&exp, (unsigned int) nbytes);
+    }
+  while (*input_line_pointer++ == ',');
+
+  input_line_pointer--;		/* Put terminator back into stream. */
+  if (*input_line_pointer == '#' || *input_line_pointer == '!')
+    {
+       while (! is_end_of_line[*input_line_pointer++]);
+    }
+  else
+    demand_empty_rest_of_line ();
+}
+#endif /* OBJ_ELF */
+
+
 /* This function is called once, at assembler startup time.  This should
    set up all the tables, etc that the MD part of the assembler needs.  */
 
@@ -1795,6 +1993,24 @@ symbolS *
 md_undefined_symbol (name)
      char *name;
 {
+#ifdef OBJ_ELF
+  /* Under ELF we need to default _GLOBAL_OFFSET_TABLE.  Otherwise we
+     have no need to default values of symbols.  */
+  if (strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0)
+    {
+      if (!GOT_symbol)
+	{
+	  if (symbol_find (name))
+	    as_bad ("GOT already in the symbol table");
+	  
+	  GOT_symbol = symbol_new (name, undefined_section,
+				   (valueT)0, & zero_address_frag);
+	}
+      
+      return GOT_symbol;
+    }
+#endif /* OBJ_ELF */
+  
   return 0;
 }
 
@@ -2544,6 +2760,20 @@ sh_fix_adjustable (fixP)
   if (fixP->fx_addsy == NULL)
     return 1;
 
+  if (fixP->fx_r_type == BFD_RELOC_SH_PCDISP8BY2
+      || fixP->fx_r_type == BFD_RELOC_SH_PCDISP12BY2
+      || fixP->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY2
+      || fixP->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY4
+      || fixP->fx_r_type == BFD_RELOC_8_PCREL
+      || fixP->fx_r_type == BFD_RELOC_SH_SWITCH16
+      || fixP->fx_r_type == BFD_RELOC_SH_SWITCH32)
+    return 1;
+
+  if (! TC_RELOC_RTSYM_LOC_FIXUP (fixP)
+      || fixP->fx_r_type == BFD_RELOC_32_GOTOFF
+      || fixP->fx_r_type == BFD_RELOC_RVA)
+    return 0;
+
   /* We need the symbol name for the VTABLE entries */
   if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
       || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
@@ -2605,6 +2835,35 @@ md_apply_fix (fixP, val)
   int shift;
 
 #ifdef BFD_ASSEMBLER
+  /* A difference between two symbols, the second of which is in the
+     current section, is transformed in a PC-relative relocation to
+     the other symbol.  We have to adjust the relocation type here.  */
+  if (fixP->fx_pcrel)
+    {
+      switch (fixP->fx_r_type)
+	{
+	default:
+	  break;
+
+	case BFD_RELOC_32:
+	  fixP->fx_r_type = BFD_RELOC_32_PCREL;
+	  break;
+
+	  /* Currently, we only support 32-bit PCREL relocations.
+	     We'd need a new reloc type to handle 16_PCREL, and
+	     8_PCREL is already taken for R_SH_SWITCH8, which
+	     apparently does something completely different than what
+	     we need.  FIXME.  */
+	case BFD_RELOC_16:
+	  bfd_set_error (bfd_error_bad_value);
+	  return false;
+	  
+	case BFD_RELOC_8:
+	  bfd_set_error (bfd_error_bad_value);
+	  return false;
+	}
+    }
+
   /* The function adjust_reloc_syms won't convert a reloc against a weak
      symbol into a reloc against a section, but bfd_install_relocation
      will screw up if the symbol is defined, so we have to adjust val here
@@ -2715,6 +2974,7 @@ md_apply_fix (fixP, val)
       break;
 
     case BFD_RELOC_32:
+    case BFD_RELOC_32_PCREL:
       md_number_to_chars (buf, val, 4);
       break;
 
@@ -2747,6 +3007,43 @@ md_apply_fix (fixP, val)
       return;
 #endif
 
+#ifdef OBJ_ELF
+    case BFD_RELOC_32_PLT_PCREL:
+      /* Make the jump instruction point to the address of the operand.  At
+	 runtime we merely add the offset to the actual PLT entry. */
+      *valp = 0xfffffffc;
+      break;
+
+    case BFD_RELOC_SH_GOTPC:
+      /* This is tough to explain.  We end up with this one if we have
+         operands that look like "_GLOBAL_OFFSET_TABLE_+[.-.L284]".
+         The goal here is to obtain the absolute address of the GOT,
+         and it is strongly preferable from a performance point of
+         view to avoid using a runtime relocation for this.  There are
+         cases where you have something like:
+        
+         .long	_GLOBAL_OFFSET_TABLE_+[.-.L66]
+        
+         and here no correction would be required.  Internally in the
+         assembler we treat operands of this form as not being pcrel
+         since the '.' is explicitly mentioned, and I wonder whether
+         it would simplify matters to do it this way.  Who knows.  In
+         earlier versions of the PIC patches, the pcrel_adjust field
+         was used to store the correction, but since the expression is
+         not pcrel, I felt it would be confusing to do it this way.  */
+      *valp -= 1;
+      md_number_to_chars (buf, val, 4);
+      break;
+
+    case BFD_RELOC_32_GOT_PCREL:
+      *valp = 0; /* Fully resolved at runtime.  No addend.  */
+      md_number_to_chars (buf, 0, 4);
+      break;
+
+    case BFD_RELOC_32_GOTOFF:
+      break;
+#endif
+
     default:
       abort ();
     }
@@ -3097,6 +3394,8 @@ tc_gen_reloc (section, fixp)
       rel->address = rel->addend = fixp->fx_offset;
     }
   else if (fixp->fx_pcrel)
+    rel->addend = fixp->fx_addnumber;
+  else if (r_type == BFD_RELOC_32 || r_type == BFD_RELOC_32_GOTOFF)
     rel->addend = fixp->fx_addnumber;
   else
     rel->addend = 0;
Index: gas/config/tc-sh.h
===================================================================
RCS file: /cvs/src/src/gas/config/tc-sh.h,v
retrieving revision 1.6
diff -u -p -r1.6 tc-sh.h
--- gas/config/tc-sh.h	2000/08/15 20:47:19	1.6
+++ gas/config/tc-sh.h	2000/08/31 14:17:48
@@ -61,6 +61,10 @@ extern int sh_force_relocation ();
 #define obj_fix_adjustable(fixP) sh_fix_adjustable(fixP)
 struct fix;
 extern boolean sh_fix_adjustable PARAMS ((struct fix *));
+
+/* This arranges for gas/write.c to not apply a relocation if
+   obj_fix_adjustable() says it is not adjustable.  */
+#define TC_FIX_ADJUSTABLE(fixP) obj_fix_adjustable (fixP)
 #endif
 
 #define IGNORE_NONSTANDARD_ESCAPES
@@ -160,6 +164,42 @@ extern int target_big_endian;
 
 #define elf_tc_final_processing sh_elf_final_processing
 extern void sh_elf_final_processing PARAMS ((void));
+
+#define DIFF_EXPR_OK		/* foo-. gets turned into PC relative relocs */
+
+#define GLOBAL_OFFSET_TABLE_NAME "_GLOBAL_OFFSET_TABLE_"
+ 
+/* This is the relocation type for direct references to
+   GLOBAL_OFFSET_TABLE.  It comes up in complicated expressions such
+   as _GLOBAL_OFFSET_TABLE_+[.-.L284], which cannot be expressed
+   normally with the regular expressions.  The fixup specified here
+   when used at runtime implies that we should add the address of the
+   GOT to the specified location, and as a result we have simplified
+   the expression into something we can use.  */
+#define TC_RELOC_GLOBAL_OFFSET_TABLE BFD_RELOC_SH_GOTPC
+
+/* This expression evaluates to false if the relocation is for a local object
+   for which we still want to do the relocation at runtime.  True if we
+   are willing to perform this relocation while building the .o file.
+   This is only used for pcrel relocations, so GOTOFF does not need to be
+   checked here.  I am not sure if some of the others are ever used with
+   pcrel, but it is easier to be safe than sorry.
+
+   We can't resolve references to the GOT or the PLT when creating the
+   object file, since these tables are only created by the linker.
+   Also, if the symbol is global, weak, common or not defined, the
+   assembler can't compute the appropriate reloc, since its location
+   can only be determined at link time.  */
+
+#define TC_RELOC_RTSYM_LOC_FIXUP(FIX)				\
+  ((FIX)->fx_r_type != BFD_RELOC_32_PLT_PCREL			\
+   && (FIX)->fx_r_type != BFD_RELOC_32_GOT_PCREL		\
+   && (FIX)->fx_r_type != BFD_RELOC_SH_GOTPC			\
+   && ((FIX)->fx_addsy == NULL					\
+       || (! S_IS_EXTERNAL ((FIX)->fx_addsy)			\
+	   && ! S_IS_WEAK ((FIX)->fx_addsy)			\
+	   && S_IS_DEFINED ((FIX)->fx_addsy)			\
+	   && ! S_IS_COMMON ((FIX)->fx_addsy))))
 
 #endif /* OBJ_ELF */
 

-- 
Alexandre Oliva   Enjoy Guarana', see http://www.ic.unicamp.br/~oliva/
Red Hat GCC Developer                  aoliva@{cygnus.com, redhat.com}
CS PhD student at IC-Unicamp        oliva@{lsd.ic.unicamp.br, gnu.org}
Free Software Evangelist    *Please* write to mailing lists, not to me

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