From aab06c2da155ae26b9c6050a0d34872544f65c2a Mon Sep 17 00:00:00 2001 From: Nick Clifton Date: Tue, 25 May 2010 14:12:35 +0000 Subject: 2010-05-21 Daniel Jacobowitz Joseph Myers Andrew Stubbs bfd/ * config.bfd (sh-*-uclinux* | sh[12]-*-uclinux*): Add bfd_elf32_shl_vec, and FDPIC vectors to targ_selvecs. * configure.in: Handle FDPIC vectors. * elf32-sh-relocs.h: Add FDPIC and movi20 relocations. * elf32-sh.c (DEFAULT_STACK_SIZE): Define. (SYMBOL_FUNCDESC_LOCAL): Define. Use it instead of SYMBOL_REFERENCES_LOCAL for function descriptors. (fdpic_object_p): New. (sh_reloc_map): Add FDPIC and movi20 relocations. (sh_elf_info_to_howto, sh_elf_relocate_section): Handle new invalid range. (struct elf_sh_plt_info): Add got20 and short_plt. Update all definitions. (FDPIC_PLT_ENTRY_SIZE, FDPIC_PLT_LAZY_OFFSET): Define. (fdpic_sh_plt_entry_be, fdpic_sh_plt_entry_le, fdpic_sh_plts): New. (FDPIC_SH2A_PLT_ENTRY_SIZE, FDPIC_SH2A_PLT_LAZY_OFFSET): Define. (fdpic_sh2a_plt_entry_be, fdpic_sh2a_plt_entry_le) (fdpic_sh2a_short_plt_be, fdpic_sh2a_short_plt_le, fdpic_sh2a_plts): New. (get_plt_info): Handle FDPIC. (MAX_SHORT_PLT): Define. (get_plt_index, get_plt_offset): Handle short_plt. (union gotref): New. (struct elf_sh_link_hash_entry): Add funcdesc, rename tls_type to got_type and adjust all uses. Add GOT_FUNCDESC. (struct sh_elf_obj_tdata): Add local_funcdesc. Rename local_got_tls_type to local_got_type. (sh_elf_local_got_type): Renamed from sh_elf_local_got_tls_type. All users changed. (sh_elf_local_funcdesc): Define. (struct elf_sh_link_hash_table): Add sfuncdesc, srelfuncdesc, fdpic_p, and srofixup. (sh_elf_link_hash_newfunc): Initialize new fields. (sh_elf_link_hash_table_create): Set fdpic_p. (sh_elf_omit_section_dynsym): New. (create_got_section): Create .got.funcdesc, .rela.got.funcdesc and .rofixup. (allocate_dynrelocs): Allocate local function descriptors and space for R_SH_FUNCDESC-related relocations, and for rofixups. Handle GOT_FUNCDESC. Create fixups. Handle GOT entries which require function descriptors. (sh_elf_always_size_sections): Handle PT_GNU_STACK and __stacksize. (sh_elf_modify_program_headers): New. (sh_elf_size_dynamic_sections): Allocate function descriptors for local symbols. Allocate .got.funcdesc contents. Allocate rofixups. Handle local GOT entries of type GOT_FUNCDESC. Create fixups for local GOT entries. Ensure that FDPIC libraries always have a PLTGOT entry in the .dynamic section. (sh_elf_add_dyn_reloc, sh_elf_got_offset, sh_elf_initialize_funcdesc) (sh_elf_add_rofixup, sh_elf_osec_to_segment) (sh_elf_osec_readonly_p, install_movi20_field): New functions. (sh_elf_relocate_section): Handle new relocations, R_SH_FUNCDESC, R_SH_GOTFUNCDESC and R_SH_GOTOFFFUNCDESC. Use sh_elf_got_offset and .got.plt throughout to find _GLOBAL_OFFSET_TABLE_. Add rofixup read-only section warnings. Handle undefined weak symbols. Generate fixups for R_SH_DIR32 and GOT entries. Check for cross-segment relocations and clear EF_SH_PIC. Handle 20-bit relocations. Always generate R_SH_DIR32 for FDPIC instead of R_SH_RELATIVE. (sh_elf_gc_sweep_hook): Handle R_SH_FUNCDESC, R_SH_GOTOFF20, R_SH_GOTFUNCDESC, R_SH_GOTFUNCDESC20, and R_SH_GOTOFFFUNCDESC. Handle 20-bit relocations. (sh_elf_copy_indirect_symbol): Copy function descriptor reference counts. (sh_elf_check_relocs): Handle new relocations. Make symbols dynamic for FDPIC relocs. Account for rofixups. Error for FDPIC symbol mismatches. Allocate a GOT for R_SH_DIR32. Allocate fixups for R_SH_DIR32. (sh_elf_copy_private_data): Copy PT_GNU_STACK size. (sh_elf_merge_private_data): Copy initial flags. Do not clobber non-mach flags. Set EF_SH_PIC for FDPIC. Reject FDPIC mismatches. (sh_elf_finish_dynamic_symbol): Do not handle got_funcdesc entries here. Rename sgot to sgotplt and srel to srelplt. Handle short_plt, FDPIC descriptors, and got20. Create R_SH_FUNCDESC_VALUE for FDPIC. Use install_movi20_field. Rename srel to srelgot. Always generate R_SH_DIR32 for FDPIC instead of R_SH_RELATIVE. (sh_elf_finish_dynamic_sections): Fill in the GOT pointer in rofixup. Do not fill in reserved GOT entries for FDPIC. Correct DT_PLTGOT. Rename sgot to sgotplt. Assert that the right number of rofixups and dynamic relocations were allocated. (sh_elf_use_relative_eh_frame, sh_elf_encode_eh_address): New. (elf_backend_omit_section_dynsym): Use sh_elf_omit_section_dynsym. (elf_backend_can_make_relative_eh_frame) (elf_backend_can_make_lsda_relative_eh_frame) (elf_backend_encode_eh_address): Define. (TARGET_BIG_SYM, TARGET_BIG_NAME, TARGET_LITTLE_SYM) (TARGET_LITTLE_NAME, elf_backend_modify_program_headers, elf32_bed): Redefine for FDPIC vector. * reloc.c: Add SH FDPIC and movi20 relocations. * targets.c (_bfd_target_vector): Add FDPIC vectors. * configure, bfd-in2.h, libbfd.h: Regenerated. binutils/ * readelf.c (get_machine_flags): Handle EF_SH_PIC and EF_SH_FDPIC. gas/ * config/tc-sh.c (sh_fdpic): New. (sh_check_fixup): Handle relocations on movi20. (parse_exp): Do not reject PIC operators here. (build_Mytes): Check for unhandled PIC operators here. Use sh_check_fixup for movi20. (enum options): Add OPTION_FDPIC. (md_longopts, md_parse_option, md_show_usage): Add --fdpic. (sh_fix_adjustable, md_apply_fix): Handle FDPIC and movi20 relocations. (sh_elf_final_processing): Handle --fdpic. (sh_uclinux_target_format): New. (sh_parse_name): Handle FDPIC relocation operators. * config/tc-sh.h (TARGET_FORMAT): Define specially for TE_UCLINUX. (sh_uclinux_target_format): Declare for TE_UCLINUX. * configure.tgt (sh-*-uclinux* | sh[12]-*-uclinux*): Set em=uclinux. * doc/c-sh.texi (SH Options): Document --fdpic. gas/testsuite/ * gas/sh/basic.exp: Run new tests. Handle uClinux like Linux. * gas/sh/fdpic.d: New file. * gas/sh/fdpic.s: New file. * gas/sh/reg-prefix.d: Force big-endian. * gas/sh/sh2a-pic.d: New file. * gas/sh/sh2a-pic.s: New file. * lib/gas-defs.exp (is_elf_format): Include sh*-*-uclinux*. include/elf/ * sh.h (EF_SH_PIC, EF_SH_FDPIC): Define. (R_SH_FIRST_INVALID_RELOC_6, R_SH_LAST_INVALID_RELOC_6): New. Adjust other invalid ranges. (R_SH_GOT20, R_SH_GOTOFF20, R_SH_GOTFUNCDESC, R_SH_GOTFUNCDESC20) (R_SH_GOTOFFFUNCDESC, R_SH_GOTOFFFUNCDESC20, R_SH_FUNCDESC) (R_SH_FUNCDESC_VALUE): New. ld/ * Makefile.am (ALL_EMULATIONS): Add eshelf_fd.o and eshlelf_fd.o. (eshelf_fd.c, eshlelf_fd.c): New rules. * Makefile.in: Regenerate. * configure.tgt (sh-*-uclinux*): Add shelf_fd and shlelf_fd emulations. * emulparams/shelf_fd.sh: New file. * emulparams/shlelf_fd.sh: New file. * emulparams/shlelf_linux.sh: Update comment. ld/testsuite/ * ld-sh/sh.exp: Handle uClinux like Linux. * lib/ld-lib.exp (is_elf_format): Include sh*-*-uclinux*. * ld-sh/fdpic-funcdesc-shared.d: New file. * ld-sh/fdpic-funcdesc-shared.s: New file. * ld-sh/fdpic-funcdesc-static.d: New file. * ld-sh/fdpic-funcdesc-static.s: New file. * ld-sh/fdpic-gotfuncdesc-shared.d: New file. * ld-sh/fdpic-gotfuncdesc-shared.s: New file. * ld-sh/fdpic-gotfuncdesc-static.d: New file. * ld-sh/fdpic-gotfuncdesc-static.s: New file. * ld-sh/fdpic-gotfuncdesci20-shared.d: New file. * ld-sh/fdpic-gotfuncdesci20-shared.s: New file. * ld-sh/fdpic-gotfuncdesci20-static.d: New file. * ld-sh/fdpic-gotfuncdesci20-static.s: New file. * ld-sh/fdpic-goti20-shared.d: New file. * ld-sh/fdpic-goti20-shared.s: New file. * ld-sh/fdpic-goti20-static.d: New file. * ld-sh/fdpic-goti20-static.s: New file. * ld-sh/fdpic-gotofffuncdesc-shared.d: New file. * ld-sh/fdpic-gotofffuncdesc-shared.s: New file. * ld-sh/fdpic-gotofffuncdesc-static.d: New file. * ld-sh/fdpic-gotofffuncdesc-static.s: New file. * ld-sh/fdpic-gotofffuncdesci20-shared.d: New file. * ld-sh/fdpic-gotofffuncdesci20-shared.s: New file. * ld-sh/fdpic-gotofffuncdesci20-static.d: New file. * ld-sh/fdpic-gotofffuncdesci20-static.s: New file. * ld-sh/fdpic-gotoffi20-shared.d: New file. * ld-sh/fdpic-gotoffi20-shared.s: New file. * ld-sh/fdpic-gotoffi20-static.d: New file. * ld-sh/fdpic-gotoffi20-static.s: New file. * ld-sh/fdpic-plt-be.d: New file. * ld-sh/fdpic-plt-le.d: New file. * ld-sh/fdpic-plt.s: New file. * ld-sh/fdpic-plti20-be.d: New file. * ld-sh/fdpic-plti20-le.d: New file. * ld-sh/fdpic-stack-default.d: New file. * ld-sh/fdpic-stack-size.d: New file. * ld-sh/fdpic-stack.s: New file. --- bfd/elf32-sh.c | 2086 +++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 1830 insertions(+), 256 deletions(-) (limited to 'bfd/elf32-sh.c') diff --git a/bfd/elf32-sh.c b/bfd/elf32-sh.c index 23ee06a1d0..1b0daf96bb 100644 --- a/bfd/elf32-sh.c +++ b/bfd/elf32-sh.c @@ -27,6 +27,7 @@ #include "elf-bfd.h" #include "elf-vxworks.h" #include "elf/sh.h" +#include "dwarf2.h" #include "libiberty.h" #include "../opcodes/sh-opc.h" @@ -54,7 +55,17 @@ static bfd_vma tpoff #define ELF_DYNAMIC_INTERPRETER "/usr/lib/libc.so.1" +/* FDPIC binaries have a default 128K stack. */ +#define DEFAULT_STACK_SIZE 0x20000 + #define MINUS_ONE ((bfd_vma) 0 - 1) + +/* Decide whether a reference to a symbol can be resolved locally or + not. If the symbol is protected, we want the local address, but + its function descriptor must be assigned by the dynamic linker. */ +#define SYMBOL_FUNCDESC_LOCAL(INFO, H) \ + (SYMBOL_REFERENCES_LOCAL (INFO, H) \ + || ! elf_hash_table (INFO)->dynamic_sections_created) #define SH_PARTIAL32 TRUE #define SH_SRC_MASK32 0xffffffff @@ -88,6 +99,22 @@ vxworks_object_p (bfd *abfd ATTRIBUTE_UNUSED) #endif } +/* Return true if OUTPUT_BFD is an FDPIC object. */ + +static bfd_boolean +fdpic_object_p (bfd *abfd ATTRIBUTE_UNUSED) +{ +#if !defined INCLUDE_SHMEDIA && !defined SH_TARGET_ALREADY_DEFINED + extern const bfd_target bfd_elf32_shfd_vec; + extern const bfd_target bfd_elf32_shbfd_vec; + + return (abfd->xvec == &bfd_elf32_shfd_vec + || abfd->xvec == &bfd_elf32_shbfd_vec); +#else + return FALSE; +#endif +} + /* Return the howto table for ABFD. */ static reloc_howto_type * @@ -333,6 +360,13 @@ static const struct elf_reloc_map sh_reloc_map[] = { BFD_RELOC_32_GOTOFF, R_SH_GOTOFF }, { BFD_RELOC_SH_GOTPC, R_SH_GOTPC }, { BFD_RELOC_SH_GOTPLT32, R_SH_GOTPLT32 }, + { BFD_RELOC_SH_GOT20, R_SH_GOT20 }, + { BFD_RELOC_SH_GOTOFF20, R_SH_GOTOFF20 }, + { BFD_RELOC_SH_GOTFUNCDESC, R_SH_GOTFUNCDESC }, + { BFD_RELOC_SH_GOTFUNCDESC20, R_SH_GOTFUNCDESC20 }, + { BFD_RELOC_SH_GOTOFFFUNCDESC, R_SH_GOTOFFFUNCDESC }, + { BFD_RELOC_SH_GOTOFFFUNCDESC20, R_SH_GOTOFFFUNCDESC20 }, + { BFD_RELOC_SH_FUNCDESC, R_SH_FUNCDESC }, #ifdef INCLUDE_SHMEDIA { BFD_RELOC_SH_GOT_LOW16, R_SH_GOT_LOW16 }, { BFD_RELOC_SH_GOT_MEDLOW16, R_SH_GOT_MEDLOW16 }, @@ -447,6 +481,7 @@ sh_elf_info_to_howto (bfd *abfd, arelent *cache_ptr, Elf_Internal_Rela *dst) BFD_ASSERT (r < R_SH_FIRST_INVALID_RELOC_3 || r > R_SH_LAST_INVALID_RELOC_3); BFD_ASSERT (r < R_SH_FIRST_INVALID_RELOC_4 || r > R_SH_LAST_INVALID_RELOC_4); BFD_ASSERT (r < R_SH_FIRST_INVALID_RELOC_5 || r > R_SH_LAST_INVALID_RELOC_5); + BFD_ASSERT (r < R_SH_FIRST_INVALID_RELOC_6 || r > R_SH_LAST_INVALID_RELOC_6); cache_ptr->howto = get_howto_table (abfd) + r; } @@ -1552,10 +1587,18 @@ struct elf_sh_plt_info bfd_vma got_entry; /* the address of the symbol's .got.plt entry */ bfd_vma plt; /* .plt (or a branch to .plt on VxWorks) */ bfd_vma reloc_offset; /* the offset of the symbol's JMP_SLOT reloc */ + bfd_boolean got20; /* TRUE if got_entry points to a movi20 + instruction (instead of a constant pool + entry). */ } symbol_fields; /* The offset of the resolver stub from the start of SYMBOL_ENTRY. */ bfd_vma symbol_resolve_offset; + + /* A different PLT layout which can be used for the first + MAX_SHORT_PLT entries. It must share the same plt0. NULL in + other cases. */ + const struct elf_sh_plt_info *short_plt; }; #ifdef INCLUDE_SHMEDIA @@ -1700,8 +1743,9 @@ static const struct elf_sh_plt_info elf_sh_plts[2][2] = { { 0, MINUS_ONE, MINUS_ONE }, elf_sh_plt_entry_be, ELF_PLT_ENTRY_SIZE, - { 0, 32, 48 }, - 33 /* includes ISA encoding */ + { 0, 32, 48, FALSE }, + 33, /* includes ISA encoding */ + NULL }, { /* Little-endian non-PIC. */ @@ -1710,8 +1754,9 @@ static const struct elf_sh_plt_info elf_sh_plts[2][2] = { { 0, MINUS_ONE, MINUS_ONE }, elf_sh_plt_entry_le, ELF_PLT_ENTRY_SIZE, - { 0, 32, 48 }, - 33 /* includes ISA encoding */ + { 0, 32, 48, FALSE }, + 33, /* includes ISA encoding */ + NULL }, }, { @@ -1722,8 +1767,9 @@ static const struct elf_sh_plt_info elf_sh_plts[2][2] = { { MINUS_ONE, MINUS_ONE, MINUS_ONE }, elf_sh_pic_plt_entry_be, ELF_PLT_ENTRY_SIZE, - { 0, MINUS_ONE, 52 }, - 33 /* includes ISA encoding */ + { 0, MINUS_ONE, 52, FALSE }, + 33, /* includes ISA encoding */ + NULL }, { /* Little-endian PIC. */ @@ -1732,8 +1778,9 @@ static const struct elf_sh_plt_info elf_sh_plts[2][2] = { { MINUS_ONE, MINUS_ONE, MINUS_ONE }, elf_sh_pic_plt_entry_le, ELF_PLT_ENTRY_SIZE, - { 0, MINUS_ONE, 52 }, - 33 /* includes ISA encoding */ + { 0, MINUS_ONE, 52, FALSE }, + 33, /* includes ISA encoding */ + NULL }, } }; @@ -1893,8 +1940,9 @@ static const struct elf_sh_plt_info elf_sh_plts[2][2] = { { MINUS_ONE, 24, 20 }, elf_sh_plt_entry_be, ELF_PLT_ENTRY_SIZE, - { 20, 16, 24 }, - 8 + { 20, 16, 24, FALSE }, + 8, + NULL }, { /* Little-endian non-PIC. */ @@ -1903,8 +1951,9 @@ static const struct elf_sh_plt_info elf_sh_plts[2][2] = { { MINUS_ONE, 24, 20 }, elf_sh_plt_entry_le, ELF_PLT_ENTRY_SIZE, - { 20, 16, 24 }, - 8 + { 20, 16, 24, FALSE }, + 8, + NULL }, }, { @@ -1915,8 +1964,9 @@ static const struct elf_sh_plt_info elf_sh_plts[2][2] = { { MINUS_ONE, MINUS_ONE, MINUS_ONE }, elf_sh_pic_plt_entry_be, ELF_PLT_ENTRY_SIZE, - { 20, MINUS_ONE, 24 }, - 8 + { 20, MINUS_ONE, 24, FALSE }, + 8, + NULL }, { /* Little-endian PIC. */ @@ -1925,8 +1975,9 @@ static const struct elf_sh_plt_info elf_sh_plts[2][2] = { { MINUS_ONE, MINUS_ONE, MINUS_ONE }, elf_sh_pic_plt_entry_le, ELF_PLT_ENTRY_SIZE, - { 20, MINUS_ONE, 24 }, - 8 + { 20, MINUS_ONE, 24, FALSE }, + 8, + NULL }, } }; @@ -2017,8 +2068,9 @@ static const struct elf_sh_plt_info vxworks_sh_plts[2][2] = { { MINUS_ONE, MINUS_ONE, 8 }, vxworks_sh_plt_entry_be, VXWORKS_PLT_ENTRY_SIZE, - { 8, 14, 20 }, - 12 + { 8, 14, 20, FALSE }, + 12, + NULL }, { /* Little-endian non-PIC. */ @@ -2027,8 +2079,9 @@ static const struct elf_sh_plt_info vxworks_sh_plts[2][2] = { { MINUS_ONE, MINUS_ONE, 8 }, vxworks_sh_plt_entry_le, VXWORKS_PLT_ENTRY_SIZE, - { 8, 14, 20 }, - 12 + { 8, 14, 20, FALSE }, + 12, + NULL }, }, { @@ -2039,8 +2092,9 @@ static const struct elf_sh_plt_info vxworks_sh_plts[2][2] = { { MINUS_ONE, MINUS_ONE, MINUS_ONE }, vxworks_sh_pic_plt_entry_be, VXWORKS_PLT_ENTRY_SIZE, - { 8, MINUS_ONE, 20 }, - 12 + { 8, MINUS_ONE, 20, FALSE }, + 12, + NULL }, { /* Little-endian PIC. */ @@ -2049,18 +2103,184 @@ static const struct elf_sh_plt_info vxworks_sh_plts[2][2] = { { MINUS_ONE, MINUS_ONE, MINUS_ONE }, vxworks_sh_pic_plt_entry_le, VXWORKS_PLT_ENTRY_SIZE, - { 8, MINUS_ONE, 20 }, - 12 + { 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 ATTRIBUTE_UNUSED, bfd_boolean pic_p) +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)]; @@ -2078,12 +2298,31 @@ install_plt_field (bfd *output_bfd, bfd_boolean code_p ATTRIBUTE_UNUSED, } #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) { - return (offset - info->plt0_entry_size) / info->symbol_entry_size; + 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. */ @@ -2091,7 +2330,20 @@ get_plt_index (const struct elf_sh_plt_info *info, bfd_vma offset) static bfd_vma get_plt_offset (const struct elf_sh_plt_info *info, bfd_vma plt_index) { - return info->plt0_entry_size + (plt_index * info->symbol_entry_size); + 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 @@ -2114,6 +2366,12 @@ struct elf_sh_dyn_relocs 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 @@ -2133,9 +2391,23 @@ struct elf_sh_link_hash_entry 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_UNKNOWN = 0, GOT_NORMAL, GOT_TLS_GD, GOT_TLS_IE - } tls_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)) @@ -2144,15 +2416,21 @@ struct sh_elf_obj_tdata { struct elf_obj_tdata root; - /* tls_type for each local got entry. */ - char *local_got_tls_type; + /* 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_tls_type(abfd) \ - (sh_elf_tdata (abfd)->local_got_tls_type) +#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 \ @@ -2183,6 +2461,9 @@ struct elf_sh_link_hash_table asection *srelplt; asection *sdynbss; asection *srelbss; + asection *sfuncdesc; + asection *srelfuncdesc; + asection *srofixup; /* The (unloaded but important) VxWorks .rela.plt.unloaded section. */ asection *srelplt2; @@ -2202,6 +2483,9 @@ struct elf_sh_link_hash_table /* 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. */ @@ -2248,7 +2532,9 @@ sh_elf_link_hash_newfunc (struct bfd_hash_entry *entry, #ifdef INCLUDE_SHMEDIA ret->datalabel_got.refcount = ret->root.got.refcount; #endif - ret->tls_type = GOT_UNKNOWN; + ret->funcdesc.refcount = 0; + ret->abs_funcdesc_refcount = 0; + ret->got_type = GOT_UNKNOWN; } return (struct bfd_hash_entry *) ret; @@ -2287,10 +2573,39 @@ sh_elf_link_hash_table_create (bfd *abfd) 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. */ @@ -2311,6 +2626,38 @@ create_got_section (bfd *dynobj, struct bfd_link_info *info) htab->srelgot = bfd_get_section_by_name (dynobj, ".rela.got"); if (! htab->sgot || ! htab->sgotplt || ! htab->srelgot) abort (); + + htab->sfuncdesc = bfd_make_section_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_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_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; } @@ -2671,6 +3018,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) || 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. */ @@ -2683,20 +3031,28 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) not generating a shared library, then set the symbol to this location in the .plt. This is required to make function pointers compare as equal between the normal executable and - the shared library. */ - if (! info->shared - && !h->def_regular) + 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. */ - s->size += htab->plt_info->symbol_entry_size; + 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. */ - htab->sgotplt->size += 4; + 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); @@ -2734,7 +3090,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) { asection *s; bfd_boolean dyn; - int tls_type = sh_elf_hash_entry (h)->tls_type; + int 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. */ @@ -2749,21 +3105,40 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) h->got.offset = s->size; s->size += 4; /* R_SH_TLS_GD needs 2 consecutive GOT slots. */ - if (tls_type == GOT_TLS_GD) + 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; + } /* R_SH_TLS_IE_32 needs one dynamic relocation if dynamic, R_SH_TLS_GD needs one if local symbol and two if global. */ - if ((tls_type == GOT_TLS_GD && h->dynindx == -1) - || (tls_type == GOT_TLS_IE && dyn)) + else if ((got_type == GOT_TLS_GD && h->dynindx == -1) + || got_type == GOT_TLS_IE) htab->srelgot->size += sizeof (Elf32_External_Rela); - else if (tls_type == GOT_TLS_GD) + 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; @@ -2794,6 +3169,46 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) 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; @@ -2889,6 +3304,10 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) { 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; @@ -2931,9 +3350,87 @@ 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) + { + struct elf_link_hash_entry *h; + + /* Force a PT_GNU_STACK segment to be created. */ + if (! elf_tdata (output_bfd)->stack_flags) + elf_tdata (output_bfd)->stack_flags = PF_R | PF_W | PF_X; + + /* Define __stacksize if it's not defined yet. */ + h = elf_link_hash_lookup (elf_hash_table (info), "__stacksize", + FALSE, FALSE, FALSE); + if (! h || h->root.type != bfd_link_hash_defined + || h->type != STT_OBJECT + || !h->def_regular) + { + struct bfd_link_hash_entry *bh = NULL; + + if (!(_bfd_generic_link_add_one_symbol + (info, output_bfd, "__stacksize", + BSF_GLOBAL, bfd_abs_section_ptr, DEFAULT_STACK_SIZE, + (const char *) NULL, FALSE, + get_elf_backend_data (output_bfd)->collect, &bh))) + return FALSE; + + h = (struct elf_link_hash_entry *) bh; + h->def_regular = 1; + h->type = STT_OBJECT; + } + } + return TRUE; +} + +#if !defined INCLUDE_SHMEDIA && !defined SH_TARGET_ALREADY_DEFINED + +static bfd_boolean +sh_elf_modify_program_headers (bfd *output_bfd, struct bfd_link_info *info) +{ + struct elf_obj_tdata *tdata = elf_tdata (output_bfd); + struct elf_segment_map *m; + Elf_Internal_Phdr *p; + + /* objcopy and strip preserve what's already there using + sh_elf_copy_private_bfd_data (). */ + if (! info) + return TRUE; + + for (p = tdata->phdr, m = tdata->segment_map; m != NULL; m = m->next, p++) + if (m->p_type == PT_GNU_STACK) + break; + + if (m) + { + struct elf_link_hash_entry *h; + + /* Obtain the pointer to the __stacksize symbol. */ + h = elf_link_hash_lookup (elf_hash_table (info), "__stacksize", + FALSE, FALSE, FALSE); + if (h) + { + while (h->root.type == bfd_link_hash_indirect + || h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + BFD_ASSERT (h->root.type == bfd_link_hash_defined); + } + + /* Set the header p_memsz from the symbol value. We + intentionally ignore the symbol section. */ + if (h && h->root.type == bfd_link_hash_defined) + p->p_memsz = h->root.u.def.value; + else + p->p_memsz = DEFAULT_STACK_SIZE; + + p->p_align = 8; + } + return TRUE; } +#endif + /* Set the sizes of the dynamic sections. */ static bfd_boolean @@ -2971,7 +3468,8 @@ sh_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, { bfd_signed_vma *local_got; bfd_signed_vma *end_local_got; - char *local_tls_type; + union gotref *local_funcdesc, *end_local_funcdesc; + char *local_got_type; bfd_size_type locsymcount; Elf_Internal_Shdr *symtab_hdr; asection *srel; @@ -3009,39 +3507,88 @@ sh_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, 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); } } } - local_got = elf_local_got_refcounts (ibfd); - if (!local_got) - continue; - symtab_hdr = &elf_symtab_hdr (ibfd); locsymcount = symtab_hdr->sh_info; #ifdef INCLUDE_SHMEDIA /* Count datalabel local GOT. */ locsymcount *= 2; #endif - end_local_got = local_got + locsymcount; - local_tls_type = sh_elf_local_got_tls_type (ibfd); s = htab->sgot; srel = htab->srelgot; - for (; local_got < end_local_got; ++local_got) + + local_got = elf_local_got_refcounts (ibfd); + if (local_got) { - if (*local_got > 0) + 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) { - *local_got = s->size; - s->size += 4; - if (*local_tls_type == GOT_TLS_GD) - s->size += 4; - if (info->shared) - srel->size += sizeof (Elf32_External_Rela); + 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; } - else - *local_got = (bfd_vma) -1; - ++local_tls_type; } + } if (htab->tls_ldm_got.refcount > 0) @@ -3055,10 +3602,30 @@ sh_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, 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; @@ -3070,6 +3637,8 @@ sh_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, 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 @@ -3143,6 +3712,12 @@ sh_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, || ! 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) { @@ -3172,67 +3747,251 @@ sh_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, return TRUE; } -/* Relocate an SH ELF section. */ +/* Add a dynamic relocation to the SRELOC 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) +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) { - 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; - bfd_vma *local_got_offsets; - asection *sgot; - asection *sgotplt; - asection *splt; - asection *sreloc; - asection *srelgot; - bfd_boolean is_vxworks_tls; + Elf_Internal_Rela outrel; + bfd_vma reloc_offset; - BFD_ASSERT (is_sh_elf (input_bfd)); + outrel.r_offset = offset; + outrel.r_info = ELF32_R_INFO (dynindx, reloc_type); + outrel.r_addend = addend; - htab = sh_elf_hash_table (info); - if (htab == NULL) - return FALSE; - symtab_hdr = &elf_symtab_hdr (input_bfd); - sym_hashes = elf_sym_hashes (input_bfd); - dynobj = htab->root.dynobj; - local_got_offsets = elf_local_got_offsets (input_bfd); + 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++; - sgot = htab->sgot; - sgotplt = htab->sgotplt; - splt = htab->splt; - sreloc = NULL; - srelgot = NULL; - /* We have to handle relocations in vxworks .tls_vars sections - specially, because the dynamic loader is 'weird'. */ - is_vxworks_tls = (htab->vxworks_p && info->shared - && !strcmp (input_section->output_section->name, - ".tls_vars")); + return reloc_offset; +} - 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; - int tls_type; +/* Add an FDPIC read-only fixup. */ - r_symndx = ELF32_R_SYM (rel->r_info); +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 = _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 ! (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; + bfd_vma *local_got_offsets; + asection *sgot; + asection *sgotplt; + asection *splt; + asection *sreloc; + asection *srelgot; + bfd_boolean is_vxworks_tls; + unsigned isec_segment, got_segment, plt_segment, check_segment[2]; + + BFD_ASSERT (is_sh_elf (input_bfd)); + + htab = sh_elf_hash_table (info); + if (htab == NULL) + return FALSE; + symtab_hdr = &elf_symtab_hdr (input_bfd); + sym_hashes = elf_sym_hashes (input_bfd); + dynobj = htab->root.dynobj; + local_got_offsets = elf_local_got_offsets (input_bfd); + + isec_segment = sh_elf_osec_to_segment (output_bfd, + input_section->output_section); + if (htab->fdpic_p && htab->sgot) + got_segment = sh_elf_osec_to_segment (output_bfd, + htab->sgot->output_section); + else + got_segment = -1; + if (htab->fdpic_p && htab->splt) + plt_segment = sh_elf_osec_to_segment (output_bfd, + htab->splt->output_section); + else + plt_segment = -1; + + sgot = htab->sgot; + sgotplt = htab->sgotplt; + splt = htab->splt; + sreloc = NULL; + srelgot = NULL; + /* We have to handle relocations in vxworks .tls_vars sections + specially, because the dynamic loader is 'weird'. */ + is_vxworks_tls = (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; + int got_type; + const char *symname = NULL; + + r_symndx = ELF32_R_SYM (rel->r_info); r_type = ELF32_R_TYPE (rel->r_info); @@ -3248,14 +4007,16 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, || 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_2 - && r_type <= (int) R_SH_LAST_INVALID_RELOC_2)) + || ( 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; @@ -3271,10 +4032,18 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, 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); @@ -3361,6 +4130,7 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, 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) { @@ -3394,6 +4164,12 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, || 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 @@ -3428,8 +4204,8 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, && ((input_section->flags & SEC_DEBUGGING) != 0 && h->def_dynamic)) || (sec->output_section == NULL - && (sh_elf_hash_entry (h)->tls_type == GOT_TLS_IE - || sh_elf_hash_entry (h)->tls_type == GOT_TLS_GD))) + && (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 @@ -3482,6 +4258,16 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, 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: @@ -3675,6 +4461,24 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, outrel.r_addend = addend; } #endif + else if (htab->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 @@ -3702,6 +4506,8 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, 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 @@ -3709,6 +4515,34 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, if (! relocate) continue; } + else if (htab->fdpic_p && !info->shared + && r_type == R_SH_DIR32 + && (input_section->flags & SEC_ALLOC) != 0) + { + bfd_vma offset; + + 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: @@ -3748,6 +4582,7 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, force_got: case R_SH_GOT32: + case R_SH_GOT20: #ifdef INCLUDE_SHMEDIA case R_SH_GOT_LOW16: case R_SH_GOT_MEDLOW16: @@ -3760,6 +4595,7 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, offset table. */ BFD_ASSERT (sgot != NULL); + check_segment[0] = check_segment[1] = -1; if (h != NULL) { @@ -3813,10 +4649,21 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, else #endif h->got.offset |= 1; + + /* If we initialize the GOT entry here with a valid + symbol address, also add a fixup. */ + if (htab->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 = sgot->output_offset + off; + relocation = sh_elf_got_offset (htab) + off; } else { @@ -3866,12 +4713,30 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, outrel.r_offset = (sgot->output_section->vma + sgot->output_offset + off); - outrel.r_info = ELF32_R_INFO (0, R_SH_RELATIVE); - outrel.r_addend = relocation; + if (htab->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 (htab->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) @@ -3881,33 +4746,39 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, local_got_offsets[r_symndx] |= 1; } - relocation = sgot->output_offset + off; + relocation = sh_elf_got_offset (htab) + off; } #ifdef GOT_BIAS relocation -= GOT_BIAS; #endif - goto final_link_relocate; + 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 - /* Relocation is relative to the start of the global offset - table. */ - - BFD_ASSERT (sgot != NULL); - - /* Note that sgot->output_offset is not involved in this - calculation. We always want the start of .got. If we - defined _GLOBAL_OFFSET_TABLE in a different way, as is - permitted by the ABI, we might have to change this - calculation. */ - relocation -= sgot->output_section->vma; + /* 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 (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; @@ -3915,7 +4786,15 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, addend = rel->r_addend; - goto final_link_relocate; + 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 @@ -3926,8 +4805,8 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, #endif /* Use global offset table as symbol value. */ - BFD_ASSERT (sgot != NULL); - relocation = sgot->output_section->vma; + BFD_ASSERT (sgotplt != NULL); + relocation = sgotplt->output_section->vma + sgotplt->output_offset; #ifdef GOT_BIAS relocation += GOT_BIAS; @@ -3952,6 +4831,13 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, 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; @@ -3964,6 +4850,7 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, } BFD_ASSERT (splt != NULL); + check_segment[1] = plt_segment; relocation = (splt->output_section->vma + splt->output_offset + h->plt.offset); @@ -3976,6 +4863,298 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, 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; + + 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 = htab->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_section_by_name (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. */ + + 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 + + htab->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; @@ -3996,20 +5175,21 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, case R_SH_TLS_GD_32: case R_SH_TLS_IE_32: + check_segment[0] = check_segment[1] = -1; r_type = sh_elf_optimized_tls_reloc (info, r_type, h == NULL); - tls_type = GOT_UNKNOWN; + got_type = GOT_UNKNOWN; if (h == NULL && local_got_offsets) - tls_type = sh_elf_local_got_tls_type (input_bfd) [r_symndx]; + got_type = sh_elf_local_got_type (input_bfd) [r_symndx]; else if (h != NULL) { - tls_type = sh_elf_hash_entry (h)->tls_type; + 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 && tls_type == GOT_TLS_IE) + 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) @@ -4097,8 +5277,7 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, continue; } - sgot = htab->sgot; - if (sgot == NULL) + if (sgot == NULL || sgotplt == NULL) abort (); if (h != NULL) @@ -4118,7 +5297,7 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, off &= ~1; bfd_put_32 (output_bfd, tpoff (info, relocation), sgot->contents + off); - bfd_put_32 (output_bfd, sgot->output_offset + off, + bfd_put_32 (output_bfd, sh_elf_got_offset (htab) + off, contents + rel->r_offset); continue; } @@ -4186,7 +5365,7 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, abort (); if (r_type == (int) ELF32_R_TYPE (rel->r_info)) - relocation = sgot->output_offset + off; + relocation = sh_elf_got_offset (htab) + off; else { bfd_vma offset; @@ -4235,7 +5414,7 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, bfd_put_16 (output_bfd, 0x0009, contents + offset + 8); bfd_put_16 (output_bfd, 0x0009, contents + offset + 10); - bfd_put_32 (output_bfd, sgot->output_offset + off, + bfd_put_32 (output_bfd, sh_elf_got_offset (htab) + off, contents + rel->r_offset); continue; @@ -4246,6 +5425,7 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, goto final_link_relocate; case R_SH_TLS_LD_32: + check_segment[0] = check_segment[1] = -1; if (! info->shared) { bfd_vma offset; @@ -4293,8 +5473,7 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, continue; } - sgot = htab->sgot; - if (sgot == NULL) + if (sgot == NULL || sgotplt == NULL) abort (); off = htab->tls_ldm_got.offset; @@ -4319,12 +5498,13 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, htab->tls_ldm_got.offset |= 1; } - relocation = sgot->output_offset + off; + 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 @@ -4339,6 +5519,8 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, Elf_Internal_Rela outrel; bfd_byte *loc; + check_segment[0] = check_segment[1] = -1; + if (! info->shared) { relocation = tpoff (info, relocation); @@ -4376,6 +5558,28 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info, } relocation_done: + if (htab->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) @@ -4574,6 +5778,7 @@ sh_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info, 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) @@ -4584,6 +5789,7 @@ sh_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info, 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++) @@ -4630,7 +5836,9 @@ sh_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info, 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: @@ -4650,6 +5858,8 @@ sh_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info, #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 @@ -4680,7 +5890,28 @@ sh_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info, } 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; @@ -4800,12 +6031,16 @@ sh_elf_copy_indirect_symbol (struct bfd_link_info *info, 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->tls_type = eind->tls_type; - eind->tls_type = GOT_UNKNOWN; + edir->got_type = eind->got_type; + eind->got_type = GOT_UNKNOWN; } if (ind->root.type != bfd_link_hash_indirect @@ -4862,7 +6097,7 @@ sh_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, asection *srelgot; asection *sreloc; unsigned int r_type; - int tls_type, old_tls_type; + int got_type, old_got_type; sgot = NULL; srelgot = NULL; @@ -4919,14 +6154,49 @@ sh_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, || 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: @@ -4953,13 +6223,10 @@ sh_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, case R_SH_TLS_GD_32: case R_SH_TLS_LD_32: case R_SH_TLS_IE_32: - if (htab->sgot == NULL) - { - if (htab->root.dynobj == NULL) - htab->root.dynobj = abfd; - if (!create_got_section (htab->root.dynobj, info)) - return FALSE; - } + if (htab->root.dynobj == NULL) + htab->root.dynobj = abfd; + if (!create_got_section (htab->root.dynobj, info)) + return FALSE; break; default: @@ -4993,6 +6260,7 @@ sh_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, 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: @@ -5001,16 +6269,22 @@ sh_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, case R_SH_GOT10BY4: case R_SH_GOT10BY8: #endif + case R_SH_GOTFUNCDESC: + case R_SH_GOTFUNCDESC20: switch (r_type) { default: - tls_type = GOT_NORMAL; + got_type = GOT_NORMAL; break; case R_SH_TLS_GD_32: - tls_type = GOT_TLS_GD; + got_type = GOT_TLS_GD; break; case R_SH_TLS_IE_32: - tls_type = GOT_TLS_IE; + got_type = GOT_TLS_IE; + break; + case R_SH_GOTFUNCDESC: + case R_SH_GOTFUNCDESC20: + got_type = GOT_FUNCDESC; break; } @@ -5027,7 +6301,7 @@ sh_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, else #endif h->got.refcount += 1; - old_tls_type = sh_elf_hash_entry (h)->tls_type; + old_got_type = sh_elf_hash_entry (h)->got_type; } else { @@ -5056,10 +6330,10 @@ sh_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, #ifdef INCLUDE_SHMEDIA /* Take care of both the datalabel and codelabel local GOT offsets. */ - sh_elf_local_got_tls_type (abfd) + sh_elf_local_got_type (abfd) = (char *) (local_got_refcounts + 2 * symtab_hdr->sh_info); #else - sh_elf_local_got_tls_type (abfd) + sh_elf_local_got_type (abfd) = (char *) (local_got_refcounts + symtab_hdr->sh_info); #endif } @@ -5069,31 +6343,42 @@ sh_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, else #endif local_got_refcounts[r_symndx] += 1; - old_tls_type = sh_elf_local_got_tls_type (abfd) [r_symndx]; + 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_tls_type != tls_type && old_tls_type != GOT_UNKNOWN - && (old_tls_type != GOT_TLS_GD || tls_type != GOT_TLS_IE)) + if (old_got_type != got_type && old_got_type != GOT_UNKNOWN + && (old_got_type != GOT_TLS_GD || got_type != GOT_TLS_IE)) { - if (old_tls_type == GOT_TLS_IE && tls_type == GOT_TLS_GD) - tls_type = GOT_TLS_IE; + if (old_got_type == GOT_TLS_IE && got_type == GOT_TLS_GD) + got_type = GOT_TLS_IE; else { - (*_bfd_error_handler) + 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_tls_type != tls_type) + if (old_got_type != got_type) { if (h != NULL) - sh_elf_hash_entry (h)->tls_type = tls_type; + sh_elf_hash_entry (h)->got_type = got_type; else - sh_elf_local_got_tls_type (abfd) [r_symndx] = tls_type; + sh_elf_local_got_type (abfd) [r_symndx] = got_type; } break; @@ -5102,6 +6387,70 @@ sh_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, 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: @@ -5267,6 +6616,13 @@ sh_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, 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: @@ -5360,6 +6716,38 @@ sh_elf_copy_private_data (bfd * ibfd, bfd * obfd) if (! is_sh_elf (ibfd) || ! is_sh_elf (obfd)) return TRUE; + /* Copy the stack size. */ + if (elf_tdata (ibfd)->phdr && elf_tdata (obfd)->phdr + && fdpic_object_p (ibfd) && fdpic_object_p (obfd)) + { + unsigned i; + + for (i = 0; i < elf_elfheader (ibfd)->e_phnum; i++) + if (elf_tdata (ibfd)->phdr[i].p_type == PT_GNU_STACK) + { + Elf_Internal_Phdr *iphdr = &elf_tdata (ibfd)->phdr[i]; + + for (i = 0; i < elf_elfheader (obfd)->e_phnum; i++) + if (elf_tdata (obfd)->phdr[i].p_type == PT_GNU_STACK) + { + memcpy (&elf_tdata (obfd)->phdr[i], iphdr, sizeof (*iphdr)); + + /* Rewrite the phdrs, since we're only called after they + were first written. */ + if (bfd_seek (obfd, + (bfd_signed_vma) get_elf_backend_data (obfd) + ->s->sizeof_ehdr, SEEK_SET) != 0 + || get_elf_backend_data (obfd)->s + ->write_out_phdrs (obfd, elf_tdata (obfd)->phdr, + elf_elfheader (obfd)->e_phnum) != 0) + return FALSE; + break; + } + + break; + } + } + return sh_elf_set_private_flags (obfd, elf_elfheader (ibfd)->e_flags); } #endif /* not sh_elf_copy_private_data */ @@ -5393,8 +6781,10 @@ sh_elf_merge_private_data (bfd *ibfd, bfd *obfd) { /* This happens when ld starts out with a 'blank' output file. */ elf_flags_init (obfd) = TRUE; - elf_elfheader (obfd)->e_flags = EF_SH1; + elf_elfheader (obfd)->e_flags = elf_elfheader (ibfd)->e_flags; sh_elf_set_mach_from_flags (obfd); + if (elf_elfheader (obfd)->e_flags & EF_SH_FDPIC) + elf_elfheader (obfd)->e_flags |= EF_SH_PIC; } if (! sh_merge_bfd_arch (ibfd, obfd)) @@ -5406,9 +6796,18 @@ sh_elf_merge_private_data (bfd *ibfd, bfd *obfd) return FALSE; } - elf_elfheader (obfd)->e_flags = + elf_elfheader (obfd)->e_flags &= ~EF_SH_MACH_MASK; + elf_elfheader (obfd)->e_flags |= sh_elf_get_flags_from_mach (bfd_get_mach (obfd)); - + + if (fdpic_object_p (ibfd) != fdpic_object_p (obfd)) + { + _bfd_error_handler ("%B: attempt to mix FDPIC and non-FDPIC objects", + ibfd); + bfd_set_error (bfd_error_bad_value); + return FALSE; + } + return TRUE; } #endif /* not sh_elf_merge_private_data */ @@ -5420,7 +6819,11 @@ sh_elf_merge_private_data (bfd *ibfd, bfd *obfd) static bfd_boolean sh_elf_object_p (bfd *abfd) { - return sh_elf_set_mach_from_flags (abfd); + if (! sh_elf_set_mach_from_flags (abfd)) + return FALSE; + + return (((elf_elfheader (abfd)->e_flags & EF_SH_FDPIC) != 0) + == fdpic_object_p (abfd)); } /* Finish up dynamic symbol handling. We set the contents of various @@ -5440,13 +6843,14 @@ sh_elf_finish_dynamic_symbol (bfd *output_bfd, struct bfd_link_info *info, if (h->plt.offset != (bfd_vma) -1) { asection *splt; - asection *sgot; - asection *srel; + asection *sgotplt; + asection *srelplt; bfd_vma plt_index; bfd_vma got_offset; Elf_Internal_Rela rel; bfd_byte *loc; + const struct elf_sh_plt_info *plt_info; /* This symbol has an entry in the procedure linkage table. Set it up. */ @@ -5454,9 +6858,9 @@ sh_elf_finish_dynamic_symbol (bfd *output_bfd, struct bfd_link_info *info, BFD_ASSERT (h->dynindx != -1); splt = htab->splt; - sgot = htab->sgotplt; - srel = htab->srelplt; - BFD_ASSERT (splt != NULL && sgot != NULL && srel != NULL); + sgotplt = htab->sgotplt; + srelplt = htab->srelplt; + BFD_ASSERT (splt != NULL && sgotplt != NULL && srelplt != NULL); /* Get the index in the procedure linkage table which corresponds to this symbol. This is the index of this symbol @@ -5464,10 +6868,21 @@ sh_elf_finish_dynamic_symbol (bfd *output_bfd, struct bfd_link_info *info, first entry in the procedure linkage table is reserved. */ plt_index = get_plt_index (htab->plt_info, h->plt.offset); + plt_info = htab->plt_info; + if (plt_info->short_plt != NULL && plt_index <= MAX_SHORT_PLT) + plt_info = plt_info->short_plt; + /* Get the offset into the .got table of the entry that - corresponds to this function. Each .got entry is 4 bytes. - The first three are reserved. */ - got_offset = (plt_index + 3) * 4; + corresponds to this function. */ + if (htab->fdpic_p) + /* The offset must be relative to the GOT symbol, twelve bytes + before the end of .got.plt. Each descriptor is eight + bytes. */ + got_offset = plt_index * 8 + 12 - sgotplt->size; + else + /* Each .got entry is 4 bytes. The first three are + reserved. */ + got_offset = (plt_index + 3) * 4; #ifdef GOT_BIAS if (info->shared) @@ -5476,23 +6891,37 @@ sh_elf_finish_dynamic_symbol (bfd *output_bfd, struct bfd_link_info *info, /* Fill in the entry in the procedure linkage table. */ memcpy (splt->contents + h->plt.offset, - htab->plt_info->symbol_entry, - htab->plt_info->symbol_entry_size); + plt_info->symbol_entry, + plt_info->symbol_entry_size); - if (info->shared) - install_plt_field (output_bfd, FALSE, got_offset, - (splt->contents - + h->plt.offset - + htab->plt_info->symbol_fields.got_entry)); + if (info->shared || htab->fdpic_p) + { + if (plt_info->symbol_fields.got20) + { + bfd_reloc_status_type r; + r = install_movi20_field (output_bfd, got_offset, + splt->owner, splt, splt->contents, + h->plt.offset + + plt_info->symbol_fields.got_entry); + BFD_ASSERT (r == bfd_reloc_ok); + } + else + install_plt_field (output_bfd, FALSE, got_offset, + (splt->contents + + h->plt.offset + + plt_info->symbol_fields.got_entry)); + } else { + BFD_ASSERT (!plt_info->symbol_fields.got20); + install_plt_field (output_bfd, FALSE, - (sgot->output_section->vma - + sgot->output_offset + (sgotplt->output_section->vma + + sgotplt->output_offset + got_offset), (splt->contents + h->plt.offset - + htab->plt_info->symbol_fields.got_entry)); + + plt_info->symbol_fields.got_entry)); if (htab->vxworks_p) { unsigned int reachable_plts, plts_per_4k; @@ -5506,61 +6935,73 @@ sh_elf_finish_dynamic_symbol (bfd *output_bfd, struct bfd_link_info *info, /* ??? It would be better to create multiple copies of the common resolver stub. */ reachable_plts = ((4096 - - htab->plt_info->plt0_entry_size - - (htab->plt_info->symbol_fields.plt + 4)) - / htab->plt_info->symbol_entry_size) + 1; - plts_per_4k = (4096 / htab->plt_info->symbol_entry_size); + - plt_info->plt0_entry_size + - (plt_info->symbol_fields.plt + 4)) + / plt_info->symbol_entry_size) + 1; + plts_per_4k = (4096 / plt_info->symbol_entry_size); if (plt_index < reachable_plts) distance = -(h->plt.offset - + htab->plt_info->symbol_fields.plt); + + plt_info->symbol_fields.plt); else distance = -(((plt_index - reachable_plts) % plts_per_4k + 1) - * htab->plt_info->symbol_entry_size); + * plt_info->symbol_entry_size); /* Install the 'bra' with this offset. */ bfd_put_16 (output_bfd, 0xa000 | (0x0fff & ((distance - 4) / 2)), (splt->contents + h->plt.offset - + htab->plt_info->symbol_fields.plt)); + + plt_info->symbol_fields.plt)); } else install_plt_field (output_bfd, TRUE, splt->output_section->vma + splt->output_offset, (splt->contents + h->plt.offset - + htab->plt_info->symbol_fields.plt)); + + plt_info->symbol_fields.plt)); } + /* Make got_offset relative to the start of .got.plt. */ #ifdef GOT_BIAS if (info->shared) got_offset += GOT_BIAS; #endif + if (htab->fdpic_p) + got_offset = plt_index * 8; - install_plt_field (output_bfd, FALSE, - plt_index * sizeof (Elf32_External_Rela), - (splt->contents - + h->plt.offset - + htab->plt_info->symbol_fields.reloc_offset)); + if (plt_info->symbol_fields.reloc_offset != MINUS_ONE) + install_plt_field (output_bfd, FALSE, + plt_index * sizeof (Elf32_External_Rela), + (splt->contents + + h->plt.offset + + plt_info->symbol_fields.reloc_offset)); /* Fill in the entry in the global offset table. */ bfd_put_32 (output_bfd, (splt->output_section->vma + splt->output_offset + h->plt.offset - + htab->plt_info->symbol_resolve_offset), - sgot->contents + got_offset); + + plt_info->symbol_resolve_offset), + sgotplt->contents + got_offset); + if (htab->fdpic_p) + bfd_put_32 (output_bfd, + sh_elf_osec_to_segment (output_bfd, + htab->splt->output_section), + sgotplt->contents + got_offset + 4); /* Fill in the entry in the .rela.plt section. */ - rel.r_offset = (sgot->output_section->vma - + sgot->output_offset + rel.r_offset = (sgotplt->output_section->vma + + sgotplt->output_offset + got_offset); - rel.r_info = ELF32_R_INFO (h->dynindx, R_SH_JMP_SLOT); + if (htab->fdpic_p) + rel.r_info = ELF32_R_INFO (h->dynindx, R_SH_FUNCDESC_VALUE); + else + rel.r_info = ELF32_R_INFO (h->dynindx, R_SH_JMP_SLOT); rel.r_addend = 0; #ifdef GOT_BIAS rel.r_addend = GOT_BIAS; #endif - loc = srel->contents + plt_index * sizeof (Elf32_External_Rela); + loc = srelplt->contents + plt_index * sizeof (Elf32_External_Rela); bfd_elf32_swap_reloca_out (output_bfd, &rel, loc); if (htab->vxworks_p && !info->shared) @@ -5575,7 +7016,7 @@ sh_elf_finish_dynamic_symbol (bfd *output_bfd, struct bfd_link_info *info, rel.r_offset = (htab->splt->output_section->vma + htab->splt->output_offset + h->plt.offset - + htab->plt_info->symbol_fields.got_entry); + + plt_info->symbol_fields.got_entry); rel.r_info = ELF32_R_INFO (htab->root.hgot->indx, R_SH_DIR32); rel.r_addend = got_offset; bfd_elf32_swap_reloca_out (output_bfd, &rel, loc); @@ -5583,8 +7024,8 @@ sh_elf_finish_dynamic_symbol (bfd *output_bfd, struct bfd_link_info *info, /* Create a .rela.plt.unloaded R_SH_DIR32 relocation for the .got.plt entry, which initially points to .plt. */ - rel.r_offset = (htab->sgotplt->output_section->vma - + htab->sgotplt->output_offset + rel.r_offset = (sgotplt->output_section->vma + + sgotplt->output_offset + got_offset); rel.r_info = ELF32_R_INFO (htab->root.hplt->indx, R_SH_DIR32); rel.r_addend = 0; @@ -5600,11 +7041,12 @@ sh_elf_finish_dynamic_symbol (bfd *output_bfd, struct bfd_link_info *info, } if (h->got.offset != (bfd_vma) -1 - && sh_elf_hash_entry (h)->tls_type != GOT_TLS_GD - && sh_elf_hash_entry (h)->tls_type != GOT_TLS_IE) + && sh_elf_hash_entry (h)->got_type != GOT_TLS_GD + && sh_elf_hash_entry (h)->got_type != GOT_TLS_IE + && sh_elf_hash_entry (h)->got_type != GOT_FUNCDESC) { asection *sgot; - asection *srel; + asection *srelgot; Elf_Internal_Rela rel; bfd_byte *loc; @@ -5612,8 +7054,8 @@ sh_elf_finish_dynamic_symbol (bfd *output_bfd, struct bfd_link_info *info, up. */ sgot = htab->sgot; - srel = htab->srelgot; - BFD_ASSERT (sgot != NULL && srel != NULL); + srelgot = htab->srelgot; + BFD_ASSERT (sgot != NULL && srelgot != NULL); rel.r_offset = (sgot->output_section->vma + sgot->output_offset @@ -5627,10 +7069,23 @@ sh_elf_finish_dynamic_symbol (bfd *output_bfd, struct bfd_link_info *info, if (info->shared && SYMBOL_REFERENCES_LOCAL (info, h)) { - rel.r_info = ELF32_R_INFO (0, R_SH_RELATIVE); - rel.r_addend = (h->root.u.def.value - + h->root.u.def.section->output_section->vma - + h->root.u.def.section->output_offset); + if (htab->fdpic_p) + { + asection *sec = h->root.u.def.section; + int dynindx + = elf_section_data (sec->output_section)->dynindx; + + rel.r_info = ELF32_R_INFO (dynindx, R_SH_DIR32); + rel.r_addend = (h->root.u.def.value + + h->root.u.def.section->output_offset); + } + else + { + rel.r_info = ELF32_R_INFO (0, R_SH_RELATIVE); + rel.r_addend = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + } } else { @@ -5639,8 +7094,8 @@ sh_elf_finish_dynamic_symbol (bfd *output_bfd, struct bfd_link_info *info, rel.r_addend = 0; } - loc = srel->contents; - loc += srel->reloc_count++ * sizeof (Elf32_External_Rela); + loc = srelgot->contents; + loc += srelgot->reloc_count++ * sizeof (Elf32_External_Rela); bfd_elf32_swap_reloca_out (output_bfd, &rel, loc); } @@ -5652,7 +7107,7 @@ sh_elf_finish_dynamic_symbol (bfd *output_bfd, struct bfd_link_info *info, if (eh->datalabel_got.offset != (bfd_vma) -1) { asection *sgot; - asection *srel; + asection *srelgot; Elf_Internal_Rela rel; bfd_byte *loc; @@ -5660,8 +7115,8 @@ sh_elf_finish_dynamic_symbol (bfd *output_bfd, struct bfd_link_info *info, Set it up. */ sgot = htab->sgot; - srel = htab->srelgot; - BFD_ASSERT (sgot != NULL && srel != NULL); + srelgot = htab->srelgot; + BFD_ASSERT (sgot != NULL && srelgot != NULL); rel.r_offset = (sgot->output_section->vma + sgot->output_offset @@ -5675,10 +7130,23 @@ sh_elf_finish_dynamic_symbol (bfd *output_bfd, struct bfd_link_info *info, if (info->shared && SYMBOL_REFERENCES_LOCAL (info, h)) { - rel.r_info = ELF32_R_INFO (0, R_SH_RELATIVE); - rel.r_addend = (h->root.u.def.value - + h->root.u.def.section->output_section->vma - + h->root.u.def.section->output_offset); + if (htab->fdpic_p) + { + asection *sec = h->root.u.def.section; + int dynindx + = elf_section_data (sec->output_section)->dynindx; + + rel.r_info = ELF32_R_INFO (dynindx, R_SH_DIR32); + rel.r_addend = (h->root.u.def.value + + h->root.u.def.section->output_offset); + } + else + { + rel.r_info = ELF32_R_INFO (0, R_SH_RELATIVE); + rel.r_addend = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + } } else { @@ -5688,8 +7156,8 @@ sh_elf_finish_dynamic_symbol (bfd *output_bfd, struct bfd_link_info *info, rel.r_addend = 0; } - loc = srel->contents; - loc += srel->reloc_count++ * sizeof (Elf32_External_Rela); + loc = srelgot->contents; + loc += srelgot->reloc_count++ * sizeof (Elf32_External_Rela); bfd_elf32_swap_reloca_out (output_bfd, &rel, loc); } } @@ -5736,14 +7204,14 @@ static bfd_boolean sh_elf_finish_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info) { struct elf_sh_link_hash_table *htab; - asection *sgot; + asection *sgotplt; asection *sdyn; htab = sh_elf_hash_table (info); if (htab == NULL) return FALSE; - sgot = htab->sgotplt; + sgotplt = htab->sgotplt; sdyn = bfd_get_section_by_name (htab->root.dynobj, ".dynamic"); if (htab->root.dynamic_sections_created) @@ -5751,7 +7219,7 @@ sh_elf_finish_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info) asection *splt; Elf32_External_Dyn *dyncon, *dynconend; - BFD_ASSERT (sgot != NULL && sdyn != NULL); + BFD_ASSERT (sgotplt != NULL && sdyn != NULL); dyncon = (Elf32_External_Dyn *) sdyn->contents; dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->size); @@ -5797,12 +7265,15 @@ sh_elf_finish_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info) #endif case DT_PLTGOT: - s = htab->sgot->output_section; - goto get_vma; + BFD_ASSERT (htab->root.hgot != NULL); + s = htab->root.hgot->root.u.def.section; + dyn.d_un.d_ptr = htab->root.hgot->root.u.def.value + + s->output_section->vma + s->output_offset; + bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); + break; case DT_JMPREL: s = htab->srelplt->output_section; - get_vma: BFD_ASSERT (s != NULL); dyn.d_un.d_ptr = s->vma; bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); @@ -5847,8 +7318,8 @@ sh_elf_finish_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info) for (i = 0; i < ARRAY_SIZE (htab->plt_info->plt0_got_fields); i++) if (htab->plt_info->plt0_got_fields[i] != MINUS_ONE) install_plt_field (output_bfd, FALSE, - (sgot->output_section->vma - + sgot->output_offset + (sgotplt->output_section->vma + + sgotplt->output_offset + (i * 4)), (splt->contents + htab->plt_info->plt0_got_fields[i])); @@ -5899,20 +7370,43 @@ sh_elf_finish_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info) } /* Fill in the first three entries in the global offset table. */ - if (sgot && sgot->size > 0) + if (sgotplt && sgotplt->size > 0 && !htab->fdpic_p) { if (sdyn == NULL) - bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents); + bfd_put_32 (output_bfd, (bfd_vma) 0, sgotplt->contents); else bfd_put_32 (output_bfd, sdyn->output_section->vma + sdyn->output_offset, - sgot->contents); - bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + 4); - bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + 8); + sgotplt->contents); + bfd_put_32 (output_bfd, (bfd_vma) 0, sgotplt->contents + 4); + bfd_put_32 (output_bfd, (bfd_vma) 0, sgotplt->contents + 8); + } - elf_section_data (sgot->output_section)->this_hdr.sh_entsize = 4; + if (sgotplt && sgotplt->size > 0) + elf_section_data (sgotplt->output_section)->this_hdr.sh_entsize = 4; + + /* At the very end of the .rofixup section is a pointer to the GOT. */ + if (htab->fdpic_p && htab->srofixup != NULL) + { + struct elf_link_hash_entry *hgot = htab->root.hgot; + bfd_vma got_value = hgot->root.u.def.value + + hgot->root.u.def.section->output_section->vma + + hgot->root.u.def.section->output_offset; + + sh_elf_add_rofixup (output_bfd, htab->srofixup, got_value); + + /* Make sure we allocated and generated the same number of fixups. */ + BFD_ASSERT (htab->srofixup->reloc_count * 4 == htab->srofixup->size); } + if (htab->srelfuncdesc) + BFD_ASSERT (htab->srelfuncdesc->reloc_count * sizeof (Elf32_External_Rela) + == htab->srelfuncdesc->size); + + if (htab->srelgot) + BFD_ASSERT (htab->srelgot->reloc_count * sizeof (Elf32_External_Rela) + == htab->srelgot->size); + return TRUE; } @@ -6010,6 +7504,59 @@ sh_elf_plt_sym_val (bfd_vma i, const asection *plt, return plt->vma + get_plt_offset (plt_info, i); } +/* Decide whether to attempt to turn absptr or lsda encodings in + shared libraries into pcrel within the given input section. */ + +static bfd_boolean +sh_elf_use_relative_eh_frame (bfd *input_bfd ATTRIBUTE_UNUSED, + struct bfd_link_info *info, + asection *eh_frame_section ATTRIBUTE_UNUSED) +{ + struct elf_sh_link_hash_table *htab = sh_elf_hash_table (info); + + /* We can't use PC-relative encodings in FDPIC binaries, in general. */ + if (htab->fdpic_p) + return FALSE; + + return TRUE; +} + +/* Adjust the contents of an eh_frame_hdr section before they're output. */ + +static bfd_byte +sh_elf_encode_eh_address (bfd *abfd, + struct bfd_link_info *info, + asection *osec, bfd_vma offset, + asection *loc_sec, bfd_vma loc_offset, + bfd_vma *encoded) +{ + struct elf_sh_link_hash_table *htab = sh_elf_hash_table (info); + struct elf_link_hash_entry *h; + + if (!htab->fdpic_p) + return _bfd_elf_encode_eh_address (abfd, info, osec, offset, loc_sec, + loc_offset, encoded); + + h = htab->root.hgot; + BFD_ASSERT (h && h->root.type == bfd_link_hash_defined); + + if (! h || (sh_elf_osec_to_segment (abfd, osec) + == sh_elf_osec_to_segment (abfd, loc_sec->output_section))) + return _bfd_elf_encode_eh_address (abfd, info, osec, offset, + loc_sec, loc_offset, encoded); + + BFD_ASSERT (sh_elf_osec_to_segment (abfd, osec) + == (sh_elf_osec_to_segment + (abfd, h->root.u.def.section->output_section))); + + *encoded = osec->vma + offset + - (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + + return DW_EH_PE_datarel | DW_EH_PE_sdata4; +} + #if !defined SH_TARGET_ALREADY_DEFINED #define TARGET_BIG_SYM bfd_elf32_sh_vec #define TARGET_BIG_NAME "elf32-sh" @@ -6059,14 +7606,19 @@ sh_elf_plt_sym_val (bfd_vma i, const asection *plt, sh_elf_always_size_sections #define elf_backend_size_dynamic_sections \ sh_elf_size_dynamic_sections -#define elf_backend_omit_section_dynsym \ - ((bfd_boolean (*) (bfd *, struct bfd_link_info *, asection *)) bfd_true) +#define elf_backend_omit_section_dynsym sh_elf_omit_section_dynsym #define elf_backend_finish_dynamic_symbol \ sh_elf_finish_dynamic_symbol #define elf_backend_finish_dynamic_sections \ sh_elf_finish_dynamic_sections #define elf_backend_reloc_type_class sh_elf_reloc_type_class #define elf_backend_plt_sym_val sh_elf_plt_sym_val +#define elf_backend_can_make_relative_eh_frame \ + sh_elf_use_relative_eh_frame +#define elf_backend_can_make_lsda_relative_eh_frame \ + sh_elf_use_relative_eh_frame +#define elf_backend_encode_eh_address \ + sh_elf_encode_eh_address #define elf_backend_can_gc_sections 1 #define elf_backend_can_refcount 1 @@ -6120,6 +7672,28 @@ sh_elf_plt_sym_val (bfd_vma i, const asection *plt, #include "elf32-target.h" + +/* FDPIC support. */ +#undef TARGET_BIG_SYM +#define TARGET_BIG_SYM bfd_elf32_shbfd_vec +#undef TARGET_BIG_NAME +#define TARGET_BIG_NAME "elf32-shbig-fdpic" +#undef TARGET_LITTLE_SYM +#define TARGET_LITTLE_SYM bfd_elf32_shfd_vec +#undef TARGET_LITTLE_NAME +#define TARGET_LITTLE_NAME "elf32-sh-fdpic" +#undef elf_backend_modify_program_headers +#define elf_backend_modify_program_headers \ + sh_elf_modify_program_headers + +#undef elf32_bed +#define elf32_bed elf32_sh_fd_bed + +#include "elf32-target.h" + +#undef elf_backend_modify_program_headers + +/* VxWorks support. */ #undef TARGET_BIG_SYM #define TARGET_BIG_SYM bfd_elf32_shvxworks_vec #undef TARGET_BIG_NAME -- cgit v1.2.1