This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] SH-2A FDPIC Support


Hi All,

The attached patch implements FDPIC support for SH-2A uClinux.

The ABI has been published here:
http://www.codesourcery.com/public/docs/sh-fdpic/sh-fdpic-abi.txt

The implementation is not perfect (there are still a number of FIXMEs and such), but it is functional. I have successfully built a working uClinux system (incorporating busybox and dynamically linked uClibc), and the Binutils and GCC testsuites run tolerably well.

GCC and uClibc patches will follow soon.

OK to apply?

Andrew
2010-05-20  Daniel Jacobowitz  <dan@codesourcery.com>
	    Joseph Myers  <joseph@codesourcery.com>
	    Andrew Stubbs  <ams@codesourcery.com>

	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*.

---
 src/binutils-mainline/bfd/bfd-in2.h                |    7 
 src/binutils-mainline/bfd/config.bfd               |    2 
 src/binutils-mainline/bfd/configure                |    2 
 src/binutils-mainline/bfd/configure.in             |    2 
 src/binutils-mainline/bfd/elf32-sh-relocs.h        |  163 ++
 src/binutils-mainline/bfd/elf32-sh.c               | 1980 ++++++++++++++++++--
 src/binutils-mainline/bfd/libbfd.h                 |    7 
 src/binutils-mainline/bfd/reloc.c                  |   14 
 src/binutils-mainline/bfd/targets.c                |    4 
 src/binutils-mainline/binutils/readelf.c           |    5 
 src/binutils-mainline/gas/config/tc-sh.c           |  111 +
 src/binutils-mainline/gas/config/tc-sh.h           |    3 
 src/binutils-mainline/gas/configure.tgt            |    3 
 src/binutils-mainline/gas/doc/c-sh.texi            |    4 
 .../gas/testsuite/gas/sh/basic.exp                 |    4 
 src/binutils-mainline/gas/testsuite/gas/sh/fdpic.d |   13 
 src/binutils-mainline/gas/testsuite/gas/sh/fdpic.s |    8 
 .../gas/testsuite/gas/sh/reg-prefix.d              |    4 
 .../gas/testsuite/gas/sh/sh2a-pic.d                |   15 
 .../gas/testsuite/gas/sh/sh2a-pic.s                |    6 
 .../gas/testsuite/lib/gas-defs.exp                 |    1 
 src/binutils-mainline/include/elf/sh.h             |   18 
 src/binutils-mainline/ld/Makefile.am               |    8 
 src/binutils-mainline/ld/Makefile.in               |    8 
 src/binutils-mainline/ld/configure.tgt             |    2 
 src/binutils-mainline/ld/emulparams/shelf_fd.sh    |    2 
 src/binutils-mainline/ld/emulparams/shlelf_fd.sh   |   16 
 .../ld/emulparams/shlelf_linux.sh                  |    2 
 src/binutils-mainline/ld/testsuite/ld-sh/sh.exp    |    2 
 src/binutils-mainline/ld/testsuite/lib/ld-lib.exp  |    1 
 30 files changed, 2185 insertions(+), 232 deletions(-)
 create mode 100644 src/binutils-mainline/gas/testsuite/gas/sh/fdpic.d
 create mode 100644 src/binutils-mainline/gas/testsuite/gas/sh/fdpic.s
 create mode 100644 src/binutils-mainline/gas/testsuite/gas/sh/sh2a-pic.d
 create mode 100644 src/binutils-mainline/gas/testsuite/gas/sh/sh2a-pic.s
 create mode 100644 src/binutils-mainline/ld/emulparams/shelf_fd.sh
 create mode 100644 src/binutils-mainline/ld/emulparams/shlelf_fd.sh

diff --git a/src/binutils-mainline/bfd/bfd-in2.h b/src/binutils-mainline/bfd/bfd-in2.h
index a3f8ccc..b11328d 100644
--- a/src/binutils-mainline/bfd/bfd-in2.h
+++ b/src/binutils-mainline/bfd/bfd-in2.h
@@ -3276,6 +3276,13 @@ pc-relative or some form of GOT-indirect relocation.  */
   BFD_RELOC_SH_TLS_DTPMOD32,
   BFD_RELOC_SH_TLS_DTPOFF32,
   BFD_RELOC_SH_TLS_TPOFF32,
+  BFD_RELOC_SH_GOT20,
+  BFD_RELOC_SH_GOTOFF20,
+  BFD_RELOC_SH_GOTFUNCDESC,
+  BFD_RELOC_SH_GOTFUNCDESC20,
+  BFD_RELOC_SH_GOTOFFFUNCDESC,
+  BFD_RELOC_SH_GOTOFFFUNCDESC20,
+  BFD_RELOC_SH_FUNCDESC,
 
 /* ARC Cores relocs.
 ARC 22 bit pc-relative branch.  The lowest two bits must be zero and are
diff --git a/src/binutils-mainline/bfd/config.bfd b/src/binutils-mainline/bfd/config.bfd
index d39ef18..abe1b5e 100644
--- a/src/binutils-mainline/bfd/config.bfd
+++ b/src/binutils-mainline/bfd/config.bfd
@@ -1257,7 +1257,7 @@ case "${targ}" in
 
   sh-*-uclinux* | sh[12]-*-uclinux*)
     targ_defvec=bfd_elf32_sh_vec
-    targ_selvecs="bfd_elf32_shblin_vec bfd_elf32_shlin_vec"
+    targ_selvecs="bfd_elf32_shl_vec bfd_elf32_shblin_vec bfd_elf32_shlin_vec bfd_elf32_shfd_vec bfd_elf32_shbfd_vec"
 #ifdef BFD64
     targ_selvecs="${targ_selvecs} bfd_elf32_sh64lin_vec bfd_elf32_sh64blin_vec bfd_elf64_sh64lin_vec bfd_elf64_sh64blin_vec"
 #endif
diff --git a/src/binutils-mainline/bfd/configure b/src/binutils-mainline/bfd/configure
index 6ace16c..d177c67 100755
--- a/src/binutils-mainline/bfd/configure
+++ b/src/binutils-mainline/bfd/configure
@@ -15132,7 +15132,9 @@ do
     bfd_elf32_sh64lnbsd_vec)	tb="$tb elf32-sh64.lo elf32-sh64-com.lo elf-vxworks.lo elf32.lo $elf cofflink.lo" ;;
     bfd_elf32_sh64nbsd_vec)	tb="$tb elf32-sh64.lo elf32-sh64-com.lo elf-vxworks.lo elf32.lo $elf cofflink.lo" ;;
     bfd_elf32_sh_vec)		tb="$tb elf32-sh.lo elf-vxworks.lo elf32.lo $elf coff-sh.lo" ;;
+    bfd_elf32_shbfd_vec)	tb="$tb elf32-sh.lo elf-vxworks.lo elf32.lo $elf coff-sh.lo cofflink.lo" ;;
     bfd_elf32_shblin_vec)	tb="$tb elf32-sh.lo elf-vxworks.lo elf32.lo $elf coff-sh.lo cofflink.lo" ;;
+    bfd_elf32_shfd_vec)		tb="$tb elf32-sh.lo elf-vxworks.lo elf32.lo $elf coff-sh.lo cofflink.lo" ;;
     bfd_elf32_shl_vec)		tb="$tb elf32-sh.lo elf-vxworks.lo elf32.lo $elf coff-sh.lo" ;;
     bfd_elf32_shl_symbian_vec)	tb="$tb elf32-sh-symbian.lo elf32-sh64-com.lo elf-vxworks.lo elf32.lo $elf coff-sh.lo" ;;
     bfd_elf32_shlin_vec)	tb="$tb elf32-sh.lo elf-vxworks.lo elf32.lo $elf coff-sh.lo cofflink.lo" ;;
diff --git a/src/binutils-mainline/bfd/configure.in b/src/binutils-mainline/bfd/configure.in
index 6f05505..a928a0c 100644
--- a/src/binutils-mainline/bfd/configure.in
+++ b/src/binutils-mainline/bfd/configure.in
@@ -770,7 +770,9 @@ do
     bfd_elf32_sh64lnbsd_vec)	tb="$tb elf32-sh64.lo elf32-sh64-com.lo elf-vxworks.lo elf32.lo $elf cofflink.lo" ;;
     bfd_elf32_sh64nbsd_vec)	tb="$tb elf32-sh64.lo elf32-sh64-com.lo elf-vxworks.lo elf32.lo $elf cofflink.lo" ;;
     bfd_elf32_sh_vec)		tb="$tb elf32-sh.lo elf-vxworks.lo elf32.lo $elf coff-sh.lo" ;;
+    bfd_elf32_shbfd_vec)	tb="$tb elf32-sh.lo elf-vxworks.lo elf32.lo $elf coff-sh.lo cofflink.lo" ;;
     bfd_elf32_shblin_vec)	tb="$tb elf32-sh.lo elf-vxworks.lo elf32.lo $elf coff-sh.lo cofflink.lo" ;;
+    bfd_elf32_shfd_vec)		tb="$tb elf32-sh.lo elf-vxworks.lo elf32.lo $elf coff-sh.lo cofflink.lo" ;;
     bfd_elf32_shl_vec)		tb="$tb elf32-sh.lo elf-vxworks.lo elf32.lo $elf coff-sh.lo" ;;
     bfd_elf32_shl_symbian_vec)	tb="$tb elf32-sh-symbian.lo elf32-sh64-com.lo elf-vxworks.lo elf32.lo $elf coff-sh.lo" ;;
     bfd_elf32_shlin_vec)	tb="$tb elf32-sh.lo elf-vxworks.lo elf32.lo $elf coff-sh.lo cofflink.lo" ;;
diff --git a/src/binutils-mainline/bfd/elf32-sh-relocs.h b/src/binutils-mainline/bfd/elf32-sh-relocs.h
index 70f3a7b..05f0875 100644
--- a/src/binutils-mainline/bfd/elf32-sh-relocs.h
+++ b/src/binutils-mainline/bfd/elf32-sh-relocs.h
@@ -1,4 +1,4 @@
-/* Copyright 2006, 2007 Free Software Foundation, Inc.
+/* Copyright 2006, 2007, 2010 Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
@@ -1462,19 +1462,164 @@
 	 0,			/* src_mask */
 	 ((bfd_vma) 0) - 1,	/* dst_mask */
 	 FALSE),		/* pcrel_offset */
+#else
+  EMPTY_HOWTO (169),
+  EMPTY_HOWTO (170),
+  EMPTY_HOWTO (171),
+  EMPTY_HOWTO (172),
+  EMPTY_HOWTO (173),
+  EMPTY_HOWTO (174),
+  EMPTY_HOWTO (175),
+  EMPTY_HOWTO (176),
+  EMPTY_HOWTO (177),
+  EMPTY_HOWTO (178),
+  EMPTY_HOWTO (179),
+  EMPTY_HOWTO (180),
+  EMPTY_HOWTO (181),
+  EMPTY_HOWTO (182),
+  EMPTY_HOWTO (183),
+  EMPTY_HOWTO (184),
+  EMPTY_HOWTO (185),
+  EMPTY_HOWTO (186),
+  EMPTY_HOWTO (187),
+  EMPTY_HOWTO (188),
+  EMPTY_HOWTO (189),
+  EMPTY_HOWTO (190),
+  EMPTY_HOWTO (191),
+  EMPTY_HOWTO (192),
+  EMPTY_HOWTO (193),
+  EMPTY_HOWTO (194),
+  EMPTY_HOWTO (195),
+  EMPTY_HOWTO (196),
+#endif
 
   EMPTY_HOWTO (197),
   EMPTY_HOWTO (198),
   EMPTY_HOWTO (199),
   EMPTY_HOWTO (200),
-  EMPTY_HOWTO (201),
-  EMPTY_HOWTO (202),
-  EMPTY_HOWTO (203),
-  EMPTY_HOWTO (204),
-  EMPTY_HOWTO (205),
-  EMPTY_HOWTO (206),
-  EMPTY_HOWTO (207),
-  EMPTY_HOWTO (208),
+
+  /* FDPIC-relative offset to a GOT entry, for movi20.  */
+  HOWTO (R_SH_GOT20,		/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 20,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 bfd_elf_generic_reloc,	/* special_function */
+	 "R_SH_GOT20",		/* name */
+	 FALSE,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x00f0ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* FDPIC-relative offset to a data object, for movi20.  */
+  HOWTO (R_SH_GOTOFF20,		/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 20,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 bfd_elf_generic_reloc,	/* special_function */
+	 "R_SH_GOTOFF20",	/* name */
+	 FALSE,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x00f0ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* FDPIC-relative offset to a GOT entry for a function descriptor.  */
+  HOWTO (R_SH_GOTFUNCDESC,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 bfd_elf_generic_reloc,	/* special_function */
+	 "R_SH_GOTFUNCDESC",	/* name */
+	 FALSE,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* FDPIC-relative offset to a GOT entry for a function descriptor,
+     for movi20.  */
+  HOWTO (R_SH_GOTFUNCDESC20,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 20,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 bfd_elf_generic_reloc,	/* special_function */
+	 "R_SH_GOTFUNCDESC20",	/* name */
+	 FALSE,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x00f0ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* FDPIC-relative offset to a function descriptor.  */
+  HOWTO (R_SH_GOTOFFFUNCDESC,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 bfd_elf_generic_reloc,	/* special_function */
+	 "R_SH_GOTOFFFUNCDESC",	/* name */
+	 FALSE,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* FDPIC-relative offset to a function descriptor, for movi20.  */
+  HOWTO (R_SH_GOTOFFFUNCDESC20,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 20,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 bfd_elf_generic_reloc,	/* special_function */
+	 "R_SH_GOTOFFFUNCDESC20", /* name */
+	 FALSE,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x00f0ffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* Address of an official function descriptor.  */
+  HOWTO (R_SH_FUNCDESC,		/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 32,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 bfd_elf_generic_reloc,	/* special_function */
+	 "R_SH_FUNCDESC",	/* name */
+	 FALSE,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+  /* Function descriptor to be filled in by the dynamic linker.  */
+  HOWTO (R_SH_FUNCDESC_VALUE,	/* type */
+	 0,			/* rightshift */
+	 2,			/* size (0 = byte, 1 = short, 2 = long) */
+	 64,			/* bitsize */
+	 FALSE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 bfd_elf_generic_reloc,	/* special_function */
+	 "R_SH_FUNCDESC_VALUE", /* name */
+	 FALSE,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0xffffffff,		/* dst_mask */
+	 FALSE),		/* pcrel_offset */
+
+#ifdef INCLUDE_SHMEDIA
   EMPTY_HOWTO (209),
   EMPTY_HOWTO (210),
   EMPTY_HOWTO (211),
diff --git a/src/binutils-mainline/bfd/elf32-sh.c b/src/binutils-mainline/bfd/elf32-sh.c
index 23ee06a..1b0daf9 100644
--- a/src/binutils-mainline/bfd/elf32-sh.c
+++ b/src/binutils-mainline/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,6 +3747,175 @@ sh_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
   return TRUE;
 }
 
+/* Add a dynamic relocation to the SRELOC section.  */
+
+inline static bfd_vma
+sh_elf_add_dyn_reloc (bfd *output_bfd, asection *sreloc, bfd_vma offset,
+		      int reloc_type, long dynindx, bfd_vma addend)
+{
+  Elf_Internal_Rela outrel;
+  bfd_vma reloc_offset;
+
+  outrel.r_offset = offset;
+  outrel.r_info = ELF32_R_INFO (dynindx, reloc_type);
+  outrel.r_addend = addend;
+
+  reloc_offset = sreloc->reloc_count * sizeof (Elf32_External_Rela);
+  BFD_ASSERT (reloc_offset < sreloc->size);
+  bfd_elf32_swap_reloca_out (output_bfd, &outrel,
+			     sreloc->contents + reloc_offset);
+  sreloc->reloc_count++;
+
+  return reloc_offset;
+}
+
+/* Add an FDPIC read-only fixup.  */
+
+inline static void
+sh_elf_add_rofixup (bfd *output_bfd, asection *srofixup, bfd_vma offset)
+{
+  bfd_vma fixup_offset;
+
+  fixup_offset = srofixup->reloc_count++ * 4;
+  BFD_ASSERT (fixup_offset < srofixup->size);
+  bfd_put_32 (output_bfd, offset, srofixup->contents + fixup_offset);
+}
+
+/* Return the offset of the generated .got section from the
+   _GLOBAL_OFFSET_TABLE_ symbol.  */
+
+static bfd_signed_vma
+sh_elf_got_offset (struct elf_sh_link_hash_table *htab)
+{
+  return (htab->sgot->output_offset - htab->sgotplt->output_offset
+	  - htab->root.hgot->root.u.def.value);
+}
+
+/* Find the segment number in which OSEC, and output section, is
+   located.  */
+
+static unsigned
+sh_elf_osec_to_segment (bfd *output_bfd, asection *osec)
+{
+  Elf_Internal_Phdr *p = _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
@@ -3193,6 +3937,7 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
   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));
 
@@ -3204,6 +3949,19 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
   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;
@@ -3230,7 +3988,8 @@ sh_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
       bfd_reloc_status_type r;
       int seen_stt_datalabel = 0;
       bfd_vma off;
-      int tls_type;
+      int got_type;
+      const char *symname = NULL;
 
       r_symndx = ELF32_R_SYM (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);
+    }
+
+  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);
 
-      elf_section_data (sgot->output_section)->this_hdr.sh_entsize = 4;
+      /* 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
diff --git a/src/binutils-mainline/bfd/libbfd.h b/src/binutils-mainline/bfd/libbfd.h
index c02063a..c6e25ab 100644
--- a/src/binutils-mainline/bfd/libbfd.h
+++ b/src/binutils-mainline/bfd/libbfd.h
@@ -1510,6 +1510,13 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_SH_TLS_DTPMOD32",
   "BFD_RELOC_SH_TLS_DTPOFF32",
   "BFD_RELOC_SH_TLS_TPOFF32",
+  "BFD_RELOC_SH_GOT20",
+  "BFD_RELOC_SH_GOTOFF20",
+  "BFD_RELOC_SH_GOTFUNCDESC",
+  "BFD_RELOC_SH_GOTFUNCDESC20",
+  "BFD_RELOC_SH_GOTOFFFUNCDESC",
+  "BFD_RELOC_SH_GOTOFFFUNCDESC20",
+  "BFD_RELOC_SH_FUNCDESC",
   "BFD_RELOC_ARC_B22_PCREL",
   "BFD_RELOC_ARC_B26",
   "BFD_RELOC_BFIN_16_IMM",
diff --git a/src/binutils-mainline/bfd/reloc.c b/src/binutils-mainline/bfd/reloc.c
index a847629..7d16869 100644
--- a/src/binutils-mainline/bfd/reloc.c
+++ b/src/binutils-mainline/bfd/reloc.c
@@ -3255,6 +3255,20 @@ ENUMX
   BFD_RELOC_SH_TLS_DTPOFF32
 ENUMX
   BFD_RELOC_SH_TLS_TPOFF32
+ENUMX
+  BFD_RELOC_SH_GOT20
+ENUMX
+  BFD_RELOC_SH_GOTOFF20
+ENUMX
+  BFD_RELOC_SH_GOTFUNCDESC
+ENUMX
+  BFD_RELOC_SH_GOTFUNCDESC20
+ENUMX
+  BFD_RELOC_SH_GOTOFFFUNCDESC
+ENUMX
+  BFD_RELOC_SH_GOTOFFFUNCDESC20
+ENUMX
+  BFD_RELOC_SH_FUNCDESC
 ENUMDOC
   Renesas / SuperH SH relocs.  Not all of these appear in object files.
 
diff --git a/src/binutils-mainline/bfd/targets.c b/src/binutils-mainline/bfd/targets.c
index 2e330e6..69a5a94 100644
--- a/src/binutils-mainline/bfd/targets.c
+++ b/src/binutils-mainline/bfd/targets.c
@@ -663,7 +663,9 @@ extern const bfd_target bfd_elf32_sh64blin_vec;
 extern const bfd_target bfd_elf32_sh64lnbsd_vec;
 extern const bfd_target bfd_elf32_sh64nbsd_vec;
 extern const bfd_target bfd_elf32_sh_vec;
+extern const bfd_target bfd_elf32_shbfd_vec;
 extern const bfd_target bfd_elf32_shblin_vec;
+extern const bfd_target bfd_elf32_shfd_vec;
 extern const bfd_target bfd_elf32_shl_vec;
 extern const bfd_target bfd_elf32_shl_symbian_vec;
 extern const bfd_target bfd_elf32_shlin_vec;
@@ -1003,7 +1005,9 @@ static const bfd_target * const _bfd_target_vector[] =
 	&bfd_elf32_littlescore_vec,
 #endif
         &bfd_elf32_sh_vec,
+        &bfd_elf32_shbfd_vec,
         &bfd_elf32_shblin_vec,
+        &bfd_elf32_shfd_vec,
         &bfd_elf32_shl_vec,
         &bfd_elf32_shl_symbian_vec,
         &bfd_elf32_shlin_vec,
diff --git a/src/binutils-mainline/binutils/readelf.c b/src/binutils-mainline/binutils/readelf.c
index 53699b2..e43c798 100644
--- a/src/binutils-mainline/binutils/readelf.c
+++ b/src/binutils-mainline/binutils/readelf.c
@@ -2440,6 +2440,11 @@ get_machine_flags (unsigned e_flags, unsigned e_machine)
 	    default: strcat (buf, _(", unknown ISA")); break;
 	    }
 
+	  if (e_flags & EF_SH_PIC)
+	    strcat (buf, ", pic");
+
+	  if (e_flags & EF_SH_FDPIC)
+	    strcat (buf, ", fdpic");
 	  break;
 
 	case EM_SH:
diff --git a/src/binutils-mainline/gas/config/tc-sh.c b/src/binutils-mainline/gas/config/tc-sh.c
index a7cdd0e..4e49e4e 100644
--- a/src/binutils-mainline/gas/config/tc-sh.c
+++ b/src/binutils-mainline/gas/config/tc-sh.c
@@ -145,6 +145,9 @@ static unsigned int preset_target_arch;
    accommodate the insns seen so far.  */
 static unsigned int valid_arch;
 
+/* Whether --fdpic was given.  */
+static int sh_fdpic;
+
 const char EXP_CHARS[] = "eE";
 
 /* Chars that mean this number is a floating point constant.  */
@@ -612,7 +615,6 @@ sh_check_fixup (expressionS *main_exp, bfd_reloc_code_real_type *r_type_p)
 
   if (exp->X_op == O_PIC_reloc)
     {
-#ifdef HAVE_SH64
       switch (*r_type_p)
 	{
 	case BFD_RELOC_NONE:
@@ -620,6 +622,31 @@ sh_check_fixup (expressionS *main_exp, bfd_reloc_code_real_type *r_type_p)
 	  *r_type_p = exp->X_md;
 	  break;
 
+	case BFD_RELOC_SH_DISP20:
+	  switch (exp->X_md)
+	    {
+	    case BFD_RELOC_32_GOT_PCREL:
+	      *r_type_p = BFD_RELOC_SH_GOT20;
+	      break;
+
+	    case BFD_RELOC_32_GOTOFF:
+	      *r_type_p = BFD_RELOC_SH_GOTOFF20;
+	      break;
+
+	    case BFD_RELOC_SH_GOTFUNCDESC:
+	      *r_type_p = BFD_RELOC_SH_GOTFUNCDESC20;
+	      break;
+
+	    case BFD_RELOC_SH_GOTOFFFUNCDESC:
+	      *r_type_p = BFD_RELOC_SH_GOTOFFFUNCDESC20;
+	      break;
+
+	    default:
+	      abort ();
+	    }
+	  break;
+
+#ifdef HAVE_SH64
 	case BFD_RELOC_SH_IMM_LOW16:
 	  switch (exp->X_md)
 	    {
@@ -715,13 +742,11 @@ sh_check_fixup (expressionS *main_exp, bfd_reloc_code_real_type *r_type_p)
 	      abort ();
 	    }
 	  break;
+#endif
 
 	default:
 	  abort ();
 	}
-#else
-      *r_type_p = exp->X_md;
-#endif
       if (exp == main_exp)
 	exp->X_op = O_symbol;
       else
@@ -1358,12 +1383,6 @@ parse_exp (char *s, sh_operand_info *op)
   expression (&op->immediate);
   if (op->immediate.X_op == O_absent)
     as_bad (_("missing operand"));
-#ifdef OBJ_ELF
-  else if (op->immediate.X_op == O_PIC_reloc
-	   || sh_PIC_related_p (op->immediate.X_add_symbol)
-	   || sh_PIC_related_p (op->immediate.X_op_symbol))
-    as_bad (_("misplaced PIC operand"));
-#endif
   new_pointer = input_line_pointer;
   input_line_pointer = save;
   return new_pointer;
@@ -2327,6 +2346,8 @@ build_Mytes (sh_opcode_info *opcode, sh_operand_info *operand)
   unsigned int size = 2;
   int low_byte = target_big_endian ? 1 : 0;
   int max_index = 4;
+  bfd_reloc_code_real_type r_type;
+  int unhandled_pic = 0;
 
   nbuf[0] = 0;
   nbuf[1] = 0;
@@ -2337,6 +2358,14 @@ build_Mytes (sh_opcode_info *opcode, sh_operand_info *operand)
   nbuf[6] = 0;
   nbuf[7] = 0;
 
+  for (indx = 0; indx < 3; indx++)
+    if (opcode->arg[indx] == A_IMM
+	&& operand[indx].type == A_IMM
+	&& (operand[indx].immediate.X_op == O_PIC_reloc
+	    || sh_PIC_related_p (operand[indx].immediate.X_add_symbol)
+	    || sh_PIC_related_p (operand[indx].immediate.X_op_symbol)))
+      unhandled_pic = 1;
+
   if (SH_MERGE_ARCH_SET (opcode->arch, arch_op32))
     {
       output = frag_more (4);
@@ -2415,7 +2444,11 @@ build_Mytes (sh_opcode_info *opcode, sh_operand_info *operand)
 	    case IMM0_20_4:
 	      break;
 	    case IMM0_20:
-	      insert4 (output, BFD_RELOC_SH_DISP20, 0, operand);
+	      r_type = BFD_RELOC_SH_DISP20;
+	      if (sh_check_fixup (&operand->immediate, &r_type))
+		as_bad (_("Invalid PIC expression."));
+	      unhandled_pic = 0;
+	      insert4 (output, r_type, 0, operand);
 	      break;
 	    case IMM0_20BY8:
 	      insert4 (output, BFD_RELOC_SH_DISP20BY8, 0, operand);
@@ -2474,6 +2507,8 @@ build_Mytes (sh_opcode_info *opcode, sh_operand_info *operand)
 	    }
 	}
     }
+  if (unhandled_pic)
+    as_bad (_("misplaced PIC operand"));
   if (!target_big_endian)
     {
       output[1] = (nbuf[0] << 4) | (nbuf[1]);
@@ -3098,6 +3133,9 @@ enum options
   OPTION_PT32,
 #endif
   OPTION_H_TICK_HEX,
+#ifdef OBJ_ELF
+  OPTION_FDPIC,
+#endif
   OPTION_DUMMY  /* Not used.  This is just here to make it easy to add and subtract options from this enum.  */
 };
 
@@ -3126,6 +3164,10 @@ struct option md_longopts[] =
 #endif /* HAVE_SH64 */
   { "h-tick-hex", no_argument,	      NULL, OPTION_H_TICK_HEX  },
 
+#ifdef OBJ_ELF
+  {"fdpic", no_argument, NULL, OPTION_FDPIC},
+#endif
+
   {NULL, no_argument, NULL, 0}
 };
 size_t md_longopts_size = sizeof (md_longopts);
@@ -3259,6 +3301,12 @@ md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
       enable_h_tick_hex = 1;
       break;
 
+#ifdef OBJ_ELF
+    case OPTION_FDPIC:
+      sh_fdpic = TRUE;
+      break;
+#endif /* OBJ_ELF */
+
     default:
       return 0;
     }
@@ -3311,6 +3359,10 @@ SH options:\n\
 --expand-pt32		with -abi=64, expand PT, PTA and PTB instructions\n\
 			to 32 bits only\n"));
 #endif /* HAVE_SH64 */
+#ifdef OBJ_ELF
+  fprintf (stream, _("\
+--fdpic			generate an FDPIC object file\n"));
+#endif /* OBJ_ELF */
 }
 
 /* This struct is used to pass arguments to sh_count_relocs through
@@ -3804,7 +3856,13 @@ sh_fix_adjustable (fixS *fixP)
 {
   if (fixP->fx_r_type == BFD_RELOC_32_PLT_PCREL
       || fixP->fx_r_type == BFD_RELOC_32_GOT_PCREL
+      || fixP->fx_r_type == BFD_RELOC_SH_GOT20
       || fixP->fx_r_type == BFD_RELOC_SH_GOTPC
+      || fixP->fx_r_type == BFD_RELOC_SH_GOTFUNCDESC
+      || fixP->fx_r_type == BFD_RELOC_SH_GOTFUNCDESC20
+      || fixP->fx_r_type == BFD_RELOC_SH_GOTOFFFUNCDESC
+      || fixP->fx_r_type == BFD_RELOC_SH_GOTOFFFUNCDESC20
+      || fixP->fx_r_type == BFD_RELOC_SH_FUNCDESC
       || ((fixP->fx_r_type == BFD_RELOC_32) && dont_adjust_reloc_32)
       || fixP->fx_r_type == BFD_RELOC_RVA)
     return 0;
@@ -3843,6 +3901,22 @@ sh_elf_final_processing (void)
 
   elf_elfheader (stdoutput)->e_flags &= ~EF_SH_MACH_MASK;
   elf_elfheader (stdoutput)->e_flags |= val;
+
+  if (sh_fdpic)
+    elf_elfheader (stdoutput)->e_flags |= EF_SH_FDPIC;
+}
+#endif
+
+#ifdef TE_UCLINUX
+/* Return the target format for uClinux.  */
+
+const char *
+sh_uclinux_target_format (void)
+{
+  if (sh_fdpic)
+    return (!target_big_endian ? "elf32-sh-fdpic" : "elf32-shbig-fdpic");
+  else
+    return (!target_big_endian ? "elf32-shl" : "elf32-sh");
 }
 #endif
 
@@ -4151,7 +4225,13 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       S_SET_THREAD_LOCAL (fixP->fx_addsy);
       /* Fallthrough */
     case BFD_RELOC_32_GOT_PCREL:
+    case BFD_RELOC_SH_GOT20:
     case BFD_RELOC_SH_GOTPLT32:
+    case BFD_RELOC_SH_GOTFUNCDESC:
+    case BFD_RELOC_SH_GOTFUNCDESC20:
+    case BFD_RELOC_SH_GOTOFFFUNCDESC:
+    case BFD_RELOC_SH_GOTOFFFUNCDESC20:
+    case BFD_RELOC_SH_FUNCDESC:
       * valP = 0; /* Fully resolved at runtime.  No addend.  */
       apply_full_field_fix (fixP, buf, 0, 4);
       break;
@@ -4161,6 +4241,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       S_SET_THREAD_LOCAL (fixP->fx_addsy);
       /* Fallthrough */
     case BFD_RELOC_32_GOTOFF:
+    case BFD_RELOC_SH_GOTOFF20:
       apply_full_field_fix (fixP, buf, val, 4);
       break;
 #endif
@@ -4468,6 +4549,14 @@ sh_parse_name (char const *name,
     reloc_type = BFD_RELOC_SH_TLS_LE_32;
   else if ((next_end = sh_end_of_match (next + 1, "DTPOFF")))
     reloc_type = BFD_RELOC_SH_TLS_LDO_32;
+  else if ((next_end = sh_end_of_match (next + 1, "PCREL")))
+    reloc_type = BFD_RELOC_32_PCREL;
+  else if ((next_end = sh_end_of_match (next + 1, "GOTFUNCDESC")))
+    reloc_type = BFD_RELOC_SH_GOTFUNCDESC;
+  else if ((next_end = sh_end_of_match (next + 1, "GOTOFFFUNCDESC")))
+    reloc_type = BFD_RELOC_SH_GOTOFFFUNCDESC;
+  else if ((next_end = sh_end_of_match (next + 1, "FUNCDESC")))
+    reloc_type = BFD_RELOC_SH_FUNCDESC;
   else
     goto no_suffix;
 
diff --git a/src/binutils-mainline/gas/config/tc-sh.h b/src/binutils-mainline/gas/config/tc-sh.h
index 1b5ec26..85e5a51 100644
--- a/src/binutils-mainline/gas/config/tc-sh.h
+++ b/src/binutils-mainline/gas/config/tc-sh.h
@@ -165,6 +165,9 @@ extern int target_big_endian;
 #define TARGET_FORMAT (!target_big_endian ? "elf32-shl-symbian" : "elf32-sh-symbian")
 #elif defined (TE_VXWORKS)
 #define TARGET_FORMAT (!target_big_endian ? "elf32-shl-vxworks" : "elf32-sh-vxworks")
+#elif defined (TE_UCLINUX)
+#define TARGET_FORMAT sh_uclinux_target_format ()
+extern const char *sh_uclinux_target_format (void);
 #else
 #define TARGET_FORMAT (!target_big_endian ? "elf32-shl" : "elf32-sh")
 #endif
diff --git a/src/binutils-mainline/gas/configure.tgt b/src/binutils-mainline/gas/configure.tgt
index 23d8662..77cbac1 100644
--- a/src/binutils-mainline/gas/configure.tgt
+++ b/src/binutils-mainline/gas/configure.tgt
@@ -360,7 +360,8 @@ case ${generic_target} in
       *)	endian=big ;;
     esac ;;
   sh*-*-symbianelf*)			fmt=elf endian=little ;;
-  sh-*-elf* | sh-*-uclinux* | sh[12]-*-uclinux*)	fmt=elf ;;
+  sh-*-elf*)				fmt=elf ;;
+  sh-*-uclinux* | sh[12]-*-uclinux*)	fmt=elf em=uclinux ;;
   sh-*-coff*)				fmt=coff ;;
   sh-*-nto*)				fmt=elf ;;
   sh-*-pe*)				fmt=coff em=pe bfd_gas=yes endian=little ;;
diff --git a/src/binutils-mainline/gas/doc/c-sh.texi b/src/binutils-mainline/gas/doc/c-sh.texi
index 00c95d7..adb21df 100644
--- a/src/binutils-mainline/gas/doc/c-sh.texi
+++ b/src/binutils-mainline/gas/doc/c-sh.texi
@@ -54,6 +54,10 @@ Renesas assembler.
 @item --allow-reg-prefix
 Allow '$' as a register name prefix.
 
+@kindex --fdpic
+@item --fdpic
+Generate an FDPIC object file.
+
 @item --isa=sh4 | sh4a
 Specify the sh4 or sh4a instruction set.
 @item --isa=dsp
diff --git a/src/binutils-mainline/gas/testsuite/gas/sh/basic.exp b/src/binutils-mainline/gas/testsuite/gas/sh/basic.exp
index 3bb7931..2daa038 100644
--- a/src/binutils-mainline/gas/testsuite/gas/sh/basic.exp
+++ b/src/binutils-mainline/gas/testsuite/gas/sh/basic.exp
@@ -142,7 +142,7 @@ if [istarget sh*-*-*] then {
 	run_dump_test "pcrel2"
     }
 
-    if {[istarget sh*-*elf] || [istarget sh*-linux*]} then {
+    if {[istarget sh*-*elf] || [istarget sh*-*linux*]} then {
 	if {![istarget "sh64*-*-*"] && ![istarget "sh5*-*-*"]} then {
 	    run_dump_test "sh4a"
 	    run_dump_test "sh4a-fp"
@@ -151,9 +151,11 @@ if [istarget sh*-*-*] then {
 	    run_dump_test "sh4al-dsp"
 
 	    run_dump_test "sh2a"
+	    run_dump_test "sh2a-pic"
 	}
 
 	run_dump_test "pic"
+	run_dump_test "fdpic"
 
 	# Test TLS.
 	run_dump_test "tlsd"
diff --git a/src/binutils-mainline/gas/testsuite/gas/sh/fdpic.d b/src/binutils-mainline/gas/testsuite/gas/sh/fdpic.d
new file mode 100644
index 0000000..33dfc71
--- /dev/null
+++ b/src/binutils-mainline/gas/testsuite/gas/sh/fdpic.d
@@ -0,0 +1,13 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: FDPIC relocations
+
+dump.o:     file format elf32-sh.*
+
+Disassembly of section .text:
+	\.\.\.
+			0: R_SH_REL32	foo
+			4: R_SH_FUNCDESC	foo
+			8: R_SH_GOT32	foo
+			c: R_SH_GOTOFF	foo
+			10: R_SH_GOTFUNCDESC	foo
+			14: R_SH_GOTOFFFUNCDESC	foo
diff --git a/src/binutils-mainline/gas/testsuite/gas/sh/fdpic.s b/src/binutils-mainline/gas/testsuite/gas/sh/fdpic.s
new file mode 100644
index 0000000..7a7ad0f
--- /dev/null
+++ b/src/binutils-mainline/gas/testsuite/gas/sh/fdpic.s
@@ -0,0 +1,8 @@
+	.text
+
+	.long	foo@PCREL
+	.long	foo@FUNCDESC
+	.long	foo@GOT
+	.long	foo@GOTOFF
+	.long	foo@GOTFUNCDESC
+	.long	foo@GOTOFFFUNCDESC
diff --git a/src/binutils-mainline/gas/testsuite/gas/sh/reg-prefix.d b/src/binutils-mainline/gas/testsuite/gas/sh/reg-prefix.d
index 1821bbc..13c708c 100644
--- a/src/binutils-mainline/gas/testsuite/gas/sh/reg-prefix.d
+++ b/src/binutils-mainline/gas/testsuite/gas/sh/reg-prefix.d
@@ -1,10 +1,10 @@
 #objdump: -dr --prefix-addresses --show-raw-insn
-#as: --allow-reg-prefix -little
+#as: --allow-reg-prefix -big
 #name: SH --allow-reg-prefix option
 # Test SH register names prefixed with $:
 
 .*:     file format elf.*sh.*
 
 Disassembly of section .text:
-0x00000000 12 60       	mov\.l	@r1,r0
+0x00000000 60 12       	mov\.l	@r1,r0
 
diff --git a/src/binutils-mainline/gas/testsuite/gas/sh/sh2a-pic.d b/src/binutils-mainline/gas/testsuite/gas/sh/sh2a-pic.d
new file mode 100644
index 0000000..504d411
--- /dev/null
+++ b/src/binutils-mainline/gas/testsuite/gas/sh/sh2a-pic.d
@@ -0,0 +1,15 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: SH2a PIC relocations
+#as: -isa=sh2a
+
+dump.o:     file format elf32-sh.*
+
+Disassembly of section .text:
+0x00000000 01 00 00 00 	movi20	#0,r1
+			0: R_SH_GOT20	foo
+0x00000004 01 00 00 00 	movi20	#0,r1
+			4: R_SH_GOTOFF20	foo
+0x00000008 01 00 00 00 	movi20	#0,r1
+			8: R_SH_GOTFUNCDESC20	foo
+0x0000000c 01 00 00 00 	movi20	#0,r1
+			c: R_SH_GOTOFFFUNCDESC20	foo
diff --git a/src/binutils-mainline/gas/testsuite/gas/sh/sh2a-pic.s b/src/binutils-mainline/gas/testsuite/gas/sh/sh2a-pic.s
new file mode 100644
index 0000000..888a7c9
--- /dev/null
+++ b/src/binutils-mainline/gas/testsuite/gas/sh/sh2a-pic.s
@@ -0,0 +1,6 @@
+	.text
+
+	movi20	#foo@GOT, r1
+	movi20	#foo@GOTOFF, r1
+	movi20	#foo@GOTFUNCDESC, r1
+	movi20	#foo@GOTOFFFUNCDESC, r1
diff --git a/src/binutils-mainline/gas/testsuite/lib/gas-defs.exp b/src/binutils-mainline/gas/testsuite/lib/gas-defs.exp
index 0506b94..fd2f179 100644
--- a/src/binutils-mainline/gas/testsuite/lib/gas-defs.exp
+++ b/src/binutils-mainline/gas/testsuite/lib/gas-defs.exp
@@ -289,6 +289,7 @@ proc is_elf_format {} {
 	 && ![istarget hppa*64*-*-hpux*] \
 	 && ![istarget *-*-linux*] \
 	 && ![istarget frv-*-uclinux*] \
+	 && ![istarget sh*-*-uclinux*] \
 	 && ![istarget *-*-irix5*] \
 	 && ![istarget *-*-irix6*] \
 	 && ![istarget *-*-netbsd*] \
diff --git a/src/binutils-mainline/include/elf/sh.h b/src/binutils-mainline/include/elf/sh.h
index fcb962a..c2bd50d 100644
--- a/src/binutils-mainline/include/elf/sh.h
+++ b/src/binutils-mainline/include/elf/sh.h
@@ -86,6 +86,12 @@ int sh_find_elf_flags (unsigned int arch_set);
 /* Convert bfd_mach_* into EF_SH*.  */
 int sh_elf_get_flags_from_mach (unsigned long mach);
 
+/* Other e_flags bits.  */
+
+#define EF_SH_PIC		0x100	/* Segments of an FDPIC binary may
+					   be relocated independently.  */
+#define EF_SH_FDPIC		0x8000	/* Uses the FDPIC ABI.  */
+
 /* Flags for the st_other symbol field.
    Keep away from the STV_ visibility flags (bit 0..1).  */
 
@@ -214,7 +220,17 @@ START_RELOC_NUMBERS (elf_sh_reloc_type)
   RELOC_NUMBER (R_SH_JMP_SLOT64, 195)
   RELOC_NUMBER (R_SH_RELATIVE64, 196)
   FAKE_RELOC (R_SH_FIRST_INVALID_RELOC_5, 197)
-  FAKE_RELOC (R_SH_LAST_INVALID_RELOC_5, 241)
+  FAKE_RELOC (R_SH_LAST_INVALID_RELOC_5, 200)
+  RELOC_NUMBER (R_SH_GOT20, 201)
+  RELOC_NUMBER (R_SH_GOTOFF20, 202)
+  RELOC_NUMBER (R_SH_GOTFUNCDESC, 203)
+  RELOC_NUMBER (R_SH_GOTFUNCDESC20, 204)
+  RELOC_NUMBER (R_SH_GOTOFFFUNCDESC, 205)
+  RELOC_NUMBER (R_SH_GOTOFFFUNCDESC20, 206)
+  RELOC_NUMBER (R_SH_FUNCDESC, 207)
+  RELOC_NUMBER (R_SH_FUNCDESC_VALUE, 208)
+  FAKE_RELOC (R_SH_FIRST_INVALID_RELOC_6, 209)
+  FAKE_RELOC (R_SH_LAST_INVALID_RELOC_6, 241)
   RELOC_NUMBER (R_SH_SHMEDIA_CODE, 242)
   RELOC_NUMBER (R_SH_PT_16, 243)
   RELOC_NUMBER (R_SH_IMMS16, 244)
diff --git a/src/binutils-mainline/ld/Makefile.am b/src/binutils-mainline/ld/Makefile.am
index 3896fda..e9a8bb5 100644
--- a/src/binutils-mainline/ld/Makefile.am
+++ b/src/binutils-mainline/ld/Makefile.am
@@ -381,6 +381,8 @@ ALL_EMULATIONS = \
 	eshelf32_nbsd.o \
 	eshlelf32_nbsd.o \
 	eshelf.o \
+	eshelf_fd.o \
+	eshlelf_fd.o \
 	eshelf_linux.o \
 	eshlelf_linux.o \
 	eshelf_nbsd.o \
@@ -1729,6 +1731,12 @@ eshelf64_nbsd.c: $(srcdir)/emulparams/shelf64_nbsd.sh \
   $(srcdir)/emulparams/shelf32_nbsd.sh $(srcdir)/emulparams/shelf32.sh \
   $(ELF_DEPS) $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
 	${GENSCRIPTS} shelf64_nbsd "$(tdir_shelf64_nbsd)"
+eshelf_fd.c: $(srcdir)/emulparams/shelf_fd.sh \
+  $(ELF_DEPS) $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
+	${GENSCRIPTS} shelf_fd "$(tdir_shelf_fd)"
+eshlelf_fd.c: $(srcdir)/emulparams/shlelf_fd.sh \
+  $(ELF_DEPS) $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
+	${GENSCRIPTS} shelf_fd "$(tdir_shelf_fd)"
 eshelf_linux.c: $(srcdir)/emulparams/shelf_linux.sh \
   $(srcdir)/emulparams/shlelf_linux.sh \
   $(ELF_DEPS) $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
diff --git a/src/binutils-mainline/ld/Makefile.in b/src/binutils-mainline/ld/Makefile.in
index 7b0bf33..84aed74 100644
--- a/src/binutils-mainline/ld/Makefile.in
+++ b/src/binutils-mainline/ld/Makefile.in
@@ -671,6 +671,8 @@ ALL_EMULATIONS = \
 	eshelf32_nbsd.o \
 	eshlelf32_nbsd.o \
 	eshelf.o \
+	eshelf_fd.o \
+	eshlelf_fd.o \
 	eshelf_linux.o \
 	eshlelf_linux.o \
 	eshelf_nbsd.o \
@@ -3083,6 +3085,12 @@ eshelf64_nbsd.c: $(srcdir)/emulparams/shelf64_nbsd.sh \
   $(srcdir)/emulparams/shelf32_nbsd.sh $(srcdir)/emulparams/shelf32.sh \
   $(ELF_DEPS) $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
 	${GENSCRIPTS} shelf64_nbsd "$(tdir_shelf64_nbsd)"
+eshelf_fd.c: $(srcdir)/emulparams/shelf_fd.sh \
+  $(ELF_DEPS) $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
+	${GENSCRIPTS} shelf_fd "$(tdir_shelf_fd)"
+eshlelf_fd.c: $(srcdir)/emulparams/shlelf_fd.sh \
+  $(ELF_DEPS) $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
+	${GENSCRIPTS} shlelf_fd "$(tdir_shlelf_fd)"
 eshelf_linux.c: $(srcdir)/emulparams/shelf_linux.sh \
   $(srcdir)/emulparams/shlelf_linux.sh \
   $(ELF_DEPS) $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
diff --git a/src/binutils-mainline/ld/configure.tgt b/src/binutils-mainline/ld/configure.tgt
index def0287..f75c96a 100644
--- a/src/binutils-mainline/ld/configure.tgt
+++ b/src/binutils-mainline/ld/configure.tgt
@@ -550,7 +550,7 @@ sh-*-elf* | sh[1234]*-*-elf | sh-*-rtems* | sh-*-kaos*)
 			targ_extra_emuls="shlelf sh shl" ;;
 sh-*-uclinux* | sh[12]-*-uclinux*)
 			targ_emul=shelf_uclinux
-			targ_extra_emuls="shelf shlelf sh shl" ;;
+			targ_extra_emuls="shelf shlelf sh shl shelf_fd shlelf_fd" ;;
 sh-*-vxworks)		targ_emul=shelf_vxworks
 			targ_extra_emuls=shlelf_vxworks ;;
 sh-*-nto*)		targ_emul=shelf_nto
diff --git a/src/binutils-mainline/ld/emulparams/shelf_fd.sh b/src/binutils-mainline/ld/emulparams/shelf_fd.sh
new file mode 100644
index 0000000..7ec25ab
--- /dev/null
+++ b/src/binutils-mainline/ld/emulparams/shelf_fd.sh
@@ -0,0 +1,2 @@
+. ${srcdir}/emulparams/shlelf_fd.sh
+OUTPUT_FORMAT="elf32-shbig-fdpic"
diff --git a/src/binutils-mainline/ld/emulparams/shlelf_fd.sh b/src/binutils-mainline/ld/emulparams/shlelf_fd.sh
new file mode 100644
index 0000000..f1f4107
--- /dev/null
+++ b/src/binutils-mainline/ld/emulparams/shlelf_fd.sh
@@ -0,0 +1,16 @@
+# If you change this file, please also look at files which source this one:
+# shelf_fd.sh
+
+. ${srcdir}/emulparams/shlelf_linux.sh
+OUTPUT_FORMAT="elf32-sh-fdpic"
+GOT=".got          ${RELOCATING-0} : { *(.got.funcdesc) *(.got.plt) *(.got) }"
+OTHER_GOT_RELOC_SECTIONS="
+  .rela.got.funcdesc      ${RELOCATING-0} : { *(.rela.got.funcdesc) }
+"
+OTHER_READONLY_SECTIONS="
+  .rofixup        : {
+    ${RELOCATING+__ROFIXUP_LIST__ = .;}
+    *(.rofixup)
+    ${RELOCATING+__ROFIXUP_END__ = .;}
+  }
+"
diff --git a/src/binutils-mainline/ld/emulparams/shlelf_linux.sh b/src/binutils-mainline/ld/emulparams/shlelf_linux.sh
index 95b6acc..c14aae2 100644
--- a/src/binutils-mainline/ld/emulparams/shlelf_linux.sh
+++ b/src/binutils-mainline/ld/emulparams/shlelf_linux.sh
@@ -1,5 +1,5 @@
 # If you change this file, please also look at files which source this one:
-# shelf_linux.sh
+# shelf_linux.sh shelf_fd.sh shlelf_fd.sh
 
 SCRIPT_NAME=elf
 OUTPUT_FORMAT="elf32-sh-linux"
diff --git a/src/binutils-mainline/ld/testsuite/ld-sh/sh.exp b/src/binutils-mainline/ld/testsuite/ld-sh/sh.exp
index 335946b..f42a79f 100644
--- a/src/binutils-mainline/ld/testsuite/ld-sh/sh.exp
+++ b/src/binutils-mainline/ld/testsuite/ld-sh/sh.exp
@@ -122,7 +122,7 @@ if { [which $CC] == 0 } {
     return
 }
 
-if [istarget sh*-linux-*] {
+if [istarget sh*-*linux*] {
     exec sed -e s/_main/main/ -e s/_trap/trap/ -e s/_stack/stack/ \
 	    < $srcdir/$subdir/start.s >tmpdir/start.s
 } else {
diff --git a/src/binutils-mainline/ld/testsuite/lib/ld-lib.exp b/src/binutils-mainline/ld/testsuite/lib/ld-lib.exp
index 5b2e62b..4897b2c 100644
--- a/src/binutils-mainline/ld/testsuite/lib/ld-lib.exp
+++ b/src/binutils-mainline/ld/testsuite/lib/ld-lib.exp
@@ -404,6 +404,7 @@ proc is_elf_format {} {
 	 && ![istarget *-*-linux*] \
 	 && ![istarget frv-*-uclinux*] \
 	 && ![istarget bfin-*-uclinux] \
++ 	 && ![istarget sh*-*-uclinux*] \
 	 && ![istarget *-*-irix5*] \
 	 && ![istarget *-*-irix6*] \
 	 && ![istarget *-*-netbsd*] \

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