[binutils-gdb] Add support for RISC-V architecture.

Nick Clifton nickc@sourceware.org
Tue Nov 1 16:47:00 GMT 2016


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=e23eba971dd409b999dd83d8df0f842680c1c642

commit e23eba971dd409b999dd83d8df0f842680c1c642
Author: Nick Clifton <nickc@redhat.com>
Date:   Tue Nov 1 16:45:57 2016 +0000

    Add support for RISC-V architecture.
    
    bfd	* Makefile.am: Add entries for riscv32-elf and riscv64-elf.
    	* config.bdf: Likewise.
    	* configure.ac: Likewise.
    	* Makefile.in: Regenerate.
    	* configure: Regenerate.
    	* archures.c: Add bfd_riscv_arch.
    	* reloc.c: Add riscv relocs.
    	* targets.c: Add riscv_elf32_vec and riscv_elf64_vec.
    	* bfd-in2.h: Regenerate.
    	* libbfd.h: Regenerate.
    	* elf-bfd.h: Add RISCV_ELF_DATA to enum elf_target_id.
    	* elfnn-riscv.c: New file.
    	* elfxx-riscv.c: New file.
    	* elfxx-riscv.h: New file.
    
    binutils* readelf.c (guess_is_rela): Add EM_RISCV.
    	(get_machine_name): Likewise.
    	(dump_relocations): Add support for riscv relocations.
    	(get_machine_flags): Add support for riscv flags.
    	(is_32bit_abs_reloc): Add R_RISCV_32.
    	(is_64bit_abs_reloc): Add R_RISCV_64.
    	(is_none_reloc): Add R_RISCV_NONE.
    	* testsuite/binutils-all/objdump.exp (cpus_expected): Add riscv.
    	Expect the debug_ranges test to fail.
    
    gas	* Makefile.am: Add riscv files.
    	* Makefile.in: Regenerate.
    	* NEWS: Mention the support for this architecture.
    	* configure.in: Define a default architecture.
    	* configure: Regenerate.
    	* configure.tgt: Add entries for riscv.
    	* doc/as.texinfo: Likewise.
    	* testsuite/gas/all/gas.exp: Expect the redef tests to fail.
    	* testsuite/gas/elf/elf.exp: Expect the groupauto tests to fail.
    	* config/tc-riscv.c: New file.
    	* config/tc-riscv.h: New file.
    	* doc/c-riscv.texi: New file.
    	* testsuite/gas/riscv: New directory.
    	* testsuite/gas/riscv/riscv.exp: New file.
    	* testsuite/gas/riscv/t_insns.d: New file.
    	* testsuite/gas/riscv/t_insns.s: New file.
    
    ld	* Makefile.am: Add riscv files.
    	* Makefile.in: Regenerate.
    	* NEWS: Mention the support for this target.
    	* configure.tgt: Add riscv entries.
    	* emulparams/elf32lriscv-defs.sh: New file.
    	* emulparams/elf32lriscv.sh: New file.
    	* emulparams/elf64lriscv-defs.sh: New file.
    	* emulparams/elf64lriscv.sh: New file.
    	* emultempl/riscvelf.em: New file.
    
    opcodes	* configure.ac: Add entry for bfd_riscv_arch.
    	* configure: Regenerate.
    	* disassemble.c (disassembler): Add support for riscv.
    	(disassembler_usage): Likewise.
    	* riscv-dis.c: New file.
    	* riscv-opc.c: New file.
    
    include	* dis-asm.h: Add prototypes for print_insn_riscv and
    	print_riscv_disassembler_options.
    	* elf/riscv.h: New file.
    	* opcode/riscv-opc.h: New file.
    	* opcode/riscv.h: New file.

Diff:
---
 bfd/ChangeLog                               |   19 +
 bfd/Makefile.am                             |   28 +-
 bfd/Makefile.in                             |   32 +-
 bfd/archures.c                              |    7 +-
 bfd/bfd-in2.h                               |   43 +
 bfd/config.bfd                              |   15 +
 bfd/configure                               |    2 +
 bfd/configure.ac                            |    2 +
 bfd/cpu-riscv.c                             |   79 +
 bfd/elf-bfd.h                               |    1 +
 bfd/elfnn-riscv.c                           | 3160 +++++++++++++++++++++++++++
 bfd/elfxx-riscv.c                           |  815 +++++++
 bfd/elfxx-riscv.h                           |   33 +
 bfd/libbfd.h                                |   38 +
 bfd/reloc.c                                 |   80 +
 bfd/targets.c                               |    6 +
 binutils/ChangeLog                          |   14 +
 binutils/readelf.c                          |   20 +-
 binutils/testsuite/binutils-all/objdump.exp |    4 +-
 gas/ChangeLog                               |   21 +
 gas/Makefile.am                             |    2 +
 gas/Makefile.in                             |   17 +
 gas/NEWS                                    |    2 +
 gas/config/tc-riscv.c                       | 2408 ++++++++++++++++++++
 gas/config/tc-riscv.h                       |  101 +
 gas/configure                               |    2 +-
 gas/configure.ac                            |    2 +-
 gas/configure.tgt                           |    6 +-
 gas/doc/as.texinfo                          |   19 +
 gas/doc/c-riscv.texi                        |   48 +
 gas/testsuite/gas/all/gas.exp               |    8 +-
 gas/testsuite/gas/elf/elf.exp               |    3 +
 gas/testsuite/gas/riscv/riscv.exp           |   23 +
 gas/testsuite/gas/riscv/t_insns.d           |   10 +
 gas/testsuite/gas/riscv/t_insns.s           |    2 +
 include/ChangeLog                           |   10 +
 include/dis-asm.h                           |    2 +
 include/elf/riscv.h                         |   92 +
 include/opcode/riscv-opc.h                  | 1160 ++++++++++
 include/opcode/riscv.h                      |  342 +++
 ld/ChangeLog                                |   14 +
 ld/Makefile.am                              |   13 +
 ld/Makefile.in                              |   15 +
 ld/NEWS                                     |    2 +-
 ld/configure.tgt                            |    6 +
 ld/emulparams/elf32lriscv-defs.sh           |   42 +
 ld/emulparams/elf32lriscv.sh                |    2 +
 ld/emulparams/elf64lriscv-defs.sh           |   13 +
 ld/emulparams/elf64lriscv.sh                |    2 +
 ld/emultempl/riscvelf.em                    |   68 +
 opcodes/ChangeLog                           |   11 +
 opcodes/configure                           |    1 +
 opcodes/configure.ac                        |    1 +
 opcodes/disassemble.c                       |    8 +
 opcodes/riscv-dis.c                         |  502 +++++
 opcodes/riscv-opc.c                         |  624 ++++++
 56 files changed, 9982 insertions(+), 20 deletions(-)

diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index 5e0a918..75ffab5 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,3 +1,22 @@
+2016-11-01  Palmer Dabbelt  <palmer@dabbelt.com>
+	    Andrew Waterman <andrew@sifive.com>
+
+	Add support for RISC-V architecture.
+	* Makefile.am: Add entries for riscv32-elf and riscv64-elf.
+	* config.bdf: Likewise.
+	* configure.ac: Likewise.
+	* Makefile.in: Regenerate.
+	* configure: Regenerate.
+	* archures.c: Add bfd_riscv_arch.
+	* reloc.c: Add riscv relocs.
+	* targets.c: Add riscv_elf32_vec and riscv_elf64_vec.
+	* bfd-in2.h: Regenerate.
+	* libbfd.h: Regenerate.
+	* elf-bfd.h: Add RISCV_ELF_DATA to enum elf_target_id.
+	* elfnn-riscv.c: New file.
+	* elfxx-riscv.c: New file.
+	* elfxx-riscv.h: New file.
+
 2016-10-31  Alan Modra  <amodra@gmail.com>
 
 	PR 20748
diff --git a/bfd/Makefile.am b/bfd/Makefile.am
index e04be5a..0d5dd4a 100644
--- a/bfd/Makefile.am
+++ b/bfd/Makefile.am
@@ -148,6 +148,7 @@ ALL_MACHINES = \
 	cpu-plugin.lo \
 	cpu-powerpc.lo \
 	cpu-rs6000.lo \
+	cpu-riscv.lo \
 	cpu-rl78.lo \
 	cpu-rx.lo \
 	cpu-s390.lo \
@@ -235,6 +236,7 @@ ALL_MACHINES_CFILES = \
 	cpu-plugin.c \
 	cpu-powerpc.c \
 	cpu-rs6000.c \
+	cpu-riscv.c \
 	cpu-rl78.c \
 	cpu-rx.c \
 	cpu-s390.c \
@@ -671,18 +673,21 @@ BFD64_BACKENDS = \
 	elf64-hppa.lo \
 	elf64-ia64.lo \
 	elf64-ia64-vms.lo \
+	elfxx-ia64.lo \
+	elfn32-mips.lo \
 	elf64-mips.lo \
+	elfxx-mips.lo \
 	elf64-mmix.lo \
 	elf64-ppc.lo \
+	elf32-riscv.lo \
+	elf64-riscv.lo \
+	elfxx-riscv.lo \
 	elf64-s390.lo \
 	elf64-sh64.lo \
 	elf64-sparc.lo \
 	elf64-tilegx.lo \
 	elf64-x86-64.lo \
 	elf64.lo \
-	elfn32-mips.lo \
-	elfxx-ia64.lo \
-	elfxx-mips.lo \
 	mach-o-aarch64.lo \
 	mach-o-x86-64.lo \
 	mmo.lo \
@@ -722,6 +727,7 @@ BFD64_BACKENDS_CFILES = \
 	elfxx-aarch64.c \
 	elfxx-ia64.c \
 	elfxx-mips.c \
+	elfxx-riscv.c \
 	mach-o-aarch64.c \
 	mach-o-x86-64.c \
 	mmo.c \
@@ -785,7 +791,9 @@ SOURCE_CFILES = \
 
 BUILD_CFILES = \
 	elf32-aarch64.c elf64-aarch64.c \
-	elf32-ia64.c elf64-ia64.c peigen.c pepigen.c pex64igen.c
+	elf32-ia64.c elf64-ia64.c \
+	elf32-riscv.c elf64-riscv.c \
+	peigen.c pepigen.c pex64igen.c
 
 CFILES = $(SOURCE_CFILES) $(BUILD_CFILES)
 
@@ -955,6 +963,18 @@ elf64-ia64.c : elfnn-ia64.c
 	$(SED) -e s/NN/64/g < $(srcdir)/elfnn-ia64.c > elf64-ia64.new
 	mv -f elf64-ia64.new elf64-ia64.c
 
+elf32-riscv.c : elfnn-riscv.c
+	rm -f elf32-riscv.c
+	echo "#line 1 \"$(srcdir)/elfnn-riscv.c\"" > elf32-riscv.new
+	sed -e s/NN/32/g < $(srcdir)/elfnn-riscv.c >> elf32-riscv.new
+	mv -f elf32-riscv.new elf32-riscv.c
+
+elf64-riscv.c : elfnn-riscv.c
+	rm -f elf64-riscv.c
+	echo "#line 1 \"$(srcdir)/elfnn-riscv.c\"" > elf64-riscv.new
+	sed -e s/NN/64/g < $(srcdir)/elfnn-riscv.c >> elf64-riscv.new
+	mv -f elf64-riscv.new elf64-riscv.c
+
 peigen.c : peXXigen.c
 	rm -f peigen.c
 	$(SED) -e s/XX/pe/g < $(srcdir)/peXXigen.c > peigen.new
diff --git a/bfd/Makefile.in b/bfd/Makefile.in
index 64658c5..661155c 100644
--- a/bfd/Makefile.in
+++ b/bfd/Makefile.in
@@ -481,6 +481,7 @@ ALL_MACHINES = \
 	cpu-plugin.lo \
 	cpu-powerpc.lo \
 	cpu-rs6000.lo \
+	cpu-riscv.lo \
 	cpu-rl78.lo \
 	cpu-rx.lo \
 	cpu-s390.lo \
@@ -568,6 +569,7 @@ ALL_MACHINES_CFILES = \
 	cpu-plugin.c \
 	cpu-powerpc.c \
 	cpu-rs6000.c \
+	cpu-riscv.c \
 	cpu-rl78.c \
 	cpu-rx.c \
 	cpu-s390.c \
@@ -1006,18 +1008,21 @@ BFD64_BACKENDS = \
 	elf64-hppa.lo \
 	elf64-ia64.lo \
 	elf64-ia64-vms.lo \
+	elfxx-ia64.lo \
+	elfn32-mips.lo \
 	elf64-mips.lo \
+	elfxx-mips.lo \
 	elf64-mmix.lo \
 	elf64-ppc.lo \
+	elf32-riscv.lo \
+	elf64-riscv.lo \
+	elfxx-riscv.lo \
 	elf64-s390.lo \
 	elf64-sh64.lo \
 	elf64-sparc.lo \
 	elf64-tilegx.lo \
 	elf64-x86-64.lo \
 	elf64.lo \
-	elfn32-mips.lo \
-	elfxx-ia64.lo \
-	elfxx-mips.lo \
 	mach-o-aarch64.lo \
 	mach-o-x86-64.lo \
 	mmo.lo \
@@ -1057,6 +1062,7 @@ BFD64_BACKENDS_CFILES = \
 	elfxx-aarch64.c \
 	elfxx-ia64.c \
 	elfxx-mips.c \
+	elfxx-riscv.c \
 	mach-o-aarch64.c \
 	mach-o-x86-64.c \
 	mmo.c \
@@ -1122,7 +1128,9 @@ SOURCE_CFILES = \
 
 BUILD_CFILES = \
 	elf32-aarch64.c elf64-aarch64.c \
-	elf32-ia64.c elf64-ia64.c peigen.c pepigen.c pex64igen.c
+	elf32-ia64.c elf64-ia64.c \
+	elf32-riscv.c elf64-riscv.c \
+	peigen.c pepigen.c pex64igen.c
 
 CFILES = $(SOURCE_CFILES) $(BUILD_CFILES)
 SOURCE_HFILES = \
@@ -1412,6 +1420,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-pj.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-plugin.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-powerpc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-riscv.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-rl78.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-rs6000.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-rx.Plo@am__quote@
@@ -1501,6 +1510,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf32-or1k.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf32-pj.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf32-ppc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf32-riscv.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf32-rl78.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf32-rx.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf32-s390.Plo@am__quote@
@@ -1532,6 +1542,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf64-mips.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf64-mmix.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf64-ppc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf64-riscv.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf64-s390.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf64-sh64.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf64-sparc.Plo@am__quote@
@@ -1543,6 +1554,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elfxx-aarch64.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elfxx-ia64.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elfxx-mips.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elfxx-riscv.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elfxx-sparc.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elfxx-tilegx.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/epoc-pe-arm.Plo@am__quote@
@@ -2080,6 +2092,18 @@ elf64-ia64.c : elfnn-ia64.c
 	$(SED) -e s/NN/64/g < $(srcdir)/elfnn-ia64.c > elf64-ia64.new
 	mv -f elf64-ia64.new elf64-ia64.c
 
+elf32-riscv.c : elfnn-riscv.c
+	rm -f elf32-riscv.c
+	echo "#line 1 \"$(srcdir)/elfnn-riscv.c\"" > elf32-riscv.new
+	sed -e s/NN/32/g < $(srcdir)/elfnn-riscv.c >> elf32-riscv.new
+	mv -f elf32-riscv.new elf32-riscv.c
+
+elf64-riscv.c : elfnn-riscv.c
+	rm -f elf64-riscv.c
+	echo "#line 1 \"$(srcdir)/elfnn-riscv.c\"" > elf64-riscv.new
+	sed -e s/NN/64/g < $(srcdir)/elfnn-riscv.c >> elf64-riscv.new
+	mv -f elf64-riscv.new elf64-riscv.c
+
 peigen.c : peXXigen.c
 	rm -f peigen.c
 	$(SED) -e s/XX/pe/g < $(srcdir)/peXXigen.c > peigen.new
diff --git a/bfd/archures.c b/bfd/archures.c
index af70b18..e503492 100644
--- a/bfd/archures.c
+++ b/bfd/archures.c
@@ -457,6 +457,9 @@ DESCRIPTION
 .#define bfd_mach_cris_v0_v10	255
 .#define bfd_mach_cris_v32	32
 .#define bfd_mach_cris_v10_v32	1032
+.  bfd_arch_riscv,
+.#define bfd_mach_riscv32	132
+.#define bfd_mach_riscv64	164
 .  bfd_arch_rl78,
 .#define bfd_mach_rl78	0x75
 .  bfd_arch_rx,        {* Renesas RX.  *}
@@ -628,6 +631,7 @@ extern const bfd_arch_info_type bfd_pj_arch;
 extern const bfd_arch_info_type bfd_plugin_arch;
 extern const bfd_arch_info_type bfd_powerpc_archs[];
 #define bfd_powerpc_arch bfd_powerpc_archs[0]
+extern const bfd_arch_info_type bfd_riscv_arch;
 extern const bfd_arch_info_type bfd_rs6000_arch;
 extern const bfd_arch_info_type bfd_rl78_arch;
 extern const bfd_arch_info_type bfd_rx_arch;
@@ -717,8 +721,9 @@ static const bfd_arch_info_type * const bfd_archures_list[] =
     &bfd_or1k_arch,
     &bfd_pdp11_arch,
     &bfd_powerpc_arch,
-    &bfd_rs6000_arch,
+    &bfd_riscv_arch,
     &bfd_rl78_arch,
+    &bfd_rs6000_arch,
     &bfd_rx_arch,
     &bfd_s390_arch,
     &bfd_score_arch,
diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 3c0ff9d..fdb7878 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -2295,6 +2295,9 @@ enum bfd_architecture
 #define bfd_mach_cris_v0_v10   255
 #define bfd_mach_cris_v32      32
 #define bfd_mach_cris_v10_v32  1032
+  bfd_arch_riscv,
+#define bfd_mach_riscv32       132
+#define bfd_mach_riscv64       164
   bfd_arch_rl78,
 #define bfd_mach_rl78  0x75
   bfd_arch_rx,        /* Renesas RX.  */
@@ -4691,6 +4694,46 @@ number for the IN and OUT instructions  */
 number for the SBIC, SBIS, SBI and CBI instructions  */
   BFD_RELOC_AVR_PORT5,
 
+/* RISC-V relocations.  */
+  BFD_RELOC_RISCV_HI20,
+  BFD_RELOC_RISCV_PCREL_HI20,
+  BFD_RELOC_RISCV_PCREL_LO12_I,
+  BFD_RELOC_RISCV_PCREL_LO12_S,
+  BFD_RELOC_RISCV_LO12_I,
+  BFD_RELOC_RISCV_LO12_S,
+  BFD_RELOC_RISCV_GPREL12_I,
+  BFD_RELOC_RISCV_GPREL12_S,
+  BFD_RELOC_RISCV_TPREL_HI20,
+  BFD_RELOC_RISCV_TPREL_LO12_I,
+  BFD_RELOC_RISCV_TPREL_LO12_S,
+  BFD_RELOC_RISCV_TPREL_ADD,
+  BFD_RELOC_RISCV_CALL,
+  BFD_RELOC_RISCV_CALL_PLT,
+  BFD_RELOC_RISCV_ADD8,
+  BFD_RELOC_RISCV_ADD16,
+  BFD_RELOC_RISCV_ADD32,
+  BFD_RELOC_RISCV_ADD64,
+  BFD_RELOC_RISCV_SUB8,
+  BFD_RELOC_RISCV_SUB16,
+  BFD_RELOC_RISCV_SUB32,
+  BFD_RELOC_RISCV_SUB64,
+  BFD_RELOC_RISCV_GOT_HI20,
+  BFD_RELOC_RISCV_TLS_GOT_HI20,
+  BFD_RELOC_RISCV_TLS_GD_HI20,
+  BFD_RELOC_RISCV_JMP,
+  BFD_RELOC_RISCV_TLS_DTPMOD32,
+  BFD_RELOC_RISCV_TLS_DTPREL32,
+  BFD_RELOC_RISCV_TLS_DTPMOD64,
+  BFD_RELOC_RISCV_TLS_DTPREL64,
+  BFD_RELOC_RISCV_TLS_TPREL32,
+  BFD_RELOC_RISCV_TLS_TPREL64,
+  BFD_RELOC_RISCV_ALIGN,
+  BFD_RELOC_RISCV_RVC_BRANCH,
+  BFD_RELOC_RISCV_RVC_JUMP,
+  BFD_RELOC_RISCV_RVC_LUI,
+  BFD_RELOC_RISCV_GPREL_I,
+  BFD_RELOC_RISCV_GPREL_S,
+
 /* Renesas RL78 Relocations.  */
   BFD_RELOC_RL78_NEG8,
   BFD_RELOC_RL78_NEG16,
diff --git a/bfd/config.bfd b/bfd/config.bfd
index 6400722..98663fd 100644
--- a/bfd/config.bfd
+++ b/bfd/config.bfd
@@ -122,6 +122,7 @@ or1k*|or1knd*)	 targ_archs=bfd_or1k_arch ;;
 pdp11*)		 targ_archs=bfd_pdp11_arch ;;
 pj*)		 targ_archs="bfd_pj_arch bfd_i386_arch";;
 powerpc*)	 targ_archs="bfd_rs6000_arch bfd_powerpc_arch" ;;
+riscv*)		 targ_archs=bfd_riscv_arch ;;
 rs6000)		 targ_archs="bfd_rs6000_arch bfd_powerpc_arch" ;;
 s390*)		 targ_archs=bfd_s390_arch ;;
 sh*)		 targ_archs=bfd_sh_arch ;;
@@ -1359,6 +1360,20 @@ case "${targ}" in
     targ_selvecs="powerpc_pei_le_vec powerpc_pei_vec powerpc_pe_le_vec powerpc_pe_vec"
     ;;
 
+#ifdef BFD64
+  riscv32-*-*)
+    targ_defvec=riscv_elf32_vec
+    targ_selvecs="riscv_elf32_vec"
+    want64=true
+    ;;
+
+  riscv64-*-*)
+    targ_defvec=riscv_elf64_vec
+    targ_selvecs="riscv_elf32_vec riscv_elf64_vec"
+    want64=true
+    ;;
+#endif
+
   rl78-*-elf)
     targ_defvec=rl78_elf32_vec
     ;;
diff --git a/bfd/configure b/bfd/configure
index 8284069..68db12f 100755
--- a/bfd/configure
+++ b/bfd/configure
@@ -14475,6 +14475,8 @@ do
     powerpc_pei_vec)		 tb="$tb pei-ppc.lo peigen.lo $coff" ;;
     powerpc_pei_le_vec)		 tb="$tb pei-ppc.lo peigen.lo $coff" ;;
     powerpc_xcoff_vec)		 tb="$tb coff-rs6000.lo $xcoff" ;;
+    riscv_elf32_vec)		 tb="$tb elf32-riscv.lo elfxx-riscv.lo elf32.lo $elf" ;;
+    riscv_elf64_vec)		 tb="$tb elf64-riscv.lo elf64.lo elfxx-riscv.lo elf32.lo $elf"; target_size=64 ;;
     rl78_elf32_vec)		 tb="$tb elf32-rl78.lo elf32.lo $elf" ;;
     rs6000_xcoff64_vec)		 tb="$tb coff64-rs6000.lo aix5ppc-core.lo $xcoff"; target_size=64 ;;
     rs6000_xcoff64_aix_vec)	 tb="$tb coff64-rs6000.lo aix5ppc-core.lo $xcoff"; target_size=64 ;;
diff --git a/bfd/configure.ac b/bfd/configure.ac
index 669cff7..6f11d29 100644
--- a/bfd/configure.ac
+++ b/bfd/configure.ac
@@ -606,6 +606,8 @@ do
     powerpc_pei_vec)		 tb="$tb pei-ppc.lo peigen.lo $coff" ;;
     powerpc_pei_le_vec)		 tb="$tb pei-ppc.lo peigen.lo $coff" ;;
     powerpc_xcoff_vec)		 tb="$tb coff-rs6000.lo $xcoff" ;;
+    riscv_elf32_vec)		 tb="$tb elf32-riscv.lo elfxx-riscv.lo elf32.lo $elf" ;;
+    riscv_elf64_vec)		 tb="$tb elf64-riscv.lo elf64.lo elfxx-riscv.lo elf32.lo $elf"; target_size=64 ;;
     rl78_elf32_vec)		 tb="$tb elf32-rl78.lo elf32.lo $elf" ;;
     rs6000_xcoff64_vec)		 tb="$tb coff64-rs6000.lo aix5ppc-core.lo $xcoff"; target_size=64 ;;
     rs6000_xcoff64_aix_vec)	 tb="$tb coff64-rs6000.lo aix5ppc-core.lo $xcoff"; target_size=64 ;;
diff --git a/bfd/cpu-riscv.c b/bfd/cpu-riscv.c
new file mode 100644
index 0000000..a938199
--- /dev/null
+++ b/bfd/cpu-riscv.c
@@ -0,0 +1,79 @@
+/* BFD backend for RISC-V
+   Copyright 2011-2016 Free Software Foundation, Inc.
+
+   Contributed by Andrew Waterman (andrew@sifive.com).
+   Based on MIPS target.
+
+   This file is part of BFD, the Binary File Descriptor library.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; see the file COPYING3. If not,
+   see <http://www.gnu.org/licenses/>.  */
+
+#include "sysdep.h"
+#include "bfd.h"
+#include "libbfd.h"
+
+/* This routine is provided two arch_infos and returns an arch_info
+   that is compatible with both, or NULL if none exists.  */
+
+static const bfd_arch_info_type *
+riscv_compatible (const bfd_arch_info_type *a, const bfd_arch_info_type *b)
+{
+  if (a->arch != b->arch)
+    return NULL;
+
+  /* Machine compatibility is checked in
+     _bfd_riscv_elf_merge_private_bfd_data.  */
+
+  return a;
+}
+
+#define N(BITS_WORD, BITS_ADDR, NUMBER, PRINT, DEFAULT, NEXT)	\
+  {								\
+    BITS_WORD, /*  bits in a word */				\
+    BITS_ADDR, /* bits in an address */				\
+    8,	/* 8 bits in a byte */					\
+    bfd_arch_riscv,						\
+    NUMBER,							\
+    "riscv",							\
+    PRINT,							\
+    3,								\
+    DEFAULT,							\
+    riscv_compatible,						\
+    bfd_default_scan,						\
+    bfd_arch_default_fill,					\
+    NEXT,							\
+  }
+
+/* This enum must be kept in the same order as arch_info_struct.  */
+enum
+{
+  I_riscv64,
+  I_riscv32
+};
+
+#define NN(index) (&arch_info_struct[(index) + 1])
+
+/* This array must be kept in the same order as the anonymous enum above,
+   and each entry except the last should end with NN (my enum value).  */
+static const bfd_arch_info_type arch_info_struct[] =
+{
+  N (64, 64, bfd_mach_riscv64, "riscv:rv64", FALSE, NN (I_riscv64)),
+  N (32, 32, bfd_mach_riscv32, "riscv:rv32", FALSE, 0)
+};
+
+/* The default architecture is riscv:rv64.  */
+
+const bfd_arch_info_type bfd_riscv_arch =
+  N (64, 64, 0, "riscv", TRUE, &arch_info_struct[0]);
diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index af2af2c..50151cb 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -477,6 +477,7 @@ enum elf_target_id
   XGATE_ELF_DATA,
   TILEGX_ELF_DATA,
   TILEPRO_ELF_DATA,
+  RISCV_ELF_DATA,
   GENERIC_ELF_DATA
 };
 
diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c
new file mode 100644
index 0000000..be1de22
--- /dev/null
+++ b/bfd/elfnn-riscv.c
@@ -0,0 +1,3160 @@
+/* RISC-V-specific support for NN-bit ELF.
+   Copyright 2011-2016 Free Software Foundation, Inc.
+
+   Contributed by Andrew Waterman (andrew@sifive.com).
+   Based on TILE-Gx and MIPS targets.
+
+   This file is part of BFD, the Binary File Descriptor library.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; see the file COPYING3. If not,
+   see <http://www.gnu.org/licenses/>.  */
+
+/* This file handles RISC-V ELF targets.  */
+
+#include "sysdep.h"
+#include "bfd.h"
+#include "libbfd.h"
+#include "bfdlink.h"
+#include "genlink.h"
+#include "elf-bfd.h"
+#include "elfxx-riscv.h"
+#include "elf/riscv.h"
+#include "opcode/riscv.h"
+
+#define ARCH_SIZE NN
+
+#define MINUS_ONE ((bfd_vma)0 - 1)
+
+#define RISCV_ELF_LOG_WORD_BYTES (ARCH_SIZE == 32 ? 2 : 3)
+
+#define RISCV_ELF_WORD_BYTES (1 << RISCV_ELF_LOG_WORD_BYTES)
+
+/* The name of the dynamic interpreter.  This is put in the .interp
+   section.  */
+
+#define ELF64_DYNAMIC_INTERPRETER "/lib/ld.so.1"
+#define ELF32_DYNAMIC_INTERPRETER "/lib32/ld.so.1"
+
+#define ELF_ARCH			bfd_arch_riscv
+#define ELF_TARGET_ID			RISCV_ELF_DATA
+#define ELF_MACHINE_CODE		EM_RISCV
+#define ELF_MAXPAGESIZE			0x1000
+#define ELF_COMMONPAGESIZE		0x1000
+
+/* The RISC-V linker needs to keep track of the number of relocs that it
+   decides to copy as dynamic relocs in check_relocs for each symbol.
+   This is so that it can later discard them if they are found to be
+   unnecessary.  We store the information in a field extending the
+   regular ELF linker hash table.  */
+
+struct riscv_elf_dyn_relocs
+{
+  struct riscv_elf_dyn_relocs *next;
+
+  /* The input section of the reloc.  */
+  asection *sec;
+
+  /* Total number of relocs copied for the input section.  */
+  bfd_size_type count;
+
+  /* Number of pc-relative relocs copied for the input section.  */
+  bfd_size_type pc_count;
+};
+
+/* RISC-V ELF linker hash entry.  */
+
+struct riscv_elf_link_hash_entry
+{
+  struct elf_link_hash_entry elf;
+
+  /* Track dynamic relocs copied for this symbol.  */
+  struct riscv_elf_dyn_relocs *dyn_relocs;
+
+#define GOT_UNKNOWN     0
+#define GOT_NORMAL      1
+#define GOT_TLS_GD      2
+#define GOT_TLS_IE      4
+#define GOT_TLS_LE      8
+  char tls_type;
+};
+
+#define riscv_elf_hash_entry(ent) \
+  ((struct riscv_elf_link_hash_entry *)(ent))
+
+struct _bfd_riscv_elf_obj_tdata
+{
+  struct elf_obj_tdata root;
+
+  /* tls_type for each local got entry.  */
+  char *local_got_tls_type;
+};
+
+#define _bfd_riscv_elf_tdata(abfd) \
+  ((struct _bfd_riscv_elf_obj_tdata *) (abfd)->tdata.any)
+
+#define _bfd_riscv_elf_local_got_tls_type(abfd) \
+  (_bfd_riscv_elf_tdata (abfd)->local_got_tls_type)
+
+#define _bfd_riscv_elf_tls_type(abfd, h, symndx)		\
+  (*((h) != NULL ? &riscv_elf_hash_entry (h)->tls_type		\
+     : &_bfd_riscv_elf_local_got_tls_type (abfd) [symndx]))
+
+#define is_riscv_elf(bfd)				\
+  (bfd_get_flavour (bfd) == bfd_target_elf_flavour	\
+   && elf_tdata (bfd) != NULL				\
+   && elf_object_id (bfd) == RISCV_ELF_DATA)
+
+#include "elf/common.h"
+#include "elf/internal.h"
+
+struct riscv_elf_link_hash_table
+{
+  struct elf_link_hash_table elf;
+
+  /* Short-cuts to get to dynamic linker sections.  */
+  asection *sdynbss;
+  asection *srelbss;
+  asection *sdyntdata;
+
+  /* Small local sym to section mapping cache.  */
+  struct sym_cache sym_cache;
+};
+
+
+/* Get the RISC-V ELF linker hash table from a link_info structure.  */
+#define riscv_elf_hash_table(p) \
+  (elf_hash_table_id ((struct elf_link_hash_table *) ((p)->hash)) \
+  == RISCV_ELF_DATA ? ((struct riscv_elf_link_hash_table *) ((p)->hash)) : NULL)
+
+static void
+riscv_info_to_howto_rela (bfd *abfd ATTRIBUTE_UNUSED,
+			  arelent *cache_ptr,
+			  Elf_Internal_Rela *dst)
+{
+  cache_ptr->howto = riscv_elf_rtype_to_howto (ELFNN_R_TYPE (dst->r_info));
+}
+
+static void
+riscv_elf_append_rela (bfd *abfd, asection *s, Elf_Internal_Rela *rel)
+{
+  const struct elf_backend_data *bed;
+  bfd_byte *loc;
+
+  bed = get_elf_backend_data (abfd);
+  loc = s->contents + (s->reloc_count++ * bed->s->sizeof_rela);
+  bed->s->swap_reloca_out (abfd, rel, loc);
+}
+
+/* PLT/GOT stuff.  */
+
+#define PLT_HEADER_INSNS 8
+#define PLT_ENTRY_INSNS 4
+#define PLT_HEADER_SIZE (PLT_HEADER_INSNS * 4)
+#define PLT_ENTRY_SIZE (PLT_ENTRY_INSNS * 4)
+
+#define GOT_ENTRY_SIZE RISCV_ELF_WORD_BYTES
+
+#define GOTPLT_HEADER_SIZE (2 * GOT_ENTRY_SIZE)
+
+#define sec_addr(sec) ((sec)->output_section->vma + (sec)->output_offset)
+
+static bfd_vma
+riscv_elf_got_plt_val (bfd_vma plt_index, struct bfd_link_info *info)
+{
+  return sec_addr (riscv_elf_hash_table (info)->elf.sgotplt)
+	 + GOTPLT_HEADER_SIZE + (plt_index * GOT_ENTRY_SIZE);
+}
+
+#if ARCH_SIZE == 32
+# define MATCH_LREG MATCH_LW
+#else
+# define MATCH_LREG MATCH_LD
+#endif
+
+/* Generate a PLT header.  */
+
+static void
+riscv_make_plt_header (bfd_vma gotplt_addr, bfd_vma addr, uint32_t *entry)
+{
+  bfd_vma gotplt_offset_high = RISCV_PCREL_HIGH_PART (gotplt_addr, addr);
+  bfd_vma gotplt_offset_low = RISCV_PCREL_LOW_PART (gotplt_addr, addr);
+
+  /* auipc  t2, %hi(.got.plt)
+     sub    t1, t1, t3               # shifted .got.plt offset + hdr size + 12
+     l[w|d] t3, %lo(.got.plt)(t2)    # _dl_runtime_resolve
+     addi   t1, t1, -(hdr size + 12) # shifted .got.plt offset
+     addi   t0, t2, %lo(.got.plt)    # &.got.plt
+     srli   t1, t1, log2(16/PTRSIZE) # .got.plt offset
+     l[w|d] t0, PTRSIZE(t0)          # link map
+     jr     t3 */
+
+  entry[0] = RISCV_UTYPE (AUIPC, X_T2, gotplt_offset_high);
+  entry[1] = RISCV_RTYPE (SUB, X_T1, X_T1, X_T3);
+  entry[2] = RISCV_ITYPE (LREG, X_T3, X_T2, gotplt_offset_low);
+  entry[3] = RISCV_ITYPE (ADDI, X_T1, X_T1, -(PLT_HEADER_SIZE + 12));
+  entry[4] = RISCV_ITYPE (ADDI, X_T0, X_T2, gotplt_offset_low);
+  entry[5] = RISCV_ITYPE (SRLI, X_T1, X_T1, 4 - RISCV_ELF_LOG_WORD_BYTES);
+  entry[6] = RISCV_ITYPE (LREG, X_T0, X_T0, RISCV_ELF_WORD_BYTES);
+  entry[7] = RISCV_ITYPE (JALR, 0, X_T3, 0);
+}
+
+/* Generate a PLT entry.  */
+
+static void
+riscv_make_plt_entry (bfd_vma got, bfd_vma addr, uint32_t *entry)
+{
+  /* auipc  t3, %hi(.got.plt entry)
+     l[w|d] t3, %lo(.got.plt entry)(t3)
+     jalr   t1, t3
+     nop */
+
+  entry[0] = RISCV_UTYPE (AUIPC, X_T3, RISCV_PCREL_HIGH_PART (got, addr));
+  entry[1] = RISCV_ITYPE (LREG,  X_T3, X_T3, RISCV_PCREL_LOW_PART(got, addr));
+  entry[2] = RISCV_ITYPE (JALR, X_T1, X_T3, 0);
+  entry[3] = RISCV_NOP;
+}
+
+/* Create an entry in an RISC-V ELF linker hash table.  */
+
+static struct bfd_hash_entry *
+link_hash_newfunc (struct bfd_hash_entry *entry,
+		   struct bfd_hash_table *table, const char *string)
+{
+  /* Allocate the structure if it has not already been allocated by a
+     subclass.  */
+  if (entry == NULL)
+    {
+      entry =
+	bfd_hash_allocate (table,
+			   sizeof (struct riscv_elf_link_hash_entry));
+      if (entry == NULL)
+	return entry;
+    }
+
+  /* Call the allocation method of the superclass.  */
+  entry = _bfd_elf_link_hash_newfunc (entry, table, string);
+  if (entry != NULL)
+    {
+      struct riscv_elf_link_hash_entry *eh;
+
+      eh = (struct riscv_elf_link_hash_entry *) entry;
+      eh->dyn_relocs = NULL;
+      eh->tls_type = GOT_UNKNOWN;
+    }
+
+  return entry;
+}
+
+/* Create a RISC-V ELF linker hash table.  */
+
+static struct bfd_link_hash_table *
+riscv_elf_link_hash_table_create (bfd *abfd)
+{
+  struct riscv_elf_link_hash_table *ret;
+  bfd_size_type amt = sizeof (struct riscv_elf_link_hash_table);
+
+  ret = (struct riscv_elf_link_hash_table *) bfd_zmalloc (amt);
+  if (ret == NULL)
+    return NULL;
+
+  if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd, link_hash_newfunc,
+				      sizeof (struct riscv_elf_link_hash_entry),
+				      RISCV_ELF_DATA))
+    {
+      free (ret);
+      return NULL;
+    }
+
+  return &ret->elf.root;
+}
+
+/* Create the .got section.  */
+
+static bfd_boolean
+riscv_elf_create_got_section (bfd *abfd, struct bfd_link_info *info)
+{
+  flagword flags;
+  asection *s, *s_got;
+  struct elf_link_hash_entry *h;
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+  struct elf_link_hash_table *htab = elf_hash_table (info);
+
+  /* This function may be called more than once.  */
+  s = bfd_get_linker_section (abfd, ".got");
+  if (s != NULL)
+    return TRUE;
+
+  flags = bed->dynamic_sec_flags;
+
+  s = bfd_make_section_anyway_with_flags (abfd,
+					  (bed->rela_plts_and_copies_p
+					   ? ".rela.got" : ".rel.got"),
+					  (bed->dynamic_sec_flags
+					   | SEC_READONLY));
+  if (s == NULL
+      || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align))
+    return FALSE;
+  htab->srelgot = s;
+
+  s = s_got = bfd_make_section_anyway_with_flags (abfd, ".got", flags);
+  if (s == NULL
+      || !bfd_set_section_alignment (abfd, s, bed->s->log_file_align))
+    return FALSE;
+  htab->sgot = s;
+
+  /* The first bit of the global offset table is the header.  */
+  s->size += bed->got_header_size;
+
+  if (bed->want_got_plt)
+    {
+      s = bfd_make_section_anyway_with_flags (abfd, ".got.plt", flags);
+      if (s == NULL
+	  || !bfd_set_section_alignment (abfd, s,
+					 bed->s->log_file_align))
+	return FALSE;
+      htab->sgotplt = s;
+
+      /* Reserve room for the header.  */
+      s->size += GOTPLT_HEADER_SIZE;
+    }
+
+  if (bed->want_got_sym)
+    {
+      /* Define the symbol _GLOBAL_OFFSET_TABLE_ at the start of the .got
+	 section.  We don't do this in the linker script because we don't want
+	 to define the symbol if we are not creating a global offset
+	 table.  */
+      h = _bfd_elf_define_linkage_sym (abfd, info, s_got,
+				       "_GLOBAL_OFFSET_TABLE_");
+      elf_hash_table (info)->hgot = h;
+      if (h == NULL)
+	return FALSE;
+    }
+
+  return TRUE;
+}
+
+/* Create .plt, .rela.plt, .got, .got.plt, .rela.got, .dynbss, and
+   .rela.bss sections in DYNOBJ, and set up shortcuts to them in our
+   hash table.  */
+
+static bfd_boolean
+riscv_elf_create_dynamic_sections (bfd *dynobj,
+				   struct bfd_link_info *info)
+{
+  struct riscv_elf_link_hash_table *htab;
+
+  htab = riscv_elf_hash_table (info);
+  BFD_ASSERT (htab != NULL);
+
+  if (!riscv_elf_create_got_section (dynobj, info))
+    return FALSE;
+
+  if (!_bfd_elf_create_dynamic_sections (dynobj, info))
+    return FALSE;
+
+  htab->sdynbss = bfd_get_linker_section (dynobj, ".dynbss");
+  if (!bfd_link_pic (info))
+    {
+      htab->srelbss = bfd_get_linker_section (dynobj, ".rela.bss");
+      htab->sdyntdata =
+	bfd_make_section_anyway_with_flags (dynobj, ".tdata.dyn",
+					    SEC_ALLOC | SEC_THREAD_LOCAL);
+    }
+
+  if (!htab->elf.splt || !htab->elf.srelplt || !htab->sdynbss
+      || (!bfd_link_pic (info) && (!htab->srelbss || !htab->sdyntdata)))
+    abort ();
+
+  return TRUE;
+}
+
+/* Copy the extra info we tack onto an elf_link_hash_entry.  */
+
+static void
+riscv_elf_copy_indirect_symbol (struct bfd_link_info *info,
+				struct elf_link_hash_entry *dir,
+				struct elf_link_hash_entry *ind)
+{
+  struct riscv_elf_link_hash_entry *edir, *eind;
+
+  edir = (struct riscv_elf_link_hash_entry *) dir;
+  eind = (struct riscv_elf_link_hash_entry *) ind;
+
+  if (eind->dyn_relocs != NULL)
+    {
+      if (edir->dyn_relocs != NULL)
+	{
+	  struct riscv_elf_dyn_relocs **pp;
+	  struct riscv_elf_dyn_relocs *p;
+
+	  /* Add reloc counts against the indirect sym to the direct sym
+	     list.  Merge any entries against the same section.  */
+	  for (pp = &eind->dyn_relocs; (p = *pp) != NULL; )
+	    {
+	      struct riscv_elf_dyn_relocs *q;
+
+	      for (q = edir->dyn_relocs; q != NULL; q = q->next)
+		if (q->sec == p->sec)
+		  {
+		    q->pc_count += p->pc_count;
+		    q->count += p->count;
+		    *pp = p->next;
+		    break;
+		  }
+	      if (q == NULL)
+		pp = &p->next;
+	    }
+	  *pp = edir->dyn_relocs;
+	}
+
+      edir->dyn_relocs = eind->dyn_relocs;
+      eind->dyn_relocs = NULL;
+    }
+
+  if (ind->root.type == bfd_link_hash_indirect
+      && dir->got.refcount <= 0)
+    {
+      edir->tls_type = eind->tls_type;
+      eind->tls_type = GOT_UNKNOWN;
+    }
+  _bfd_elf_link_hash_copy_indirect (info, dir, ind);
+}
+
+static bfd_boolean
+riscv_elf_record_tls_type (bfd *abfd, struct elf_link_hash_entry *h,
+			   unsigned long symndx, char tls_type)
+{
+  char *new_tls_type = &_bfd_riscv_elf_tls_type (abfd, h, symndx);
+
+  *new_tls_type |= tls_type;
+  if ((*new_tls_type & GOT_NORMAL) && (*new_tls_type & ~GOT_NORMAL))
+    {
+      (*_bfd_error_handler)
+	(_("%B: `%s' accessed both as normal and thread local symbol"),
+	 abfd, h ? h->root.root.string : "<local>");
+      return FALSE;
+    }
+  return TRUE;
+}
+
+static bfd_boolean
+riscv_elf_record_got_reference (bfd *abfd, struct bfd_link_info *info,
+				struct elf_link_hash_entry *h, long symndx)
+{
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+  Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+
+  if (htab->elf.sgot == NULL)
+    {
+      if (!riscv_elf_create_got_section (htab->elf.dynobj, info))
+	return FALSE;
+    }
+
+  if (h != NULL)
+    {
+      h->got.refcount += 1;
+      return TRUE;
+    }
+
+  /* This is a global offset table entry for a local symbol.  */
+  if (elf_local_got_refcounts (abfd) == NULL)
+    {
+      bfd_size_type size = symtab_hdr->sh_info * (sizeof (bfd_vma) + 1);
+      if (!(elf_local_got_refcounts (abfd) = bfd_zalloc (abfd, size)))
+	return FALSE;
+      _bfd_riscv_elf_local_got_tls_type (abfd)
+	= (char *) (elf_local_got_refcounts (abfd) + symtab_hdr->sh_info);
+    }
+  elf_local_got_refcounts (abfd) [symndx] += 1;
+
+  return TRUE;
+}
+
+static bfd_boolean
+bad_static_reloc (bfd *abfd, unsigned r_type, struct elf_link_hash_entry *h)
+{
+  (*_bfd_error_handler)
+    (_("%B: relocation %s against `%s' can not be used when making a shared "
+       "object; recompile with -fPIC"),
+      abfd, riscv_elf_rtype_to_howto (r_type)->name,
+      h != NULL ? h->root.root.string : "a local symbol");
+  bfd_set_error (bfd_error_bad_value);
+  return FALSE;
+}
+/* Look through the relocs for a section during the first phase, and
+   allocate space in the global offset table or procedure linkage
+   table.  */
+
+static bfd_boolean
+riscv_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
+			asection *sec, const Elf_Internal_Rela *relocs)
+{
+  struct riscv_elf_link_hash_table *htab;
+  Elf_Internal_Shdr *symtab_hdr;
+  struct elf_link_hash_entry **sym_hashes;
+  const Elf_Internal_Rela *rel;
+  asection *sreloc = NULL;
+
+  if (bfd_link_relocatable (info))
+    return TRUE;
+
+  htab = riscv_elf_hash_table (info);
+  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+  sym_hashes = elf_sym_hashes (abfd);
+
+  if (htab->elf.dynobj == NULL)
+    htab->elf.dynobj = abfd;
+
+  for (rel = relocs; rel < relocs + sec->reloc_count; rel++)
+    {
+      unsigned int r_type;
+      unsigned long r_symndx;
+      struct elf_link_hash_entry *h;
+
+      r_symndx = ELFNN_R_SYM (rel->r_info);
+      r_type = ELFNN_R_TYPE (rel->r_info);
+
+      if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr))
+	{
+	  (*_bfd_error_handler) (_("%B: bad symbol index: %d"),
+				 abfd, r_symndx);
+	  return FALSE;
+	}
+
+      if (r_symndx < symtab_hdr->sh_info)
+	h = NULL;
+      else
+	{
+	  h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+	  while (h->root.type == bfd_link_hash_indirect
+		 || h->root.type == bfd_link_hash_warning)
+	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+	  /* PR15323, ref flags aren't set for references in the same
+	     object.  */
+	  h->root.non_ir_ref = 1;
+	}
+
+      switch (r_type)
+	{
+	case R_RISCV_TLS_GD_HI20:
+	  if (!riscv_elf_record_got_reference (abfd, info, h, r_symndx)
+	      || !riscv_elf_record_tls_type (abfd, h, r_symndx, GOT_TLS_GD))
+	    return FALSE;
+	  break;
+
+	case R_RISCV_TLS_GOT_HI20:
+	  if (bfd_link_pic (info))
+	    info->flags |= DF_STATIC_TLS;
+	  if (!riscv_elf_record_got_reference (abfd, info, h, r_symndx)
+	      || !riscv_elf_record_tls_type (abfd, h, r_symndx, GOT_TLS_IE))
+	    return FALSE;
+	  break;
+
+	case R_RISCV_GOT_HI20:
+	  if (!riscv_elf_record_got_reference (abfd, info, h, r_symndx)
+	      || !riscv_elf_record_tls_type (abfd, h, r_symndx, GOT_NORMAL))
+	    return FALSE;
+	  break;
+
+	case R_RISCV_CALL_PLT:
+	  /* This symbol requires a procedure linkage table entry.  We
+	     actually build the entry in adjust_dynamic_symbol,
+	     because this might be a case of linking PIC code without
+	     linking in any dynamic objects, in which case we don't
+	     need to generate a procedure linkage table after all.  */
+
+	  if (h != NULL)
+	    {
+	      h->needs_plt = 1;
+	      h->plt.refcount += 1;
+	    }
+	  break;
+
+	case R_RISCV_CALL:
+	case R_RISCV_JAL:
+	case R_RISCV_BRANCH:
+	case R_RISCV_RVC_BRANCH:
+	case R_RISCV_RVC_JUMP:
+	case R_RISCV_PCREL_HI20:
+	  /* In shared libraries, these relocs are known to bind locally.  */
+	  if (bfd_link_pic (info))
+	    break;
+	  goto static_reloc;
+
+	case R_RISCV_TPREL_HI20:
+	  if (!bfd_link_executable (info))
+	    return bad_static_reloc (abfd, r_type, h);
+	  if (h != NULL)
+	    riscv_elf_record_tls_type (abfd, h, r_symndx, GOT_TLS_LE);
+	  goto static_reloc;
+
+	case R_RISCV_HI20:
+	  if (bfd_link_pic (info))
+	    return bad_static_reloc (abfd, r_type, h);
+	  /* Fall through.  */
+
+	case R_RISCV_COPY:
+	case R_RISCV_JUMP_SLOT:
+	case R_RISCV_RELATIVE:
+	case R_RISCV_64:
+	case R_RISCV_32:
+	  /* Fall through.  */
+
+	static_reloc:
+	  /* This reloc might not bind locally.  */
+	  if (h != NULL)
+	    h->non_got_ref = 1;
+
+	  if (h != NULL && !bfd_link_pic (info))
+	    {
+	      /* We may need a .plt entry if the function this reloc
+		 refers to is in a shared lib.  */
+	      h->plt.refcount += 1;
+	    }
+
+	  /* If we are creating a shared library, and this is a reloc
+	     against a global symbol, or a non PC relative reloc
+	     against a local symbol, then we need to copy the reloc
+	     into the shared library.  However, if we are linking with
+	     -Bsymbolic, we do not need to copy a reloc against a
+	     global symbol which is defined in an object we are
+	     including in the link (i.e., DEF_REGULAR is set).  At
+	     this point we have not seen all the input files, so it is
+	     possible that DEF_REGULAR is not set now but will be set
+	     later (it is never cleared).  In case of a weak definition,
+	     DEF_REGULAR may be cleared later by a strong definition in
+	     a shared library.  We account for that possibility below by
+	     storing information in the relocs_copied field of the hash
+	     table entry.  A similar situation occurs when creating
+	     shared libraries and symbol visibility changes render the
+	     symbol local.
+
+	     If on the other hand, we are creating an executable, we
+	     may need to keep relocations for symbols satisfied by a
+	     dynamic library if we manage to avoid copy relocs for the
+	     symbol.  */
+	  if ((bfd_link_pic (info)
+	       && (sec->flags & SEC_ALLOC) != 0
+	       && (! riscv_elf_rtype_to_howto (r_type)->pc_relative
+		   || (h != NULL
+		       && (! info->symbolic
+			   || h->root.type == bfd_link_hash_defweak
+			   || !h->def_regular))))
+	      || (!bfd_link_pic (info)
+		  && (sec->flags & SEC_ALLOC) != 0
+		  && h != NULL
+		  && (h->root.type == bfd_link_hash_defweak
+		      || !h->def_regular)))
+	    {
+	      struct riscv_elf_dyn_relocs *p;
+	      struct riscv_elf_dyn_relocs **head;
+
+	      /* When creating a shared object, we must copy these
+		 relocs into the output file.  We create a reloc
+		 section in dynobj and make room for the reloc.  */
+	      if (sreloc == NULL)
+		{
+		  sreloc = _bfd_elf_make_dynamic_reloc_section
+		    (sec, htab->elf.dynobj, RISCV_ELF_LOG_WORD_BYTES,
+		    abfd, /*rela?*/ TRUE);
+
+		  if (sreloc == NULL)
+		    return FALSE;
+		}
+
+	      /* If this is a global symbol, we count the number of
+		 relocations we need for this symbol.  */
+	      if (h != NULL)
+		head = &((struct riscv_elf_link_hash_entry *) h)->dyn_relocs;
+	      else
+		{
+		  /* Track dynamic relocs needed for local syms too.
+		     We really need local syms available to do this
+		     easily.  Oh well.  */
+
+		  asection *s;
+		  void *vpp;
+		  Elf_Internal_Sym *isym;
+
+		  isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+						abfd, r_symndx);
+		  if (isym == NULL)
+		    return FALSE;
+
+		  s = bfd_section_from_elf_index (abfd, isym->st_shndx);
+		  if (s == NULL)
+		    s = sec;
+
+		  vpp = &elf_section_data (s)->local_dynrel;
+		  head = (struct riscv_elf_dyn_relocs **) vpp;
+		}
+
+	      p = *head;
+	      if (p == NULL || p->sec != sec)
+		{
+		  bfd_size_type amt = sizeof *p;
+		  p = ((struct riscv_elf_dyn_relocs *)
+		       bfd_alloc (htab->elf.dynobj, amt));
+		  if (p == NULL)
+		    return FALSE;
+		  p->next = *head;
+		  *head = p;
+		  p->sec = sec;
+		  p->count = 0;
+		  p->pc_count = 0;
+		}
+
+	      p->count += 1;
+	      p->pc_count += riscv_elf_rtype_to_howto (r_type)->pc_relative;
+	    }
+
+	  break;
+
+	case R_RISCV_GNU_VTINHERIT:
+	  if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
+	    return FALSE;
+	  break;
+
+	case R_RISCV_GNU_VTENTRY:
+	  if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
+	    return FALSE;
+	  break;
+
+	default:
+	  break;
+	}
+    }
+
+  return TRUE;
+}
+
+static asection *
+riscv_elf_gc_mark_hook (asection *sec,
+			struct bfd_link_info *info,
+			Elf_Internal_Rela *rel,
+			struct elf_link_hash_entry *h,
+			Elf_Internal_Sym *sym)
+{
+  if (h != NULL)
+    switch (ELFNN_R_TYPE (rel->r_info))
+      {
+      case R_RISCV_GNU_VTINHERIT:
+      case R_RISCV_GNU_VTENTRY:
+	return NULL;
+      }
+
+  return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
+}
+
+/* Update the got entry reference counts for the section being removed.  */
+
+static bfd_boolean
+riscv_elf_gc_sweep_hook (bfd *abfd,
+			 struct bfd_link_info *info,
+			 asection *sec,
+			 const Elf_Internal_Rela *relocs)
+{
+  const Elf_Internal_Rela *rel, *relend;
+  Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (abfd);
+  struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (abfd);
+  bfd_signed_vma *local_got_refcounts = elf_local_got_refcounts (abfd);
+
+  if (bfd_link_relocatable (info))
+    return TRUE;
+
+  elf_section_data (sec)->local_dynrel = NULL;
+
+  for (rel = relocs, relend = relocs + sec->reloc_count; rel < relend; rel++)
+    {
+      unsigned long r_symndx;
+      struct elf_link_hash_entry *h = NULL;
+
+      r_symndx = ELFNN_R_SYM (rel->r_info);
+      if (r_symndx >= symtab_hdr->sh_info)
+	{
+	  struct riscv_elf_link_hash_entry *eh;
+	  struct riscv_elf_dyn_relocs **pp;
+	  struct riscv_elf_dyn_relocs *p;
+
+	  h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+	  while (h->root.type == bfd_link_hash_indirect
+		 || h->root.type == bfd_link_hash_warning)
+	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
+	  eh = (struct riscv_elf_link_hash_entry *) h;
+	  for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next)
+	    if (p->sec == sec)
+	      {
+		/* Everything must go for SEC.  */
+		*pp = p->next;
+		break;
+	      }
+	}
+
+      switch (ELFNN_R_TYPE (rel->r_info))
+	{
+	case R_RISCV_GOT_HI20:
+	case R_RISCV_TLS_GOT_HI20:
+	case R_RISCV_TLS_GD_HI20:
+	  if (h != NULL)
+	    {
+	      if (h->got.refcount > 0)
+		h->got.refcount--;
+	    }
+	  else
+	    {
+	      if (local_got_refcounts &&
+		  local_got_refcounts[r_symndx] > 0)
+		local_got_refcounts[r_symndx]--;
+	    }
+	  break;
+
+	case R_RISCV_HI20:
+	case R_RISCV_PCREL_HI20:
+	case R_RISCV_COPY:
+	case R_RISCV_JUMP_SLOT:
+	case R_RISCV_RELATIVE:
+	case R_RISCV_64:
+	case R_RISCV_32:
+	case R_RISCV_BRANCH:
+	case R_RISCV_CALL:
+	case R_RISCV_JAL:
+	case R_RISCV_RVC_BRANCH:
+	case R_RISCV_RVC_JUMP:
+	  if (bfd_link_pic (info))
+	    break;
+	  /* Fall through.  */
+
+	case R_RISCV_CALL_PLT:
+	  if (h != NULL)
+	    {
+	      if (h->plt.refcount > 0)
+		h->plt.refcount--;
+	    }
+	  break;
+
+	default:
+	  break;
+	}
+    }
+
+  return TRUE;
+}
+
+/* Adjust a symbol defined by a dynamic object and referenced by a
+   regular object.  The current definition is in some section of the
+   dynamic object, but we're not including those sections.  We have to
+   change the definition to something the rest of the link can
+   understand.  */
+
+static bfd_boolean
+riscv_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
+				 struct elf_link_hash_entry *h)
+{
+  struct riscv_elf_link_hash_table *htab;
+  struct riscv_elf_link_hash_entry * eh;
+  struct riscv_elf_dyn_relocs *p;
+  bfd *dynobj;
+  asection *s;
+
+  htab = riscv_elf_hash_table (info);
+  BFD_ASSERT (htab != NULL);
+
+  dynobj = htab->elf.dynobj;
+
+  /* Make sure we know what is going on here.  */
+  BFD_ASSERT (dynobj != NULL
+	      && (h->needs_plt
+		  || h->type == STT_GNU_IFUNC
+		  || h->u.weakdef != NULL
+		  || (h->def_dynamic
+		      && h->ref_regular
+		      && !h->def_regular)));
+
+  /* If this is a function, put it in the procedure linkage table.  We
+     will fill in the contents of the procedure linkage table later
+     (although we could actually do it here).  */
+  if (h->type == STT_FUNC || h->type == STT_GNU_IFUNC || h->needs_plt)
+    {
+      if (h->plt.refcount <= 0
+	  || SYMBOL_CALLS_LOCAL (info, h)
+	  || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+	      && h->root.type == bfd_link_hash_undefweak))
+	{
+	  /* This case can occur if we saw a R_RISCV_CALL_PLT reloc in an
+	     input file, but the symbol was never referred to by a dynamic
+	     object, or if all references were garbage collected.  In such
+	     a case, we don't actually need to build a PLT entry.  */
+	  h->plt.offset = (bfd_vma) -1;
+	  h->needs_plt = 0;
+	}
+
+      return TRUE;
+    }
+  else
+    h->plt.offset = (bfd_vma) -1;
+
+  /* If this is a weak symbol, and there is a real definition, the
+     processor independent code will have arranged for us to see the
+     real definition first, and we can just use the same value.  */
+  if (h->u.weakdef != NULL)
+    {
+      BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined
+		  || h->u.weakdef->root.type == bfd_link_hash_defweak);
+      h->root.u.def.section = h->u.weakdef->root.u.def.section;
+      h->root.u.def.value = h->u.weakdef->root.u.def.value;
+      return TRUE;
+    }
+
+  /* This is a reference to a symbol defined by a dynamic object which
+     is not a function.  */
+
+  /* If we are creating a shared library, we must presume that the
+     only references to the symbol are via the global offset table.
+     For such cases we need not do anything here; the relocations will
+     be handled correctly by relocate_section.  */
+  if (bfd_link_pic (info))
+    return TRUE;
+
+  /* If there are no references to this symbol that do not use the
+     GOT, we don't need to generate a copy reloc.  */
+  if (!h->non_got_ref)
+    return TRUE;
+
+  /* If -z nocopyreloc was given, we won't generate them either.  */
+  if (info->nocopyreloc)
+    {
+      h->non_got_ref = 0;
+      return TRUE;
+    }
+
+  eh = (struct riscv_elf_link_hash_entry *) h;
+  for (p = eh->dyn_relocs; p != NULL; p = p->next)
+    {
+      s = p->sec->output_section;
+      if (s != NULL && (s->flags & SEC_READONLY) != 0)
+	break;
+    }
+
+  /* If we didn't find any dynamic relocs in read-only sections, then
+     we'll be keeping the dynamic relocs and avoiding the copy reloc.  */
+  if (p == NULL)
+    {
+      h->non_got_ref = 0;
+      return TRUE;
+    }
+
+  /* We must allocate the symbol in our .dynbss section, which will
+     become part of the .bss section of the executable.  There will be
+     an entry for this symbol in the .dynsym section.  The dynamic
+     object will contain position independent code, so all references
+     from the dynamic object to this symbol will go through the global
+     offset table.  The dynamic linker will use the .dynsym entry to
+     determine the address it must put in the global offset table, so
+     both the dynamic object and the regular object will refer to the
+     same memory location for the variable.  */
+
+  /* We must generate a R_RISCV_COPY reloc to tell the dynamic linker
+     to copy the initial value out of the dynamic object and into the
+     runtime process image.  We need to remember the offset into the
+     .rel.bss section we are going to use.  */
+  if ((h->root.u.def.section->flags & SEC_ALLOC) != 0 && h->size != 0)
+    {
+      htab->srelbss->size += sizeof (ElfNN_External_Rela);
+      h->needs_copy = 1;
+    }
+
+  if (eh->tls_type & ~GOT_NORMAL)
+    return _bfd_elf_adjust_dynamic_copy (info, h, htab->sdyntdata);
+
+  return _bfd_elf_adjust_dynamic_copy (info, h, htab->sdynbss);
+}
+
+/* Allocate space in .plt, .got and associated reloc sections for
+   dynamic relocs.  */
+
+static bfd_boolean
+allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
+{
+  struct bfd_link_info *info;
+  struct riscv_elf_link_hash_table *htab;
+  struct riscv_elf_link_hash_entry *eh;
+  struct riscv_elf_dyn_relocs *p;
+
+  if (h->root.type == bfd_link_hash_indirect)
+    return TRUE;
+
+  info = (struct bfd_link_info *) inf;
+  htab = riscv_elf_hash_table (info);
+  BFD_ASSERT (htab != NULL);
+
+  if (htab->elf.dynamic_sections_created
+      && h->plt.refcount > 0)
+    {
+      /* Make sure this symbol is output as a dynamic symbol.
+	 Undefined weak syms won't yet be marked as dynamic.  */
+      if (h->dynindx == -1
+	  && !h->forced_local)
+	{
+	  if (! bfd_elf_link_record_dynamic_symbol (info, h))
+	    return FALSE;
+	}
+
+      if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, bfd_link_pic (info), h))
+	{
+	  asection *s = htab->elf.splt;
+
+	  if (s->size == 0)
+	    s->size = PLT_HEADER_SIZE;
+
+	  h->plt.offset = s->size;
+
+	  /* Make room for this entry.  */
+	  s->size += PLT_ENTRY_SIZE;
+
+	  /* We also need to make an entry in the .got.plt section.  */
+	  htab->elf.sgotplt->size += GOT_ENTRY_SIZE;
+
+	  /* We also need to make an entry in the .rela.plt section.  */
+	  htab->elf.srelplt->size += sizeof (ElfNN_External_Rela);
+
+	  /* If this symbol is not defined in a regular file, and we are
+	     not generating a shared library, then set the symbol to this
+	     location in the .plt.  This is required to make function
+	     pointers compare as equal between the normal executable and
+	     the shared library.  */
+	  if (! bfd_link_pic (info)
+	      && !h->def_regular)
+	    {
+	      h->root.u.def.section = s;
+	      h->root.u.def.value = h->plt.offset;
+	    }
+	}
+      else
+	{
+	  h->plt.offset = (bfd_vma) -1;
+	  h->needs_plt = 0;
+	}
+    }
+  else
+    {
+      h->plt.offset = (bfd_vma) -1;
+      h->needs_plt = 0;
+    }
+
+  if (h->got.refcount > 0)
+    {
+      asection *s;
+      bfd_boolean dyn;
+      int tls_type = riscv_elf_hash_entry (h)->tls_type;
+
+      /* Make sure this symbol is output as a dynamic symbol.
+	 Undefined weak syms won't yet be marked as dynamic.  */
+      if (h->dynindx == -1
+	  && !h->forced_local)
+	{
+	  if (! bfd_elf_link_record_dynamic_symbol (info, h))
+	    return FALSE;
+	}
+
+      s = htab->elf.sgot;
+      h->got.offset = s->size;
+      dyn = htab->elf.dynamic_sections_created;
+      if (tls_type & (GOT_TLS_GD | GOT_TLS_IE))
+	{
+	  /* TLS_GD needs two dynamic relocs and two GOT slots.  */
+	  if (tls_type & GOT_TLS_GD)
+	    {
+	      s->size += 2 * RISCV_ELF_WORD_BYTES;
+	      htab->elf.srelgot->size += 2 * sizeof (ElfNN_External_Rela);
+	    }
+
+	  /* TLS_IE needs one dynamic reloc and one GOT slot.  */
+	  if (tls_type & GOT_TLS_IE)
+	    {
+	      s->size += RISCV_ELF_WORD_BYTES;
+	      htab->elf.srelgot->size += sizeof (ElfNN_External_Rela);
+	    }
+	}
+      else
+	{
+	  s->size += RISCV_ELF_WORD_BYTES;
+	  if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, bfd_link_pic (info), h))
+	    htab->elf.srelgot->size += sizeof (ElfNN_External_Rela);
+	}
+    }
+  else
+    h->got.offset = (bfd_vma) -1;
+
+  eh = (struct riscv_elf_link_hash_entry *) h;
+  if (eh->dyn_relocs == NULL)
+    return TRUE;
+
+  /* In the shared -Bsymbolic case, discard space allocated for
+     dynamic pc-relative relocs against symbols which turn out to be
+     defined in regular objects.  For the normal shared case, discard
+     space for pc-relative relocs that have become local due to symbol
+     visibility changes.  */
+
+  if (bfd_link_pic (info))
+    {
+      if (SYMBOL_CALLS_LOCAL (info, h))
+	{
+	  struct riscv_elf_dyn_relocs **pp;
+
+	  for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
+	    {
+	      p->count -= p->pc_count;
+	      p->pc_count = 0;
+	      if (p->count == 0)
+		*pp = p->next;
+	      else
+		pp = &p->next;
+	    }
+	}
+
+      /* Also discard relocs on undefined weak syms with non-default
+	 visibility.  */
+      if (eh->dyn_relocs != NULL
+	  && h->root.type == bfd_link_hash_undefweak)
+	{
+	  if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
+	    eh->dyn_relocs = NULL;
+
+	  /* Make sure undefined weak symbols are output as a dynamic
+	     symbol in PIEs.  */
+	  else if (h->dynindx == -1
+		   && !h->forced_local)
+	    {
+	      if (! bfd_elf_link_record_dynamic_symbol (info, h))
+		return FALSE;
+	    }
+	}
+    }
+  else
+    {
+      /* For the non-shared case, discard space for relocs against
+	 symbols which turn out to need copy relocs or are not
+	 dynamic.  */
+
+      if (!h->non_got_ref
+	  && ((h->def_dynamic
+	       && !h->def_regular)
+	      || (htab->elf.dynamic_sections_created
+		  && (h->root.type == bfd_link_hash_undefweak
+		      || h->root.type == bfd_link_hash_undefined))))
+	{
+	  /* Make sure this symbol is output as a dynamic symbol.
+	     Undefined weak syms won't yet be marked as dynamic.  */
+	  if (h->dynindx == -1
+	      && !h->forced_local)
+	    {
+	      if (! bfd_elf_link_record_dynamic_symbol (info, h))
+		return FALSE;
+	    }
+
+	  /* If that succeeded, we know we'll be keeping all the
+	     relocs.  */
+	  if (h->dynindx != -1)
+	    goto keep;
+	}
+
+      eh->dyn_relocs = NULL;
+
+    keep: ;
+    }
+
+  /* Finally, allocate space.  */
+  for (p = eh->dyn_relocs; p != NULL; p = p->next)
+    {
+      asection *sreloc = elf_section_data (p->sec)->sreloc;
+      sreloc->size += p->count * sizeof (ElfNN_External_Rela);
+    }
+
+  return TRUE;
+}
+
+/* Find any dynamic relocs that apply to read-only sections.  */
+
+static bfd_boolean
+readonly_dynrelocs (struct elf_link_hash_entry *h, void *inf)
+{
+  struct riscv_elf_link_hash_entry *eh;
+  struct riscv_elf_dyn_relocs *p;
+
+  eh = (struct riscv_elf_link_hash_entry *) h;
+  for (p = eh->dyn_relocs; p != NULL; p = p->next)
+    {
+      asection *s = p->sec->output_section;
+
+      if (s != NULL && (s->flags & SEC_READONLY) != 0)
+	{
+	  ((struct bfd_link_info *) inf)->flags |= DF_TEXTREL;
+	  return FALSE;
+	}
+    }
+  return TRUE;
+}
+
+static bfd_boolean
+riscv_elf_size_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info)
+{
+  struct riscv_elf_link_hash_table *htab;
+  bfd *dynobj;
+  asection *s;
+  bfd *ibfd;
+
+  htab = riscv_elf_hash_table (info);
+  BFD_ASSERT (htab != NULL);
+  dynobj = htab->elf.dynobj;
+  BFD_ASSERT (dynobj != NULL);
+
+  if (elf_hash_table (info)->dynamic_sections_created)
+    {
+      /* Set the contents of the .interp section to the interpreter.  */
+      if (bfd_link_executable (info) && !info->nointerp)
+	{
+	  s = bfd_get_linker_section (dynobj, ".interp");
+	  BFD_ASSERT (s != NULL);
+	  s->size = strlen (ELFNN_DYNAMIC_INTERPRETER) + 1;
+	  s->contents = (unsigned char *) ELFNN_DYNAMIC_INTERPRETER;
+	}
+    }
+
+  /* Set up .got offsets for local syms, and space for local dynamic
+     relocs.  */
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+    {
+      bfd_signed_vma *local_got;
+      bfd_signed_vma *end_local_got;
+      char *local_tls_type;
+      bfd_size_type locsymcount;
+      Elf_Internal_Shdr *symtab_hdr;
+      asection *srel;
+
+      if (! is_riscv_elf (ibfd))
+	continue;
+
+      for (s = ibfd->sections; s != NULL; s = s->next)
+	{
+	  struct riscv_elf_dyn_relocs *p;
+
+	  for (p = elf_section_data (s)->local_dynrel; p != NULL; p = p->next)
+	    {
+	      if (!bfd_is_abs_section (p->sec)
+		  && bfd_is_abs_section (p->sec->output_section))
+		{
+		  /* Input section has been discarded, either because
+		     it is a copy of a linkonce section or due to
+		     linker script /DISCARD/, so we'll be discarding
+		     the relocs too.  */
+		}
+	      else if (p->count != 0)
+		{
+		  srel = elf_section_data (p->sec)->sreloc;
+		  srel->size += p->count * sizeof (ElfNN_External_Rela);
+		  if ((p->sec->output_section->flags & SEC_READONLY) != 0)
+		    info->flags |= DF_TEXTREL;
+		}
+	    }
+	}
+
+      local_got = elf_local_got_refcounts (ibfd);
+      if (!local_got)
+	continue;
+
+      symtab_hdr = &elf_symtab_hdr (ibfd);
+      locsymcount = symtab_hdr->sh_info;
+      end_local_got = local_got + locsymcount;
+      local_tls_type = _bfd_riscv_elf_local_got_tls_type (ibfd);
+      s = htab->elf.sgot;
+      srel = htab->elf.srelgot;
+      for (; local_got < end_local_got; ++local_got, ++local_tls_type)
+	{
+	  if (*local_got > 0)
+	    {
+	      *local_got = s->size;
+	      s->size += RISCV_ELF_WORD_BYTES;
+	      if (*local_tls_type & GOT_TLS_GD)
+		s->size += RISCV_ELF_WORD_BYTES;
+	      if (bfd_link_pic (info)
+		  || (*local_tls_type & (GOT_TLS_GD | GOT_TLS_IE)))
+		srel->size += sizeof (ElfNN_External_Rela);
+	    }
+	  else
+	    *local_got = (bfd_vma) -1;
+	}
+    }
+
+  /* Allocate global sym .plt and .got entries, and space for global
+     sym dynamic relocs.  */
+  elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, info);
+
+  if (htab->elf.sgotplt)
+    {
+      struct elf_link_hash_entry *got;
+      got = elf_link_hash_lookup (elf_hash_table (info),
+				  "_GLOBAL_OFFSET_TABLE_",
+				  FALSE, FALSE, FALSE);
+
+      /* Don't allocate .got.plt section if there are no GOT nor PLT
+	 entries and there is no refeence to _GLOBAL_OFFSET_TABLE_.  */
+      if ((got == NULL
+	   || !got->ref_regular_nonweak)
+	  && (htab->elf.sgotplt->size == GOTPLT_HEADER_SIZE)
+	  && (htab->elf.splt == NULL
+	      || htab->elf.splt->size == 0)
+	  && (htab->elf.sgot == NULL
+	      || (htab->elf.sgot->size
+		  == get_elf_backend_data (output_bfd)->got_header_size)))
+	htab->elf.sgotplt->size = 0;
+    }
+
+  /* The check_relocs and adjust_dynamic_symbol entry points have
+     determined the sizes of the various dynamic sections.  Allocate
+     memory for them.  */
+  for (s = dynobj->sections; s != NULL; s = s->next)
+    {
+      if ((s->flags & SEC_LINKER_CREATED) == 0)
+	continue;
+
+      if (s == htab->elf.splt
+	  || s == htab->elf.sgot
+	  || s == htab->elf.sgotplt
+	  || s == htab->sdynbss)
+	{
+	  /* Strip this section if we don't need it; see the
+	     comment below.  */
+	}
+      else if (strncmp (s->name, ".rela", 5) == 0)
+	{
+	  if (s->size != 0)
+	    {
+	      /* We use the reloc_count field as a counter if we need
+		 to copy relocs into the output file.  */
+	      s->reloc_count = 0;
+	    }
+	}
+      else
+	{
+	  /* It's not one of our sections.  */
+	  continue;
+	}
+
+      if (s->size == 0)
+	{
+	  /* If we don't need this section, strip it from the
+	     output file.  This is mostly to handle .rela.bss and
+	     .rela.plt.  We must create both sections in
+	     create_dynamic_sections, because they must be created
+	     before the linker maps input sections to output
+	     sections.  The linker does that before
+	     adjust_dynamic_symbol is called, and it is that
+	     function which decides whether anything needs to go
+	     into these sections.  */
+	  s->flags |= SEC_EXCLUDE;
+	  continue;
+	}
+
+      if ((s->flags & SEC_HAS_CONTENTS) == 0)
+	continue;
+
+      /* Allocate memory for the section contents.  Zero the memory
+	 for the benefit of .rela.plt, which has 4 unused entries
+	 at the beginning, and we don't want garbage.  */
+      s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->size);
+      if (s->contents == NULL)
+	return FALSE;
+    }
+
+  if (elf_hash_table (info)->dynamic_sections_created)
+    {
+      /* Add some entries to the .dynamic section.  We fill in the
+	 values later, in riscv_elf_finish_dynamic_sections, but we
+	 must add the entries now so that we get the correct size for
+	 the .dynamic section.  The DT_DEBUG entry is filled in by the
+	 dynamic linker and used by the debugger.  */
+#define add_dynamic_entry(TAG, VAL) \
+  _bfd_elf_add_dynamic_entry (info, TAG, VAL)
+
+      if (bfd_link_executable (info))
+	{
+	  if (!add_dynamic_entry (DT_DEBUG, 0))
+	    return FALSE;
+	}
+
+      if (htab->elf.srelplt->size != 0)
+	{
+	  if (!add_dynamic_entry (DT_PLTGOT, 0)
+	      || !add_dynamic_entry (DT_PLTRELSZ, 0)
+	      || !add_dynamic_entry (DT_PLTREL, DT_RELA)
+	      || !add_dynamic_entry (DT_JMPREL, 0))
+	    return FALSE;
+	}
+
+      if (!add_dynamic_entry (DT_RELA, 0)
+	  || !add_dynamic_entry (DT_RELASZ, 0)
+	  || !add_dynamic_entry (DT_RELAENT, sizeof (ElfNN_External_Rela)))
+	return FALSE;
+
+      /* If any dynamic relocs apply to a read-only section,
+	 then we need a DT_TEXTREL entry.  */
+      if ((info->flags & DF_TEXTREL) == 0)
+	elf_link_hash_traverse (&htab->elf, readonly_dynrelocs, info);
+
+      if (info->flags & DF_TEXTREL)
+	{
+	  if (!add_dynamic_entry (DT_TEXTREL, 0))
+	    return FALSE;
+	}
+    }
+#undef add_dynamic_entry
+
+  return TRUE;
+}
+
+#define TP_OFFSET 0
+#define DTP_OFFSET 0x800
+
+/* Return the relocation value for a TLS dtp-relative reloc.  */
+
+static bfd_vma
+dtpoff (struct bfd_link_info *info, bfd_vma address)
+{
+  /* If tls_sec is NULL, we should have signalled an error already.  */
+  if (elf_hash_table (info)->tls_sec == NULL)
+    return 0;
+  return address - elf_hash_table (info)->tls_sec->vma - DTP_OFFSET;
+}
+
+/* Return the relocation value for a static TLS tp-relative relocation.  */
+
+static bfd_vma
+tpoff (struct bfd_link_info *info, bfd_vma address)
+{
+  /* If tls_sec is NULL, we should have signalled an error already.  */
+  if (elf_hash_table (info)->tls_sec == NULL)
+    return 0;
+  return address - elf_hash_table (info)->tls_sec->vma - TP_OFFSET;
+}
+
+/* Return the global pointer's value, or 0 if it is not in use.  */
+
+static bfd_vma
+riscv_global_pointer_value (struct bfd_link_info *info)
+{
+  struct bfd_link_hash_entry *h;
+
+  h = bfd_link_hash_lookup (info->hash, "_gp", FALSE, FALSE, TRUE);
+  if (h == NULL || h->type != bfd_link_hash_defined)
+    return 0;
+
+  return h->u.def.value + sec_addr (h->u.def.section);
+}
+
+/* Emplace a static relocation.  */
+
+static bfd_reloc_status_type
+perform_relocation (const reloc_howto_type *howto,
+		    const Elf_Internal_Rela *rel,
+		    bfd_vma value,
+		    asection *input_section,
+		    bfd *input_bfd,
+		    bfd_byte *contents)
+{
+  if (howto->pc_relative)
+    value -= sec_addr (input_section) + rel->r_offset;
+  value += rel->r_addend;
+
+  switch (ELFNN_R_TYPE (rel->r_info))
+    {
+    case R_RISCV_HI20:
+    case R_RISCV_TPREL_HI20:
+    case R_RISCV_PCREL_HI20:
+    case R_RISCV_GOT_HI20:
+    case R_RISCV_TLS_GOT_HI20:
+    case R_RISCV_TLS_GD_HI20:
+      if (ARCH_SIZE > 32 && !VALID_UTYPE_IMM (RISCV_CONST_HIGH_PART (value)))
+	return bfd_reloc_overflow;
+      value = ENCODE_UTYPE_IMM (RISCV_CONST_HIGH_PART (value));
+      break;
+
+    case R_RISCV_LO12_I:
+    case R_RISCV_GPREL_I:
+    case R_RISCV_TPREL_LO12_I:
+    case R_RISCV_PCREL_LO12_I:
+      value = ENCODE_ITYPE_IMM (value);
+      break;
+
+    case R_RISCV_LO12_S:
+    case R_RISCV_GPREL_S:
+    case R_RISCV_TPREL_LO12_S:
+    case R_RISCV_PCREL_LO12_S:
+      value = ENCODE_STYPE_IMM (value);
+      break;
+
+    case R_RISCV_CALL:
+    case R_RISCV_CALL_PLT:
+      if (ARCH_SIZE > 32 && !VALID_UTYPE_IMM (RISCV_CONST_HIGH_PART (value)))
+	return bfd_reloc_overflow;
+      value = ENCODE_UTYPE_IMM (RISCV_CONST_HIGH_PART (value))
+	      | (ENCODE_ITYPE_IMM (value) << 32);
+      break;
+
+    case R_RISCV_JAL:
+      if (!VALID_UJTYPE_IMM (value))
+	return bfd_reloc_overflow;
+      value = ENCODE_UJTYPE_IMM (value);
+      break;
+
+    case R_RISCV_BRANCH:
+      if (!VALID_SBTYPE_IMM (value))
+	return bfd_reloc_overflow;
+      value = ENCODE_SBTYPE_IMM (value);
+      break;
+
+    case R_RISCV_RVC_BRANCH:
+      if (!VALID_RVC_B_IMM (value))
+	return bfd_reloc_overflow;
+      value = ENCODE_RVC_B_IMM (value);
+      break;
+
+    case R_RISCV_RVC_JUMP:
+      if (!VALID_RVC_J_IMM (value))
+	return bfd_reloc_overflow;
+      value = ENCODE_RVC_J_IMM (value);
+      break;
+
+    case R_RISCV_RVC_LUI:
+      if (!VALID_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (value)))
+	return bfd_reloc_overflow;
+      value = ENCODE_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (value));
+      break;
+
+    case R_RISCV_32:
+    case R_RISCV_64:
+    case R_RISCV_ADD8:
+    case R_RISCV_ADD16:
+    case R_RISCV_ADD32:
+    case R_RISCV_ADD64:
+    case R_RISCV_SUB8:
+    case R_RISCV_SUB16:
+    case R_RISCV_SUB32:
+    case R_RISCV_SUB64:
+    case R_RISCV_TLS_DTPREL32:
+    case R_RISCV_TLS_DTPREL64:
+      break;
+
+    default:
+      return bfd_reloc_notsupported;
+    }
+
+  bfd_vma word = bfd_get (howto->bitsize, input_bfd, contents + rel->r_offset);
+  word = (word & ~howto->dst_mask) | (value & howto->dst_mask);
+  bfd_put (howto->bitsize, input_bfd, word, contents + rel->r_offset);
+
+  return bfd_reloc_ok;
+}
+
+/* Remember all PC-relative high-part relocs we've encountered to help us
+   later resolve the corresponding low-part relocs.  */
+
+typedef struct
+{
+  bfd_vma address;
+  bfd_vma value;
+} riscv_pcrel_hi_reloc;
+
+typedef struct riscv_pcrel_lo_reloc
+{
+  asection *                     input_section;
+  struct bfd_link_info *         info;
+  reloc_howto_type *             howto;
+  const Elf_Internal_Rela *      reloc;
+  bfd_vma                        addr;
+  const char *                   name;
+  bfd_byte *                     contents;
+  struct riscv_pcrel_lo_reloc *  next;
+} riscv_pcrel_lo_reloc;
+
+typedef struct
+{
+  htab_t hi_relocs;
+  riscv_pcrel_lo_reloc *lo_relocs;
+} riscv_pcrel_relocs;
+
+static hashval_t
+riscv_pcrel_reloc_hash (const void *entry)
+{
+  const riscv_pcrel_hi_reloc *e = entry;
+  return (hashval_t)(e->address >> 2);
+}
+
+static bfd_boolean
+riscv_pcrel_reloc_eq (const void *entry1, const void *entry2)
+{
+  const riscv_pcrel_hi_reloc *e1 = entry1, *e2 = entry2;
+  return e1->address == e2->address;
+}
+
+static bfd_boolean
+riscv_init_pcrel_relocs (riscv_pcrel_relocs *p)
+{
+
+  p->lo_relocs = NULL;
+  p->hi_relocs = htab_create (1024, riscv_pcrel_reloc_hash,
+			      riscv_pcrel_reloc_eq, free);
+  return p->hi_relocs != NULL;
+}
+
+static void
+riscv_free_pcrel_relocs (riscv_pcrel_relocs *p)
+{
+  riscv_pcrel_lo_reloc *cur = p->lo_relocs;
+
+  while (cur != NULL)
+    {
+      riscv_pcrel_lo_reloc *next = cur->next;
+      free (cur);
+      cur = next;
+    }
+
+  htab_delete (p->hi_relocs);
+}
+
+static bfd_boolean
+riscv_record_pcrel_hi_reloc (riscv_pcrel_relocs *p, bfd_vma addr, bfd_vma value)
+{
+  riscv_pcrel_hi_reloc entry = {addr, value - addr};
+  riscv_pcrel_hi_reloc **slot =
+    (riscv_pcrel_hi_reloc **) htab_find_slot (p->hi_relocs, &entry, INSERT);
+
+  BFD_ASSERT (*slot == NULL);
+  *slot = (riscv_pcrel_hi_reloc *) bfd_malloc (sizeof (riscv_pcrel_hi_reloc));
+  if (*slot == NULL)
+    return FALSE;
+  **slot = entry;
+  return TRUE;
+}
+
+static bfd_boolean
+riscv_record_pcrel_lo_reloc (riscv_pcrel_relocs *p,
+			     asection *input_section,
+			     struct bfd_link_info *info,
+			     reloc_howto_type *howto,
+			     const Elf_Internal_Rela *reloc,
+			     bfd_vma addr,
+			     const char *name,
+			     bfd_byte *contents)
+{
+  riscv_pcrel_lo_reloc *entry;
+  entry = (riscv_pcrel_lo_reloc *) bfd_malloc (sizeof (riscv_pcrel_lo_reloc));
+  if (entry == NULL)
+    return FALSE;
+  *entry = (riscv_pcrel_lo_reloc) {input_section, info, howto, reloc, addr,
+				   name, contents, p->lo_relocs};
+  p->lo_relocs = entry;
+  return TRUE;
+}
+
+static bfd_boolean
+riscv_resolve_pcrel_lo_relocs (riscv_pcrel_relocs *p)
+{
+  riscv_pcrel_lo_reloc *r;
+
+  for (r = p->lo_relocs; r != NULL; r = r->next)
+    {
+      bfd *input_bfd = r->input_section->owner;
+
+      riscv_pcrel_hi_reloc search = {r->addr, 0};
+      riscv_pcrel_hi_reloc *entry = htab_find (p->hi_relocs, &search);
+      if (entry == NULL)
+        {
+	  ((*r->info->callbacks->reloc_overflow)
+	   (r->info, NULL, r->name, r->howto->name, (bfd_vma) 0,
+	    input_bfd, r->input_section, r->reloc->r_offset));
+	  return TRUE;
+        }
+
+      perform_relocation (r->howto, r->reloc, entry->value, r->input_section,
+			  input_bfd, r->contents);
+    }
+
+  return TRUE;
+}
+
+/* Relocate a RISC-V ELF section.
+
+   The RELOCATE_SECTION function is called by the new ELF backend linker
+   to handle the relocations for a section.
+
+   The relocs are always passed as Rela structures.
+
+   This function is responsible for adjusting the section contents as
+   necessary, and (if generating a relocatable output file) adjusting
+   the reloc addend as necessary.
+
+   This function does not have to worry about setting the reloc
+   address or the reloc symbol index.
+
+   LOCAL_SYMS is a pointer to the swapped in local symbols.
+
+   LOCAL_SECTIONS is an array giving the section in the input file
+   corresponding to the st_shndx field of each local symbol.
+
+   The global hash table entry for the global symbols can be found
+   via elf_sym_hashes (input_bfd).
+
+   When generating relocatable output, this function must handle
+   STB_LOCAL/STT_SECTION symbols specially.  The output symbol is
+   going to be the section symbol corresponding to the output
+   section, which means that the addend must be adjusted
+   accordingly.  */
+
+static bfd_boolean
+riscv_elf_relocate_section (bfd *output_bfd,
+			    struct bfd_link_info *info,
+			    bfd *input_bfd,
+			    asection *input_section,
+			    bfd_byte *contents,
+			    Elf_Internal_Rela *relocs,
+			    Elf_Internal_Sym *local_syms,
+			    asection **local_sections)
+{
+  Elf_Internal_Rela *rel;
+  Elf_Internal_Rela *relend;
+  riscv_pcrel_relocs pcrel_relocs;
+  bfd_boolean ret = FALSE;
+  asection *sreloc = elf_section_data (input_section)->sreloc;
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+  Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (input_bfd);
+  struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (input_bfd);
+  bfd_vma *local_got_offsets = elf_local_got_offsets (input_bfd);
+
+  if (!riscv_init_pcrel_relocs (&pcrel_relocs))
+    return FALSE;
+
+  relend = relocs + input_section->reloc_count;
+  for (rel = relocs; rel < relend; rel++)
+    {
+      unsigned long r_symndx;
+      struct elf_link_hash_entry *h;
+      Elf_Internal_Sym *sym;
+      asection *sec;
+      bfd_vma relocation;
+      bfd_reloc_status_type r = bfd_reloc_ok;
+      const char *name;
+      bfd_vma off, ie_off;
+      bfd_boolean unresolved_reloc, is_ie = FALSE;
+      bfd_vma pc = sec_addr (input_section) + rel->r_offset;
+      int r_type = ELFNN_R_TYPE (rel->r_info), tls_type;
+      reloc_howto_type *howto = riscv_elf_rtype_to_howto (r_type);
+      const char *msg = NULL;
+
+      if (r_type == R_RISCV_GNU_VTINHERIT || r_type == R_RISCV_GNU_VTENTRY)
+	continue;
+
+      /* This is a final link.  */
+      r_symndx = ELFNN_R_SYM (rel->r_info);
+      h = NULL;
+      sym = NULL;
+      sec = NULL;
+      unresolved_reloc = FALSE;
+      if (r_symndx < symtab_hdr->sh_info)
+	{
+	  sym = local_syms + r_symndx;
+	  sec = local_sections[r_symndx];
+	  relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
+	}
+      else
+	{
+	  bfd_boolean warned, ignored;
+
+	  RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
+				   r_symndx, symtab_hdr, sym_hashes,
+				   h, sec, relocation,
+				   unresolved_reloc, warned, ignored);
+	  if (warned)
+	    {
+	      /* To avoid generating warning messages about truncated
+		 relocations, set the relocation's address to be the same as
+		 the start of this section.  */
+	      if (input_section->output_section != NULL)
+		relocation = input_section->output_section->vma;
+	      else
+		relocation = 0;
+	    }
+	}
+
+      if (sec != NULL && discarded_section (sec))
+	RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
+					 rel, 1, relend, howto, 0, contents);
+
+      if (bfd_link_relocatable (info))
+	continue;
+
+      if (h != NULL)
+	name = h->root.root.string;
+      else
+	{
+	  name = (bfd_elf_string_from_elf_section
+		  (input_bfd, symtab_hdr->sh_link, sym->st_name));
+	  if (name == NULL || *name == '\0')
+	    name = bfd_section_name (input_bfd, sec);
+	}
+
+      switch (r_type)
+	{
+	case R_RISCV_NONE:
+	case R_RISCV_TPREL_ADD:
+	case R_RISCV_COPY:
+	case R_RISCV_JUMP_SLOT:
+	case R_RISCV_RELATIVE:
+	  /* These require nothing of us at all.  */
+	  continue;
+
+	case R_RISCV_HI20:
+	case R_RISCV_BRANCH:
+	case R_RISCV_RVC_BRANCH:
+	case R_RISCV_RVC_LUI:
+	case R_RISCV_LO12_I:
+	case R_RISCV_LO12_S:
+	  /* These require no special handling beyond perform_relocation.  */
+	  break;
+
+	case R_RISCV_GOT_HI20:
+	  if (h != NULL)
+	    {
+	      bfd_boolean dyn, pic;
+
+	      off = h->got.offset;
+	      BFD_ASSERT (off != (bfd_vma) -1);
+	      dyn = elf_hash_table (info)->dynamic_sections_created;
+	      pic = bfd_link_pic (info);
+
+	      if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, pic, h)
+		  || (pic && SYMBOL_REFERENCES_LOCAL (info, h)))
+		{
+		  /* This is actually a static link, or it is a
+		     -Bsymbolic link and the symbol is defined
+		     locally, or the symbol was forced to be local
+		     because of a version file.  We must initialize
+		     this entry in the global offset table.  Since the
+		     offset must always be a multiple of the word size,
+		     we use the least significant bit to record whether
+		     we have initialized it already.
+
+		     When doing a dynamic link, we create a .rela.got
+		     relocation entry to initialize the value.  This
+		     is done in the finish_dynamic_symbol routine.  */
+		  if ((off & 1) != 0)
+		    off &= ~1;
+		  else
+		    {
+		      bfd_put_NN (output_bfd, relocation,
+				  htab->elf.sgot->contents + off);
+		      h->got.offset |= 1;
+		    }
+		}
+	      else
+		unresolved_reloc = FALSE;
+	    }
+	  else
+	    {
+	      BFD_ASSERT (local_got_offsets != NULL
+			  && local_got_offsets[r_symndx] != (bfd_vma) -1);
+
+	      off = local_got_offsets[r_symndx];
+
+	      /* The offset must always be a multiple of the word size.
+		 So, we can use the least significant bit to record
+		 whether we have already processed this entry.  */
+	      if ((off & 1) != 0)
+		off &= ~1;
+	      else
+		{
+		  if (bfd_link_pic (info))
+		    {
+		      asection *s;
+		      Elf_Internal_Rela outrel;
+
+		      /* We need to generate a R_RISCV_RELATIVE reloc
+			 for the dynamic linker.  */
+		      s = htab->elf.srelgot;
+		      BFD_ASSERT (s != NULL);
+
+		      outrel.r_offset = sec_addr (htab->elf.sgot) + off;
+		      outrel.r_info =
+			ELFNN_R_INFO (0, R_RISCV_RELATIVE);
+		      outrel.r_addend = relocation;
+		      relocation = 0;
+		      riscv_elf_append_rela (output_bfd, s, &outrel);
+		    }
+
+		  bfd_put_NN (output_bfd, relocation,
+			      htab->elf.sgot->contents + off);
+		  local_got_offsets[r_symndx] |= 1;
+		}
+	    }
+	  relocation = sec_addr (htab->elf.sgot) + off;
+	  if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc, relocation))
+	    r = bfd_reloc_overflow;
+	  break;
+
+	case R_RISCV_ADD8:
+	case R_RISCV_ADD16:
+	case R_RISCV_ADD32:
+	case R_RISCV_ADD64:
+	  {
+	    bfd_vma old_value = bfd_get (howto->bitsize, input_bfd,
+					 contents + rel->r_offset);
+	    relocation = old_value + relocation;
+	  }
+	  break;
+
+	case R_RISCV_SUB8:
+	case R_RISCV_SUB16:
+	case R_RISCV_SUB32:
+	case R_RISCV_SUB64:
+	  {
+	    bfd_vma old_value = bfd_get (howto->bitsize, input_bfd,
+					 contents + rel->r_offset);
+	    relocation = old_value - relocation;
+	  }
+	  break;
+
+	case R_RISCV_CALL_PLT:
+	case R_RISCV_CALL:
+	case R_RISCV_JAL:
+	case R_RISCV_RVC_JUMP:
+	  if (bfd_link_pic (info) && h != NULL && h->plt.offset != MINUS_ONE)
+	    {
+	      /* Refer to the PLT entry.  */
+	      relocation = sec_addr (htab->elf.splt) + h->plt.offset;
+	      unresolved_reloc = FALSE;
+	    }
+	  break;
+
+	case R_RISCV_TPREL_HI20:
+	  relocation = tpoff (info, relocation);
+	  break;
+
+	case R_RISCV_TPREL_LO12_I:
+	case R_RISCV_TPREL_LO12_S:
+	  relocation = tpoff (info, relocation);
+	  if (VALID_ITYPE_IMM (relocation + rel->r_addend))
+	    {
+	      /* We can use tp as the base register.  */
+	      bfd_vma insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+	      insn &= ~(OP_MASK_RS1 << OP_SH_RS1);
+	      insn |= X_TP << OP_SH_RS1;
+	      bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+	    }
+	  break;
+
+	case R_RISCV_GPREL_I:
+	case R_RISCV_GPREL_S:
+	  {
+	    bfd_vma gp = riscv_global_pointer_value (info);
+	    bfd_boolean x0_base = VALID_ITYPE_IMM (relocation + rel->r_addend);
+	    if (x0_base || VALID_ITYPE_IMM (relocation + rel->r_addend - gp))
+	      {
+		/* We can use x0 or gp as the base register.  */
+		bfd_vma insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+		insn &= ~(OP_MASK_RS1 << OP_SH_RS1);
+		if (!x0_base)
+		  {
+		    rel->r_addend -= gp;
+		    insn |= X_GP << OP_SH_RS1;
+		  }
+		bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+	      }
+	    else
+	      r = bfd_reloc_overflow;
+	    break;
+	  }
+
+	case R_RISCV_PCREL_HI20:
+	  if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc,
+					    relocation + rel->r_addend))
+	    r = bfd_reloc_overflow;
+	  break;
+
+	case R_RISCV_PCREL_LO12_I:
+	case R_RISCV_PCREL_LO12_S:
+	  if (riscv_record_pcrel_lo_reloc (&pcrel_relocs, input_section, info,
+					   howto, rel, relocation, name,
+					   contents))
+	    continue;
+	  r = bfd_reloc_overflow;
+	  break;
+
+	case R_RISCV_TLS_DTPREL32:
+	case R_RISCV_TLS_DTPREL64:
+	  relocation = dtpoff (info, relocation);
+	  break;
+
+	case R_RISCV_32:
+	case R_RISCV_64:
+	  if ((input_section->flags & SEC_ALLOC) == 0)
+	    break;
+
+	  if ((bfd_link_pic (info)
+	       && (h == NULL
+		   || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+		   || h->root.type != bfd_link_hash_undefweak)
+	       && (! howto->pc_relative
+		   || !SYMBOL_CALLS_LOCAL (info, h)))
+	      || (!bfd_link_pic (info)
+		  && h != NULL
+		  && h->dynindx != -1
+		  && !h->non_got_ref
+		  && ((h->def_dynamic
+		       && !h->def_regular)
+		      || h->root.type == bfd_link_hash_undefweak
+		      || h->root.type == bfd_link_hash_undefined)))
+	    {
+	      Elf_Internal_Rela outrel;
+	      bfd_boolean skip_static_relocation, skip_dynamic_relocation;
+
+	      /* When generating a shared object, these relocations
+		 are copied into the output file to be resolved at run
+		 time.  */
+
+	      outrel.r_offset =
+		_bfd_elf_section_offset (output_bfd, info, input_section,
+					 rel->r_offset);
+	      skip_static_relocation = outrel.r_offset != (bfd_vma) -2;
+	      skip_dynamic_relocation = outrel.r_offset >= (bfd_vma) -2;
+	      outrel.r_offset += sec_addr (input_section);
+
+	      if (skip_dynamic_relocation)
+		memset (&outrel, 0, sizeof outrel);
+	      else if (h != NULL && h->dynindx != -1
+		       && !(bfd_link_pic (info)
+			    && SYMBOLIC_BIND (info, h)
+			    && h->def_regular))
+		{
+		  outrel.r_info = ELFNN_R_INFO (h->dynindx, r_type);
+		  outrel.r_addend = rel->r_addend;
+		}
+	      else
+		{
+		  outrel.r_info = ELFNN_R_INFO (0, R_RISCV_RELATIVE);
+		  outrel.r_addend = relocation + rel->r_addend;
+		}
+
+	      riscv_elf_append_rela (output_bfd, sreloc, &outrel);
+	      if (skip_static_relocation)
+		continue;
+	    }
+	  break;
+
+	case R_RISCV_TLS_GOT_HI20:
+	  is_ie = TRUE;
+	  /* Fall through.  */
+
+	case R_RISCV_TLS_GD_HI20:
+	  if (h != NULL)
+	    {
+	      off = h->got.offset;
+	      h->got.offset |= 1;
+	    }
+	  else
+	    {
+	      off = local_got_offsets[r_symndx];
+	      local_got_offsets[r_symndx] |= 1;
+	    }
+
+	  tls_type = _bfd_riscv_elf_tls_type (input_bfd, h, r_symndx);
+	  BFD_ASSERT (tls_type & (GOT_TLS_IE | GOT_TLS_GD));
+	  /* If this symbol is referenced by both GD and IE TLS, the IE
+	     reference's GOT slot follows the GD reference's slots.  */
+	  ie_off = 0;
+	  if ((tls_type & GOT_TLS_GD) && (tls_type & GOT_TLS_IE))
+	    ie_off = 2 * GOT_ENTRY_SIZE;
+
+	  if ((off & 1) != 0)
+	    off &= ~1;
+	  else
+	    {
+	      Elf_Internal_Rela outrel;
+	      int indx = 0;
+	      bfd_boolean need_relocs = FALSE;
+
+	      if (htab->elf.srelgot == NULL)
+		abort ();
+
+	      if (h != NULL)
+		{
+		  bfd_boolean dyn, pic;
+		  dyn = htab->elf.dynamic_sections_created;
+		  pic = bfd_link_pic (info);
+
+		  if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, pic, h)
+		      && (!pic || !SYMBOL_REFERENCES_LOCAL (info, h)))
+		    indx = h->dynindx;
+		}
+
+	      /* The GOT entries have not been initialized yet.  Do it
+	         now, and emit any relocations.  */
+	      if ((bfd_link_pic (info) || indx != 0)
+		  && (h == NULL
+		      || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+		      || h->root.type != bfd_link_hash_undefweak))
+		    need_relocs = TRUE;
+
+	      if (tls_type & GOT_TLS_GD)
+		{
+		  if (need_relocs)
+		    {
+		      outrel.r_offset = sec_addr (htab->elf.sgot) + off;
+		      outrel.r_addend = 0;
+		      outrel.r_info = ELFNN_R_INFO (indx, R_RISCV_TLS_DTPMODNN);
+		      bfd_put_NN (output_bfd, 0,
+				  htab->elf.sgot->contents + off);
+		      riscv_elf_append_rela (output_bfd, htab->elf.srelgot, &outrel);
+		      if (indx == 0)
+			{
+			  BFD_ASSERT (! unresolved_reloc);
+			  bfd_put_NN (output_bfd,
+				      dtpoff (info, relocation),
+				      (htab->elf.sgot->contents + off +
+				       RISCV_ELF_WORD_BYTES));
+			}
+		      else
+			{
+			  bfd_put_NN (output_bfd, 0,
+				      (htab->elf.sgot->contents + off +
+				       RISCV_ELF_WORD_BYTES));
+			  outrel.r_info = ELFNN_R_INFO (indx, R_RISCV_TLS_DTPRELNN);
+			  outrel.r_offset += RISCV_ELF_WORD_BYTES;
+			  riscv_elf_append_rela (output_bfd, htab->elf.srelgot, &outrel);
+			}
+		    }
+		  else
+		    {
+		      /* If we are not emitting relocations for a
+			 general dynamic reference, then we must be in a
+			 static link or an executable link with the
+			 symbol binding locally.  Mark it as belonging
+			 to module 1, the executable.  */
+		      bfd_put_NN (output_bfd, 1,
+				  htab->elf.sgot->contents + off);
+		      bfd_put_NN (output_bfd,
+				  dtpoff (info, relocation),
+				  (htab->elf.sgot->contents + off +
+				   RISCV_ELF_WORD_BYTES));
+		   }
+		}
+
+	      if (tls_type & GOT_TLS_IE)
+		{
+		  if (need_relocs)
+		    {
+		      bfd_put_NN (output_bfd, 0,
+				  htab->elf.sgot->contents + off + ie_off);
+		      outrel.r_offset = sec_addr (htab->elf.sgot)
+				       + off + ie_off;
+		      outrel.r_addend = 0;
+		      if (indx == 0)
+			outrel.r_addend = tpoff (info, relocation);
+		      outrel.r_info = ELFNN_R_INFO (indx, R_RISCV_TLS_TPRELNN);
+		      riscv_elf_append_rela (output_bfd, htab->elf.srelgot, &outrel);
+		    }
+		  else
+		    {
+		      bfd_put_NN (output_bfd, tpoff (info, relocation),
+				  htab->elf.sgot->contents + off + ie_off);
+		    }
+		}
+	    }
+
+	  BFD_ASSERT (off < (bfd_vma) -2);
+	  relocation = sec_addr (htab->elf.sgot) + off + (is_ie ? ie_off : 0);
+	  if (!riscv_record_pcrel_hi_reloc (&pcrel_relocs, pc, relocation))
+	    r = bfd_reloc_overflow;
+	  unresolved_reloc = FALSE;
+	  break;
+
+	default:
+	  r = bfd_reloc_notsupported;
+	}
+
+      /* Dynamic relocs are not propagated for SEC_DEBUGGING sections
+	 because such sections are not SEC_ALLOC and thus ld.so will
+	 not process them.  */
+      if (unresolved_reloc
+	  && !((input_section->flags & SEC_DEBUGGING) != 0
+	       && h->def_dynamic)
+	  && _bfd_elf_section_offset (output_bfd, info, input_section,
+				      rel->r_offset) != (bfd_vma) -1)
+	{
+	  (*_bfd_error_handler)
+	    (_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"),
+	     input_bfd,
+	     input_section,
+	     (long) rel->r_offset,
+	     howto->name,
+	     h->root.root.string);
+	  continue;
+	}
+
+      if (r == bfd_reloc_ok)
+	r = perform_relocation (howto, rel, relocation, input_section,
+				input_bfd, contents);
+
+      switch (r)
+	{
+	case bfd_reloc_ok:
+	  continue;
+
+	case bfd_reloc_overflow:
+	  info->callbacks->reloc_overflow
+	    (info, (h ? &h->root : NULL), name, howto->name,
+	     (bfd_vma) 0, input_bfd, input_section, rel->r_offset);
+	  break;
+
+	case bfd_reloc_undefined:
+	  info->callbacks->undefined_symbol
+	    (info, name, input_bfd, input_section, rel->r_offset,
+	     TRUE);
+	  break;
+
+	case bfd_reloc_outofrange:
+	  msg = _("internal error: out of range error");
+	  break;
+
+	case bfd_reloc_notsupported:
+	  msg = _("internal error: unsupported relocation error");
+	  break;
+
+	case bfd_reloc_dangerous:
+	  msg = _("internal error: dangerous relocation");
+	  break;
+
+	default:
+	  msg = _("internal error: unknown error");
+	  break;
+	}
+
+      if (msg)
+	info->callbacks->warning
+	  (info, msg, name, input_bfd, input_section, rel->r_offset);
+      goto out;
+    }
+
+  ret = riscv_resolve_pcrel_lo_relocs (&pcrel_relocs);
+out:
+  riscv_free_pcrel_relocs (&pcrel_relocs);
+  return ret;
+}
+
+/* Finish up dynamic symbol handling.  We set the contents of various
+   dynamic sections here.  */
+
+static bfd_boolean
+riscv_elf_finish_dynamic_symbol (bfd *output_bfd,
+				 struct bfd_link_info *info,
+				 struct elf_link_hash_entry *h,
+				 Elf_Internal_Sym *sym)
+{
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+  const struct elf_backend_data *bed = get_elf_backend_data (output_bfd);
+
+  if (h->plt.offset != (bfd_vma) -1)
+    {
+      /* We've decided to create a PLT entry for this symbol.  */
+      bfd_byte *loc;
+      bfd_vma i, header_address, plt_idx, got_address;
+      uint32_t plt_entry[PLT_ENTRY_INSNS];
+      Elf_Internal_Rela rela;
+
+      BFD_ASSERT (h->dynindx != -1);
+
+      /* Calculate the address of the PLT header.  */
+      header_address = sec_addr (htab->elf.splt);
+
+      /* Calculate the index of the entry.  */
+      plt_idx = (h->plt.offset - PLT_HEADER_SIZE) / PLT_ENTRY_SIZE;
+
+      /* Calculate the address of the .got.plt entry.  */
+      got_address = riscv_elf_got_plt_val (plt_idx, info);
+
+      /* Find out where the .plt entry should go.  */
+      loc = htab->elf.splt->contents + h->plt.offset;
+
+      /* Fill in the PLT entry itself.  */
+      riscv_make_plt_entry (got_address, header_address + h->plt.offset,
+			    plt_entry);
+      for (i = 0; i < PLT_ENTRY_INSNS; i++)
+	bfd_put_32 (output_bfd, plt_entry[i], loc + 4*i);
+
+      /* Fill in the initial value of the .got.plt entry.  */
+      loc = htab->elf.sgotplt->contents
+	    + (got_address - sec_addr (htab->elf.sgotplt));
+      bfd_put_NN (output_bfd, sec_addr (htab->elf.splt), loc);
+
+      /* Fill in the entry in the .rela.plt section.  */
+      rela.r_offset = got_address;
+      rela.r_addend = 0;
+      rela.r_info = ELFNN_R_INFO (h->dynindx, R_RISCV_JUMP_SLOT);
+
+      loc = htab->elf.srelplt->contents + plt_idx * sizeof (ElfNN_External_Rela);
+      bed->s->swap_reloca_out (output_bfd, &rela, loc);
+
+      if (!h->def_regular)
+	{
+	  /* Mark the symbol as undefined, rather than as defined in
+	     the .plt section.  Leave the value alone.  */
+	  sym->st_shndx = SHN_UNDEF;
+	  /* If the symbol is weak, we do need to clear the value.
+	     Otherwise, the PLT entry would provide a definition for
+	     the symbol even if the symbol wasn't defined anywhere,
+	     and so the symbol would never be NULL.  */
+	  if (!h->ref_regular_nonweak)
+	    sym->st_value = 0;
+	}
+    }
+
+  if (h->got.offset != (bfd_vma) -1
+      && !(riscv_elf_hash_entry(h)->tls_type & (GOT_TLS_GD | GOT_TLS_IE)))
+    {
+      asection *sgot;
+      asection *srela;
+      Elf_Internal_Rela rela;
+
+      /* This symbol has an entry in the GOT.  Set it up.  */
+
+      sgot = htab->elf.sgot;
+      srela = htab->elf.srelgot;
+      BFD_ASSERT (sgot != NULL && srela != NULL);
+
+      rela.r_offset = sec_addr (sgot) + (h->got.offset &~ (bfd_vma) 1);
+
+      /* If this is a -Bsymbolic link, and the symbol is defined
+	 locally, we just want to emit a RELATIVE reloc.  Likewise if
+	 the symbol was forced to be local because of a version file.
+	 The entry in the global offset table will already have been
+	 initialized in the relocate_section function.  */
+      if (bfd_link_pic (info)
+	  && (info->symbolic || h->dynindx == -1)
+	  && h->def_regular)
+	{
+	  asection *sec = h->root.u.def.section;
+	  rela.r_info = ELFNN_R_INFO (0, R_RISCV_RELATIVE);
+	  rela.r_addend = (h->root.u.def.value
+			   + sec->output_section->vma
+			   + sec->output_offset);
+	}
+      else
+	{
+	  BFD_ASSERT (h->dynindx != -1);
+	  rela.r_info = ELFNN_R_INFO (h->dynindx, R_RISCV_NN);
+	  rela.r_addend = 0;
+	}
+
+      bfd_put_NN (output_bfd, 0,
+		  sgot->contents + (h->got.offset & ~(bfd_vma) 1));
+      riscv_elf_append_rela (output_bfd, srela, &rela);
+    }
+
+  if (h->needs_copy)
+    {
+      Elf_Internal_Rela rela;
+
+      /* This symbols needs a copy reloc.  Set it up.  */
+      BFD_ASSERT (h->dynindx != -1);
+
+      rela.r_offset = sec_addr (h->root.u.def.section) + h->root.u.def.value;
+      rela.r_info = ELFNN_R_INFO (h->dynindx, R_RISCV_COPY);
+      rela.r_addend = 0;
+      riscv_elf_append_rela (output_bfd, htab->srelbss, &rela);
+    }
+
+  /* Mark some specially defined symbols as absolute.  */
+  if (h == htab->elf.hdynamic
+      || (h == htab->elf.hgot || h == htab->elf.hplt))
+    sym->st_shndx = SHN_ABS;
+
+  return TRUE;
+}
+
+/* Finish up the dynamic sections.  */
+
+static bfd_boolean
+riscv_finish_dyn (bfd *output_bfd, struct bfd_link_info *info,
+		  bfd *dynobj, asection *sdyn)
+{
+  struct riscv_elf_link_hash_table *htab = riscv_elf_hash_table (info);
+  const struct elf_backend_data *bed = get_elf_backend_data (output_bfd);
+  size_t dynsize = bed->s->sizeof_dyn;
+  bfd_byte *dyncon, *dynconend;
+
+  dynconend = sdyn->contents + sdyn->size;
+  for (dyncon = sdyn->contents; dyncon < dynconend; dyncon += dynsize)
+    {
+      Elf_Internal_Dyn dyn;
+      asection *s;
+
+      bed->s->swap_dyn_in (dynobj, dyncon, &dyn);
+
+      switch (dyn.d_tag)
+	{
+	case DT_PLTGOT:
+	  s = htab->elf.sgotplt;
+	  dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
+	  break;
+	case DT_JMPREL:
+	  s = htab->elf.srelplt;
+	  dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
+	  break;
+	case DT_PLTRELSZ:
+	  s = htab->elf.srelplt;
+	  dyn.d_un.d_val = s->size;
+	  break;
+	default:
+	  continue;
+	}
+
+      bed->s->swap_dyn_out (output_bfd, &dyn, dyncon);
+    }
+  return TRUE;
+}
+
+static bfd_boolean
+riscv_elf_finish_dynamic_sections (bfd *output_bfd,
+				   struct bfd_link_info *info)
+{
+  bfd *dynobj;
+  asection *sdyn;
+  struct riscv_elf_link_hash_table *htab;
+
+  htab = riscv_elf_hash_table (info);
+  BFD_ASSERT (htab != NULL);
+  dynobj = htab->elf.dynobj;
+
+  sdyn = bfd_get_linker_section (dynobj, ".dynamic");
+
+  if (elf_hash_table (info)->dynamic_sections_created)
+    {
+      asection *splt;
+      bfd_boolean ret;
+
+      splt = htab->elf.splt;
+      BFD_ASSERT (splt != NULL && sdyn != NULL);
+
+      ret = riscv_finish_dyn (output_bfd, info, dynobj, sdyn);
+
+      if (ret != TRUE)
+	return ret;
+
+      /* Fill in the head and tail entries in the procedure linkage table.  */
+      if (splt->size > 0)
+	{
+	  int i;
+	  uint32_t plt_header[PLT_HEADER_INSNS];
+	  riscv_make_plt_header (sec_addr (htab->elf.sgotplt),
+				 sec_addr (splt), plt_header);
+
+	  for (i = 0; i < PLT_HEADER_INSNS; i++)
+	    bfd_put_32 (output_bfd, plt_header[i], splt->contents + 4*i);
+	}
+
+      elf_section_data (splt->output_section)->this_hdr.sh_entsize
+	= PLT_ENTRY_SIZE;
+    }
+
+  if (htab->elf.sgotplt)
+    {
+      asection *output_section = htab->elf.sgotplt->output_section;
+
+      if (bfd_is_abs_section (output_section))
+	{
+	  (*_bfd_error_handler)
+	    (_("discarded output section: `%A'"), htab->elf.sgotplt);
+	  return FALSE;
+	}
+
+      if (htab->elf.sgotplt->size > 0)
+	{
+	  /* Write the first two entries in .got.plt, needed for the dynamic
+	     linker.  */
+	  bfd_put_NN (output_bfd, (bfd_vma) -1, htab->elf.sgotplt->contents);
+	  bfd_put_NN (output_bfd, (bfd_vma) 0,
+		      htab->elf.sgotplt->contents + GOT_ENTRY_SIZE);
+	}
+
+      elf_section_data (output_section)->this_hdr.sh_entsize = GOT_ENTRY_SIZE;
+    }
+
+  if (htab->elf.sgot)
+    {
+      asection *output_section = htab->elf.sgot->output_section;
+
+      if (htab->elf.sgot->size > 0)
+	{
+	  /* Set the first entry in the global offset table to the address of
+	     the dynamic section.  */
+	  bfd_vma val = sdyn ? sec_addr (sdyn) : 0;
+	  bfd_put_NN (output_bfd, val, htab->elf.sgot->contents);
+	}
+
+      elf_section_data (output_section)->this_hdr.sh_entsize = GOT_ENTRY_SIZE;
+    }
+
+  return TRUE;
+}
+
+/* Return address for Ith PLT stub in section PLT, for relocation REL
+   or (bfd_vma) -1 if it should not be included.  */
+
+static bfd_vma
+riscv_elf_plt_sym_val (bfd_vma i, const asection *plt,
+		       const arelent *rel ATTRIBUTE_UNUSED)
+{
+  return plt->vma + PLT_HEADER_SIZE + i * PLT_ENTRY_SIZE;
+}
+
+static enum elf_reloc_type_class
+riscv_reloc_type_class (const struct bfd_link_info *info ATTRIBUTE_UNUSED,
+			const asection *rel_sec ATTRIBUTE_UNUSED,
+			const Elf_Internal_Rela *rela)
+{
+  switch (ELFNN_R_TYPE (rela->r_info))
+    {
+    case R_RISCV_RELATIVE:
+      return reloc_class_relative;
+    case R_RISCV_JUMP_SLOT:
+      return reloc_class_plt;
+    case R_RISCV_COPY:
+      return reloc_class_copy;
+    default:
+      return reloc_class_normal;
+    }
+}
+
+/* Merge backend specific data from an object file to the output
+   object file when linking.  */
+
+static bfd_boolean
+_bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
+{
+  bfd *obfd = info->output_bfd;
+  flagword new_flags = elf_elfheader (ibfd)->e_flags;
+  flagword old_flags = elf_elfheader (obfd)->e_flags;
+
+  if (!is_riscv_elf (ibfd) || !is_riscv_elf (obfd))
+    return TRUE;
+
+  if (strcmp (bfd_get_target (ibfd), bfd_get_target (obfd)) != 0)
+    {
+      (*_bfd_error_handler)
+	(_("%B: ABI is incompatible with that of the selected emulation"),
+	 ibfd);
+      return FALSE;
+    }
+
+  if (!_bfd_elf_merge_object_attributes (ibfd, info))
+    return FALSE;
+
+  if (! elf_flags_init (obfd))
+    {
+      elf_flags_init (obfd) = TRUE;
+      elf_elfheader (obfd)->e_flags = new_flags;
+      return TRUE;
+    }
+
+  /* Disallow linking soft-float and hard-float.  */
+  if ((old_flags ^ new_flags) & EF_RISCV_SOFT_FLOAT)
+    {
+      (*_bfd_error_handler)
+	(_("%B: can't link hard-float modules with soft-float modules"), ibfd);
+      goto fail;
+    }
+
+  /* Allow linking RVC and non-RVC, and keep the RVC flag.  */
+  elf_elfheader (obfd)->e_flags |= new_flags & EF_RISCV_RVC;
+
+  return TRUE;
+
+fail:
+  bfd_set_error (bfd_error_bad_value);
+  return FALSE;
+}
+
+/* Delete some bytes from a section while relaxing.  */
+
+static bfd_boolean
+riscv_relax_delete_bytes (bfd *abfd, asection *sec, bfd_vma addr, size_t count)
+{
+  unsigned int i, symcount;
+  bfd_vma toaddr = sec->size;
+  struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (abfd);
+  Elf_Internal_Shdr *symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+  unsigned int sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec);
+  struct bfd_elf_section_data *data = elf_section_data (sec);
+  bfd_byte *contents = data->this_hdr.contents;
+
+  /* Actually delete the bytes.  */
+  sec->size -= count;
+  memmove (contents + addr, contents + addr + count, toaddr - addr - count);
+
+  /* Adjust the location of all of the relocs.  Note that we need not
+     adjust the addends, since all PC-relative references must be against
+     symbols, which we will adjust below.  */
+  for (i = 0; i < sec->reloc_count; i++)
+    if (data->relocs[i].r_offset > addr && data->relocs[i].r_offset < toaddr)
+      data->relocs[i].r_offset -= count;
+
+  /* Adjust the local symbols defined in this section.  */
+  for (i = 0; i < symtab_hdr->sh_info; i++)
+    {
+      Elf_Internal_Sym *sym = (Elf_Internal_Sym *) symtab_hdr->contents + i;
+      if (sym->st_shndx == sec_shndx)
+	{
+	  /* If the symbol is in the range of memory we just moved, we
+	     have to adjust its value.  */
+	  if (sym->st_value > addr && sym->st_value <= toaddr)
+	    sym->st_value -= count;
+
+	  /* If the symbol *spans* the bytes we just deleted (i.e. its
+	     *end* is in the moved bytes but its *start* isn't), then we
+	     must adjust its size.  */
+	  if (sym->st_value <= addr
+	      && sym->st_value + sym->st_size > addr
+	      && sym->st_value + sym->st_size <= toaddr)
+	    sym->st_size -= count;
+	}
+    }
+
+  /* Now adjust the global symbols defined in this section.  */
+  symcount = ((symtab_hdr->sh_size / sizeof (ElfNN_External_Sym))
+	      - symtab_hdr->sh_info);
+
+  for (i = 0; i < symcount; i++)
+    {
+      struct elf_link_hash_entry *sym_hash = sym_hashes[i];
+
+      if ((sym_hash->root.type == bfd_link_hash_defined
+	   || sym_hash->root.type == bfd_link_hash_defweak)
+	  && sym_hash->root.u.def.section == sec)
+	{
+	  /* As above, adjust the value if needed.  */
+	  if (sym_hash->root.u.def.value > addr
+	      && sym_hash->root.u.def.value <= toaddr)
+	    sym_hash->root.u.def.value -= count;
+
+	  /* As above, adjust the size if needed.  */
+	  if (sym_hash->root.u.def.value <= addr
+	      && sym_hash->root.u.def.value + sym_hash->size > addr
+	      && sym_hash->root.u.def.value + sym_hash->size <= toaddr)
+	    sym_hash->size -= count;
+	}
+    }
+
+  return TRUE;
+}
+
+/* Relax AUIPC + JALR into JAL.  */
+
+static bfd_boolean
+_bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec,
+		       struct bfd_link_info *link_info,
+		       Elf_Internal_Rela *rel,
+		       bfd_vma symval,
+		       unsigned int max_alignment,
+		       bfd_boolean *again)
+{
+  bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
+  bfd_signed_vma foff = symval - (sec_addr (sec) + rel->r_offset);
+  bfd_boolean near_zero = (symval + RISCV_IMM_REACH/2) < RISCV_IMM_REACH;
+  bfd_vma auipc, jalr;
+  int rd, r_type, len = 4, rvc = elf_elfheader (abfd)->e_flags & EF_RISCV_RVC;
+
+  /* If the call crosses section boundaries, an alignment directive could
+     cause the PC-relative offset to later increase.  */
+  if (VALID_UJTYPE_IMM (foff) && sym_sec->output_section != sec->output_section)
+    foff += (foff < 0 ? -max_alignment : max_alignment);
+
+  /* See if this function call can be shortened.  */
+  if (!VALID_UJTYPE_IMM (foff) && !(!bfd_link_pic (link_info) && near_zero))
+    return TRUE;
+
+  /* Shorten the function call.  */
+  BFD_ASSERT (rel->r_offset + 8 <= sec->size);
+
+  auipc = bfd_get_32 (abfd, contents + rel->r_offset);
+  jalr = bfd_get_32 (abfd, contents + rel->r_offset + 4);
+  rd = (jalr >> OP_SH_RD) & OP_MASK_RD;
+  rvc = rvc && VALID_RVC_J_IMM (foff) && ARCH_SIZE == 32;
+
+  if (rvc && (rd == 0 || rd == X_RA))
+    {
+      /* Relax to C.J[AL] rd, addr.  */
+      r_type = R_RISCV_RVC_JUMP;
+      auipc = rd == 0 ? MATCH_C_J : MATCH_C_JAL;
+      len = 2;
+    }
+  else if (VALID_UJTYPE_IMM (foff))
+    {
+      /* Relax to JAL rd, addr.  */
+      r_type = R_RISCV_JAL;
+      auipc = MATCH_JAL | (rd << OP_SH_RD);
+    }
+  else /* near_zero */
+    {
+      /* Relax to JALR rd, x0, addr.  */
+      r_type = R_RISCV_LO12_I;
+      auipc = MATCH_JALR | (rd << OP_SH_RD);
+    }
+
+  /* Replace the R_RISCV_CALL reloc.  */
+  rel->r_info = ELFNN_R_INFO (ELFNN_R_SYM (rel->r_info), r_type);
+  /* Replace the AUIPC.  */
+  bfd_put (8 * len, abfd, auipc, contents + rel->r_offset);
+
+  /* Delete unnecessary JALR.  */
+  *again = TRUE;
+  return riscv_relax_delete_bytes (abfd, sec, rel->r_offset + len, 8 - l[...]

[diff truncated at 100000 bytes]



More information about the Binutils-cvs mailing list