* elf32-sh.c (R_SH_GOT32, R_SH_PLT32, R_SH_COPY, R_SH_GLOB_DAT,
authorAlexandre Oliva <aoliva@redhat.com>
Sat, 2 Sep 2000 02:33:26 +0000 (02:33 +0000)
committerAlexandre Oliva <aoliva@redhat.com>
Sat, 2 Sep 2000 02:33:26 +0000 (02:33 +0000)
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.

bfd/ChangeLog
bfd/bfd-in2.h
bfd/elf32-sh.c
bfd/libbfd.h
bfd/reloc.c

index bbaf98797a8d9d9b7bcff612c4f05d17d22574d5..9255101f86cdde9e68b2fe61fadbf9bd400cf313 100644 (file)
@@ -1,3 +1,43 @@
+2000-09-01  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.
+
 2000-09-01  Alan Modra  <alan@linuxcare.com.au>
 
        * elf32-i386.c (elf_i386_finish_dynamic_symbol): Revert 2000-08-27
index d755d754e01bda9644aaad35d0882d291329005a..aac942f520dfb2dc6a7ef4b249ae14df79d40da6 100644 (file)
@@ -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 1f756ee1dd80e721a0d1c739e737317fe8662206..acbf66147b4d5c1b52e4cd3e71c99d807b254a59 100644 (file)
@@ -55,6 +55,27 @@ 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[] =
 {
@@ -215,14 +236,118 @@ static reloc_howto_type sh_elf_howto_table[] =
         0xff,                  /* 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),
+  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 (18),
   EMPTY_HOWTO (19),
   EMPTY_HOWTO (20),
@@ -687,6 +812,14 @@ static const struct elf_reloc_map sh_reloc_map[] =
   { 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,181 +1952,1264 @@ sh_elf_swap_insns (abfd, sec, relocs, contents, addr)
   return true;
 }
 \f
-/* 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
-                 {
+/* 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;
+}
+\f
+
+/* 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;
+}
+
+\f
+/* 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)
@@ -2193,6 +3409,8 @@ sh_elf_gc_mark_hook (abfd, info, rel, h, sym)
   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;
@@ -2200,7 +3418,10 @@ sh_elf_gc_sweep_hook (abfd, info, sec, relocs)
      asection *sec ATTRIBUTE_UNUSED;
      const Elf_Internal_Rela *relocs ATTRIBUTE_UNUSED;
 {
-  /* we don't use got and plt entries for sh. */
+  /* We use got and plt entries for sh, 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, relocs)
   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, relocs)
   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, relocs)
       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, relocs)
           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;
         }
     }
  
@@ -2366,6 +3823,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,5 +4182,23 @@ sh_elf_merge_private_data (ibfd, obfd)
 #define elf_backend_gc_sweep_hook       sh_elf_gc_sweep_hook
 #define elf_backend_check_relocs        sh_elf_check_relocs
 
-#define elf_backend_can_gc_sections 1
+#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 ba25d1c69549c362316bbdb416c4ed9768b446bb..b09c0ca4aa9ed149d9f48eae1358cf1e8bdec409 100644 (file)
@@ -813,6 +813,11 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "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 b3df3b896044e73949a7e5fc413e054b307e48b5..06da4d2b010b48cc3805ed56d3476279e77563a2 100644 (file)
@@ -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.