+static const bfd_byte elf_sh_plt_entry_be[ELF_PLT_ENTRY_SIZE] =
+{
+ 0xd0, 0x04, /* mov.l 1f,r0 */
+ 0x60, 0x02, /* mov.l @(r0,r12),r0 */
+ 0xd1, 0x02, /* mov.l 0f,r1 */
+ 0x40, 0x2b, /* jmp @r0 */
+ 0x60, 0x13, /* mov r1,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. */
+};
+
+static const bfd_byte elf_sh_plt_entry_le[ELF_PLT_ENTRY_SIZE] =
+{
+ 0x04, 0xd0, /* mov.l 1f,r0 */
+ 0x02, 0x60, /* mov.l @r0,r0 */
+ 0x02, 0xd1, /* mov.l 0f,r1 */
+ 0x2b, 0x40, /* jmp @r0 */
+ 0x13, 0x60, /* mov r1,r0 */
+ 0x03, 0xd1, /* mov.l 2f,r1 */
+ 0x2b, 0x40, /* jmp @r0 */
+ 0x09, 0x00, /* 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. */
+};
+
+/* Entries in a PIC procedure linkage table look like this. */
+
+static const bfd_byte elf_sh_pic_plt_entry_be[ELF_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, /* mov.l @(8,r12),r0 */
+ 0xd1, 0x03, /* mov.l 2f,r1 */
+ 0x40, 0x2b, /* jmp @r0 */
+ 0x50, 0xc1, /* mov.l @(4,r12),r0 */
+ 0x00, 0x09, /* nop */
+ 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. */
+};
+
+static const bfd_byte elf_sh_pic_plt_entry_le[ELF_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, /* mov.l @(8,r12),r0 */
+ 0x03, 0xd1, /* mov.l 2f,r1 */
+ 0x2b, 0x40, /* jmp @r0 */
+ 0xc1, 0x50, /* mov.l @(4,r12),r0 */
+ 0x09, 0x00, /* nop */
+ 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. */
+};
+
+static const struct elf_sh_plt_info elf_sh_plts[2][2] = {
+ {
+ {
+ /* Big-endian non-PIC. */
+ elf_sh_plt0_entry_be,
+ ELF_PLT_ENTRY_SIZE,
+ { MINUS_ONE, 24, 20 },
+ elf_sh_plt_entry_be,
+ ELF_PLT_ENTRY_SIZE,
+ { 20, 16, 24, FALSE },
+ 8,
+ NULL
+ },
+ {
+ /* Little-endian non-PIC. */
+ elf_sh_plt0_entry_le,
+ ELF_PLT_ENTRY_SIZE,
+ { MINUS_ONE, 24, 20 },
+ elf_sh_plt_entry_le,
+ ELF_PLT_ENTRY_SIZE,
+ { 20, 16, 24, FALSE },
+ 8,
+ NULL
+ },
+ },
+ {
+ {
+ /* Big-endian PIC. */
+ elf_sh_plt0_entry_be,
+ ELF_PLT_ENTRY_SIZE,
+ { MINUS_ONE, MINUS_ONE, MINUS_ONE },
+ elf_sh_pic_plt_entry_be,
+ ELF_PLT_ENTRY_SIZE,
+ { 20, MINUS_ONE, 24, FALSE },
+ 8,
+ NULL
+ },
+ {
+ /* Little-endian PIC. */
+ elf_sh_plt0_entry_le,
+ ELF_PLT_ENTRY_SIZE,
+ { MINUS_ONE, MINUS_ONE, MINUS_ONE },
+ elf_sh_pic_plt_entry_le,
+ ELF_PLT_ENTRY_SIZE,
+ { 20, MINUS_ONE, 24, FALSE },
+ 8,
+ NULL
+ },
+ }
+};
+
+#define VXWORKS_PLT_HEADER_SIZE 12
+#define VXWORKS_PLT_ENTRY_SIZE 24
+
+static const bfd_byte vxworks_sh_plt0_entry_be[VXWORKS_PLT_HEADER_SIZE] =
+{
+ 0xd1, 0x01, /* mov.l @(8,pc),r1 */
+ 0x61, 0x12, /* mov.l @r1,r1 */
+ 0x41, 0x2b, /* jmp @r1 */
+ 0x00, 0x09, /* nop */
+ 0, 0, 0, 0 /* 0: replaced with _GLOBAL_OFFSET_TABLE+8. */
+};
+
+static const bfd_byte vxworks_sh_plt0_entry_le[VXWORKS_PLT_HEADER_SIZE] =
+{
+ 0x01, 0xd1, /* mov.l @(8,pc),r1 */
+ 0x12, 0x61, /* mov.l @r1,r1 */
+ 0x2b, 0x41, /* jmp @r1 */
+ 0x09, 0x00, /* nop */
+ 0, 0, 0, 0 /* 0: replaced with _GLOBAL_OFFSET_TABLE+8. */
+};
+
+static const bfd_byte vxworks_sh_plt_entry_be[VXWORKS_PLT_ENTRY_SIZE] =
+{
+ 0xd0, 0x01, /* mov.l @(8,pc),r0 */
+ 0x60, 0x02, /* mov.l @r0,r0 */
+ 0x40, 0x2b, /* jmp @r0 */
+ 0x00, 0x09, /* nop */
+ 0, 0, 0, 0, /* 0: replaced with address of this symbol in .got. */
+ 0xd0, 0x01, /* mov.l @(8,pc),r0 */
+ 0xa0, 0x00, /* bra PLT (We need to fix the offset.) */
+ 0x00, 0x09, /* nop */
+ 0x00, 0x09, /* nop */
+ 0, 0, 0, 0, /* 1: replaced with offset into relocation table. */
+};
+
+static const bfd_byte vxworks_sh_plt_entry_le[VXWORKS_PLT_ENTRY_SIZE] =
+{
+ 0x01, 0xd0, /* mov.l @(8,pc),r0 */
+ 0x02, 0x60, /* mov.l @r0,r0 */
+ 0x2b, 0x40, /* jmp @r0 */
+ 0x09, 0x00, /* nop */
+ 0, 0, 0, 0, /* 0: replaced with address of this symbol in .got. */
+ 0x01, 0xd0, /* mov.l @(8,pc),r0 */
+ 0x00, 0xa0, /* bra PLT (We need to fix the offset.) */
+ 0x09, 0x00, /* nop */
+ 0x09, 0x00, /* nop */
+ 0, 0, 0, 0, /* 1: replaced with offset into relocation table. */
+};
+
+static const bfd_byte vxworks_sh_pic_plt_entry_be[VXWORKS_PLT_ENTRY_SIZE] =
+{
+ 0xd0, 0x01, /* mov.l @(8,pc),r0 */
+ 0x00, 0xce, /* mov.l @(r0,r12),r0 */
+ 0x40, 0x2b, /* jmp @r0 */
+ 0x00, 0x09, /* nop */
+ 0, 0, 0, 0, /* 0: replaced with offset of this symbol in .got. */
+ 0xd0, 0x01, /* mov.l @(8,pc),r0 */
+ 0x51, 0xc2, /* mov.l @(8,r12),r1 */
+ 0x41, 0x2b, /* jmp @r1 */
+ 0x00, 0x09, /* nop */
+ 0, 0, 0, 0, /* 1: replaced with offset into relocation table. */
+};
+
+static const bfd_byte vxworks_sh_pic_plt_entry_le[VXWORKS_PLT_ENTRY_SIZE] =
+{
+ 0x01, 0xd0, /* mov.l @(8,pc),r0 */
+ 0xce, 0x00, /* mov.l @(r0,r12),r0 */
+ 0x2b, 0x40, /* jmp @r0 */
+ 0x09, 0x00, /* nop */
+ 0, 0, 0, 0, /* 0: replaced with offset of this symbol in .got. */
+ 0x01, 0xd0, /* mov.l @(8,pc),r0 */
+ 0xc2, 0x51, /* mov.l @(8,r12),r1 */
+ 0x2b, 0x41, /* jmp @r1 */
+ 0x09, 0x00, /* nop */
+ 0, 0, 0, 0, /* 1: replaced with offset into relocation table. */
+};
+
+static const struct elf_sh_plt_info vxworks_sh_plts[2][2] = {
+ {
+ {
+ /* Big-endian non-PIC. */
+ vxworks_sh_plt0_entry_be,
+ VXWORKS_PLT_HEADER_SIZE,
+ { MINUS_ONE, MINUS_ONE, 8 },
+ vxworks_sh_plt_entry_be,
+ VXWORKS_PLT_ENTRY_SIZE,
+ { 8, 14, 20, FALSE },
+ 12,
+ NULL
+ },
+ {
+ /* Little-endian non-PIC. */
+ vxworks_sh_plt0_entry_le,
+ VXWORKS_PLT_HEADER_SIZE,
+ { MINUS_ONE, MINUS_ONE, 8 },
+ vxworks_sh_plt_entry_le,
+ VXWORKS_PLT_ENTRY_SIZE,
+ { 8, 14, 20, FALSE },
+ 12,
+ NULL
+ },
+ },
+ {
+ {
+ /* Big-endian PIC. */
+ NULL,
+ 0,
+ { MINUS_ONE, MINUS_ONE, MINUS_ONE },
+ vxworks_sh_pic_plt_entry_be,
+ VXWORKS_PLT_ENTRY_SIZE,
+ { 8, MINUS_ONE, 20, FALSE },
+ 12,
+ NULL
+ },
+ {
+ /* Little-endian PIC. */
+ NULL,
+ 0,
+ { MINUS_ONE, MINUS_ONE, MINUS_ONE },
+ vxworks_sh_pic_plt_entry_le,
+ VXWORKS_PLT_ENTRY_SIZE,
+ { 8, MINUS_ONE, 20, FALSE },
+ 12,
+ NULL
+ },
+ }
+};
+
+/* FDPIC PLT entries. Two unimplemented optimizations for lazy
+ binding are to omit the lazy binding stub when linking with -z now
+ and to move lazy binding stubs into a separate region for better
+ cache behavior. */
+
+#define FDPIC_PLT_ENTRY_SIZE 28
+#define FDPIC_PLT_LAZY_OFFSET 20
+
+/* FIXME: The lazy binding stub requires a plt0 - which may need to be
+ duplicated if it is out of range, or which can be inlined. So
+ right now it is always inlined, which wastes a word per stub. It
+ might be easier to handle the duplication if we put the lazy
+ stubs separately. */
+
+static const bfd_byte fdpic_sh_plt_entry_be[FDPIC_PLT_ENTRY_SIZE] =
+{
+ 0xd0, 0x02, /* mov.l @(12,pc),r0 */
+ 0x01, 0xce, /* mov.l @(r0,r12),r1 */
+ 0x70, 0x04, /* add #4, r0 */
+ 0x41, 0x2b, /* jmp @r1 */
+ 0x0c, 0xce, /* mov.l @(r0,r12),r12 */
+ 0x00, 0x09, /* nop */
+ 0, 0, 0, 0, /* 0: replaced with offset of this symbol's funcdesc */
+ 0, 0, 0, 0, /* 1: replaced with offset into relocation table. */
+ 0x60, 0xc2, /* mov.l @r12,r0 */
+ 0x40, 0x2b, /* jmp @r0 */
+ 0x53, 0xc1, /* mov.l @(4,r12),r3 */
+ 0x00, 0x09, /* nop */
+};
+
+static const bfd_byte fdpic_sh_plt_entry_le[FDPIC_PLT_ENTRY_SIZE] =
+{
+ 0x02, 0xd0, /* mov.l @(12,pc),r0 */
+ 0xce, 0x01, /* mov.l @(r0,r12),r1 */
+ 0x04, 0x70, /* add #4, r0 */
+ 0x2b, 0x41, /* jmp @r1 */
+ 0xce, 0x0c, /* mov.l @(r0,r12),r12 */
+ 0x09, 0x00, /* nop */
+ 0, 0, 0, 0, /* 0: replaced with offset of this symbol's funcdesc */
+ 0, 0, 0, 0, /* 1: replaced with offset into relocation table. */
+ 0xc2, 0x60, /* mov.l @r12,r0 */
+ 0x2b, 0x40, /* jmp @r0 */
+ 0xc1, 0x53, /* mov.l @(4,r12),r3 */
+ 0x09, 0x00, /* nop */
+};
+
+static const struct elf_sh_plt_info fdpic_sh_plts[2] = {
+ {
+ /* Big-endian PIC. */
+ NULL,
+ 0,
+ { MINUS_ONE, MINUS_ONE, MINUS_ONE },
+ fdpic_sh_plt_entry_be,
+ FDPIC_PLT_ENTRY_SIZE,
+ { 12, MINUS_ONE, 16, FALSE },
+ FDPIC_PLT_LAZY_OFFSET,
+ NULL
+ },
+ {
+ /* Little-endian PIC. */
+ NULL,
+ 0,
+ { MINUS_ONE, MINUS_ONE, MINUS_ONE },
+ fdpic_sh_plt_entry_le,
+ FDPIC_PLT_ENTRY_SIZE,
+ { 12, MINUS_ONE, 16, FALSE },
+ FDPIC_PLT_LAZY_OFFSET,
+ NULL
+ },
+};
+
+/* On SH2A, we can use the movi20 instruction to generate shorter PLT
+ entries for the first 64K slots. We use the normal FDPIC PLT entry
+ past that point; we could also use movi20s, which might be faster,
+ but would not be any smaller. */
+
+#define FDPIC_SH2A_PLT_ENTRY_SIZE 24
+#define FDPIC_SH2A_PLT_LAZY_OFFSET 16
+
+static const bfd_byte fdpic_sh2a_plt_entry_be[FDPIC_SH2A_PLT_ENTRY_SIZE] =
+{
+ 0, 0, 0, 0, /* movi20 #gotofffuncdesc,r0 */
+ 0x01, 0xce, /* mov.l @(r0,r12),r1 */
+ 0x70, 0x04, /* add #4, r0 */
+ 0x41, 0x2b, /* jmp @r1 */
+ 0x0c, 0xce, /* mov.l @(r0,r12),r12 */
+ 0, 0, 0, 0, /* 1: replaced with offset into relocation table. */
+ 0x60, 0xc2, /* mov.l @r12,r0 */
+ 0x40, 0x2b, /* jmp @r0 */
+ 0x53, 0xc1, /* mov.l @(4,r12),r3 */
+ 0x00, 0x09, /* nop */
+};
+
+static const bfd_byte fdpic_sh2a_plt_entry_le[FDPIC_SH2A_PLT_ENTRY_SIZE] =
+{
+ 0, 0, 0, 0, /* movi20 #gotofffuncdesc,r0 */
+ 0xce, 0x01, /* mov.l @(r0,r12),r1 */
+ 0x04, 0x70, /* add #4, r0 */
+ 0x2b, 0x41, /* jmp @r1 */
+ 0xce, 0x0c, /* mov.l @(r0,r12),r12 */
+ 0, 0, 0, 0, /* 1: replaced with offset into relocation table. */
+ 0xc2, 0x60, /* mov.l @r12,r0 */
+ 0x2b, 0x40, /* jmp @r0 */
+ 0xc1, 0x53, /* mov.l @(4,r12),r3 */
+ 0x09, 0x00, /* nop */
+};
+
+static const struct elf_sh_plt_info fdpic_sh2a_short_plt_be = {
+ /* Big-endian FDPIC, max index 64K. */
+ NULL,
+ 0,
+ { MINUS_ONE, MINUS_ONE, MINUS_ONE },
+ fdpic_sh2a_plt_entry_be,
+ FDPIC_SH2A_PLT_ENTRY_SIZE,
+ { 0, MINUS_ONE, 12, TRUE },
+ FDPIC_SH2A_PLT_LAZY_OFFSET,
+ NULL
+};
+
+static const struct elf_sh_plt_info fdpic_sh2a_short_plt_le = {
+ /* Little-endian FDPIC, max index 64K. */
+ NULL,
+ 0,
+ { MINUS_ONE, MINUS_ONE, MINUS_ONE },
+ fdpic_sh2a_plt_entry_le,
+ FDPIC_SH2A_PLT_ENTRY_SIZE,
+ { 0, MINUS_ONE, 12, TRUE },
+ FDPIC_SH2A_PLT_LAZY_OFFSET,
+ NULL
+};
+
+static const struct elf_sh_plt_info fdpic_sh2a_plts[2] = {
+ {
+ /* Big-endian PIC. */
+ NULL,
+ 0,
+ { MINUS_ONE, MINUS_ONE, MINUS_ONE },
+ fdpic_sh_plt_entry_be,
+ FDPIC_PLT_ENTRY_SIZE,
+ { 12, MINUS_ONE, 16, FALSE },
+ FDPIC_PLT_LAZY_OFFSET,
+ &fdpic_sh2a_short_plt_be
+ },
+ {
+ /* Little-endian PIC. */
+ NULL,
+ 0,
+ { MINUS_ONE, MINUS_ONE, MINUS_ONE },
+ fdpic_sh_plt_entry_le,
+ FDPIC_PLT_ENTRY_SIZE,
+ { 12, MINUS_ONE, 16, FALSE },
+ FDPIC_PLT_LAZY_OFFSET,
+ &fdpic_sh2a_short_plt_le
+ },
+};
+
+/* Return the type of PLT associated with ABFD. PIC_P is true if
+ the object is position-independent. */
+
+static const struct elf_sh_plt_info *
+get_plt_info (bfd *abfd, bfd_boolean pic_p)
+{
+ if (fdpic_object_p (abfd))
+ {
+ /* If any input file requires SH2A we can use a shorter PLT
+ sequence. */
+ if (sh_get_arch_from_bfd_mach (bfd_get_mach (abfd)) & arch_sh2a_base)
+ return &fdpic_sh2a_plts[!bfd_big_endian (abfd)];
+ else
+ return &fdpic_sh_plts[!bfd_big_endian (abfd)];
+ }
+ if (vxworks_object_p (abfd))
+ return &vxworks_sh_plts[pic_p][!bfd_big_endian (abfd)];
+ return &elf_sh_plts[pic_p][!bfd_big_endian (abfd)];
+}
+
+/* Install a 32-bit PLT field starting at ADDR, which occurs in OUTPUT_BFD.
+ VALUE is the field's value and CODE_P is true if VALUE refers to code,
+ not data. */
+
+inline static void
+install_plt_field (bfd *output_bfd, bfd_boolean code_p ATTRIBUTE_UNUSED,
+ unsigned long value, bfd_byte *addr)
+{
+ bfd_put_32 (output_bfd, value, addr);
+}
+#endif
+
+/* The number of PLT entries which can use a shorter PLT, if any.
+ Currently always 64K, since only SH-2A FDPIC uses this; a
+ 20-bit movi20 can address that many function descriptors below
+ _GLOBAL_OFFSET_TABLE_. */
+#define MAX_SHORT_PLT 65536
+
+/* Return the index of the PLT entry at byte offset OFFSET. */
+
+static bfd_vma
+get_plt_index (const struct elf_sh_plt_info *info, bfd_vma offset)
+{
+ bfd_vma plt_index = 0;
+
+ offset -= info->plt0_entry_size;
+ if (info->short_plt != NULL)
+ {
+ if (offset > MAX_SHORT_PLT * info->short_plt->symbol_entry_size)
+ {
+ plt_index = MAX_SHORT_PLT;
+ offset -= MAX_SHORT_PLT * info->short_plt->symbol_entry_size;
+ }
+ else
+ info = info->short_plt;
+ }
+ return plt_index + offset / info->symbol_entry_size;
+}
+
+/* Do the inverse operation. */
+
+static bfd_vma
+get_plt_offset (const struct elf_sh_plt_info *info, bfd_vma plt_index)
+{
+ bfd_vma offset = 0;
+
+ if (info->short_plt != NULL)
+ {
+ if (plt_index > MAX_SHORT_PLT)
+ {
+ offset = MAX_SHORT_PLT * info->short_plt->symbol_entry_size;
+ plt_index -= MAX_SHORT_PLT;
+ }
+ else
+ info = info->short_plt;
+ }
+ return (offset + info->plt0_entry_size
+ + (plt_index * info->symbol_entry_size));
+}
+
+/* The sh linker needs to keep track of the number of relocs that it
+ decides to copy as dynamic relocs in check_relocs for each symbol.
+ This is so that it can later discard them if they are found to be
+ unnecessary. We store the information in a field extending the
+ regular ELF linker hash table. */
+
+struct elf_sh_dyn_relocs
+{
+ struct elf_sh_dyn_relocs *next;
+
+ /* The input section of the reloc. */
+ asection *sec;
+
+ /* Total number of relocs copied for the input section. */
+ bfd_size_type count;
+
+ /* Number of pc-relative relocs copied for the input section. */
+ bfd_size_type pc_count;
+};
+
+union gotref
+{
+ bfd_signed_vma refcount;
+ bfd_vma offset;
+};
+
+/* sh ELF linker hash entry. */
+
+struct elf_sh_link_hash_entry
+{
+ struct elf_link_hash_entry root;
+
+#ifdef INCLUDE_SHMEDIA
+ union
+ {
+ bfd_signed_vma refcount;
+ bfd_vma offset;
+ } datalabel_got;
+#endif
+
+ /* Track dynamic relocs copied for this symbol. */
+ struct elf_sh_dyn_relocs *dyn_relocs;
+
+ bfd_signed_vma gotplt_refcount;
+
+ /* A local function descriptor, for FDPIC. The refcount counts
+ R_SH_FUNCDESC, R_SH_GOTOFFFUNCDESC, and R_SH_GOTOFFFUNCDESC20
+ relocations; the PLT and GOT entry are accounted
+ for separately. After adjust_dynamic_symbol, the offset is
+ MINUS_ONE if there is no local descriptor (dynamic linker
+ managed and no PLT entry, or undefined weak non-dynamic).
+ During check_relocs we do not yet know whether the local
+ descriptor will be canonical. */
+ union gotref funcdesc;
+
+ /* How many of the above refcounted relocations were R_SH_FUNCDESC,
+ and thus require fixups or relocations. */
+ bfd_signed_vma abs_funcdesc_refcount;
+
+ enum got_type {
+ GOT_UNKNOWN = 0, GOT_NORMAL, GOT_TLS_GD, GOT_TLS_IE, GOT_FUNCDESC
+ } got_type;
+};
+
+#define sh_elf_hash_entry(ent) ((struct elf_sh_link_hash_entry *)(ent))
+
+struct sh_elf_obj_tdata
+{
+ struct elf_obj_tdata root;
+
+ /* got_type for each local got entry. */
+ char *local_got_type;
+
+ /* Function descriptor refcount and offset for each local symbol. */
+ union gotref *local_funcdesc;
+};
+
+#define sh_elf_tdata(abfd) \
+ ((struct sh_elf_obj_tdata *) (abfd)->tdata.any)
+
+#define sh_elf_local_got_type(abfd) \
+ (sh_elf_tdata (abfd)->local_got_type)
+
+#define sh_elf_local_funcdesc(abfd) \
+ (sh_elf_tdata (abfd)->local_funcdesc)
+
+#define is_sh_elf(bfd) \
+ (bfd_get_flavour (bfd) == bfd_target_elf_flavour \
+ && elf_tdata (bfd) != NULL \
+ && elf_object_id (bfd) == SH_ELF_DATA)
+
+/* Override the generic function because we need to store sh_elf_obj_tdata
+ as the specific tdata. */
+
+static bfd_boolean
+sh_elf_mkobject (bfd *abfd)
+{
+ return bfd_elf_allocate_object (abfd, sizeof (struct sh_elf_obj_tdata),
+ SH_ELF_DATA);
+}
+
+/* sh ELF linker hash table. */
+
+struct elf_sh_link_hash_table
+{
+ struct elf_link_hash_table root;
+
+ /* Short-cuts to get to dynamic linker sections. */
+ asection *sgot;
+ asection *sgotplt;
+ asection *srelgot;
+ asection *splt;
+ asection *srelplt;
+ asection *sdynbss;
+ asection *srelbss;
+ asection *sfuncdesc;
+ asection *srelfuncdesc;
+ asection *srofixup;
+
+ /* The (unloaded but important) VxWorks .rela.plt.unloaded section. */
+ asection *srelplt2;
+
+ /* Small local sym cache. */
+ struct sym_cache sym_cache;
+
+ /* A counter or offset to track a TLS got entry. */
+ union
+ {
+ bfd_signed_vma refcount;
+ bfd_vma offset;
+ } tls_ldm_got;
+
+ /* The type of PLT to use. */
+ const struct elf_sh_plt_info *plt_info;
+
+ /* True if the target system is VxWorks. */
+ bfd_boolean vxworks_p;
+
+ /* True if the target system uses FDPIC. */
+ bfd_boolean fdpic_p;
+};
+
+/* Traverse an sh ELF linker hash table. */
+
+#define sh_elf_link_hash_traverse(table, func, info) \
+ (elf_link_hash_traverse \
+ (&(table)->root, \
+ (bfd_boolean (*) (struct elf_link_hash_entry *, void *)) (func), \
+ (info)))
+
+/* Get the sh ELF linker hash table from a link_info structure. */
+
+#define sh_elf_hash_table(p) \
+ (elf_hash_table_id ((struct elf_link_hash_table *) ((p)->hash)) \
+ == SH_ELF_DATA ? ((struct elf_sh_link_hash_table *) ((p)->hash)) : NULL)
+
+/* Create an entry in an sh ELF linker hash table. */
+
+static struct bfd_hash_entry *
+sh_elf_link_hash_newfunc (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->dyn_relocs = NULL;
+ ret->gotplt_refcount = 0;
+#ifdef INCLUDE_SHMEDIA
+ ret->datalabel_got.refcount = ret->root.got.refcount;
+#endif
+ ret->funcdesc.refcount = 0;
+ ret->abs_funcdesc_refcount = 0;
+ ret->got_type = GOT_UNKNOWN;
+ }
+
+ 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 (bfd *abfd)
+{
+ struct elf_sh_link_hash_table *ret;
+ bfd_size_type amt = sizeof (struct elf_sh_link_hash_table);
+
+ ret = (struct elf_sh_link_hash_table *) bfd_malloc (amt);
+ 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,
+ sizeof (struct elf_sh_link_hash_entry),
+ SH_ELF_DATA))
+ {
+ free (ret);
+ return NULL;
+ }
+
+ ret->sgot = NULL;
+ ret->sgotplt = NULL;
+ ret->srelgot = NULL;
+ ret->splt = NULL;
+ ret->srelplt = NULL;
+ ret->sdynbss = NULL;
+ ret->srelbss = NULL;
+ ret->srelplt2 = NULL;
+ ret->sym_cache.abfd = NULL;
+ ret->tls_ldm_got.refcount = 0;
+ ret->plt_info = NULL;
+ ret->vxworks_p = vxworks_object_p (abfd);
+ ret->fdpic_p = fdpic_object_p (abfd);
+
+ return &ret->root.root;
+}
+
+static bfd_boolean
+sh_elf_omit_section_dynsym (bfd *output_bfd ATTRIBUTE_UNUSED,
+ struct bfd_link_info *info, asection *p)
+{
+ struct elf_sh_link_hash_table *htab = sh_elf_hash_table (info);
+
+ /* Non-FDPIC binaries do not need dynamic symbols for sections. */
+ if (!htab->fdpic_p)
+ return TRUE;
+
+ /* We need dynamic symbols for every section, since segments can
+ relocate independently. */
+ switch (elf_section_data (p)->this_hdr.sh_type)
+ {
+ case SHT_PROGBITS:
+ case SHT_NOBITS:
+ /* If sh_type is yet undecided, assume it could be
+ SHT_PROGBITS/SHT_NOBITS. */
+ case SHT_NULL:
+ return FALSE;
+
+ /* There shouldn't be section relative relocations
+ against any other section. */
+ default:
+ return TRUE;
+ }
+}
+
+/* Create .got, .gotplt, and .rela.got sections in DYNOBJ, and set up
+ shortcuts to them in our hash table. */
+
+static bfd_boolean
+create_got_section (bfd *dynobj, struct bfd_link_info *info)
+{
+ struct elf_sh_link_hash_table *htab;
+
+ if (! _bfd_elf_create_got_section (dynobj, info))
+ return FALSE;
+
+ htab = sh_elf_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
+
+ htab->sgot = bfd_get_linker_section (dynobj, ".got");
+ htab->sgotplt = bfd_get_linker_section (dynobj, ".got.plt");
+ htab->srelgot = bfd_get_linker_section (dynobj, ".rela.got");
+ if (! htab->sgot || ! htab->sgotplt || ! htab->srelgot)
+ abort ();
+
+ htab->sfuncdesc = bfd_make_section_anyway_with_flags (dynobj, ".got.funcdesc",
+ (SEC_ALLOC | SEC_LOAD
+ | SEC_HAS_CONTENTS
+ | SEC_IN_MEMORY
+ | SEC_LINKER_CREATED));
+ if (htab->sfuncdesc == NULL
+ || ! bfd_set_section_alignment (dynobj, htab->sfuncdesc, 2))
+ return FALSE;
+
+ htab->srelfuncdesc = bfd_make_section_anyway_with_flags (dynobj,
+ ".rela.got.funcdesc",
+ (SEC_ALLOC | SEC_LOAD
+ | SEC_HAS_CONTENTS
+ | SEC_IN_MEMORY
+ | SEC_LINKER_CREATED
+ | SEC_READONLY));
+ if (htab->srelfuncdesc == NULL
+ || ! bfd_set_section_alignment (dynobj, htab->srelfuncdesc, 2))
+ return FALSE;
+
+ /* Also create .rofixup. */
+ htab->srofixup = bfd_make_section_anyway_with_flags (dynobj, ".rofixup",
+ (SEC_ALLOC | SEC_LOAD
+ | SEC_HAS_CONTENTS
+ | SEC_IN_MEMORY
+ | SEC_LINKER_CREATED
+ | SEC_READONLY));
+ if (htab->srofixup == NULL
+ || ! bfd_set_section_alignment (dynobj, htab->srofixup, 2))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Create dynamic sections when linking against a dynamic object. */
+
+static bfd_boolean
+sh_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
+{
+ struct elf_sh_link_hash_table *htab;
+ flagword flags, pltflags;
+ asection *s;
+ const 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;
+ }
+
+ htab = sh_elf_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
+
+ if (htab->root.dynamic_sections_created)
+ return TRUE;
+
+ /* 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_anyway_with_flags (abfd, ".plt", pltflags);
+ htab->splt = s;
+ if (s == NULL
+ || ! 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;
+ struct bfd_link_hash_entry *bh = 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, &bh)))
+ return FALSE;
+
+ h = (struct elf_link_hash_entry *) bh;
+ h->def_regular = 1;
+ h->type = STT_OBJECT;
+ htab->root.hplt = h;
+
+ if (info->shared
+ && ! bfd_elf_link_record_dynamic_symbol (info, h))
+ return FALSE;
+ }
+
+ s = bfd_make_section_anyway_with_flags (abfd,
+ bed->default_use_rela_p
+ ? ".rela.plt" : ".rel.plt",
+ flags | SEC_READONLY);
+ htab->srelplt = s;
+ if (s == NULL
+ || ! bfd_set_section_alignment (abfd, s, ptralign))
+ return FALSE;
+
+ if (htab->sgot == NULL
+ && !create_got_section (abfd, info))
+ 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_anyway_with_flags (abfd, ".dynbss",
+ SEC_ALLOC | SEC_LINKER_CREATED);
+ htab->sdynbss = s;
+ if (s == NULL)
+ 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_anyway_with_flags (abfd,
+ (bed->default_use_rela_p
+ ? ".rela.bss" : ".rel.bss"),
+ flags | SEC_READONLY);
+ htab->srelbss = s;
+ if (s == NULL
+ || ! bfd_set_section_alignment (abfd, s, ptralign))
+ return FALSE;
+ }
+ }
+
+ if (htab->vxworks_p)
+ {
+ if (!elf_vxworks_create_dynamic_sections (abfd, info, &htab->srelplt2))
+ 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 bfd_boolean
+sh_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
+ struct elf_link_hash_entry *h)
+{
+ struct elf_sh_link_hash_table *htab;
+ struct elf_sh_link_hash_entry *eh;
+ struct elf_sh_dyn_relocs *p;
+ asection *s;
+
+ htab = sh_elf_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
+
+ /* Make sure we know what is going on here. */
+ BFD_ASSERT (htab->root.dynobj != NULL
+ && (h->needs_plt
+ || h->u.weakdef != NULL
+ || (h->def_dynamic
+ && h->ref_regular
+ && !h->def_regular)));
+
+ /* 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->needs_plt)
+ {
+ if (h->plt.refcount <= 0
+ || SYMBOL_CALLS_LOCAL (info, h)
+ || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+ && h->root.type == bfd_link_hash_undefweak))
+ {
+ /* 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. */
+ h->plt.offset = (bfd_vma) -1;
+ h->needs_plt = 0;
+ }
+
+ return TRUE;
+ }
+ else
+ h->plt.offset = (bfd_vma) -1;
+
+ /* 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->u.weakdef != NULL)
+ {
+ BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined
+ || h->u.weakdef->root.type == bfd_link_hash_defweak);
+ h->root.u.def.section = h->u.weakdef->root.u.def.section;
+ h->root.u.def.value = h->u.weakdef->root.u.def.value;
+ if (info->nocopyreloc)
+ h->non_got_ref = h->u.weakdef->non_got_ref;
+ 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->non_got_ref)
+ return TRUE;
+
+ /* If -z nocopyreloc was given, we won't generate them either. */
+ if (info->nocopyreloc)
+ {
+ h->non_got_ref = 0;
+ return TRUE;
+ }
+
+ eh = (struct elf_sh_link_hash_entry *) h;
+ for (p = eh->dyn_relocs; p != NULL; p = p->next)
+ {
+ s = p->sec->output_section;
+ if (s != NULL && (s->flags & (SEC_READONLY | SEC_HAS_CONTENTS)) != 0)
+ break;
+ }
+
+ /* If we didn't find any dynamic relocs in sections which needs the
+ copy reloc, then we'll be keeping the dynamic relocs and avoiding
+ the copy reloc. */
+ if (p == NULL)
+ {
+ h->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 = htab->sdynbss;
+ 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 && h->size != 0)
+ {
+ asection *srel;
+
+ srel = htab->srelbss;
+ BFD_ASSERT (srel != NULL);
+ srel->size += sizeof (Elf32_External_Rela);
+ h->needs_copy = 1;
+ }
+
+ return _bfd_elf_adjust_dynamic_copy (h, s);
+}
+
+/* Allocate space in .plt, .got and associated reloc sections for
+ dynamic relocs. */
+
+static bfd_boolean
+allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
+{
+ struct bfd_link_info *info;
+ struct elf_sh_link_hash_table *htab;
+ struct elf_sh_link_hash_entry *eh;
+ struct elf_sh_dyn_relocs *p;
+
+ if (h->root.type == bfd_link_hash_indirect)
+ return TRUE;
+
+ info = (struct bfd_link_info *) inf;
+ htab = sh_elf_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
+
+ eh = (struct elf_sh_link_hash_entry *) h;
+ if ((h->got.refcount > 0
+ || h->forced_local)
+ && eh->gotplt_refcount > 0)
+ {
+ /* The symbol has been forced local, or we have some direct got refs,
+ so treat all the gotplt refs as got refs. */
+ h->got.refcount += eh->gotplt_refcount;
+ if (h->plt.refcount >= eh->gotplt_refcount)
+ h->plt.refcount -= eh->gotplt_refcount;
+ }
+
+ if (htab->root.dynamic_sections_created
+ && h->plt.refcount > 0
+ && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+ || h->root.type != bfd_link_hash_undefweak))
+ {
+ /* Make sure this symbol is output as a dynamic symbol.
+ Undefined weak syms won't yet be marked as dynamic. */
+ if (h->dynindx == -1
+ && !h->forced_local)
+ {
+ if (! bfd_elf_link_record_dynamic_symbol (info, h))
+ return FALSE;
+ }
+
+ if (info->shared
+ || WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, 0, h))
+ {
+ asection *s = htab->splt;
+ const struct elf_sh_plt_info *plt_info;
+
+ /* If this is the first .plt entry, make room for the special
+ first entry. */
+ if (s->size == 0)
+ s->size += htab->plt_info->plt0_entry_size;
+
+ h->plt.offset = s->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. Skip this for FDPIC, since the
+ function's address will be the address of the canonical
+ function descriptor. */
+ if (!htab->fdpic_p && !info->shared && !h->def_regular)
+ {
+ h->root.u.def.section = s;
+ h->root.u.def.value = h->plt.offset;
+ }
+
+ /* Make room for this entry. */
+ plt_info = htab->plt_info;
+ if (plt_info->short_plt != NULL
+ && (get_plt_index (plt_info->short_plt, s->size) < MAX_SHORT_PLT))
+ plt_info = plt_info->short_plt;
+ s->size += plt_info->symbol_entry_size;
+
+ /* We also need to make an entry in the .got.plt section, which
+ will be placed in the .got section by the linker script. */
+ if (!htab->fdpic_p)
+ htab->sgotplt->size += 4;
+ else
+ htab->sgotplt->size += 8;
+
+ /* We also need to make an entry in the .rel.plt section. */
+ htab->srelplt->size += sizeof (Elf32_External_Rela);
+
+ if (htab->vxworks_p && !info->shared)
+ {
+ /* VxWorks executables have a second set of relocations
+ for each PLT entry. They go in a separate relocation
+ section, which is processed by the kernel loader. */
+
+ /* There is a relocation for the initial PLT entry:
+ an R_SH_DIR32 relocation for _GLOBAL_OFFSET_TABLE_. */
+ if (h->plt.offset == htab->plt_info->plt0_entry_size)
+ htab->srelplt2->size += sizeof (Elf32_External_Rela);
+
+ /* There are two extra relocations for each subsequent
+ PLT entry: an R_SH_DIR32 relocation for the GOT entry,
+ and an R_SH_DIR32 relocation for the PLT entry. */
+ htab->srelplt2->size += sizeof (Elf32_External_Rela) * 2;
+ }
+ }
+ else
+ {
+ h->plt.offset = (bfd_vma) -1;
+ h->needs_plt = 0;
+ }
+ }
+ else
+ {
+ h->plt.offset = (bfd_vma) -1;
+ h->needs_plt = 0;
+ }
+
+ if (h->got.refcount > 0)
+ {
+ asection *s;
+ bfd_boolean dyn;
+ enum got_type got_type = sh_elf_hash_entry (h)->got_type;
+
+ /* Make sure this symbol is output as a dynamic symbol.
+ Undefined weak syms won't yet be marked as dynamic. */
+ if (h->dynindx == -1
+ && !h->forced_local)
+ {
+ if (! bfd_elf_link_record_dynamic_symbol (info, h))
+ return FALSE;
+ }
+
+ s = htab->sgot;
+ h->got.offset = s->size;
+ s->size += 4;
+ /* R_SH_TLS_GD needs 2 consecutive GOT slots. */
+ if (got_type == GOT_TLS_GD)
+ s->size += 4;
+ dyn = htab->root.dynamic_sections_created;
+ if (!dyn)
+ {
+ /* No dynamic relocations required. */
+ if (htab->fdpic_p && !info->shared
+ && h->root.type != bfd_link_hash_undefweak
+ && (got_type == GOT_NORMAL || got_type == GOT_FUNCDESC))
+ htab->srofixup->size += 4;
+ }
+ /* No dynamic relocations required when IE->LE conversion happens. */
+ else if (got_type == GOT_TLS_IE && !h->def_dynamic && !info->shared)
+ ;
+ /* R_SH_TLS_IE_32 needs one dynamic relocation if dynamic,
+ R_SH_TLS_GD needs one if local symbol and two if global. */
+ else if ((got_type == GOT_TLS_GD && h->dynindx == -1)
+ || got_type == GOT_TLS_IE)
+ htab->srelgot->size += sizeof (Elf32_External_Rela);
+ else if (got_type == GOT_TLS_GD)
+ htab->srelgot->size += 2 * sizeof (Elf32_External_Rela);
+ else if (got_type == GOT_FUNCDESC)
+ {
+ if (!info->shared && SYMBOL_FUNCDESC_LOCAL (info, h))
+ htab->srofixup->size += 4;
+ else
+ htab->srelgot->size += sizeof (Elf32_External_Rela);
+ }
+ else if ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+ || h->root.type != bfd_link_hash_undefweak)
+ && (info->shared
+ || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)))
+ htab->srelgot->size += sizeof (Elf32_External_Rela);
+ else if (htab->fdpic_p && !info->shared && got_type == GOT_NORMAL
+ && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+ || h->root.type != bfd_link_hash_undefweak))
+ htab->srofixup->size += 4;
+ }
+ else
+ h->got.offset = (bfd_vma) -1;
+
+#ifdef INCLUDE_SHMEDIA
+ if (eh->datalabel_got.refcount > 0)
+ {
+ asection *s;
+ bfd_boolean dyn;
+
+ /* Make sure this symbol is output as a dynamic symbol.
+ Undefined weak syms won't yet be marked as dynamic. */
+ if (h->dynindx == -1
+ && !h->forced_local)
+ {
+ if (! bfd_elf_link_record_dynamic_symbol (info, h))
+ return FALSE;
+ }
+
+ s = htab->sgot;
+ eh->datalabel_got.offset = s->size;
+ s->size += 4;
+ dyn = htab->root.dynamic_sections_created;
+ if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h))
+ htab->srelgot->size += sizeof (Elf32_External_Rela);
+ }
+ else
+ eh->datalabel_got.offset = (bfd_vma) -1;
+#endif
+
+ /* Allocate space for any dynamic relocations to function
+ descriptors, canonical or otherwise. We need to relocate the
+ reference unless it resolves to zero, which only happens for
+ undefined weak symbols (either non-default visibility, or when
+ static linking). Any GOT slot is accounted for elsewhere. */
+ if (eh->abs_funcdesc_refcount > 0
+ && (h->root.type != bfd_link_hash_undefweak
+ || (htab->root.dynamic_sections_created
+ && ! SYMBOL_CALLS_LOCAL (info, h))))
+ {
+ if (!info->shared && SYMBOL_FUNCDESC_LOCAL (info, h))
+ htab->srofixup->size += eh->abs_funcdesc_refcount * 4;
+ else
+ htab->srelgot->size
+ += eh->abs_funcdesc_refcount * sizeof (Elf32_External_Rela);
+ }
+
+ /* We must allocate a function descriptor if there are references to
+ a canonical descriptor (R_SH_GOTFUNCDESC or R_SH_FUNCDESC) and
+ the dynamic linker isn't going to allocate it. None of this
+ applies if we already created one in .got.plt, but if the
+ canonical function descriptor can be in this object, there
+ won't be a PLT entry at all. */
+ if ((eh->funcdesc.refcount > 0
+ || (h->got.offset != MINUS_ONE && eh->got_type == GOT_FUNCDESC))
+ && h->root.type != bfd_link_hash_undefweak
+ && SYMBOL_FUNCDESC_LOCAL (info, h))
+ {
+ /* Make room for this function descriptor. */
+ eh->funcdesc.offset = htab->sfuncdesc->size;
+ htab->sfuncdesc->size += 8;
+
+ /* We will need a relocation or two fixups to initialize the
+ function descriptor, so allocate those too. */
+ if (!info->shared && SYMBOL_CALLS_LOCAL (info, h))
+ htab->srofixup->size += 8;
+ else
+ htab->srelfuncdesc->size += sizeof (Elf32_External_Rela);
+ }
+
+ if (eh->dyn_relocs == NULL)
+ return TRUE;
+
+ /* In the shared -Bsymbolic case, discard space allocated for
+ dynamic pc-relative relocs against symbols which turn out to be
+ defined in regular objects. For the normal shared case, discard
+ space for pc-relative relocs that have become local due to symbol
+ visibility changes. */
+
+ if (info->shared)
+ {
+ if (SYMBOL_CALLS_LOCAL (info, h))
+ {
+ struct elf_sh_dyn_relocs **pp;
+
+ for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
+ {
+ p->count -= p->pc_count;
+ p->pc_count = 0;
+ if (p->count == 0)
+ *pp = p->next;
+ else
+ pp = &p->next;
+ }
+ }
+
+ if (htab->vxworks_p)
+ {
+ struct elf_sh_dyn_relocs **pp;
+
+ for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
+ {
+ if (strcmp (p->sec->output_section->name, ".tls_vars") == 0)
+ *pp = p->next;
+ else
+ pp = &p->next;
+ }
+ }
+
+ /* Also discard relocs on undefined weak syms with non-default
+ visibility. */
+ if (eh->dyn_relocs != NULL
+ && h->root.type == bfd_link_hash_undefweak)
+ {
+ if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
+ eh->dyn_relocs = NULL;
+
+ /* Make sure undefined weak symbols are output as a dynamic
+ symbol in PIEs. */
+ else if (h->dynindx == -1
+ && !h->forced_local)
+ {
+ if (! bfd_elf_link_record_dynamic_symbol (info, h))
+ return FALSE;
+ }
+ }
+ }
+ else
+ {
+ /* For the non-shared case, discard space for relocs against
+ symbols which turn out to need copy relocs or are not
+ dynamic. */
+
+ if (!h->non_got_ref
+ && ((h->def_dynamic
+ && !h->def_regular)
+ || (htab->root.dynamic_sections_created
+ && (h->root.type == bfd_link_hash_undefweak
+ || h->root.type == bfd_link_hash_undefined))))
+ {
+ /* Make sure this symbol is output as a dynamic symbol.
+ Undefined weak syms won't yet be marked as dynamic. */
+ if (h->dynindx == -1
+ && !h->forced_local)
+ {
+ if (! bfd_elf_link_record_dynamic_symbol (info, h))
+ return FALSE;
+ }
+
+ /* If that succeeded, we know we'll be keeping all the
+ relocs. */
+ if (h->dynindx != -1)
+ goto keep;
+ }
+
+ eh->dyn_relocs = NULL;
+
+ keep: ;
+ }
+
+ /* Finally, allocate space. */
+ for (p = eh->dyn_relocs; p != NULL; p = p->next)
+ {
+ asection *sreloc = elf_section_data (p->sec)->sreloc;
+ sreloc->size += p->count * sizeof (Elf32_External_Rela);
+
+ /* If we need relocations, we do not need fixups. */
+ if (htab->fdpic_p && !info->shared)
+ htab->srofixup->size -= 4 * (p->count - p->pc_count);
+ }
+
+ return TRUE;
+}
+
+/* Find any dynamic relocs that apply to read-only sections. */
+
+static bfd_boolean
+readonly_dynrelocs (struct elf_link_hash_entry *h, void *inf)
+{
+ struct elf_sh_link_hash_entry *eh;
+ struct elf_sh_dyn_relocs *p;
+
+ eh = (struct elf_sh_link_hash_entry *) h;
+ for (p = eh->dyn_relocs; p != NULL; p = p->next)
+ {
+ asection *s = p->sec->output_section;
+
+ if (s != NULL && (s->flags & SEC_READONLY) != 0)
+ {
+ struct bfd_link_info *info = (struct bfd_link_info *) inf;
+
+ info->flags |= DF_TEXTREL;
+
+ /* Not an error, just cut short the traversal. */
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/* This function is called after all the input files have been read,
+ and the input sections have been assigned to output sections.
+ It's a convenient place to determine the PLT style. */
+
+static bfd_boolean
+sh_elf_always_size_sections (bfd *output_bfd, struct bfd_link_info *info)
+{
+ sh_elf_hash_table (info)->plt_info = get_plt_info (output_bfd, info->shared);
+
+ if (sh_elf_hash_table (info)->fdpic_p && !info->relocatable
+ && !bfd_elf_stack_segment_size (output_bfd, info,
+ "__stacksize", DEFAULT_STACK_SIZE))
+ return FALSE;
+ return TRUE;
+}
+
+/* Set the sizes of the dynamic sections. */
+
+static bfd_boolean
+sh_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
+ struct bfd_link_info *info)
+{
+ struct elf_sh_link_hash_table *htab;
+ bfd *dynobj;
+ asection *s;
+ bfd_boolean relocs;
+ bfd *ibfd;
+
+ htab = sh_elf_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
+
+ dynobj = htab->root.dynobj;
+ BFD_ASSERT (dynobj != NULL);
+
+ if (htab->root.dynamic_sections_created)
+ {
+ /* Set the contents of the .interp section to the interpreter. */
+ if (info->executable)
+ {
+ s = bfd_get_linker_section (dynobj, ".interp");
+ BFD_ASSERT (s != NULL);
+ s->size = sizeof ELF_DYNAMIC_INTERPRETER;
+ s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER;
+ }
+ }
+
+ /* Set up .got offsets for local syms, and space for local dynamic
+ relocs. */
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+ {
+ bfd_signed_vma *local_got;
+ bfd_signed_vma *end_local_got;
+ union gotref *local_funcdesc, *end_local_funcdesc;
+ char *local_got_type;
+ bfd_size_type locsymcount;
+ Elf_Internal_Shdr *symtab_hdr;
+ asection *srel;
+
+ if (! is_sh_elf (ibfd))
+ continue;
+
+ for (s = ibfd->sections; s != NULL; s = s->next)
+ {
+ struct elf_sh_dyn_relocs *p;
+
+ for (p = ((struct elf_sh_dyn_relocs *)
+ elf_section_data (s)->local_dynrel);
+ p != NULL;
+ p = p->next)
+ {
+ if (! bfd_is_abs_section (p->sec)
+ && bfd_is_abs_section (p->sec->output_section))
+ {
+ /* Input section has been discarded, either because
+ it is a copy of a linkonce section or due to
+ linker script /DISCARD/, so we'll be discarding
+ the relocs too. */
+ }
+ else if (htab->vxworks_p
+ && strcmp (p->sec->output_section->name,
+ ".tls_vars") == 0)
+ {
+ /* Relocations in vxworks .tls_vars sections are
+ handled specially by the loader. */
+ }
+ else if (p->count != 0)
+ {
+ srel = elf_section_data (p->sec)->sreloc;
+ srel->size += p->count * sizeof (Elf32_External_Rela);
+ if ((p->sec->output_section->flags & SEC_READONLY) != 0)
+ info->flags |= DF_TEXTREL;
+
+ /* If we need relocations, we do not need fixups. */
+ if (htab->fdpic_p && !info->shared)
+ htab->srofixup->size -= 4 * (p->count - p->pc_count);
+ }
+ }
+ }
+
+ symtab_hdr = &elf_symtab_hdr (ibfd);
+ locsymcount = symtab_hdr->sh_info;
+#ifdef INCLUDE_SHMEDIA
+ /* Count datalabel local GOT. */
+ locsymcount *= 2;
+#endif
+ s = htab->sgot;
+ srel = htab->srelgot;
+
+ local_got = elf_local_got_refcounts (ibfd);
+ if (local_got)
+ {
+ end_local_got = local_got + locsymcount;
+ local_got_type = sh_elf_local_got_type (ibfd);
+ local_funcdesc = sh_elf_local_funcdesc (ibfd);
+ for (; local_got < end_local_got; ++local_got)
+ {
+ if (*local_got > 0)
+ {
+ *local_got = s->size;
+ s->size += 4;
+ if (*local_got_type == GOT_TLS_GD)
+ s->size += 4;
+ if (info->shared)
+ srel->size += sizeof (Elf32_External_Rela);
+ else
+ htab->srofixup->size += 4;
+
+ if (*local_got_type == GOT_FUNCDESC)
+ {
+ if (local_funcdesc == NULL)
+ {
+ bfd_size_type size;
+
+ size = locsymcount * sizeof (union gotref);
+ local_funcdesc = (union gotref *) bfd_zalloc (ibfd,
+ size);
+ if (local_funcdesc == NULL)
+ return FALSE;
+ sh_elf_local_funcdesc (ibfd) = local_funcdesc;
+ local_funcdesc += (local_got
+ - elf_local_got_refcounts (ibfd));
+ }
+ local_funcdesc->refcount++;
+ ++local_funcdesc;
+ }
+ }
+ else
+ *local_got = (bfd_vma) -1;
+ ++local_got_type;
+ }
+ }
+
+ local_funcdesc = sh_elf_local_funcdesc (ibfd);
+ if (local_funcdesc)
+ {
+ end_local_funcdesc = local_funcdesc + locsymcount;
+
+ for (; local_funcdesc < end_local_funcdesc; ++local_funcdesc)
+ {
+ if (local_funcdesc->refcount > 0)
+ {
+ local_funcdesc->offset = htab->sfuncdesc->size;
+ htab->sfuncdesc->size += 8;
+ if (!info->shared)
+ htab->srofixup->size += 8;
+ else
+ htab->srelfuncdesc->size += sizeof (Elf32_External_Rela);
+ }
+ else
+ local_funcdesc->offset = MINUS_ONE;
+ }
+ }
+
+ }
+
+ if (htab->tls_ldm_got.refcount > 0)
+ {
+ /* Allocate 2 got entries and 1 dynamic reloc for R_SH_TLS_LD_32
+ relocs. */
+ htab->tls_ldm_got.offset = htab->sgot->size;
+ htab->sgot->size += 8;
+ htab->srelgot->size += sizeof (Elf32_External_Rela);
+ }
+ else
+ htab->tls_ldm_got.offset = -1;
+
+ /* Only the reserved entries should be present. For FDPIC, they go at
+ the end of .got.plt. */
+ if (htab->fdpic_p)
+ {
+ BFD_ASSERT (htab->sgotplt && htab->sgotplt->size == 12);
+ htab->sgotplt->size = 0;
+ }
+
+ /* Allocate global sym .plt and .got entries, and space for global
+ sym dynamic relocs. */
+ elf_link_hash_traverse (&htab->root, allocate_dynrelocs, info);
+
+ /* Move the reserved entries and the _GLOBAL_OFFSET_TABLE_ symbol to the
+ end of the FDPIC .got.plt. */
+ if (htab->fdpic_p)
+ {
+ htab->root.hgot->root.u.def.value = htab->sgotplt->size;
+ htab->sgotplt->size += 12;
+ }
+
+ /* At the very end of the .rofixup section is a pointer to the GOT. */
+ if (htab->fdpic_p && htab->srofixup != NULL)
+ htab->srofixup->size += 4;
+
+ /* We now have determined the sizes of the various dynamic sections.
+ Allocate memory for them. */
+ relocs = FALSE;
+ for (s = dynobj->sections; s != NULL; s = s->next)
+ {
+ if ((s->flags & SEC_LINKER_CREATED) == 0)
+ continue;
+
+ if (s == htab->splt
+ || s == htab->sgot
+ || s == htab->sgotplt
+ || s == htab->sfuncdesc
+ || s == htab->srofixup
+ || s == htab->sdynbss)
+ {
+ /* Strip this section if we don't need it; see the
+ comment below. */
+ }
+ else if (CONST_STRNEQ (bfd_get_section_name (dynobj, s), ".rela"))
+ {
+ if (s->size != 0 && s != htab->srelplt && s != htab->srelplt2)
+ relocs = 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
+ {
+ /* It's not one of our sections, so don't allocate space. */
+ continue;
+ }
+
+ if (s->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. */
+
+ s->flags |= SEC_EXCLUDE;
+ continue;
+ }
+
+ if ((s->flags & SEC_HAS_CONTENTS) == 0)
+ continue;
+
+ /* Allocate memory for the section contents. We use bfd_zalloc
+ here in case unused entries are not reclaimed before the
+ section's contents are written out. This should not happen,
+ but this way if it does, we get a R_SH_NONE reloc instead
+ of garbage. */
+ s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->size);
+ if (s->contents == NULL)
+ return FALSE;
+ }
+
+ if (htab->root.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. */
+#define add_dynamic_entry(TAG, VAL) \
+ _bfd_elf_add_dynamic_entry (info, TAG, VAL)
+
+ if (info->executable)
+ {
+ if (! add_dynamic_entry (DT_DEBUG, 0))
+ return FALSE;
+ }
+
+ if (htab->splt->size != 0)
+ {
+ if (! add_dynamic_entry (DT_PLTGOT, 0)
+ || ! add_dynamic_entry (DT_PLTRELSZ, 0)
+ || ! add_dynamic_entry (DT_PLTREL, DT_RELA)
+ || ! add_dynamic_entry (DT_JMPREL, 0))
+ return FALSE;
+ }
+ else if ((elf_elfheader (output_bfd)->e_flags & EF_SH_FDPIC)
+ && htab->sgot->size != 0)
+ {
+ if (! add_dynamic_entry (DT_PLTGOT, 0))
+ return FALSE;
+ }
+
+ if (relocs)
+ {
+ if (! add_dynamic_entry (DT_RELA, 0)
+ || ! add_dynamic_entry (DT_RELASZ, 0)
+ || ! add_dynamic_entry (DT_RELAENT,
+ sizeof (Elf32_External_Rela)))
+ return FALSE;
+
+ /* If any dynamic relocs apply to a read-only section,
+ then we need a DT_TEXTREL entry. */
+ if ((info->flags & DF_TEXTREL) == 0)
+ elf_link_hash_traverse (&htab->root, readonly_dynrelocs, info);
+
+ if ((info->flags & DF_TEXTREL) != 0)
+ {
+ if (! add_dynamic_entry (DT_TEXTREL, 0))
+ return FALSE;
+ }
+ }
+ if (htab->vxworks_p
+ && !elf_vxworks_add_dynamic_entries (output_bfd, info))
+ return FALSE;
+ }
+#undef add_dynamic_entry
+
+ return TRUE;
+}
+\f
+/* Add a dynamic relocation to the SRELOC section. */
+
+inline static bfd_vma
+sh_elf_add_dyn_reloc (bfd *output_bfd, asection *sreloc, bfd_vma offset,
+ int reloc_type, long dynindx, bfd_vma addend)
+{
+ Elf_Internal_Rela outrel;
+ bfd_vma reloc_offset;
+
+ outrel.r_offset = offset;
+ outrel.r_info = ELF32_R_INFO (dynindx, reloc_type);
+ outrel.r_addend = addend;
+
+ reloc_offset = sreloc->reloc_count * sizeof (Elf32_External_Rela);
+ BFD_ASSERT (reloc_offset < sreloc->size);
+ bfd_elf32_swap_reloca_out (output_bfd, &outrel,
+ sreloc->contents + reloc_offset);
+ sreloc->reloc_count++;
+
+ return reloc_offset;
+}
+
+/* Add an FDPIC read-only fixup. */
+
+inline static void
+sh_elf_add_rofixup (bfd *output_bfd, asection *srofixup, bfd_vma offset)
+{
+ bfd_vma fixup_offset;
+
+ fixup_offset = srofixup->reloc_count++ * 4;
+ BFD_ASSERT (fixup_offset < srofixup->size);
+ bfd_put_32 (output_bfd, offset, srofixup->contents + fixup_offset);
+}
+
+/* Return the offset of the generated .got section from the
+ _GLOBAL_OFFSET_TABLE_ symbol. */
+
+static bfd_signed_vma
+sh_elf_got_offset (struct elf_sh_link_hash_table *htab)
+{
+ return (htab->sgot->output_offset - htab->sgotplt->output_offset
+ - htab->root.hgot->root.u.def.value);
+}
+
+/* Find the segment number in which OSEC, and output section, is
+ located. */
+
+static unsigned
+sh_elf_osec_to_segment (bfd *output_bfd, asection *osec)
+{
+ Elf_Internal_Phdr *p = NULL;
+
+ if (output_bfd->xvec->flavour == bfd_target_elf_flavour)
+ p = _bfd_elf_find_segment_containing_section (output_bfd, osec);
+
+ /* FIXME: Nothing ever says what this index is relative to. The kernel
+ supplies data in terms of the number of load segments but this is
+ a phdr index and the first phdr may not be a load segment. */
+ return (p != NULL) ? p - elf_tdata (output_bfd)->phdr : -1;
+}
+
+static bfd_boolean
+sh_elf_osec_readonly_p (bfd *output_bfd, asection *osec)
+{
+ unsigned seg = sh_elf_osec_to_segment (output_bfd, osec);
+
+ return (seg != (unsigned) -1
+ && ! (elf_tdata (output_bfd)->phdr[seg].p_flags & PF_W));
+}
+
+/* Generate the initial contents of a local function descriptor, along
+ with any relocations or fixups required. */
+static bfd_boolean
+sh_elf_initialize_funcdesc (bfd *output_bfd,
+ struct bfd_link_info *info,
+ struct elf_link_hash_entry *h,
+ bfd_vma offset,
+ asection *section,
+ bfd_vma value)
+{
+ struct elf_sh_link_hash_table *htab;
+ int dynindx;
+ bfd_vma addr, seg;
+
+ htab = sh_elf_hash_table (info);
+
+ /* FIXME: The ABI says that the offset to the function goes in the
+ descriptor, along with the segment index. We're RELA, so it could
+ go in the reloc instead... */
+
+ if (h != NULL && SYMBOL_CALLS_LOCAL (info, h))
+ {
+ section = h->root.u.def.section;
+ value = h->root.u.def.value;
+ }
+
+ if (h == NULL || SYMBOL_CALLS_LOCAL (info, h))
+ {
+ dynindx = elf_section_data (section->output_section)->dynindx;
+ addr = value + section->output_offset;
+ seg = sh_elf_osec_to_segment (output_bfd, section->output_section);
+ }
+ else
+ {
+ BFD_ASSERT (h->dynindx != -1);
+ dynindx = h->dynindx;
+ addr = seg = 0;
+ }
+
+ if (!info->shared && SYMBOL_CALLS_LOCAL (info, h))
+ {
+ if (h == NULL || h->root.type != bfd_link_hash_undefweak)
+ {
+ sh_elf_add_rofixup (output_bfd, htab->srofixup,
+ offset
+ + htab->sfuncdesc->output_section->vma
+ + htab->sfuncdesc->output_offset);
+ sh_elf_add_rofixup (output_bfd, htab->srofixup,
+ offset + 4
+ + htab->sfuncdesc->output_section->vma
+ + htab->sfuncdesc->output_offset);
+ }
+
+ /* There are no dynamic relocations so fill in the final
+ address and gp value (barring fixups). */
+ addr += section->output_section->vma;
+ seg = htab->root.hgot->root.u.def.value
+ + htab->root.hgot->root.u.def.section->output_section->vma
+ + htab->root.hgot->root.u.def.section->output_offset;
+ }
+ else
+ sh_elf_add_dyn_reloc (output_bfd, htab->srelfuncdesc,
+ offset
+ + htab->sfuncdesc->output_section->vma
+ + htab->sfuncdesc->output_offset,
+ R_SH_FUNCDESC_VALUE, dynindx, 0);
+
+ bfd_put_32 (output_bfd, addr, htab->sfuncdesc->contents + offset);
+ bfd_put_32 (output_bfd, seg, htab->sfuncdesc->contents + offset + 4);
+
+ return TRUE;
+}
+
+/* Install a 20-bit movi20 field starting at ADDR, which occurs in OUTPUT_BFD.
+ VALUE is the field's value. Return bfd_reloc_ok if successful or an error
+ otherwise. */
+
+static bfd_reloc_status_type
+install_movi20_field (bfd *output_bfd, unsigned long relocation,
+ bfd *input_bfd, asection *input_section,
+ bfd_byte *contents, bfd_vma offset)
+{
+ unsigned long cur_val;
+ bfd_byte *addr;
+ bfd_reloc_status_type r;
+
+ if (offset > bfd_get_section_limit (input_bfd, input_section))
+ return bfd_reloc_outofrange;
+
+ r = bfd_check_overflow (complain_overflow_signed, 20, 0,
+ bfd_arch_bits_per_address (input_bfd), relocation);
+ if (r != bfd_reloc_ok)
+ return r;
+
+ addr = contents + offset;
+ cur_val = bfd_get_16 (output_bfd, addr);
+ bfd_put_16 (output_bfd, cur_val | ((relocation & 0xf0000) >> 12), addr);
+ bfd_put_16 (output_bfd, relocation & 0xffff, addr + 2);
+
+ return bfd_reloc_ok;
+}
+
+/* Relocate an SH ELF section. */
+
+static bfd_boolean
+sh_elf_relocate_section (bfd *output_bfd, 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)
+{
+ struct elf_sh_link_hash_table *htab;
+ Elf_Internal_Shdr *symtab_hdr;
+ struct elf_link_hash_entry **sym_hashes;
+ Elf_Internal_Rela *rel, *relend;
+ bfd *dynobj = NULL;
+ bfd_vma *local_got_offsets;
+ asection *sgot = NULL;
+ asection *sgotplt = NULL;
+ asection *splt = NULL;
+ asection *sreloc = NULL;
+ asection *srelgot = NULL;
+ bfd_boolean is_vxworks_tls;
+ unsigned isec_segment, got_segment, plt_segment, check_segment[2];
+ bfd_boolean fdpic_p = FALSE;
+
+ BFD_ASSERT (is_sh_elf (input_bfd));
+
+ htab = sh_elf_hash_table (info);
+ if (htab != NULL)
+ {
+ dynobj = htab->root.dynobj;
+ sgot = htab->sgot;
+ sgotplt = htab->sgotplt;
+ splt = htab->splt;
+ fdpic_p = htab->fdpic_p;
+ }
+ symtab_hdr = &elf_symtab_hdr (input_bfd);
+ sym_hashes = elf_sym_hashes (input_bfd);
+ local_got_offsets = elf_local_got_offsets (input_bfd);
+
+ isec_segment = sh_elf_osec_to_segment (output_bfd,
+ input_section->output_section);
+ if (fdpic_p && sgot)
+ got_segment = sh_elf_osec_to_segment (output_bfd,
+ sgot->output_section);
+ else
+ got_segment = -1;
+ if (fdpic_p && splt)
+ plt_segment = sh_elf_osec_to_segment (output_bfd,
+ splt->output_section);
+ else
+ plt_segment = -1;
+
+ /* We have to handle relocations in vxworks .tls_vars sections
+ specially, because the dynamic loader is 'weird'. */
+ is_vxworks_tls = (htab && htab->vxworks_p && info->shared
+ && !strcmp (input_section->output_section->name,
+ ".tls_vars"));
+
+ 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;
+ int seen_stt_datalabel = 0;
+ bfd_vma off;
+ enum got_type got_type;
+ const char *symname = NULL;
+
+ r_symndx = ELF32_R_SYM (rel->r_info);
+
+ 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_GNU_VTINHERIT
+ && r_type <= (int) R_SH_LABEL)
+ continue;
+ if (r_type == (int) R_SH_NONE)
+ 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)
+ || (r_type >= (int) R_SH_FIRST_INVALID_RELOC_2
+ && r_type <= (int) R_SH_LAST_INVALID_RELOC_2)
+ || ( r_type >= (int) R_SH_FIRST_INVALID_RELOC_3
+ && r_type <= (int) R_SH_LAST_INVALID_RELOC_3)
+ || ( r_type >= (int) R_SH_FIRST_INVALID_RELOC_4
+ && r_type <= (int) R_SH_LAST_INVALID_RELOC_4)
+ || ( r_type >= (int) R_SH_FIRST_INVALID_RELOC_5
+ && r_type <= (int) R_SH_LAST_INVALID_RELOC_5)
+ || ( r_type >= (int) R_SH_FIRST_INVALID_RELOC_6
+ && r_type <= (int) R_SH_LAST_INVALID_RELOC_6))
+ {
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+
+ howto = get_howto_table (output_bfd) + r_type;
+
+ /* For relocs that aren't partial_inplace, we get the addend from
+ the relocation. */
+ if (! howto->partial_inplace)
+ addend = rel->r_addend;
+
+ h = NULL;
+ sym = NULL;
+ sec = NULL;
+ check_segment[0] = -1;
+ check_segment[1] = -1;
+ if (r_symndx < symtab_hdr->sh_info)
+ {
+ sym = local_syms + r_symndx;
+ sec = local_sections[r_symndx];
+
+ symname = bfd_elf_string_from_elf_section
+ (input_bfd, symtab_hdr->sh_link, sym->st_name);
+ if (symname == NULL || *symname == '\0')
+ symname = bfd_section_name (input_bfd, sec);
+
+ relocation = (sec->output_section->vma
+ + sec->output_offset
+ + sym->st_value);
+ /* A local symbol never has STO_SH5_ISA32, so we don't need
+ datalabel processing here. Make sure this does not change
+ without notice. */
+ if ((sym->st_other & STO_SH5_ISA32) != 0)
+ ((*info->callbacks->reloc_dangerous)
+ (info,
+ _("Unexpected STO_SH5_ISA32 on local symbol is not handled"),
+ input_bfd, input_section, rel->r_offset));
+
+ if (sec != NULL && discarded_section (sec))
+ /* Handled below. */
+ ;
+ else if (info->relocatable)
+ {
+ /* This is a relocatable 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 (ELF_ST_TYPE (sym->st_info) == STT_SECTION)
+ {
+ if (! howto->partial_inplace)
+ {
+ /* For relocations with the addend in the
+ relocation, we need just to update the addend.
+ All real relocs are of type partial_inplace; this
+ code is mostly for completeness. */
+ rel->r_addend += sec->output_offset;
+
+ continue;
+ }
+
+ /* Relocs of type partial_inplace need to pick up the
+ contents in the contents and add the offset resulting
+ from the changed location of the section symbol.
+ Using _bfd_final_link_relocate (e.g. goto
+ final_link_relocate) here would be wrong, because
+ relocations marked pc_relative would get the current
+ location subtracted, and we must only do that at the
+ final link. */
+ r = _bfd_relocate_contents (howto, input_bfd,
+ sec->output_offset
+ + sym->st_value,
+ contents + rel->r_offset);
+ goto relocation_done;
+ }
+
+ continue;
+ }
+ else if (! howto->partial_inplace)
+ {
+ relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
+ addend = rel->r_addend;
+ }
+ else if ((sec->flags & SEC_MERGE)
+ && ELF_ST_TYPE (sym->st_info) == STT_SECTION)
+ {
+ asection *msec;
+
+ if (howto->rightshift || howto->src_mask != 0xffffffff)
+ {
+ (*_bfd_error_handler)
+ (_("%B(%A+0x%lx): %s relocation against SEC_MERGE section"),
+ input_bfd, input_section,
+ (long) rel->r_offset, howto->name);
+ return FALSE;
+ }
+
+ addend = bfd_get_32 (input_bfd, contents + rel->r_offset);
+ msec = sec;
+ addend =
+ _bfd_elf_rel_local_sym (output_bfd, sym, &msec, addend)
+ - relocation;
+ addend += msec->output_section->vma + msec->output_offset;
+ bfd_put_32 (input_bfd, addend, contents + rel->r_offset);
+ addend = 0;
+ }
+ }
+ else
+ {
+ /* FIXME: Ought to make use of the RELOC_FOR_GLOBAL_SYMBOL macro. */
+
+ relocation = 0;
+ h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+ symname = h->root.root.string;
+ while (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ {
+#ifdef INCLUDE_SHMEDIA
+ /* If the reference passes a symbol marked with
+ STT_DATALABEL, then any STO_SH5_ISA32 on the final value
+ doesn't count. */
+ seen_stt_datalabel |= h->type == STT_DATALABEL;
+#endif
+ 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)
+ {
+ bfd_boolean dyn;
+
+ dyn = htab ? htab->root.dynamic_sections_created : FALSE;
+ 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_GOTPC_LOW16
+ || r_type == R_SH_GOTPC_MEDLOW16
+ || r_type == R_SH_GOTPC_MEDHI16
+ || r_type == R_SH_GOTPC_HI16
+ || ((r_type == R_SH_PLT32
+ || r_type == R_SH_PLT_LOW16
+ || r_type == R_SH_PLT_MEDLOW16
+ || r_type == R_SH_PLT_MEDHI16
+ || r_type == R_SH_PLT_HI16)
+ && h->plt.offset != (bfd_vma) -1)
+ || ((r_type == R_SH_GOT32
+ || r_type == R_SH_GOT20
+ || r_type == R_SH_GOTFUNCDESC
+ || r_type == R_SH_GOTFUNCDESC20
+ || r_type == R_SH_GOTOFFFUNCDESC
+ || r_type == R_SH_GOTOFFFUNCDESC20
+ || r_type == R_SH_FUNCDESC
+ || r_type == R_SH_GOT_LOW16
+ || r_type == R_SH_GOT_MEDLOW16
+ || r_type == R_SH_GOT_MEDHI16
+ || r_type == R_SH_GOT_HI16)
+ && WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
+ && (! info->shared
+ || (! info->symbolic && h->dynindx != -1)
+ || !h->def_regular))
+ /* 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->def_regular)
+ && ((r_type == R_SH_DIR32
+ && !h->forced_local)
+ || (r_type == R_SH_REL32
+ && !SYMBOL_CALLS_LOCAL (info, h)))
+ && ((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
+ && h->def_dynamic)))
+ /* Dynamic relocs are not propagated for SEC_DEBUGGING
+ sections because such sections are not SEC_ALLOC and
+ thus ld.so will not process them. */
+ || (sec->output_section == NULL
+ && ((input_section->flags & SEC_DEBUGGING) != 0
+ && h->def_dynamic))
+ || (sec->output_section == NULL
+ && (sh_elf_hash_entry (h)->got_type == GOT_TLS_IE
+ || sh_elf_hash_entry (h)->got_type == GOT_TLS_GD)))
+ ;
+ else if (sec->output_section != NULL)
+ relocation = ((h->root.u.def.value
+ + sec->output_section->vma
+ + sec->output_offset)
+ /* A STO_SH5_ISA32 causes a "bitor 1" to the
+ symbol value, unless we've seen
+ STT_DATALABEL on the way to it. */
+ | ((h->other & STO_SH5_ISA32) != 0
+ && ! seen_stt_datalabel));
+ else if (!info->relocatable
+ && (_bfd_elf_section_offset (output_bfd, info,
+ input_section,
+ rel->r_offset)
+ != (bfd_vma) -1))
+ {
+ (*_bfd_error_handler)
+ (_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"),
+ input_bfd,
+ input_section,
+ (long) rel->r_offset,
+ howto->name,
+ h->root.root.string);
+ return FALSE;
+ }
+ }
+ else if (h->root.type == bfd_link_hash_undefweak)
+ ;
+ else if (info->unresolved_syms_in_objects == RM_IGNORE
+ && ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
+ ;
+ else if (!info->relocatable)
+ {
+ if (! info->callbacks->undefined_symbol
+ (info, h->root.root.string, input_bfd,
+ input_section, rel->r_offset,
+ (info->unresolved_syms_in_objects == RM_GENERATE_ERROR
+ || ELF_ST_VISIBILITY (h->other))))
+ return FALSE;
+ }
+ }
+
+ if (sec != NULL && discarded_section (sec))
+ RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
+ rel, 1, relend, howto, 0, contents);
+
+ if (info->relocatable)
+ continue;
+
+ /* Check for inter-segment relocations in FDPIC files. Most
+ relocations connect the relocation site to the location of
+ the target symbol, but there are some exceptions below. */
+ check_segment[0] = isec_segment;
+ if (sec != NULL)
+ check_segment[1] = sh_elf_osec_to_segment (output_bfd,
+ sec->output_section);
+ else
+ check_segment[1] = -1;
+
+ 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:
+ goto final_link_relocate;
+
+ case R_SH_DIR8WPN:
+ case R_SH_DIR8WPZ:
+ case R_SH_DIR8WPL:
+ /* If the reloc is against the start of this section, then
+ the assembler has already taken care of it and the reloc
+ is here only to assist in relaxing. If the reloc is not
+ against the start of this section, then it's against an
+ external symbol and we must deal with it ourselves. */
+ if (input_section->output_section->vma + input_section->output_offset
+ != relocation)
+ {
+ int disp = (relocation
+ - input_section->output_section->vma
+ - input_section->output_offset
+ - rel->r_offset);
+ int mask = 0;
+ switch (r_type)
+ {
+ case R_SH_DIR8WPN:
+ case R_SH_DIR8WPZ: mask = 1; break;
+ case R_SH_DIR8WPL: mask = 3; break;
+ default: mask = 0; break;
+ }
+ if (disp & mask)
+ {
+ ((*_bfd_error_handler)
+ (_("%B: 0x%lx: fatal: unaligned branch target for relax-support relocation"),
+ input_section->owner,
+ (unsigned long) rel->r_offset));
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+ relocation -= 4;
+ goto final_link_relocate;
+ }
+ r = bfd_reloc_ok;
+ break;
+
+ default:
+#ifdef INCLUDE_SHMEDIA
+ if (shmedia_prepare_reloc (info, input_bfd, input_section,
+ contents, rel, &relocation))
+ goto final_link_relocate;
+#endif
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+
+ case R_SH_DIR16:
+ case R_SH_DIR8:
+ case R_SH_DIR8U:
+ case R_SH_DIR8S:
+ case R_SH_DIR4U:
+ goto final_link_relocate;
+
+ case R_SH_DIR8UL:
+ case R_SH_DIR4UL:
+ if (relocation & 3)
+ {
+ ((*_bfd_error_handler)
+ (_("%B: 0x%lx: fatal: unaligned %s relocation 0x%lx"),
+ input_section->owner,
+ (unsigned long) rel->r_offset, howto->name,
+ (unsigned long) relocation));
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+ goto final_link_relocate;
+
+ case R_SH_DIR8UW:
+ case R_SH_DIR8SW:
+ case R_SH_DIR4UW:
+ if (relocation & 1)
+ {
+ ((*_bfd_error_handler)
+ (_("%B: 0x%lx: fatal: unaligned %s relocation 0x%lx"),
+ input_section->owner,
+ (unsigned long) rel->r_offset, howto->name,
+ (unsigned long) relocation));
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+ goto final_link_relocate;
+
+ case R_SH_PSHA:
+ if ((signed int)relocation < -32
+ || (signed int)relocation > 32)
+ {
+ ((*_bfd_error_handler)
+ (_("%B: 0x%lx: fatal: R_SH_PSHA relocation %d not in range -32..32"),
+ input_section->owner,
+ (unsigned long) rel->r_offset,
+ (unsigned long) relocation));
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+ goto final_link_relocate;
+
+ case R_SH_PSHL:
+ if ((signed int)relocation < -16
+ || (signed int)relocation > 16)
+ {
+ ((*_bfd_error_handler)
+ (_("%B: 0x%lx: fatal: R_SH_PSHL relocation %d not in range -32..32"),
+ input_section->owner,
+ (unsigned long) rel->r_offset,
+ (unsigned long) relocation));
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+ goto final_link_relocate;
+
+ case R_SH_DIR32:
+ case R_SH_REL32:
+#ifdef INCLUDE_SHMEDIA
+ case R_SH_IMM_LOW16_PCREL:
+ case R_SH_IMM_MEDLOW16_PCREL:
+ case R_SH_IMM_MEDHI16_PCREL:
+ case R_SH_IMM_HI16_PCREL:
+#endif
+ if (info->shared
+ && (h == NULL
+ || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+ || h->root.type != bfd_link_hash_undefweak)
+ && r_symndx != STN_UNDEF
+ && (input_section->flags & SEC_ALLOC) != 0
+ && !is_vxworks_tls
+ && (r_type == R_SH_DIR32
+ || !SYMBOL_CALLS_LOCAL (info, h)))
+ {
+ Elf_Internal_Rela outrel;
+ bfd_byte *loc;
+ bfd_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)
+ {
+ sreloc = _bfd_elf_get_dynamic_reloc_section
+ (input_bfd, input_section, /*rela?*/ TRUE);
+ if (sreloc == NULL)
+ return FALSE;
+ }
+
+ skip = FALSE;
+ relocate = FALSE;
+
+ outrel.r_offset =
+ _bfd_elf_section_offset (output_bfd, info, input_section,
+ rel->r_offset);
+ if (outrel.r_offset == (bfd_vma) -1)
+ skip = TRUE;
+ else if (outrel.r_offset == (bfd_vma) -2)
+ skip = TRUE, relocate = TRUE;
+ outrel.r_offset += (input_section->output_section->vma
+ + input_section->output_offset);
+
+ if (skip)
+ memset (&outrel, 0, sizeof outrel);
+ else if (r_type == R_SH_REL32)
+ {
+ BFD_ASSERT (h != NULL && h->dynindx != -1);
+ outrel.r_info = ELF32_R_INFO (h->dynindx, R_SH_REL32);
+ outrel.r_addend
+ = (howto->partial_inplace
+ ? bfd_get_32 (input_bfd, contents + rel->r_offset)
+ : addend);
+ }
+#ifdef INCLUDE_SHMEDIA
+ else if (r_type == R_SH_IMM_LOW16_PCREL
+ || r_type == R_SH_IMM_MEDLOW16_PCREL
+ || r_type == R_SH_IMM_MEDHI16_PCREL
+ || r_type == R_SH_IMM_HI16_PCREL)
+ {
+ BFD_ASSERT (h != NULL && h->dynindx != -1);
+ outrel.r_info = ELF32_R_INFO (h->dynindx, r_type);
+ outrel.r_addend = addend;
+ }
+#endif
+ else if (fdpic_p
+ && (h == NULL
+ || ((info->symbolic || h->dynindx == -1)
+ && h->def_regular)))
+ {
+ int dynindx;
+
+ BFD_ASSERT (sec != NULL);
+ BFD_ASSERT (sec->output_section != NULL);
+ dynindx = elf_section_data (sec->output_section)->dynindx;
+ outrel.r_info = ELF32_R_INFO (dynindx, R_SH_DIR32);
+ outrel.r_addend = relocation;
+ outrel.r_addend
+ += (howto->partial_inplace
+ ? bfd_get_32 (input_bfd, contents + rel->r_offset)
+ : addend);
+ outrel.r_addend -= sec->output_section->vma;
+ }
+ else
+ {
+ /* h->dynindx may be -1 if this symbol was marked to
+ become local. */
+ if (h == NULL
+ || ((info->symbolic || h->dynindx == -1)
+ && h->def_regular))
+ {
+ relocate = howto->partial_inplace;
+ outrel.r_info = ELF32_R_INFO (0, R_SH_RELATIVE);
+ }
+ else
+ {
+ BFD_ASSERT (h->dynindx != -1);
+ outrel.r_info = ELF32_R_INFO (h->dynindx, R_SH_DIR32);
+ }
+ outrel.r_addend = relocation;
+ outrel.r_addend
+ += (howto->partial_inplace
+ ? bfd_get_32 (input_bfd, contents + rel->r_offset)
+ : addend);
+ }
+
+ loc = sreloc->contents;
+ loc += sreloc->reloc_count++ * sizeof (Elf32_External_Rela);
+ bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
+
+ check_segment[0] = check_segment[1] = -1;
+
+ /* 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 (fdpic_p && !info->shared
+ && r_type == R_SH_DIR32
+ && (input_section->flags & SEC_ALLOC) != 0)
+ {
+ bfd_vma offset;
+
+ BFD_ASSERT (htab);
+
+ if (sh_elf_osec_readonly_p (output_bfd,
+ input_section->output_section))
+ {
+ (*_bfd_error_handler)
+ (_("%B(%A+0x%lx): cannot emit fixup to `%s' in read-only section"),
+ input_bfd,
+ input_section,
+ (long) rel->r_offset,
+ symname);
+ return FALSE;
+ }
+
+ offset = _bfd_elf_section_offset (output_bfd, info,
+ input_section, rel->r_offset);
+ if (offset != (bfd_vma)-1)
+ sh_elf_add_rofixup (output_bfd, htab->srofixup,
+ input_section->output_section->vma
+ + input_section->output_offset
+ + rel->r_offset);
+
+ check_segment[0] = check_segment[1] = -1;
+ }
+ goto final_link_relocate;
+
+ case R_SH_GOTPLT32:
+#ifdef INCLUDE_SHMEDIA
+ case R_SH_GOTPLT_LOW16:
+ case R_SH_GOTPLT_MEDLOW16:
+ case R_SH_GOTPLT_MEDHI16:
+ case R_SH_GOTPLT_HI16:
+ case R_SH_GOTPLT10BY4:
+ case R_SH_GOTPLT10BY8:
+#endif
+ /* Relocation is to the entry for this symbol in the
+ procedure linkage table. */
+
+ if (h == NULL
+ || h->forced_local
+ || ! info->shared
+ || info->symbolic
+ || h->dynindx == -1
+ || h->plt.offset == (bfd_vma) -1
+ || h->got.offset != (bfd_vma) -1)
+ goto force_got;
+
+ /* Relocation is to the entry for this symbol in the global
+ offset table extension for the procedure linkage table. */
+
+ BFD_ASSERT (htab);
+ BFD_ASSERT (sgotplt != NULL);
+ relocation = (sgotplt->output_offset
+ + (get_plt_index (htab->plt_info, h->plt.offset)
+ + 3) * 4);
+
+#ifdef GOT_BIAS
+ relocation -= GOT_BIAS;
+#endif
+
+ goto final_link_relocate;
+
+ force_got:
+ case R_SH_GOT32:
+ case R_SH_GOT20:
+#ifdef INCLUDE_SHMEDIA
+ case R_SH_GOT_LOW16:
+ case R_SH_GOT_MEDLOW16:
+ case R_SH_GOT_MEDHI16:
+ case R_SH_GOT_HI16:
+ case R_SH_GOT10BY4:
+ case R_SH_GOT10BY8:
+#endif
+ /* Relocation is to the entry for this symbol in the global
+ offset table. */
+
+ BFD_ASSERT (htab);
+ BFD_ASSERT (sgot != NULL);
+ check_segment[0] = check_segment[1] = -1;
+
+ if (h != NULL)
+ {
+ bfd_boolean dyn;
+
+ off = h->got.offset;
+#ifdef INCLUDE_SHMEDIA
+ if (seen_stt_datalabel)
+ {
+ struct elf_sh_link_hash_entry *hsh;
+
+ hsh = (struct elf_sh_link_hash_entry *)h;
+ off = hsh->datalabel_got.offset;
+ }
+#endif
+ BFD_ASSERT (off != (bfd_vma) -1);
+
+ dyn = htab->root.dynamic_sections_created;
+ if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
+ || (info->shared
+ && SYMBOL_REFERENCES_LOCAL (info, h))
+ || (ELF_ST_VISIBILITY (h->other)
+ && h->root.type == bfd_link_hash_undefweak))
+ {
+ /* 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);
+#ifdef INCLUDE_SHMEDIA
+ if (seen_stt_datalabel)
+ {
+ struct elf_sh_link_hash_entry *hsh;
+
+ hsh = (struct elf_sh_link_hash_entry *)h;
+ hsh->datalabel_got.offset |= 1;
+ }
+ else
+#endif
+ h->got.offset |= 1;
+
+ /* If we initialize the GOT entry here with a valid
+ symbol address, also add a fixup. */
+ if (fdpic_p && !info->shared
+ && sh_elf_hash_entry (h)->got_type == GOT_NORMAL
+ && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+ || h->root.type != bfd_link_hash_undefweak))
+ sh_elf_add_rofixup (output_bfd, htab->srofixup,
+ sgot->output_section->vma
+ + sgot->output_offset
+ + off);
+ }
+ }
+
+ relocation = sh_elf_got_offset (htab) + off;
+ }
+ else
+ {
+#ifdef INCLUDE_SHMEDIA
+ if (rel->r_addend)
+ {
+ BFD_ASSERT (local_got_offsets != NULL
+ && (local_got_offsets[symtab_hdr->sh_info
+ + r_symndx]
+ != (bfd_vma) -1));
+
+ off = local_got_offsets[symtab_hdr->sh_info
+ + r_symndx];
+ }
+ else
+ {
+#endif
+ BFD_ASSERT (local_got_offsets != NULL
+ && local_got_offsets[r_symndx] != (bfd_vma) -1);
+
+ off = local_got_offsets[r_symndx];
+#ifdef INCLUDE_SHMEDIA
+ }
+#endif
+
+ /* 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)
+ {
+ Elf_Internal_Rela outrel;
+ bfd_byte *loc;
+
+ if (srelgot == NULL)
+ {
+ srelgot = bfd_get_linker_section (dynobj,
+ ".rela.got");
+ BFD_ASSERT (srelgot != NULL);
+ }
+
+ outrel.r_offset = (sgot->output_section->vma
+ + sgot->output_offset
+ + off);
+ if (fdpic_p)
+ {
+ int dynindx
+ = elf_section_data (sec->output_section)->dynindx;
+ outrel.r_info = ELF32_R_INFO (dynindx, R_SH_DIR32);
+ outrel.r_addend = relocation;
+ outrel.r_addend -= sec->output_section->vma;
+ }
+ else
+ {
+ outrel.r_info = ELF32_R_INFO (0, R_SH_RELATIVE);
+ outrel.r_addend = relocation;
+ }
+ loc = srelgot->contents;
+ loc += srelgot->reloc_count++ * sizeof (Elf32_External_Rela);
+ bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
+ }
+ else if (fdpic_p
+ && (sh_elf_local_got_type (input_bfd) [r_symndx]
+ == GOT_NORMAL))
+ sh_elf_add_rofixup (output_bfd, htab->srofixup,
+ sgot->output_section->vma
+ + sgot->output_offset
+ + off);
+
+#ifdef INCLUDE_SHMEDIA
+ if (rel->r_addend)
+ local_got_offsets[symtab_hdr->sh_info + r_symndx] |= 1;
+ else
+#endif
+ local_got_offsets[r_symndx] |= 1;
+ }
+
+ relocation = sh_elf_got_offset (htab) + off;
+ }
+
+#ifdef GOT_BIAS
+ relocation -= GOT_BIAS;
+#endif
+
+ if (r_type == R_SH_GOT20)
+ {
+ r = install_movi20_field (output_bfd, relocation + addend,
+ input_bfd, input_section, contents,
+ rel->r_offset);
+ break;
+ }
+ else
+ goto final_link_relocate;
+
+ case R_SH_GOTOFF:
+ case R_SH_GOTOFF20:
+#ifdef INCLUDE_SHMEDIA
+ case R_SH_GOTOFF_LOW16:
+ case R_SH_GOTOFF_MEDLOW16:
+ case R_SH_GOTOFF_MEDHI16:
+ case R_SH_GOTOFF_HI16:
+#endif
+ /* GOTOFF relocations are relative to _GLOBAL_OFFSET_TABLE_, which
+ we place at the start of the .got.plt section. This is the same
+ as the start of the output .got section, unless there are function
+ descriptors in front of it. */
+ BFD_ASSERT (htab);
+ BFD_ASSERT (sgotplt != NULL);
+ check_segment[0] = got_segment;
+ relocation -= sgotplt->output_section->vma + sgotplt->output_offset
+ + htab->root.hgot->root.u.def.value;
+
+#ifdef GOT_BIAS
+ relocation -= GOT_BIAS;
+#endif
+
+ addend = rel->r_addend;
+
+ if (r_type == R_SH_GOTOFF20)
+ {
+ r = install_movi20_field (output_bfd, relocation + addend,
+ input_bfd, input_section, contents,
+ rel->r_offset);
+ break;
+ }
+ else
+ goto final_link_relocate;
+
+ case R_SH_GOTPC:
+#ifdef INCLUDE_SHMEDIA
+ case R_SH_GOTPC_LOW16:
+ case R_SH_GOTPC_MEDLOW16:
+ case R_SH_GOTPC_MEDHI16:
+ case R_SH_GOTPC_HI16:
+#endif
+ /* Use global offset table as symbol value. */
+
+ BFD_ASSERT (sgotplt != NULL);
+ relocation = sgotplt->output_section->vma + sgotplt->output_offset;
+
+#ifdef GOT_BIAS
+ relocation += GOT_BIAS;
+#endif
+
+ addend = rel->r_addend;
+
+ goto final_link_relocate;
+
+ case R_SH_PLT32:
+#ifdef INCLUDE_SHMEDIA
+ case R_SH_PLT_LOW16:
+ case R_SH_PLT_MEDLOW16:
+ case R_SH_PLT_MEDHI16:
+ case R_SH_PLT_HI16:
+#endif
+ /* 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;
+
+ /* We don't want to warn on calls to undefined weak symbols,
+ as calls to them must be protected by non-NULL tests
+ anyway, and unprotected calls would invoke undefined
+ behavior. */
+ if (h->root.type == bfd_link_hash_undefweak)
+ check_segment[0] = check_segment[1] = -1;
+
+ if (h->forced_local)
+ 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;
+ }
+
+ BFD_ASSERT (splt != NULL);
+ check_segment[1] = plt_segment;
+ relocation = (splt->output_section->vma
+ + splt->output_offset
+ + h->plt.offset);
+
+#ifdef INCLUDE_SHMEDIA
+ relocation++;
+#endif
+
+ addend = rel->r_addend;
+
+ goto final_link_relocate;
+
+ /* Relocation is to the canonical function descriptor for this
+ symbol, possibly via the GOT. Initialize the GOT
+ entry and function descriptor if necessary. */
+ case R_SH_GOTFUNCDESC:
+ case R_SH_GOTFUNCDESC20:
+ case R_SH_FUNCDESC:
+ {
+ int dynindx = -1;
+ asection *reloc_section;
+ bfd_vma reloc_offset;
+ int reloc_type = R_SH_FUNCDESC;
+
+ BFD_ASSERT (htab);
+
+ check_segment[0] = check_segment[1] = -1;
+
+ /* FIXME: See what FRV does for global symbols in the
+ executable, with --export-dynamic. Do they need ld.so
+ to allocate official descriptors? See what this code
+ does. */
+
+ relocation = 0;
+ addend = 0;
+
+ if (r_type == R_SH_FUNCDESC)
+ {
+ reloc_section = input_section;
+ reloc_offset = rel->r_offset;
+ }
+ else
+ {
+ reloc_section = sgot;
+
+ if (h != NULL)
+ reloc_offset = h->got.offset;
+ else
+ {
+ BFD_ASSERT (local_got_offsets != NULL);
+ reloc_offset = local_got_offsets[r_symndx];
+ }
+ BFD_ASSERT (reloc_offset != MINUS_ONE);
+
+ if (reloc_offset & 1)
+ {
+ reloc_offset &= ~1;
+ goto funcdesc_done_got;
+ }
+ }
+
+ if (h && h->root.type == bfd_link_hash_undefweak
+ && (SYMBOL_CALLS_LOCAL (info, h)
+ || !htab->root.dynamic_sections_created))
+ /* Undefined weak symbol which will not be dynamically
+ resolved later; leave it at zero. */
+ goto funcdesc_leave_zero;
+ else if (SYMBOL_CALLS_LOCAL (info, h)
+ && ! SYMBOL_FUNCDESC_LOCAL (info, h))
+ {
+ /* If the symbol needs a non-local function descriptor
+ but binds locally (i.e., its visibility is
+ protected), emit a dynamic relocation decayed to
+ section+offset. This is an optimization; the dynamic
+ linker would resolve our function descriptor request
+ to our copy of the function anyway. */
+ dynindx = elf_section_data (h->root.u.def.section
+ ->output_section)->dynindx;
+ relocation += h->root.u.def.section->output_offset
+ + h->root.u.def.value;
+ }
+ else if (! SYMBOL_FUNCDESC_LOCAL (info, h))
+ {
+ /* If the symbol is dynamic and there will be dynamic
+ symbol resolution because we are or are linked with a
+ shared library, emit a FUNCDESC relocation such that
+ the dynamic linker will allocate the function
+ descriptor. */
+ BFD_ASSERT (h->dynindx != -1);
+ dynindx = h->dynindx;
+ }
+ else
+ {
+ bfd_vma offset;
+
+ /* Otherwise, we know we have a private function
+ descriptor, so reference it directly. */
+ reloc_type = R_SH_DIR32;
+ dynindx = elf_section_data (htab->sfuncdesc
+ ->output_section)->dynindx;
+
+ if (h)
+ {
+ offset = sh_elf_hash_entry (h)->funcdesc.offset;
+ BFD_ASSERT (offset != MINUS_ONE);
+ if ((offset & 1) == 0)
+ {
+ if (!sh_elf_initialize_funcdesc (output_bfd, info, h,
+ offset, NULL, 0))
+ return FALSE;
+ sh_elf_hash_entry (h)->funcdesc.offset |= 1;
+ }
+ }
+ else
+ {
+ union gotref *local_funcdesc;
+
+ local_funcdesc = sh_elf_local_funcdesc (input_bfd);
+ offset = local_funcdesc[r_symndx].offset;
+ BFD_ASSERT (offset != MINUS_ONE);
+ if ((offset & 1) == 0)
+ {
+ if (!sh_elf_initialize_funcdesc (output_bfd, info, NULL,
+ offset, sec,
+ sym->st_value))
+ return FALSE;
+ local_funcdesc[r_symndx].offset |= 1;
+ }
+ }
+
+ relocation = htab->sfuncdesc->output_offset + (offset & ~1);
+ }
+
+ if (!info->shared && SYMBOL_FUNCDESC_LOCAL (info, h))
+ {
+ bfd_vma offset;
+
+ if (sh_elf_osec_readonly_p (output_bfd,
+ reloc_section->output_section))
+ {
+ (*_bfd_error_handler)
+ (_("%B(%A+0x%lx): cannot emit fixup to `%s' in read-only section"),
+ input_bfd,
+ input_section,
+ (long) rel->r_offset,
+ symname);
+ return FALSE;
+ }
+
+ offset = _bfd_elf_section_offset (output_bfd, info,
+ reloc_section, reloc_offset);
+
+ if (offset != (bfd_vma)-1)
+ sh_elf_add_rofixup (output_bfd, htab->srofixup,
+ offset
+ + reloc_section->output_section->vma
+ + reloc_section->output_offset);
+ }
+ else if ((reloc_section->output_section->flags
+ & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD))
+ {
+ bfd_vma offset;
+
+ if (sh_elf_osec_readonly_p (output_bfd,
+ reloc_section->output_section))
+ {
+ info->callbacks->warning
+ (info,
+ _("cannot emit dynamic relocations in read-only section"),
+ symname, input_bfd, reloc_section, reloc_offset);
+ return FALSE;
+ }
+
+ if (srelgot == NULL)
+ {
+ srelgot = bfd_get_linker_section (dynobj, ".rela.got");
+ BFD_ASSERT (srelgot != NULL);
+ }
+
+ offset = _bfd_elf_section_offset (output_bfd, info,
+ reloc_section, reloc_offset);
+
+ if (offset != (bfd_vma)-1)
+ sh_elf_add_dyn_reloc (output_bfd, srelgot,
+ offset
+ + reloc_section->output_section->vma
+ + reloc_section->output_offset,
+ reloc_type, dynindx, relocation);
+
+ if (r_type == R_SH_FUNCDESC)
+ {
+ r = bfd_reloc_ok;
+ break;
+ }
+ else
+ {
+ relocation = 0;
+ goto funcdesc_leave_zero;
+ }
+ }
+
+ if (SYMBOL_FUNCDESC_LOCAL (info, h))
+ relocation += htab->sfuncdesc->output_section->vma;
+ funcdesc_leave_zero:
+ if (r_type != R_SH_FUNCDESC)
+ {
+ bfd_put_32 (output_bfd, relocation,
+ reloc_section->contents + reloc_offset);
+ if (h != NULL)
+ h->got.offset |= 1;
+ else
+ local_got_offsets[r_symndx] |= 1;
+
+ funcdesc_done_got:
+
+ relocation = sh_elf_got_offset (htab) + reloc_offset;
+#ifdef GOT_BIAS
+ relocation -= GOT_BIAS;
+#endif
+ }
+ if (r_type == R_SH_GOTFUNCDESC20)
+ {
+ r = install_movi20_field (output_bfd, relocation + addend,
+ input_bfd, input_section, contents,
+ rel->r_offset);
+ break;
+ }
+ else
+ goto final_link_relocate;
+ }
+ break;
+
+ case R_SH_GOTOFFFUNCDESC:
+ case R_SH_GOTOFFFUNCDESC20:
+ /* FIXME: See R_SH_FUNCDESC comment about global symbols in the
+ executable and --export-dynamic. If such symbols get
+ ld.so-allocated descriptors we can not use R_SH_GOTOFFFUNCDESC
+ for them. */
+ BFD_ASSERT (htab);
+
+ check_segment[0] = check_segment[1] = -1;
+ relocation = 0;
+ addend = rel->r_addend;
+
+ if (h && (h->root.type == bfd_link_hash_undefweak
+ || !SYMBOL_FUNCDESC_LOCAL (info, h)))
+ {
+ _bfd_error_handler
+ (_("%B(%A+0x%lx): %s relocation against external symbol \"%s\""),
+ input_bfd, input_section, (long) rel->r_offset, howto->name,
+ h->root.root.string);
+ return FALSE;
+ }
+ else
+ {
+ bfd_vma offset;
+
+ /* Otherwise, we know we have a private function
+ descriptor, so reference it directly. */
+ if (h)
+ {
+ offset = sh_elf_hash_entry (h)->funcdesc.offset;
+ BFD_ASSERT (offset != MINUS_ONE);
+ if ((offset & 1) == 0)
+ {
+ if (!sh_elf_initialize_funcdesc (output_bfd, info, h,
+ offset, NULL, 0))
+ return FALSE;
+ sh_elf_hash_entry (h)->funcdesc.offset |= 1;
+ }
+ }
+ else
+ {
+ union gotref *local_funcdesc;
+
+ local_funcdesc = sh_elf_local_funcdesc (input_bfd);
+ offset = local_funcdesc[r_symndx].offset;
+ BFD_ASSERT (offset != MINUS_ONE);
+ if ((offset & 1) == 0)
+ {
+ if (!sh_elf_initialize_funcdesc (output_bfd, info, NULL,
+ offset, sec,
+ sym->st_value))
+ return FALSE;
+ local_funcdesc[r_symndx].offset |= 1;
+ }
+ }
+
+ relocation = htab->sfuncdesc->output_offset + (offset & ~1);
+ }
+
+ relocation -= (htab->root.hgot->root.u.def.value
+ + sgotplt->output_offset);
+#ifdef GOT_BIAS
+ relocation -= GOT_BIAS;
+#endif
+
+ if (r_type == R_SH_GOTOFFFUNCDESC20)
+ {
+ r = install_movi20_field (output_bfd, relocation + addend,
+ input_bfd, input_section, contents,
+ rel->r_offset);
+ break;
+ }
+ else
+ 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;
+ }
+
+ case R_SH_TLS_GD_32:
+ case R_SH_TLS_IE_32:
+ BFD_ASSERT (htab);
+ check_segment[0] = check_segment[1] = -1;
+ r_type = sh_elf_optimized_tls_reloc (info, r_type, h == NULL);
+ got_type = GOT_UNKNOWN;
+ if (h == NULL && local_got_offsets)
+ got_type = sh_elf_local_got_type (input_bfd) [r_symndx];
+ else if (h != NULL)
+ {
+ got_type = sh_elf_hash_entry (h)->got_type;
+ if (! info->shared
+ && (h->dynindx == -1
+ || h->def_regular))
+ r_type = R_SH_TLS_LE_32;
+ }
+
+ if (r_type == R_SH_TLS_GD_32 && got_type == GOT_TLS_IE)
+ r_type = R_SH_TLS_IE_32;
+
+ if (r_type == R_SH_TLS_LE_32)
+ {
+ bfd_vma offset;
+ unsigned short insn;
+
+ if (ELF32_R_TYPE (rel->r_info) == R_SH_TLS_GD_32)
+ {
+ /* GD->LE transition:
+ mov.l 1f,r4; mova 2f,r0; mov.l 2f,r1; add r0,r1;
+ jsr @r1; add r12,r4; bra 3f; nop; .align 2;
+ 1: .long x$TLSGD; 2: .long __tls_get_addr@PLT; 3:
+ We change it into:
+ mov.l 1f,r4; stc gbr,r0; add r4,r0; nop;
+ nop; nop; ...
+ 1: .long x@TPOFF; 2: .long __tls_get_addr@PLT; 3:. */
+
+ offset = rel->r_offset;
+ BFD_ASSERT (offset >= 16);
+ /* Size of GD instructions is 16 or 18. */
+ offset -= 16;
+ insn = bfd_get_16 (input_bfd, contents + offset + 0);
+ if ((insn & 0xff00) == 0xc700)
+ {
+ BFD_ASSERT (offset >= 2);
+ offset -= 2;
+ insn = bfd_get_16 (input_bfd, contents + offset + 0);
+ }
+
+ BFD_ASSERT ((insn & 0xff00) == 0xd400);
+ insn = bfd_get_16 (input_bfd, contents + offset + 2);
+ BFD_ASSERT ((insn & 0xff00) == 0xc700);
+ insn = bfd_get_16 (input_bfd, contents + offset + 4);
+ BFD_ASSERT ((insn & 0xff00) == 0xd100);
+ insn = bfd_get_16 (input_bfd, contents + offset + 6);
+ BFD_ASSERT (insn == 0x310c);
+ insn = bfd_get_16 (input_bfd, contents + offset + 8);
+ BFD_ASSERT (insn == 0x410b);
+ insn = bfd_get_16 (input_bfd, contents + offset + 10);
+ BFD_ASSERT (insn == 0x34cc);
+
+ bfd_put_16 (output_bfd, 0x0012, contents + offset + 2);
+ bfd_put_16 (output_bfd, 0x304c, contents + offset + 4);
+ bfd_put_16 (output_bfd, 0x0009, contents + offset + 6);
+ bfd_put_16 (output_bfd, 0x0009, contents + offset + 8);
+ bfd_put_16 (output_bfd, 0x0009, contents + offset + 10);
+ }
+ else
+ {
+ int target;
+
+ /* IE->LE transition:
+ mov.l 1f,r0; stc gbr,rN; mov.l @(r0,r12),rM;
+ bra 2f; add ...; .align 2; 1: x@GOTTPOFF; 2:
+ We change it into:
+ mov.l .Ln,rM; stc gbr,rN; nop; ...;
+ 1: x@TPOFF; 2:. */
+
+ offset = rel->r_offset;
+ BFD_ASSERT (offset >= 16);
+ /* Size of IE instructions is 10 or 12. */
+ offset -= 10;
+ insn = bfd_get_16 (input_bfd, contents + offset + 0);
+ if ((insn & 0xf0ff) == 0x0012)
+ {
+ BFD_ASSERT (offset >= 2);
+ offset -= 2;
+ insn = bfd_get_16 (input_bfd, contents + offset + 0);
+ }
+
+ BFD_ASSERT ((insn & 0xff00) == 0xd000);
+ target = insn & 0x00ff;
+ insn = bfd_get_16 (input_bfd, contents + offset + 2);
+ BFD_ASSERT ((insn & 0xf0ff) == 0x0012);
+ insn = bfd_get_16 (input_bfd, contents + offset + 4);
+ BFD_ASSERT ((insn & 0xf0ff) == 0x00ce);
+ insn = 0xd000 | (insn & 0x0f00) | target;
+ bfd_put_16 (output_bfd, insn, contents + offset + 0);
+ bfd_put_16 (output_bfd, 0x0009, contents + offset + 4);
+ }
+
+ bfd_put_32 (output_bfd, tpoff (info, relocation),
+ contents + rel->r_offset);
+ continue;
+ }
+
+ if (sgot == NULL || sgotplt == NULL)
+ abort ();
+
+ if (h != NULL)
+ off = h->got.offset;
+ else
+ {
+ if (local_got_offsets == NULL)
+ abort ();
+
+ off = local_got_offsets[r_symndx];
+ }
+
+ /* Relocate R_SH_TLS_IE_32 directly when statically linking. */
+ if (r_type == R_SH_TLS_IE_32
+ && ! htab->root.dynamic_sections_created)
+ {
+ off &= ~1;
+ bfd_put_32 (output_bfd, tpoff (info, relocation),
+ sgot->contents + off);
+ bfd_put_32 (output_bfd, sh_elf_got_offset (htab) + off,
+ contents + rel->r_offset);
+ continue;
+ }
+
+ if ((off & 1) != 0)
+ off &= ~1;
+ else
+ {
+ Elf_Internal_Rela outrel;
+ bfd_byte *loc;
+ int dr_type, indx;
+
+ if (srelgot == NULL)
+ {
+ srelgot = bfd_get_linker_section (dynobj, ".rela.got");
+ BFD_ASSERT (srelgot != NULL);
+ }
+
+ outrel.r_offset = (sgot->output_section->vma
+ + sgot->output_offset + off);
+
+ if (h == NULL || h->dynindx == -1)
+ indx = 0;
+ else
+ indx = h->dynindx;
+
+ dr_type = (r_type == R_SH_TLS_GD_32 ? R_SH_TLS_DTPMOD32 :
+ R_SH_TLS_TPOFF32);
+ if (dr_type == R_SH_TLS_TPOFF32 && indx == 0)
+ outrel.r_addend = relocation - dtpoff_base (info);
+ else
+ outrel.r_addend = 0;
+ outrel.r_info = ELF32_R_INFO (indx, dr_type);
+ loc = srelgot->contents;
+ loc += srelgot->reloc_count++ * sizeof (Elf32_External_Rela);
+ bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
+
+ if (r_type == R_SH_TLS_GD_32)
+ {
+ if (indx == 0)
+ {
+ bfd_put_32 (output_bfd,
+ relocation - dtpoff_base (info),
+ sgot->contents + off + 4);
+ }
+ else
+ {
+ outrel.r_info = ELF32_R_INFO (indx,
+ R_SH_TLS_DTPOFF32);
+ outrel.r_offset += 4;
+ outrel.r_addend = 0;
+ srelgot->reloc_count++;
+ loc += sizeof (Elf32_External_Rela);
+ bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
+ }
+ }
+
+ if (h != NULL)
+ h->got.offset |= 1;
+ else
+ local_got_offsets[r_symndx] |= 1;
+ }
+
+ if (off >= (bfd_vma) -2)
+ abort ();
+
+ if (r_type == (int) ELF32_R_TYPE (rel->r_info))
+ relocation = sh_elf_got_offset (htab) + off;
+ else
+ {
+ bfd_vma offset;
+ unsigned short insn;
+
+ /* GD->IE transition:
+ mov.l 1f,r4; mova 2f,r0; mov.l 2f,r1; add r0,r1;
+ jsr @r1; add r12,r4; bra 3f; nop; .align 2;
+ 1: .long x$TLSGD; 2: .long __tls_get_addr@PLT; 3:
+ We change it into:
+ mov.l 1f,r0; stc gbr,r4; mov.l @(r0,r12),r0; add r4,r0;
+ nop; nop; bra 3f; nop; .align 2;
+ 1: .long x@TPOFF; 2:...; 3:. */
+
+ offset = rel->r_offset;
+ BFD_ASSERT (offset >= 16);
+ /* Size of GD instructions is 16 or 18. */
+ offset -= 16;
+ insn = bfd_get_16 (input_bfd, contents + offset + 0);
+ if ((insn & 0xff00) == 0xc700)
+ {
+ BFD_ASSERT (offset >= 2);
+ offset -= 2;
+ insn = bfd_get_16 (input_bfd, contents + offset + 0);
+ }
+
+ BFD_ASSERT ((insn & 0xff00) == 0xd400);
+
+ /* Replace mov.l 1f,R4 with mov.l 1f,r0. */
+ bfd_put_16 (output_bfd, insn & 0xf0ff, contents + offset);
+
+ insn = bfd_get_16 (input_bfd, contents + offset + 2);
+ BFD_ASSERT ((insn & 0xff00) == 0xc700);
+ insn = bfd_get_16 (input_bfd, contents + offset + 4);
+ BFD_ASSERT ((insn & 0xff00) == 0xd100);
+ insn = bfd_get_16 (input_bfd, contents + offset + 6);
+ BFD_ASSERT (insn == 0x310c);
+ insn = bfd_get_16 (input_bfd, contents + offset + 8);
+ BFD_ASSERT (insn == 0x410b);
+ insn = bfd_get_16 (input_bfd, contents + offset + 10);
+ BFD_ASSERT (insn == 0x34cc);
+
+ bfd_put_16 (output_bfd, 0x0412, contents + offset + 2);
+ bfd_put_16 (output_bfd, 0x00ce, contents + offset + 4);
+ bfd_put_16 (output_bfd, 0x304c, contents + offset + 6);
+ bfd_put_16 (output_bfd, 0x0009, contents + offset + 8);
+ bfd_put_16 (output_bfd, 0x0009, contents + offset + 10);
+
+ bfd_put_32 (output_bfd, sh_elf_got_offset (htab) + off,
+ contents + rel->r_offset);
+
+ continue;
+ }
+
+ addend = rel->r_addend;
+
+ goto final_link_relocate;
+
+ case R_SH_TLS_LD_32:
+ BFD_ASSERT (htab);
+ check_segment[0] = check_segment[1] = -1;
+ if (! info->shared)
+ {
+ bfd_vma offset;
+ unsigned short insn;
+
+ /* LD->LE transition:
+ mov.l 1f,r4; mova 2f,r0; mov.l 2f,r1; add r0,r1;
+ jsr @r1; add r12,r4; bra 3f; nop; .align 2;
+ 1: .long x$TLSLD; 2: .long __tls_get_addr@PLT; 3:
+ We change it into:
+ stc gbr,r0; nop; nop; nop;
+ nop; nop; bra 3f; ...; 3:. */
+
+ offset = rel->r_offset;
+ BFD_ASSERT (offset >= 16);
+ /* Size of LD instructions is 16 or 18. */
+ offset -= 16;
+ insn = bfd_get_16 (input_bfd, contents + offset + 0);
+ if ((insn & 0xff00) == 0xc700)
+ {
+ BFD_ASSERT (offset >= 2);
+ offset -= 2;
+ insn = bfd_get_16 (input_bfd, contents + offset + 0);
+ }
+
+ BFD_ASSERT ((insn & 0xff00) == 0xd400);
+ insn = bfd_get_16 (input_bfd, contents + offset + 2);
+ BFD_ASSERT ((insn & 0xff00) == 0xc700);
+ insn = bfd_get_16 (input_bfd, contents + offset + 4);
+ BFD_ASSERT ((insn & 0xff00) == 0xd100);
+ insn = bfd_get_16 (input_bfd, contents + offset + 6);
+ BFD_ASSERT (insn == 0x310c);
+ insn = bfd_get_16 (input_bfd, contents + offset + 8);
+ BFD_ASSERT (insn == 0x410b);
+ insn = bfd_get_16 (input_bfd, contents + offset + 10);
+ BFD_ASSERT (insn == 0x34cc);
+
+ bfd_put_16 (output_bfd, 0x0012, contents + offset + 0);
+ bfd_put_16 (output_bfd, 0x0009, contents + offset + 2);
+ bfd_put_16 (output_bfd, 0x0009, contents + offset + 4);
+ bfd_put_16 (output_bfd, 0x0009, contents + offset + 6);
+ bfd_put_16 (output_bfd, 0x0009, contents + offset + 8);
+ bfd_put_16 (output_bfd, 0x0009, contents + offset + 10);
+
+ continue;
+ }
+
+ if (sgot == NULL || sgotplt == NULL)
+ abort ();
+
+ off = htab->tls_ldm_got.offset;
+ if (off & 1)
+ off &= ~1;
+ else
+ {
+ Elf_Internal_Rela outrel;
+ bfd_byte *loc;
+
+ srelgot = htab->srelgot;
+ if (srelgot == NULL)
+ abort ();
+
+ outrel.r_offset = (sgot->output_section->vma
+ + sgot->output_offset + off);
+ outrel.r_addend = 0;
+ outrel.r_info = ELF32_R_INFO (0, R_SH_TLS_DTPMOD32);
+ loc = srelgot->contents;
+ loc += srelgot->reloc_count++ * sizeof (Elf32_External_Rela);
+ bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
+ htab->tls_ldm_got.offset |= 1;
+ }
+
+ relocation = sh_elf_got_offset (htab) + off;
+ addend = rel->r_addend;
+
+ goto final_link_relocate;
+
+ case R_SH_TLS_LDO_32:
+ check_segment[0] = check_segment[1] = -1;
+ if (! info->shared)
+ relocation = tpoff (info, relocation);
+ else
+ relocation -= dtpoff_base (info);
+
+ addend = rel->r_addend;
+ goto final_link_relocate;
+
+ case R_SH_TLS_LE_32:
+ {
+ int indx;
+ Elf_Internal_Rela outrel;
+ bfd_byte *loc;
+
+ check_segment[0] = check_segment[1] = -1;
+
+ if (! info->shared || info->pie)
+ {
+ relocation = tpoff (info, relocation);
+ addend = rel->r_addend;
+ goto final_link_relocate;
+ }
+
+ if (sreloc == NULL)
+ {
+ sreloc = _bfd_elf_get_dynamic_reloc_section
+ (input_bfd, input_section, /*rela?*/ TRUE);
+ if (sreloc == NULL)
+ return FALSE;
+ }
+
+ if (h == NULL || h->dynindx == -1)
+ indx = 0;
+ else
+ indx = h->dynindx;
+
+ outrel.r_offset = (input_section->output_section->vma
+ + input_section->output_offset
+ + rel->r_offset);
+ outrel.r_info = ELF32_R_INFO (indx, R_SH_TLS_TPOFF32);
+ if (indx == 0)
+ outrel.r_addend = relocation - dtpoff_base (info);
+ else
+ outrel.r_addend = 0;
+
+ loc = sreloc->contents;
+ loc += sreloc->reloc_count++ * sizeof (Elf32_External_Rela);
+ bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
+ continue;
+ }
+ }
+
+ relocation_done:
+ if (fdpic_p && check_segment[0] != (unsigned) -1
+ && check_segment[0] != check_segment[1])
+ {
+ /* We don't want duplicate errors for undefined symbols. */
+ if (!h || h->root.type != bfd_link_hash_undefined)
+ {
+ if (info->shared)
+ {
+ info->callbacks->einfo
+ (_("%X%C: relocation to \"%s\" references a different segment\n"),
+ input_bfd, input_section, rel->r_offset, symname);
+ return FALSE;
+ }
+ else
+ info->callbacks->einfo
+ (_("%C: warning: relocation to \"%s\" references a different segment\n"),
+ input_bfd, input_section, rel->r_offset, symname);
+ }
+
+ elf_elfheader (output_bfd)->e_flags &= ~EF_SH_PIC;
+ }
+
+ if (r != bfd_reloc_ok)
+ {
+ switch (r)
+ {
+ default:
+ case bfd_reloc_outofrange:
+ abort ();
+ case bfd_reloc_overflow:
+ {
+ const char *name;
+
+ if (h != NULL)
+ name = NULL;
+ else
+ {
+ name = (bfd_elf_string_from_elf_section
+ (input_bfd, symtab_hdr->sh_link, sym->st_name));
+ if (name == NULL)
+ return FALSE;
+ if (*name == '\0')
+ name = bfd_section_name (input_bfd, sec);
+ }
+ if (! ((*info->callbacks->reloc_overflow)
+ (info, (h ? &h->root : NULL), name, howto->name,
+ (bfd_vma) 0, input_bfd, input_section,
+ rel->r_offset)))
+ return FALSE;
+ }
+ break;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+/* This is a version of bfd_generic_get_relocated_section_contents
+ which uses sh_elf_relocate_section. */
+
+static bfd_byte *
+sh_elf_get_relocated_section_contents (bfd *output_bfd,
+ struct bfd_link_info *link_info,
+ struct bfd_link_order *link_order,
+ bfd_byte *data,
+ bfd_boolean relocatable,
+ asymbol **symbols)
+{
+ Elf_Internal_Shdr *symtab_hdr;
+ asection *input_section = link_order->u.indirect.section;
+ bfd *input_bfd = input_section->owner;
+ asection **sections = NULL;
+ Elf_Internal_Rela *internal_relocs = NULL;
+ Elf_Internal_Sym *isymbuf = NULL;
+
+ /* We only need to handle the case of relaxing, or of having a
+ particular set of section contents, specially. */
+ if (relocatable
+ || elf_section_data (input_section)->this_hdr.contents == NULL)
+ return bfd_generic_get_relocated_section_contents (output_bfd, link_info,
+ link_order, data,
+ relocatable,
+ symbols);
+
+ symtab_hdr = &elf_symtab_hdr (input_bfd);
+
+ memcpy (data, elf_section_data (input_section)->this_hdr.contents,
+ (size_t) input_section->size);
+
+ if ((input_section->flags & SEC_RELOC) != 0
+ && input_section->reloc_count > 0)
+ {
+ asection **secpp;
+ Elf_Internal_Sym *isym, *isymend;
+ bfd_size_type amt;
+
+ internal_relocs = (_bfd_elf_link_read_relocs
+ (input_bfd, input_section, NULL,
+ (Elf_Internal_Rela *) NULL, FALSE));
+ if (internal_relocs == NULL)
+ goto error_return;
+
+ if (symtab_hdr->sh_info != 0)
+ {
+ isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
+ if (isymbuf == NULL)
+ isymbuf = bfd_elf_get_elf_syms (input_bfd, symtab_hdr,
+ symtab_hdr->sh_info, 0,
+ NULL, NULL, NULL);
+ if (isymbuf == NULL)
+ goto error_return;
+ }
+
+ amt = symtab_hdr->sh_info;
+ amt *= sizeof (asection *);
+ sections = (asection **) bfd_malloc (amt);
+ if (sections == NULL && amt != 0)
+ goto error_return;
+
+ isymend = isymbuf + symtab_hdr->sh_info;
+ for (isym = isymbuf, secpp = sections; isym < isymend; ++isym, ++secpp)
+ {
+ asection *isec;
+
+ if (isym->st_shndx == SHN_UNDEF)
+ isec = bfd_und_section_ptr;
+ else if (isym->st_shndx == SHN_ABS)
+ isec = bfd_abs_section_ptr;
+ else if (isym->st_shndx == SHN_COMMON)
+ isec = bfd_com_section_ptr;
+ else
+ isec = bfd_section_from_elf_index (input_bfd, isym->st_shndx);
+
+ *secpp = isec;
+ }
+
+ if (! sh_elf_relocate_section (output_bfd, link_info, input_bfd,
+ input_section, data, internal_relocs,
+ isymbuf, sections))
+ goto error_return;
+
+ if (sections != NULL)
+ free (sections);
+ if (isymbuf != NULL
+ && symtab_hdr->contents != (unsigned char *) isymbuf)
+ free (isymbuf);
+ if (elf_section_data (input_section)->relocs != internal_relocs)
+ free (internal_relocs);
+ }
+
+ return data;
+
+ error_return:
+ if (sections != NULL)
+ free (sections);
+ if (isymbuf != NULL
+ && symtab_hdr->contents != (unsigned char *) isymbuf)
+ free (isymbuf);
+ if (internal_relocs != NULL
+ && elf_section_data (input_section)->relocs != internal_relocs)
+ free (internal_relocs);
+ return NULL;
+}
+
+/* Return the base VMA address which should be subtracted from real addresses
+ when resolving @dtpoff relocation.
+ This is PT_TLS segment p_vaddr. */
+
+static bfd_vma
+dtpoff_base (struct bfd_link_info *info)
+{
+ /* If tls_sec is NULL, we should have signalled an error already. */
+ if (elf_hash_table (info)->tls_sec == NULL)
+ return 0;
+ return elf_hash_table (info)->tls_sec->vma;
+}
+
+/* Return the relocation value for R_SH_TLS_TPOFF32.. */
+
+static bfd_vma
+tpoff (struct bfd_link_info *info, bfd_vma address)
+{
+ /* If tls_sec is NULL, we should have signalled an error already. */
+ if (elf_hash_table (info)->tls_sec == NULL)
+ return 0;
+ /* SH TLS ABI is variant I and static TLS block start just after tcbhead
+ structure which has 2 pointer fields. */
+ return (address - elf_hash_table (info)->tls_sec->vma
+ + align_power ((bfd_vma) 8,
+ elf_hash_table (info)->tls_sec->alignment_power));
+}
+
+static asection *
+sh_elf_gc_mark_hook (asection *sec,
+ struct bfd_link_info *info,
+ Elf_Internal_Rela *rel,
+ struct elf_link_hash_entry *h,
+ Elf_Internal_Sym *sym)
+{
+ if (h != NULL)
+ switch (ELF32_R_TYPE (rel->r_info))
+ {
+ case R_SH_GNU_VTINHERIT:
+ case R_SH_GNU_VTENTRY:
+ return NULL;
+ }
+
+ return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
+}
+
+/* Update the got entry reference counts for the section being removed. */
+
+static bfd_boolean
+sh_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info,
+ asection *sec, const Elf_Internal_Rela *relocs)
+{
+ Elf_Internal_Shdr *symtab_hdr;
+ struct elf_link_hash_entry **sym_hashes;
+ bfd_signed_vma *local_got_refcounts;
+ union gotref *local_funcdesc;
+ const Elf_Internal_Rela *rel, *relend;
+
+ if (info->relocatable)
+ return TRUE;
+
+ elf_section_data (sec)->local_dynrel = NULL;
+
+ symtab_hdr = &elf_symtab_hdr (abfd);
+ sym_hashes = elf_sym_hashes (abfd);
+ local_got_refcounts = elf_local_got_refcounts (abfd);
+ local_funcdesc = sh_elf_local_funcdesc (abfd);
+
+ relend = relocs + sec->reloc_count;
+ for (rel = relocs; rel < relend; rel++)
+ {
+ unsigned long r_symndx;
+ unsigned int r_type;
+ struct elf_link_hash_entry *h = NULL;
+#ifdef INCLUDE_SHMEDIA
+ int seen_stt_datalabel = 0;
+#endif
+
+ r_symndx = ELF32_R_SYM (rel->r_info);
+ if (r_symndx >= symtab_hdr->sh_info)
+ {
+ struct elf_sh_link_hash_entry *eh;
+ struct elf_sh_dyn_relocs **pp;
+ struct elf_sh_dyn_relocs *p;
+
+ h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+ while (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ {
+#ifdef INCLUDE_SHMEDIA
+ seen_stt_datalabel |= h->type == STT_DATALABEL;
+#endif
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+ }
+ eh = (struct elf_sh_link_hash_entry *) h;
+ for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next)
+ if (p->sec == sec)
+ {
+ /* Everything must go for SEC. */
+ *pp = p->next;
+ break;
+ }
+ }
+
+ r_type = ELF32_R_TYPE (rel->r_info);
+ switch (sh_elf_optimized_tls_reloc (info, r_type, h != NULL))
+ {
+ case R_SH_TLS_LD_32:
+ if (sh_elf_hash_table (info)->tls_ldm_got.refcount > 0)
+ sh_elf_hash_table (info)->tls_ldm_got.refcount -= 1;
+ break;
+
+ case R_SH_GOT32:
+ case R_SH_GOT20:
+ case R_SH_GOTOFF:
+ case R_SH_GOTOFF20:
+ case R_SH_GOTPC:
+#ifdef INCLUDE_SHMEDIA
+ case R_SH_GOT_LOW16:
+ case R_SH_GOT_MEDLOW16:
+ case R_SH_GOT_MEDHI16:
+ case R_SH_GOT_HI16:
+ case R_SH_GOT10BY4:
+ case R_SH_GOT10BY8:
+ case R_SH_GOTOFF_LOW16:
+ case R_SH_GOTOFF_MEDLOW16:
+ case R_SH_GOTOFF_MEDHI16:
+ case R_SH_GOTOFF_HI16:
+ case R_SH_GOTPC_LOW16:
+ case R_SH_GOTPC_MEDLOW16:
+ case R_SH_GOTPC_MEDHI16:
+ case R_SH_GOTPC_HI16:
+#endif
+ case R_SH_TLS_GD_32:
+ case R_SH_TLS_IE_32:
+ case R_SH_GOTFUNCDESC:
+ case R_SH_GOTFUNCDESC20:
+ if (h != NULL)
+ {
+#ifdef INCLUDE_SHMEDIA
+ if (seen_stt_datalabel)
+ {
+ struct elf_sh_link_hash_entry *eh;
+ eh = (struct elf_sh_link_hash_entry *) h;
+ if (eh->datalabel_got.refcount > 0)
+ eh->datalabel_got.refcount -= 1;
+ }
+ else
+#endif
+ if (h->got.refcount > 0)
+ h->got.refcount -= 1;
+ }
+ else if (local_got_refcounts != NULL)
+ {
+#ifdef INCLUDE_SHMEDIA
+ if (rel->r_addend & 1)
+ {
+ if (local_got_refcounts[symtab_hdr->sh_info + r_symndx] > 0)
+ local_got_refcounts[symtab_hdr->sh_info + r_symndx] -= 1;
+ }
+ else
+#endif
+ if (local_got_refcounts[r_symndx] > 0)
+ local_got_refcounts[r_symndx] -= 1;
+ }
+ break;
+
+ case R_SH_FUNCDESC:
+ if (h != NULL)
+ sh_elf_hash_entry (h)->abs_funcdesc_refcount -= 1;
+ else if (sh_elf_hash_table (info)->fdpic_p && !info->shared)
+ sh_elf_hash_table (info)->srofixup->size -= 4;
+
+ /* Fall through. */
+
+ case R_SH_GOTOFFFUNCDESC:
+ case R_SH_GOTOFFFUNCDESC20:
+ if (h != NULL)
+ sh_elf_hash_entry (h)->funcdesc.refcount -= 1;
+ else
+ local_funcdesc[r_symndx].refcount -= 1;
+ break;
+
+ case R_SH_DIR32:
+ if (sh_elf_hash_table (info)->fdpic_p && !info->shared
+ && (sec->flags & SEC_ALLOC) != 0)
+ sh_elf_hash_table (info)->srofixup->size -= 4;
+ /* Fall thru */
+
+ case R_SH_REL32:
+ if (info->shared)
+ break;
+ /* Fall thru */
+
+ case R_SH_PLT32:
+#ifdef INCLUDE_SHMEDIA
+ case R_SH_PLT_LOW16:
+ case R_SH_PLT_MEDLOW16:
+ case R_SH_PLT_MEDHI16:
+ case R_SH_PLT_HI16:
+#endif
+ if (h != NULL)
+ {
+ if (h->plt.refcount > 0)
+ h->plt.refcount -= 1;
+ }
+ break;
+
+ case R_SH_GOTPLT32:
+#ifdef INCLUDE_SHMEDIA
+ case R_SH_GOTPLT_LOW16:
+ case R_SH_GOTPLT_MEDLOW16:
+ case R_SH_GOTPLT_MEDHI16:
+ case R_SH_GOTPLT_HI16:
+ case R_SH_GOTPLT10BY4:
+ case R_SH_GOTPLT10BY8:
+#endif
+ if (h != NULL)
+ {
+ struct elf_sh_link_hash_entry *eh;
+ eh = (struct elf_sh_link_hash_entry *) h;
+ if (eh->gotplt_refcount > 0)
+ {
+ eh->gotplt_refcount -= 1;
+ if (h->plt.refcount > 0)
+ h->plt.refcount -= 1;
+ }
+#ifdef INCLUDE_SHMEDIA
+ else if (seen_stt_datalabel)
+ {
+ if (eh->datalabel_got.refcount > 0)
+ eh->datalabel_got.refcount -= 1;
+ }
+#endif
+ else if (h->got.refcount > 0)
+ h->got.refcount -= 1;
+ }
+ else if (local_got_refcounts != NULL)
+ {
+#ifdef INCLUDE_SHMEDIA
+ if (rel->r_addend & 1)
+ {
+ if (local_got_refcounts[symtab_hdr->sh_info + r_symndx] > 0)
+ local_got_refcounts[symtab_hdr->sh_info + r_symndx] -= 1;
+ }
+ else
+#endif
+ if (local_got_refcounts[r_symndx] > 0)
+ local_got_refcounts[r_symndx] -= 1;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+/* Copy the extra info we tack onto an elf_link_hash_entry. */
+
+static void
+sh_elf_copy_indirect_symbol (struct bfd_link_info *info,
+ struct elf_link_hash_entry *dir,
+ struct elf_link_hash_entry *ind)
+{
+ struct elf_sh_link_hash_entry *edir, *eind;
+
+ edir = (struct elf_sh_link_hash_entry *) dir;
+ eind = (struct elf_sh_link_hash_entry *) ind;
+
+ if (eind->dyn_relocs != NULL)
+ {
+ if (edir->dyn_relocs != NULL)
+ {
+ struct elf_sh_dyn_relocs **pp;
+ struct elf_sh_dyn_relocs *p;
+
+ /* Add reloc counts against the indirect sym to the direct sym
+ list. Merge any entries against the same section. */
+ for (pp = &eind->dyn_relocs; (p = *pp) != NULL; )
+ {
+ struct elf_sh_dyn_relocs *q;
+
+ for (q = edir->dyn_relocs; q != NULL; q = q->next)
+ if (q->sec == p->sec)
+ {
+ q->pc_count += p->pc_count;
+ q->count += p->count;
+ *pp = p->next;
+ break;
+ }
+ if (q == NULL)
+ pp = &p->next;
+ }
+ *pp = edir->dyn_relocs;
+ }
+
+ edir->dyn_relocs = eind->dyn_relocs;
+ eind->dyn_relocs = NULL;
+ }
+ edir->gotplt_refcount = eind->gotplt_refcount;
+ eind->gotplt_refcount = 0;
+#ifdef INCLUDE_SHMEDIA
+ edir->datalabel_got.refcount += eind->datalabel_got.refcount;
+ eind->datalabel_got.refcount = 0;
+#endif
+ edir->funcdesc.refcount += eind->funcdesc.refcount;
+ eind->funcdesc.refcount = 0;
+ edir->abs_funcdesc_refcount += eind->abs_funcdesc_refcount;
+ eind->abs_funcdesc_refcount = 0;
+
+ if (ind->root.type == bfd_link_hash_indirect
+ && dir->got.refcount <= 0)
+ {
+ edir->got_type = eind->got_type;
+ eind->got_type = GOT_UNKNOWN;
+ }
+
+ if (ind->root.type != bfd_link_hash_indirect
+ && dir->dynamic_adjusted)
+ {
+ /* If called to transfer flags for a weakdef during processing
+ of elf_adjust_dynamic_symbol, don't copy non_got_ref.
+ We clear it ourselves for ELIMINATE_COPY_RELOCS. */
+ dir->ref_dynamic |= ind->ref_dynamic;
+ dir->ref_regular |= ind->ref_regular;
+ dir->ref_regular_nonweak |= ind->ref_regular_nonweak;
+ dir->needs_plt |= ind->needs_plt;
+ }
+ else
+ _bfd_elf_link_hash_copy_indirect (info, dir, ind);
+}
+
+static int
+sh_elf_optimized_tls_reloc (struct bfd_link_info *info, int r_type,
+ int is_local)
+{
+ if (info->shared)
+ return r_type;
+
+ switch (r_type)
+ {
+ case R_SH_TLS_GD_32:
+ case R_SH_TLS_IE_32:
+ if (is_local)
+ return R_SH_TLS_LE_32;
+ return R_SH_TLS_IE_32;
+ case R_SH_TLS_LD_32:
+ return R_SH_TLS_LE_32;
+ }
+
+ return r_type;
+}
+
+/* Look through the relocs for a section during the first phase.
+ Since we don't do .gots or .plts, we just need to consider the
+ virtual table relocs for gc. */
+
+static bfd_boolean
+sh_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec,
+ const Elf_Internal_Rela *relocs)
+{
+ Elf_Internal_Shdr *symtab_hdr;
+ struct elf_link_hash_entry **sym_hashes;
+ struct elf_sh_link_hash_table *htab;
+ const Elf_Internal_Rela *rel;
+ const Elf_Internal_Rela *rel_end;
+ asection *sreloc;
+ unsigned int r_type;
+ enum got_type got_type, old_got_type;
+
+ sreloc = NULL;
+
+ if (info->relocatable)
+ return TRUE;
+
+ BFD_ASSERT (is_sh_elf (abfd));
+
+ symtab_hdr = &elf_symtab_hdr (abfd);
+ sym_hashes = elf_sym_hashes (abfd);
+
+ htab = sh_elf_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
+
+ rel_end = relocs + sec->reloc_count;
+ for (rel = relocs; rel < rel_end; rel++)
+ {
+ struct elf_link_hash_entry *h;
+ unsigned long r_symndx;
+#ifdef INCLUDE_SHMEDIA
+ int seen_stt_datalabel = 0;
+#endif
+
+ r_symndx = ELF32_R_SYM (rel->r_info);
+ r_type = ELF32_R_TYPE (rel->r_info);
+
+ if (r_symndx < symtab_hdr->sh_info)
+ h = NULL;
+ 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)
+ {
+#ifdef INCLUDE_SHMEDIA
+ seen_stt_datalabel |= h->type == STT_DATALABEL;
+#endif
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+ }
+ }
+
+ r_type = sh_elf_optimized_tls_reloc (info, r_type, h == NULL);
+ if (! info->shared
+ && r_type == R_SH_TLS_IE_32
+ && h != NULL
+ && h->root.type != bfd_link_hash_undefined
+ && h->root.type != bfd_link_hash_undefweak
+ && (h->dynindx == -1
+ || h->def_regular))
+ r_type = R_SH_TLS_LE_32;
+
+ if (htab->fdpic_p)
+ switch (r_type)
+ {
+ case R_SH_GOTOFFFUNCDESC:
+ case R_SH_GOTOFFFUNCDESC20:
+ case R_SH_FUNCDESC:
+ case R_SH_GOTFUNCDESC:
+ case R_SH_GOTFUNCDESC20:
+ if (h != NULL)
+ {
+ if (h->dynindx == -1)
+ switch (ELF_ST_VISIBILITY (h->other))
+ {
+ case STV_INTERNAL:
+ case STV_HIDDEN:
+ break;
+ default:
+ bfd_elf_link_record_dynamic_symbol (info, h);
+ break;
+ }
+ }
+ break;
+ }
+
+ /* Some relocs require a global offset table. */
+ if (htab->sgot == NULL)
+ {
+ switch (r_type)
+ {
+ case R_SH_DIR32:
+ /* This may require an rofixup. */
+ if (!htab->fdpic_p)
+ break;
+ case R_SH_GOTPLT32:
+ case R_SH_GOT32:
+ case R_SH_GOT20:
+ case R_SH_GOTOFF:
+ case R_SH_GOTOFF20:
+ case R_SH_FUNCDESC:
+ case R_SH_GOTFUNCDESC:
+ case R_SH_GOTFUNCDESC20:
+ case R_SH_GOTOFFFUNCDESC:
+ case R_SH_GOTOFFFUNCDESC20:
+ case R_SH_GOTPC:
+#ifdef INCLUDE_SHMEDIA
+ case R_SH_GOTPLT_LOW16:
+ case R_SH_GOTPLT_MEDLOW16:
+ case R_SH_GOTPLT_MEDHI16:
+ case R_SH_GOTPLT_HI16:
+ case R_SH_GOTPLT10BY4:
+ case R_SH_GOTPLT10BY8:
+ case R_SH_GOT_LOW16:
+ case R_SH_GOT_MEDLOW16:
+ case R_SH_GOT_MEDHI16:
+ case R_SH_GOT_HI16:
+ case R_SH_GOT10BY4:
+ case R_SH_GOT10BY8:
+ case R_SH_GOTOFF_LOW16:
+ case R_SH_GOTOFF_MEDLOW16:
+ case R_SH_GOTOFF_MEDHI16:
+ case R_SH_GOTOFF_HI16:
+ case R_SH_GOTPC_LOW16:
+ case R_SH_GOTPC_MEDLOW16:
+ case R_SH_GOTPC_MEDHI16:
+ case R_SH_GOTPC_HI16:
+#endif
+ case R_SH_TLS_GD_32:
+ case R_SH_TLS_LD_32:
+ case R_SH_TLS_IE_32:
+ if (htab->root.dynobj == NULL)
+ htab->root.dynobj = abfd;
+ if (!create_got_section (htab->root.dynobj, info))
+ return FALSE;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ switch (r_type)
+ {
+ /* This relocation describes the C++ object vtable hierarchy.
+ Reconstruct it for later use during GC. */
+ case R_SH_GNU_VTINHERIT:
+ if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
+ return FALSE;
+ break;
+
+ /* This relocation describes which C++ vtable entries are actually
+ used. Record for later use during GC. */
+ case R_SH_GNU_VTENTRY:
+ BFD_ASSERT (h != NULL);
+ if (h != NULL
+ && !bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
+ return FALSE;
+ break;
+
+ case R_SH_TLS_IE_32:
+ if (info->shared)
+ info->flags |= DF_STATIC_TLS;
+
+ /* FALLTHROUGH */
+ force_got:
+ case R_SH_TLS_GD_32:
+ case R_SH_GOT32:
+ case R_SH_GOT20:
+#ifdef INCLUDE_SHMEDIA
+ case R_SH_GOT_LOW16:
+ case R_SH_GOT_MEDLOW16:
+ case R_SH_GOT_MEDHI16:
+ case R_SH_GOT_HI16:
+ case R_SH_GOT10BY4:
+ case R_SH_GOT10BY8:
+#endif
+ case R_SH_GOTFUNCDESC:
+ case R_SH_GOTFUNCDESC20:
+ switch (r_type)
+ {
+ default:
+ got_type = GOT_NORMAL;
+ break;
+ case R_SH_TLS_GD_32:
+ got_type = GOT_TLS_GD;
+ break;
+ case R_SH_TLS_IE_32:
+ got_type = GOT_TLS_IE;
+ break;
+ case R_SH_GOTFUNCDESC:
+ case R_SH_GOTFUNCDESC20:
+ got_type = GOT_FUNCDESC;
+ break;
+ }
+
+ if (h != NULL)
+ {
+#ifdef INCLUDE_SHMEDIA
+ if (seen_stt_datalabel)
+ {
+ struct elf_sh_link_hash_entry *eh
+ = (struct elf_sh_link_hash_entry *) h;
+
+ eh->datalabel_got.refcount += 1;
+ }
+ else
+#endif
+ h->got.refcount += 1;
+ old_got_type = sh_elf_hash_entry (h)->got_type;
+ }
+ else
+ {
+ bfd_signed_vma *local_got_refcounts;
+
+ /* This is a global offset table entry for a local
+ symbol. */
+ local_got_refcounts = elf_local_got_refcounts (abfd);
+ if (local_got_refcounts == NULL)
+ {
+ bfd_size_type size;
+
+ size = symtab_hdr->sh_info;
+ size *= sizeof (bfd_signed_vma);
+#ifdef INCLUDE_SHMEDIA
+ /* Reserve space for both the datalabel and
+ codelabel local GOT offsets. */
+ size *= 2;
+#endif
+ size += symtab_hdr->sh_info;
+ local_got_refcounts = ((bfd_signed_vma *)
+ bfd_zalloc (abfd, size));
+ if (local_got_refcounts == NULL)
+ return FALSE;
+ elf_local_got_refcounts (abfd) = local_got_refcounts;
+#ifdef INCLUDE_SHMEDIA
+ /* Take care of both the datalabel and codelabel local
+ GOT offsets. */
+ sh_elf_local_got_type (abfd)
+ = (char *) (local_got_refcounts + 2 * symtab_hdr->sh_info);
+#else
+ sh_elf_local_got_type (abfd)
+ = (char *) (local_got_refcounts + symtab_hdr->sh_info);
+#endif
+ }
+#ifdef INCLUDE_SHMEDIA
+ if (rel->r_addend & 1)
+ local_got_refcounts[symtab_hdr->sh_info + r_symndx] += 1;
+ else
+#endif
+ local_got_refcounts[r_symndx] += 1;
+ old_got_type = sh_elf_local_got_type (abfd) [r_symndx];
+ }
+
+ /* If a TLS symbol is accessed using IE at least once,
+ there is no point to use dynamic model for it. */
+ if (old_got_type != got_type && old_got_type != GOT_UNKNOWN
+ && (old_got_type != GOT_TLS_GD || got_type != GOT_TLS_IE))
+ {
+ if (old_got_type == GOT_TLS_IE && got_type == GOT_TLS_GD)
+ got_type = GOT_TLS_IE;
+ else
+ {
+ if ((old_got_type == GOT_FUNCDESC || got_type == GOT_FUNCDESC)
+ && (old_got_type == GOT_NORMAL || got_type == GOT_NORMAL))
+ (*_bfd_error_handler)
+ (_("%B: `%s' accessed both as normal and FDPIC symbol"),
+ abfd, h->root.root.string);
+ else if (old_got_type == GOT_FUNCDESC
+ || got_type == GOT_FUNCDESC)
+ (*_bfd_error_handler)
+ (_("%B: `%s' accessed both as FDPIC and thread local symbol"),
+ abfd, h->root.root.string);
+ else
+ (*_bfd_error_handler)
+ (_("%B: `%s' accessed both as normal and thread local symbol"),
+ abfd, h->root.root.string);
+ return FALSE;
+ }
+ }
+
+ if (old_got_type != got_type)
+ {
+ if (h != NULL)
+ sh_elf_hash_entry (h)->got_type = got_type;
+ else
+ sh_elf_local_got_type (abfd) [r_symndx] = got_type;
+ }
+
+ break;
+
+ case R_SH_TLS_LD_32:
+ sh_elf_hash_table(info)->tls_ldm_got.refcount += 1;
+ break;
+
+ case R_SH_FUNCDESC:
+ case R_SH_GOTOFFFUNCDESC:
+ case R_SH_GOTOFFFUNCDESC20:
+ if (rel->r_addend)
+ {
+ (*_bfd_error_handler)
+ (_("%B: Function descriptor relocation with non-zero addend"),
+ abfd);
+ return FALSE;
+ }
+
+ if (h == NULL)
+ {
+ union gotref *local_funcdesc;
+
+ /* We need a function descriptor for a local symbol. */
+ local_funcdesc = sh_elf_local_funcdesc (abfd);
+ if (local_funcdesc == NULL)
+ {
+ bfd_size_type size;
+
+ size = symtab_hdr->sh_info * sizeof (union gotref);
+#ifdef INCLUDE_SHMEDIA
+ /* Count datalabel local GOT. */
+ size *= 2;
+#endif
+ local_funcdesc = (union gotref *) bfd_zalloc (abfd, size);
+ if (local_funcdesc == NULL)
+ return FALSE;
+ sh_elf_local_funcdesc (abfd) = local_funcdesc;
+ }
+ local_funcdesc[r_symndx].refcount += 1;
+
+ if (r_type == R_SH_FUNCDESC)
+ {
+ if (!info->shared)
+ htab->srofixup->size += 4;
+ else
+ htab->srelgot->size += sizeof (Elf32_External_Rela);
+ }
+ }
+ else
+ {
+ sh_elf_hash_entry (h)->funcdesc.refcount++;
+ if (r_type == R_SH_FUNCDESC)
+ sh_elf_hash_entry (h)->abs_funcdesc_refcount++;
+
+ /* If there is a function descriptor reference, then
+ there should not be any non-FDPIC references. */
+ old_got_type = sh_elf_hash_entry (h)->got_type;
+ if (old_got_type != GOT_FUNCDESC && old_got_type != GOT_UNKNOWN)
+ {
+ if (old_got_type == GOT_NORMAL)
+ (*_bfd_error_handler)
+ (_("%B: `%s' accessed both as normal and FDPIC symbol"),
+ abfd, h->root.root.string);
+ else
+ (*_bfd_error_handler)
+ (_("%B: `%s' accessed both as FDPIC and thread local symbol"),
+ abfd, h->root.root.string);
+ }
+ }
+ break;
+
+ case R_SH_GOTPLT32:
+#ifdef INCLUDE_SHMEDIA
+ case R_SH_GOTPLT_LOW16:
+ case R_SH_GOTPLT_MEDLOW16:
+ case R_SH_GOTPLT_MEDHI16:
+ case R_SH_GOTPLT_HI16:
+ case R_SH_GOTPLT10BY4:
+ case R_SH_GOTPLT10BY8:
+#endif
+ /* If this is a local symbol, we resolve it directly without
+ creating a procedure linkage table entry. */
+
+ if (h == NULL
+ || h->forced_local
+ || ! info->shared
+ || info->symbolic
+ || h->dynindx == -1)
+ goto force_got;
+
+ h->needs_plt = 1;
+ h->plt.refcount += 1;
+ ((struct elf_sh_link_hash_entry *) h)->gotplt_refcount += 1;
+
+ break;
+
+ case R_SH_PLT32:
+#ifdef INCLUDE_SHMEDIA
+ case R_SH_PLT_LOW16:
+ case R_SH_PLT_MEDLOW16:
+ case R_SH_PLT_MEDHI16:
+ case R_SH_PLT_HI16:
+#endif
+ /* 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 (h->forced_local)
+ break;
+
+ h->needs_plt = 1;
+ h->plt.refcount += 1;
+ break;
+
+ case R_SH_DIR32:
+ case R_SH_REL32:
+#ifdef INCLUDE_SHMEDIA
+ case R_SH_IMM_LOW16_PCREL:
+ case R_SH_IMM_MEDLOW16_PCREL:
+ case R_SH_IMM_MEDHI16_PCREL:
+ case R_SH_IMM_HI16_PCREL:
+#endif
+ if (h != NULL && ! info->shared)
+ {
+ h->non_got_ref = 1;
+ h->plt.refcount += 1;
+ }
+
+ /* 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
+ dyn_relocs field of the hash table entry. A similar
+ situation occurs when creating shared libraries and symbol
+ visibility changes render the symbol local.
+
+ If on the other hand, we are creating an executable, we
+ may need to keep relocations for symbols satisfied by a
+ dynamic library if we manage to avoid copy relocs for the
+ symbol. */
+ if ((info->shared
+ && (sec->flags & SEC_ALLOC) != 0
+ && (r_type != R_SH_REL32
+ || (h != NULL
+ && (! info->symbolic
+ || h->root.type == bfd_link_hash_defweak
+ || !h->def_regular))))
+ || (! info->shared
+ && (sec->flags & SEC_ALLOC) != 0
+ && h != NULL
+ && (h->root.type == bfd_link_hash_defweak
+ || !h->def_regular)))
+ {
+ struct elf_sh_dyn_relocs *p;
+ struct elf_sh_dyn_relocs **head;
+
+ if (htab->root.dynobj == NULL)
+ htab->root.dynobj = abfd;
+
+ /* 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)
+ {
+ sreloc = _bfd_elf_make_dynamic_reloc_section
+ (sec, htab->root.dynobj, 2, abfd, /*rela?*/ TRUE);
+
+ if (sreloc == NULL)
+ return FALSE;
+ }
+
+ /* If this is a global symbol, we count the number of
+ relocations we need for this symbol. */
+ if (h != NULL)
+ head = &((struct elf_sh_link_hash_entry *) h)->dyn_relocs;
+ else
+ {
+ /* Track dynamic relocs needed for local syms too. */
+ asection *s;
+ void *vpp;
+ Elf_Internal_Sym *isym;
+
+ isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+ abfd, r_symndx);
+ if (isym == NULL)
+ return FALSE;
+
+ s = bfd_section_from_elf_index (abfd, isym->st_shndx);
+ if (s == NULL)
+ s = sec;
+
+ vpp = &elf_section_data (s)->local_dynrel;
+ head = (struct elf_sh_dyn_relocs **) vpp;
+ }
+
+ p = *head;
+ if (p == NULL || p->sec != sec)
+ {
+ bfd_size_type amt = sizeof (*p);
+ p = bfd_alloc (htab->root.dynobj, amt);
+ if (p == NULL)
+ return FALSE;
+ p->next = *head;
+ *head = p;
+ p->sec = sec;
+ p->count = 0;
+ p->pc_count = 0;
+ }
+
+ p->count += 1;
+ if (r_type == R_SH_REL32
+#ifdef INCLUDE_SHMEDIA
+ || r_type == R_SH_IMM_LOW16_PCREL
+ || r_type == R_SH_IMM_MEDLOW16_PCREL
+ || r_type == R_SH_IMM_MEDHI16_PCREL
+ || r_type == R_SH_IMM_HI16_PCREL
+#endif
+ )
+ p->pc_count += 1;
+ }
+
+ /* Allocate the fixup regardless of whether we need a relocation.
+ If we end up generating the relocation, we'll unallocate the
+ fixup. */
+ if (htab->fdpic_p && !info->shared
+ && r_type == R_SH_DIR32
+ && (sec->flags & SEC_ALLOC) != 0)
+ htab->srofixup->size += 4;
+ break;
+
+ case R_SH_TLS_LE_32:
+ if (info->shared && !info->pie)
+ {
+ (*_bfd_error_handler)
+ (_("%B: TLS local exec code cannot be linked into shared objects"),
+ abfd);
+ return FALSE;
+ }
+
+ break;
+
+ case R_SH_TLS_LDO_32:
+ /* Nothing to do. */
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+#ifndef sh_elf_set_mach_from_flags
+static unsigned int sh_ef_bfd_table[] = { EF_SH_BFD_TABLE };
+
+static bfd_boolean
+sh_elf_set_mach_from_flags (bfd *abfd)
+{
+ flagword flags = elf_elfheader (abfd)->e_flags & EF_SH_MACH_MASK;
+
+ if (flags >= sizeof(sh_ef_bfd_table))
+ return FALSE;
+
+ if (sh_ef_bfd_table[flags] == 0)
+ return FALSE;
+
+ bfd_default_set_arch_mach (abfd, bfd_arch_sh, sh_ef_bfd_table[flags]);
+
+ return TRUE;
+}
+
+
+/* Reverse table lookup for sh_ef_bfd_table[].
+ Given a bfd MACH value from archures.c
+ return the equivalent ELF flags from the table.
+ Return -1 if no match is found. */
+
+int
+sh_elf_get_flags_from_mach (unsigned long mach)
+{
+ int i = ARRAY_SIZE (sh_ef_bfd_table) - 1;